1 /*
2 *class++
3 *  Name:
4 *     FitsChan
5 
6 *  Purpose:
7 *     I/O Channel using FITS header cards to represent Objects.
8 
9 *  Constructor Function:
10 c     astFitsChan
11 f     AST_FITSCHAN
12 
13 *  Description:
14 *     A FitsChan is a specialised form of Channel which supports I/O
15 *     operations involving the use of FITS (Flexible Image Transport
16 *     System) header cards. Writing an Object to a FitsChan (using
17 c     astWrite) will, if the Object is suitable, generate a
18 f     AST_WRITE) will, if the Object is suitable, generate a
19 *     description of that Object composed of FITS header cards, and
20 *     reading from a FitsChan will create a new Object from its FITS
21 *     header card description.
22 *
23 *     While a FitsChan is active, it represents a buffer which may
24 *     contain zero or more 80-character "header cards" conforming to
25 *     FITS conventions. Any sequence of FITS-conforming header cards
26 *     may be stored, apart from the "END" card whose existence is
27 *     merely implied.  The cards may be accessed in any order by using
28 *     the FitsChan's integer Card attribute, which identifies a "current"
29 *     card, to which subsequent operations apply. Searches
30 c     based on keyword may be performed (using astFindFits), new
31 c     cards may be inserted (astPutFits, astPutCards, astSetFits<X>) and
32 c     existing ones may be deleted (astDelFits), extracted (astGetFits<X>),
33 c     or changed (astSetFits<X>).
34 f     based on keyword may be performed (using AST_FINDFITS), new
35 f     cards may be inserted (AST_PUTFITS, AST_PUTCARDS, AST_SETFITS<X>) and
36 f     existing ones may be deleted (AST_DELFITS), extracted
37 f     (AST_GETFITS<X>) or changed (AST_SETFITS<X>).
38 *
39 *     When you create a FitsChan, you have the option of specifying
40 *     "source" and "sink" functions which connect it to external data
41 *     stores by reading and writing FITS header cards. If you provide
42 *     a source function, it is used to fill the FitsChan with header cards
43 *     when it is accessed for the first time. If you do not provide a
44 *     source function, the FitsChan remains empty until you explicitly enter
45 c     data into it (e.g. using astPutFits, astPutCards, astWrite
46 f     data into it (e.g. using AST_PUTFITS, AST_PUTCARDS, AST_WRITE
47 *     or by using the SourceFile attribute to specifying a text file from
48 *     which headers should be read). When the FitsChan is deleted, any
49 *     remaining header cards in the FitsChan can be saved in either of
50 *     two ways: 1) by specifying a value for the SinkFile attribute (the
51 *     name of a text file to which header cards should be written), or 2)
52 *     by providing a sink function (used to to deliver header cards to an
53 *     external data store). If you do not provide a sink function or a
54 *     value for SinkFile, any header cards remaining when the FitsChan
55 *     is deleted will be lost, so you should arrange to extract them
56 *     first if necessary
57 c     (e.g. using astFindFits or astRead).
58 f     (e.g. using AST_FINDFITS or AST_READ).
59 *
60 *     Coordinate system information may be described using FITS header
61 *     cards using several different conventions, termed
62 *     "encodings". When an AST Object is written to (or read from) a
63 *     FitsChan, the value of the FitsChan's Encoding attribute
64 *     determines how the Object is converted to (or from) a
65 *     description involving FITS header cards. In general, different
66 *     encodings will result in different sets of header cards to
67 *     describe the same Object. Examples of encodings include the DSS
68 *     encoding (based on conventions used by the STScI Digitised Sky
69 *     Survey data), the FITS-WCS encoding (based on a proposed FITS
70 *     standard) and the NATIVE encoding (a near loss-less way of
71 *     storing AST Objects in FITS headers).
72 *
73 *     The available encodings differ in the range of Objects they can
74 *     represent, in the number of Object descriptions that can coexist
75 *     in the same FitsChan, and in their accessibility to other
76 *     (external) astronomy applications (see the Encoding attribute
77 *     for details). Encodings are not necessarily mutually exclusive
78 *     and it may sometimes be possible to describe the same Object in
79 *     several ways within a particular set of FITS header cards by
80 *     using several different encodings.
81 *
82 c     The detailed behaviour of astRead and astWrite, when used with
83 f     The detailed behaviour of AST_READ and AST_WRITE, when used with
84 *     a FitsChan, depends on the encoding in use. In general, however,
85 c     all successful use of astRead is destructive, so that FITS header cards
86 f     all successful use of AST_READ is destructive, so that FITS header cards
87 *     are consumed in the process of reading an Object, and are
88 *     removed from the FitsChan (this deletion can be prevented for
89 *     specific cards by calling the
90 c     astRetainFits function).
91 f     AST_RETAINFITS routine).
92 *     An unsuccessful call of
93 c     astRead
94 f     AST_READ
95 *     (for instance, caused by the FitsChan not containing the necessary
96 *     FITS headers cards needed to create an Object) results in the
97 *     contents of the FitsChan being left unchanged.
98 *
99 *     If the encoding in use allows only a single Object description
100 *     to be stored in a FitsChan (e.g. the DSS, FITS-WCS and FITS-IRAF
101 c     encodings), then write operations using astWrite will
102 f     encodings), then write operations using AST_WRITE will
103 *     over-write any existing Object description using that
104 *     encoding. Otherwise (e.g. the NATIVE encoding), multiple Object
105 *     descriptions are written sequentially and may later be read
106 *     back in the same sequence.
107 
108 *  Inheritance:
109 *     The FitsChan class inherits from the Channel class.
110 
111 *  Attributes:
112 *     In addition to those attributes common to all Channels, every
113 
114 *     FitsChan also has the following attributes:
115 *
116 *     - AllWarnings: A list of the available conditions
117 *     - Card: Index of current FITS card in a FitsChan
118 *     - CardComm: The comment of the current FITS card in a FitsChan
119 *     - CardName: The keyword name of the current FITS card in a FitsChan
120 *     - CardType: The data type of the current FITS card in a FitsChan
121 *     - CarLin: Ignore spherical rotations on CAR projections?
122 *     - CDMatrix: Use a CD matrix instead of a PC matrix?
123 *     - Clean: Remove cards used whilst reading even if an error occurs?
124 *     - DefB1950: Use FK4 B1950 as default equatorial coordinates?
125 *     - Encoding: System for encoding Objects as FITS headers
126 *     - FitsAxisOrder: Sets the order of WCS axes within new FITS-WCS headers
127 *     - FitsDigits: Digits of precision for floating-point FITS values
128 *     - Iwc: Add a Frame describing Intermediate World Coords?
129 *     - Ncard: Number of FITS header cards in a FitsChan
130 *     - Nkey: Number of unique keywords in a FitsChan
131 *     - TabOK: Should the FITS "-TAB" algorithm be recognised?
132 *     - PolyTan: Use PVi_m keywords to define distorted TAN projection?
133 *     - Warnings: Produces warnings about selected conditions
134 
135 *  Functions:
136 c     In addition to those functions applicable to all Channels, the
137 c     following functions may also be applied to all FitsChans:
138 f     In addition to those routines applicable to all Channels, the
139 f     following routines may also be applied to all FitsChans:
140 *
141 c     - astDelFits: Delete the current FITS card in a FitsChan
142 c     - astEmptyFits: Delete all cards in a FitsChan
143 c     - astFindFits: Find a FITS card in a FitsChan by keyword
144 c     - astGetFits<X>: Get a keyword value from a FitsChan
145 c     - astGetTables: Retrieve any FitsTables from a FitsChan
146 c     - astPurgeWCS: Delete all WCS-related cards in a FitsChan
147 c     - astPutCards: Stores a set of FITS header card in a FitsChan
148 c     - astPutFits: Store a FITS header card in a FitsChan
149 c     - astPutTable: Store a single FitsTable in a FitsChan
150 c     - astPutTables: Store multiple FitsTables in a FitsChan
151 c     - astReadFits: Read cards in through the source function
152 c     - astRemoveTables: Remove one or more FitsTables from a FitsChan
153 c     - astRetainFits: Ensure current card is retained in a FitsChan
154 c     - astSetFits<X>: Store a new keyword value in a FitsChan
155 c     - astShowFits: Display the contents of a FitsChan on standard output
156 c     - astTableSource: Register a source function for FITS table access
157 c     - astTestFits: Test if a keyword has a defined value in a FitsChan
158 c     - astWriteFits: Write all cards out to the sink function
159 f     - AST_DELFITS: Delete the current FITS card in a FitsChan
160 f     - AST_EMPTYFITS: Delete all cards in a FitsChan
161 f     - AST_FINDFITS: Find a FITS card in a FitsChan by keyword
162 f     - AST_GETFITS<X>: Get a keyword value from a FitsChan
163 f     - AST_GETTABLES: Retrieve any FitsTables from a FitsChan
164 f     - AST_PURGEWCS: Delete all WCS-related cards in a FitsChan
165 f     - AST_PUTCARDS: Stores a set of FITS header card in a FitsChan
166 f     - AST_PUTFITS: Store a FITS header card in a FitsChan
167 f     - AST_PUTTABLE: Store a single FitsTables in a FitsChan
168 f     - AST_PUTTABLES: Store multiple FitsTables in a FitsChan
169 f     - AST_READFITS: Read cards in through the source function
170 f     - AST_REMOVETABLES: Remove one or more FitsTables from a FitsChan
171 f     - AST_RETAINFITS: Ensure current card is retained in a FitsChan
172 f     - AST_SETFITS<X>: Store a new keyword value in a FitsChan
173 c     - AST_SHOWFITS: Display the contents of a FitsChan on standard output
174 f     - AST_TABLESOURCE: Register a source function for FITS table access
175 f     - AST_TESTFITS: Test if a keyword has a defined value in a FitsChan
176 f     - AST_WRITEFITS: Write all cards out to the sink function
177 
178 *  Copyright:
179 *     Copyright (C) 1997-2006 Council for the Central Laboratory of the
180 *     Research Councils
181 *     Copyright (C) 2008-2011 Science & Technology Facilities Council.
182 *     All Rights Reserved.
183 
184 *  Licence:
185 *     This program is free software: you can redistribute it and/or
186 *     modify it under the terms of the GNU Lesser General Public
187 *     License as published by the Free Software Foundation, either
188 *     version 3 of the License, or (at your option) any later
189 *     version.
190 *
191 *     This program is distributed in the hope that it will be useful,
192 *     but WITHOUT ANY WARRANTY; without even the implied warranty of
193 *     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
194 *     GNU Lesser General Public License for more details.
195 *
196 *     You should have received a copy of the GNU Lesser General
197 *     License along with this program.  If not, see
198 *     <http://www.gnu.org/licenses/>.
199 
200 *  Authors:
201 *     DSB: David Berry (Starlink)
202 *     RFWS: R.F. Warren-Smith (Starlink, RAL)
203 *     TIMJ: Tim Jenness (JAC, Hawaii)
204 
205 *  History:
206 *     11-DEC-1996 (DSB):
207 *        Original version.
208 *     20-MAR-1997 (DSB):
209 *        Made keyword setting and getting functions protected instead of
210 *        public. Renamed public methods. Added Ncard attribute.
211 *     20-MAY-1997 (RFWS):
212 *        Tidied public prologues.
213 *     30-JUN-1997 (DSB):
214 *        Added support for reading post-2000 DATE-OBS strings. Reading DSS
215 *        or FITS-WCS objects now returns NULL unless the FitsChan is
216 *        positioned at the start-of-file prior to the read. Bug fixed
217 *        which caused Ncard to be returned too large by one. Removed
218 *        dependancy on hard-wired header and footer text in Native
219 *        FitsChans.
220 *     18-AUG-1997 (DSB):
221 *        Bug fixed in WcsNative which caused incorrect CRVAL values
222 *        to be used if the axes needed permuting. Values assigned to the
223 *        Projection attribute fo the SkyFrames created by astRead.
224 *     2-SEP-1997 (DSB):
225 *        Added the IRAF convention that EPOCH=0.0 really means EPOCH=1950.0
226 *        (the EPOCH keyword is deprecated in the new FITS-WCS conventions
227 *        and is taken always as a Besselian epoch).
228 *     19-SEP-1997 (DSB):
229 *        Corrected interpretation of the FITS CD matrix.
230 *     25-SEP-1997 (DSB):
231 *        o  Fix bug in LinearMap which caused it always to detect a linear
232 *        mapping. For instance, this allowed DssMaps to be erroneously
233 *        written out using FITS-WCS encoding with a CAR projection.
234 *        o  Assign a full textual description to SkyFrame's Projection
235 *        attribute instead of a 3 letter acronym.
236 *        o  If DATE-OBS >= 1999.0 then DATE-OBS is now written in new
237 *        Y2000 format. For DATE-OBS < 1999.0, the old format is written.
238 *        o  Add new attribute CDMatrix to determine whether PC or CD
239 *        matrices should be used when writing objects using FITS-WCS
240 *        encoding.
241 *        o  Modified the way floating point values are formatted to omit
242 *        unnecessary leading zeros from the exponent (i.e. E-5 instead of
243 *        E-05).
244 *        o  New-line characters at the end of supplied header cards are now
245 *        ignored.
246 *        o  Cater for EQUINOX specified as a string prefixed by B or J
247 *        rather than as a floating point value (some HST data does this).
248 *        o  Corrected SetValue so that it always inserts comment cards
249 *        rather than over-write existing comment cards. Previously,
250 *        writing a FrameSet to a DSS encoded FitsChan resulted in all
251 *        comments cards being stripped except for the last one.
252 *        o  Reading a FrameSet from a DSS-encoded FrameSet now only
253 *        removes the keywords actually required to construct the FrameSet.
254 *        Previously, all keywords were removed.
255 *        o  The EPOCH and EQUINOX keywords created when a FrameSet is
256 *        written to a DSS-encoded FitsChan are now determined from the
257 *        epoch and equinox of the current Frame, instead of from a copy
258 *        of the original FitsChan stored within the DssMap.
259 *        o  The Encoding and CDMatrix attributes, and keyword types are
260 *        now stored as strings externally instead of integers.
261 *     11-NOV-1997 (DSB):
262 *        o  Assume default of j2000 for DSS EQUINOX value.
263 *        o  Check for null object pointers in the interfaces for
264 *        virtual functions which execute even if an error has previously
265 *        occurred. Otherwise, a segmentation violation can occur when
266 *        trying to find the member function pointer.
267 *        o  Trailing spaces ignored in Encoding attribute.
268 *        o  Bugs fixed in FindWcs and SetValue which resulted in WCS cards
269 *        being written at the wrong place if the supplied FitsChan does not
270 *        contain any WCS keywords.
271 *        o  Default for CDMatrix (if no axis rotation keywords can be found)
272 *        changed to 2 (i.e. use "CDi_j" form keywords).
273 *        o  Write now leaves the current card unchanged if nothing is
274 *        written to the FitsChan.
275 *     17-NOV-1997 (RFWS):
276 *        Disabled use of CDmatrix. Fixed initialisation problems in
277 *        astLoadFitsChan.
278 *     24-NOV-1997 (DSB):
279 *        Replace references to error code AST__OPT with AST__RDERR.
280 *     28-NOV-1997 (DSB):
281 *        o  Function WcsValues modified to prevent it from changing the
282 *        current card. Previously, this could cause new cards to be
283 *        written to the wrong place in a FITS-WCS encoded FitsChan.
284 *        o  Description of argument "value" corrected in prologue of
285 *        function SetFits.
286 *        o  Argument "lastkey" removed from function SetValue since it
287 *        was never used (it was a relic from a previous method of
288 *        determining where to store new cards). Corresponding changes
289 *        have been made to all the functions which create "lastkey" values
290 *        or pass them on to SetValue (i.e DescWcs, WcsPrimary, WcsSecondary,
291 *        WriteWcs and WriteDss).
292 *     10-DEC-1997 (DSB):
293 *        Bug fixed which caused the initial character designating the system
294 *        within CTYPE value (eg E in ELON, G in GLON, etc) to be omitted.
295 *     1-JUN-1998 (DSB):
296 *        CDELT values of zero are now replaced by a small non-zero value
297 *        when creating the "pixel-to-relative physical" transformation
298 *        matrix. Previously, zero CDELT values could cause the matrix to
299 *        be non-invertable.
300 *     4-SEP-1998 (DSB):
301 *        - Indicate that SphMaps created by this class when using FITS-WCS
302 *        encoding all operate on the unit sphere. This aids simplification.
303 *        - Fix a bug in StoreFits which caused CD matrices to be indexed
304 *        incorrectly (sometimes causing floating exceptions) if they do not
305 *        describe a celestial longitude/latitude system.
306 *        - Changed astFindFits to ignore trailing spaces in the keyword
307 *        template.
308 *        - astSplit changed so that an error is not reported if a textual
309 *        keyword value ends before column 20.
310 *     7-OCT-1998 (DSB):
311 *        - Corrected test for linearity in LinearMap to include a factor
312 *        of the test vector length. Also LinearMap now uses a simplified
313 *        Mapping.
314 *     5-NOV-1998 (DSB):
315 *        Added FITS-IRAF encoding.
316 *     9-NOV-1998 (DSB):
317 *        - Corrected values of macros DSS_ENCODING and MAX_ENCODING.
318 *        - Corrected erroneous success indication in IrafStore.
319 *        - Included checks for bad values in function LinearMap.
320 *     17-NOV-1998 (DSB):
321 *        The Domain name GRID is now given to the Base Frame in any FrameSets
322 *        created by astRead when using FitsChans with DSS, FITS-WCS or
323 *        FITS-IRAF encodings.
324 *     18-DEC-1998 (DSB):
325 *        Check for "D" exponents in floating point keyword strings.
326 *     12-FEB-1998 (DSB):
327 *        Modified EncodeFloat to avoid exceeding the 20 character FITS
328 *        limit wherever possible if FitsDigits is positive.
329 *     10-MAY-1998 (DSB):
330 *        Bug fixed in astSplit which caused comments associated with string
331 *        keywords to be lost when storing the card in a FitsChan.
332 *     15-JUN-1999 (DSB):
333 *        Report an error if an unrecognised projection name is supplied.
334 *     9-DEC-1999 (DSB):
335 *        - Fixed bug in WcsNatPole which could result in longitude values
336 *        being out by 180 degrees for cylindrical projections such as CAR.
337 *        - Only report an "unrecognised projection" error for CTYPE values
338 *        which look like celestial longitude or latitude axes (i.e. if the
339 *        first 4 characters are "RA--", "DEC-", "xLON" or "xLAT", and the
340 *        fifth character is "-").
341 *        - Added function SpecTrans to translated keywords related to the
342 *        IRAF ZPX projection into keyword for the standard ZPN projection.
343 *        - Add ICRS as a valid value for the RADECSYS keyword. Since the
344 *        SkyFrame class does not yet support ICRS, an FK5 SkyFrame is
345 *        created if RADECSYS=ICRS.
346 *     16-DEC-1999 (DSB):
347 *        - Modified SpecTrans so that all keywords used to created a
348 *        standard WCS representation from a non-standard one are consumed
349 *        by the astRead operation.
350 *        - Changed the text of ASTWARN cards added to the FitsChan if an
351 *        IRAF ZPX projection is found to require unsupported corrections.
352 *        - Simplified the documentation describing the handling of the IRAF
353 *        ZPX projection.
354 *        - Fixed code which assumed that the 10 FITS-WCS projection
355 *        parameters were PROJP1 -> PROJP10. In fact they are PROJP0 -
356 *        PROJP9. This could cause projection parameter values to be
357 *        incorrectly numbered when they are written out upon deletion of
358 *        the FitsChan.
359 *     1-FEB-2000 (DSB):
360 *        Check that FITS_IRAF encoding is not being used before using a
361 *        PC matrix when reading WCS information from a header. This is
362 *        important if the header contains both PC and CD matrices.
363 *     8-FEB-2000 (DSB):
364 *        - Header cards are now only consumed by an astRead operation if the
365 *        operation succeeds (i.e. returns a non-null Object).
366 *        - The original FITS-WCS encoding has been renamed as FITS-PC (to
367 *        indicate the use of a PCiiijjj matrix), and a new FITS-WCS
368 *        encoding has been added.
369 *        - The disabled CDMatrix attribute has been removed.
370 *        - Bug in LinearMap corrected which prevented genuinely linear
371 *        Mappings from being judged to be linear. This bug was previously
372 *        fudged (so it now appears) by the introduction of the test vector
373 *        length factor (see History entry for 7-OCT-1998). This test
374 *        vector length scale factor has consequently now been removed.
375 *        - Added FITS-AIPS encoding.
376 *        - The critical keywords used to select default encoding have been
377 *        changed.
378 *        - Support for common flavours of IRAF TNX projections added.
379 *        - The algorithm used to find a WcsMap in the supplied FrameSet
380 *        has been improved so that compound Mappings which contain complex
381 *        mixtures of parallel and serial Mappings can be translated into
382 *        FITS-WCS encoding.
383 *        - Trailing white space in string keyword values is now retained
384 *        when using foreign encodings to enable correct concatenation where
385 *        a string has been split over several keywords. E.g. if 2 string
386 *        keywords contain a list of formatted numerical values (e.g. IRAF
387 *        WAT... keywords), and the 1st one ends "0.123 " and the next one
388 *        begins "1234.5 ", the trailing space at the end of the first keyword
389 *        is needed to prevent the two numbers being merged into "0.1231234.5".
390 *        Trailing spaces in native encodings is still protected by enclosing
391 *        the whole string in double quotes.
392 *        - The Channel methods WriteString and GetNextData can now save
393 *        and restore strings of arbitary length. This is done by storing
394 *        as much of the string as possible in the usual way, and then
395 *        storing any remaining characters in subsequent CONTINUE cards,
396 *        using the FITSIO conventions. This storage and retrieval of long
397 *        strings is only available for native encodings.
398 *     19-MAY-2000 (DSB):
399 *        Added attribute Warnings. Lowered DSS in the priority list
400 *        of encodings implemented by GetEncoding.
401 *     6-OCT-2000 (DSB):
402 *        Increased size of buffers used to store CTYPE values to take
403 *        account of the possiblity of lots of trailing spaces.
404 *     5-DEC-2000 (DSB):
405 *        Add support for the WCSNAME FITS keyword.
406 *     12-DEC-2000 (DSB):
407 *        Add a title to each physical, non-celestial coord Frame based on
408 *        its Domain name (if any).
409 *     3-APR-2001 (DSB):
410 *        -  Use an "unknown" celestial coordinate system, instead of a
411 *        Cartesian coordinate system, if the CTYPE keywords specify an
412 *        unknown celestial coordinate system.
413 *        -  Do not report an error if there are no CTYPE keywords in the
414 *        header (assume a unit mapping, like in La Palma FITS files).
415 *        -  Add a NoCTYPE warning condition.
416 *        -  Added AllWarnings attribute.
417 *        -  Ensure multiple copies of identical warnings are not produced.
418 *        -  Use the Object Ident attribute to store the identifier letter
419 *        associated with each Frame read from a secondary axis description,
420 *        so that they can be given the same letter when they are written
421 *        out to a new FITS file.
422 *     10-AUG-2001 (DSB):
423 *        - Corrected function value returned by SkySys to be 1 unless an
424 *        error occurs. This error resulted in CAR headers being produced
425 *        by astWrite with CRVAL and CD values till in radians rather than
426 *        degrees.
427 *        - Introduced SplitMap2 in order to guard against producing
428 *        celestial FITS headers for a Mapping which includes more than
429 *        one WcsMap.
430 *     13-AUG-2001 (DSB):
431 *        - Modified FixNew so that it retains the current card index if possible.
432 *        This fixed a bug which could cause headers written out using Native
433 *        encodings to be non-contiguous.
434 *        - Corrected ComBlock to correctly remove AST comment blocks in
435 *        native encoded fitschans.
436 *     14-AUG-2001 (DSB):
437 *        - Modified FixUsed so that it it does not set the current card
438 *        back to the start of file if the last card in the FitsChan is
439 *        deleted.
440 *     16-AUG-2001 (DSB):
441 *        Modified WcsNative to limit reference point latitude to range
442 *        +/-90 degs (previously values outside this range were wrapped
443 *        round onto the opposite meridian). Also added new warning
444 *        condition "badlat".
445 *     23-AUG-2001 (DSB):
446 *        - Re-write LinearMap to use a least squares fit.
447 *        - Check that CDj_i is not AST__BAD within WcsWithWcs when
448 *        forming the increments along each physical axis.
449 *     28-SEP-2001 (DSB):
450 *        GoodWarns changed so that no error is reported if a blank list
451 *        of conditions is supplied.
452 *     12-OCT-2001 (DSB):
453 *        - Added DefB1950 attribute.
454 *        - Corrected equations which calculate CROTA when writing
455 *        FITS-AIPS encodings.
456 *        - Corrected equations which turn a CROTA value into a CD matrix.
457 *     29-NOV-2001 (DSB):
458 *        Corrected use of "_" and "-" characters when referring to FK4-NO-E
459 *        system in function SkySys.
460 *     20-FEB-2002 (DSB)
461 *        Added CarLin attribute.
462 *     8-MAY-2002 (DSB):
463 *        Correct DSSToStore to ignore trailing blanks in the PLTDECSN
464 *        keyword value.
465 *     9-MAY-2002 (DSB):
466 *        Correct GetCard to avoid infinite loop if the current card has
467 *        been marked as deleted.
468 *     25-SEP-2002 (DSB):
469 *        AIPSFromStore: use larger of coscro and sincro when determining
470 *        CDELT values. Previously a non-zero coscro was always used, even
471 *        if it was a s small as 1.0E-17.
472 *     3-OCT-2002 (DSB):
473 *        - SkySys: Corrected calculation of longitude axis index for unknown
474 *        celestial systems.
475 *        - SpecTrans: Corrected check for latcor terms for ZPX projections.
476 *        - WcsFrame: Only store an explicit equinox value in a skyframe if
477 *        it needs one (i.e. if the system is ecliptic or equatorial).
478 *        - WcsWithWcs: For Zenithal projections, always use the default
479 *        LONPOLE value, and absorb any excess rotation caused by this
480 *        into the CD matrix.
481 *        - WcsWithWcs: Improve the check that the native->celestial mapping
482 *        is a pure rotation, allowing for rotations which change the
483 *        handed-ness of the system (if possible).
484 *        - WcsWithWcs: Avoid using LONPOLE keywords when creating headers
485 *        for a zenithal projection. Instead, add the corresponding rotation
486 *        into the CD matrix.
487 *     22-OCT-2002 (DSB):
488 *        - Retain leading and trailing white space within COMMENT cards.
489 *        - Only use CTYPE comments as axis labels if all non-celestial
490 *          axes have a unique non-blank comment (otherwise use CTYPE
491 *          values as labels).
492 *        - Updated to use latest FITS-WCS projections. This means that the
493 *          "TAN with projection terms" is no longer a standard FITS
494 *          projection. It is now represented using the AST-specific TPN
495 *          projection (until such time as FITS-WCS paper IV is finished).
496 *        - Remove trailing "Z" from DATE-OBS values created by astWrite.
497 *     14-NOV-2002 (DSB):
498 *        - WcsWithWcs: Corrected to ignore longitude axis returned by
499 *        astPrimaryFrame since it does not take into account any axis
500 *        permutation.
501 *     26-NOV-2002 (DSB):
502 *        - SpecTrans: Corrected no. of characters copied from CTYPE to PRJ,
503 *        (from 5 to 4), and terminate PRJ correctly.
504 *     8-JAN-2003 (DSB):
505 *        Changed private InitVtab method to protected astInitFitsChanVtab
506 *        method.
507 *     22-JAN-2003 (DSB):
508 *        Restructured the functions used for reading FITS_WCS headers to
509 *        make the distinction between the generic parts (pixel->intermediate
510 *        world coordinates) and the specialised parts (e.g. celestial,
511 *        spectral, etc) clearer.
512 *     31-JAN-2003 (DSB)
513 *        - Added Clean attribute.
514 *        - Corrected initialisation and defaulting of CarLin and DefB1950
515 *        attributes.
516 *        - Extensive changes to allow foreign encodings to be produced in
517 *        cases where the Base Frame has fewer axes than the Current Frame.
518 *     12-FEB-2003 (DSB)
519 *        - Modified SetFits so that the existing card comment is retained
520 *        if the new data value equals the existing data value.
521 *     30-APR-2003 (DSB):
522 *        - Revert to standard "TAN" code for distorted tan projections,
523 *        rather than using the "TPN" code. Also recognise QVi_m (produced
524 *        by AUTOASTROM) as an alternative to PVi_m when reading distorted
525 *        TAN headers.
526 *     22-MAY-2003 (DSB):
527 *        Modified GetEncoding so that the presence of RADECSYS and/or
528 *        PROJPm is only considered significant if the modern equivalent
529 *        keyword (REDESYS or PVi_m) is *NOT* present.
530 *     2-JUN-2003 (DSB):
531 *        - Added support for PCi_j kewwords within FITS-WCS encoding
532 *        - Added CDMatrix attribute
533 *        - Changed internal FitsStore usage to use PC/CDELT instead of CD
534 *        (as preparation for FITS-WCS paper IV).
535 *        - Added warning "BadMat".
536 *     11-JUN-2003 (DSB):
537 *        - Modified WcsNative to use the new SphMap PolarLong attribute
538 *        in order to ensure correct propagation of the longitude CRVAL
539 *        value in cases where the fiducial point is coincident with a pole.
540 *        - Use PVi_3 and PVi_4 for longitude axis "i" (if present) in
541 *        preference to LONPOLE and LATPOLE when reading a FITS-WCS header.
542 *        Note, these projection values are never written out (LONPOLE and
543 *        LATPOLE are written instead).
544 *        - Associate "RADESYS=ICRS" with SkyFrame( "System=ICRS" ), rather
545 *        than SkyFrame( "System=FK5" ).
546 *        - If DefB1950 is zero, use ICRS instead of FK5 as the default RADESYS
547 *        if no EQUINOX is present.
548 *     1-SEP-2003 (DSB):
549 *        - Modify Dump so that it dumps all cards including those flagged as
550 *        having been read.
551 *        - Added "reset" parameter to FixUsed.
552 *        - WcsMapFrm: store an Ident of ' ' for the primary coordinate
553 *        description (previously Ident was left unset)
554 *        - Default value for DefB1950 attribute now depends on the value
555 *        of the Encoding attribute.
556 *     15-SEP-2003 (DSB):
557 *        - Added Warnings "BadVal", "Distortion".
558 *        - Ignore FITS-WCS paper IV CTYPE distortion codes (except for
559 *          "-SIP" which is interpreted correctly on reading).
560 *     22-OCT-2003 (DSB):
561 *        - GetEncoding: If the header contains CDi_j but does not contain
562 *        any of the old IRAF keywords (RADECSYS, etc) then assume FITS-WCS
563 *        encoding. This allows a FITS-WCS header to have both CDi_j *and*
564 *        CROTA keywords.
565 *     5-JAN-2004 (DSB):
566 *        - SpecTrans: Use 1.0 (instead of the CDELT value) as the
567 *        diagonal PCi_j term for non-celestial axes with associated CROTA
568 *        values.
569 *     12-JAN-2004 (DSB):
570 *        - CelestialAxes: Initialise "tmap1" pointer to NULL in case of error
571 *        (avoids a segvio happening in the case of an error).
572 *        - AddVersion: Do not attempt to add a Frame into the FITS header
573 *        if the mapping from grid to frame is not invertable.
574 *        - WorldAxes: Initialise the returned "perm" values to safe values,
575 *        and return these values if no basis vectors cen be created.
576 *     19-JAN-2004 (DSB):
577 *        - When reading a FITS-WCS header, allow all keywords to be defaulted
578 *        as decribed in paper I.
579 *     27-JAN-2004 (DSB):
580 *        - Modify FitLine to use correlation between actual and estimated
581 *        axis value as the test for linearity.
582 *        - Modify RoundFString to avoid writing beyond the end of the
583 *        supplied buffer if the supplied string contains a long list of 9's.
584 *     11-MAR-2004 (DSB):
585 *        - Modified SpecTrans to check all axis descriptions for keywords
586 *        to be translated.
587 *     19-MAR-2004 (DSB):
588 *        - Added astPutCards to support new fits_hdr2str function in
589 *        CFITSIO.
590 *     25-MAR-2004 (DSB):
591 *        - Corrected bug in astSplit which causes legal cards to be
592 *        rejected because characters beyond the 80 char limit are being
593 *        considered significant.
594 *        - Corrected bug in SpecTrans which caused QV keywords to be
595 *        ignored.
596 *     15-APR-2004 (DSB):
597 *        - SpecTrans modified to include translation of old "-WAV", "-FRQ"
598 *        and "-VEL" spectral algorithm codes to modern "-X2P" form.
599 *        - WcsFromStore modified to supress creation of WCSAXES keywords
600 *        for un-used axis versions.
601 *        - IsMapLinear modified to improve fit by doing a second least
602 *        squares fit to the residualleft by the first least squares fit.
603 *     16-APR-2004 (DSB):
604 *        - NonLinSpecWcs: Issue a warning if an illegal non-linear
605 *        spectral code is encountered.
606 *        - Add a BadCTYPE warning condition.
607 *        - Corrected default value for Clean so that it is zero (as
608 *        documented).
609 *     21-APR-2004 (DSB):
610 *        - FindWcs: Corrected to use correct OBSGEO template. This bug
611 *        caused OBSGEO keywords to be misplaced in written headers.
612 *     23-APR-2004 (DSB):
613 *        - SplitMap: Modified so that a Mapping which has celestial axes
614 *        with constant values (such as produced by a PermMap) are treated
615 *        as a valid sky coordinate Mapping.
616 *        - AddFrame modified so that WCS Frames with a different number
617 *        of axes ot the pixel Frame can be added into the FrameSet.
618 *        - IRAFFromStore and AIPSFromStore modified so that they do not
619 *        create any output keywords if the number of WCS axes is different
620 *        to the number of pixel axes.
621 *        - Handling of OBSGEO-X/Y/Z corrected again.
622 *        - WCSFromStore modified to avouid writing partial axis descriptions.
623 *     26-APR-2004 (DSB):
624 *        - Corrected text of output SPECSYS keyword values.
625 *     17-MAY-2004 (DSB):
626 *        - Added IWC attribute.
627 *     15-JUN-2004 (DSB):
628 *        - Ensure out-of-bounds longitude CRPIX values for CAR
629 *        projections are wrapped back into bounds.
630 *     21-JUN-2004 (DSB):
631 *        - Ensure primary MJD-OBS value is used when reading foreign FITS
632 *        headers.
633 *     7-JUL-2004 (DSB):
634 *        - Issue errors if an un-invertable PC/CD matrix is supplied in a
635 *        FITS-WCS Header.
636 *     11-JUL-2004 (DSB):
637 *        - Re-factor code for checking spectral axis CTYPE values into
638 *        new function IsSpectral.
639 *        - Modify AIPSFromSTore to create spectral axis keywords if
640 *        possible.
641 *        - Modify SpecTrans to recognize AIPS spectral axis keywords, and
642 *        to convert "HZ" to "Hz".
643 *        - Added FITS-AIPS++ encoding.
644 *     12-AUG-2004 (DSB):
645 *        - Convert GLS projection codes to equivalent SFL in SpecTrans.
646 *        - Added FITS-CLASS encoding.
647 *     16-AUG-2004 (DSB):
648 *        - Removed support for paper III keyword VSOURCE, and added
649 *        support for SSYSSRC keyword.
650 *        - Added initial support for CLASS encoding.
651 *        - In FitOK: Changed tolerance for detecting constant values
652 *        from 1.0E-10 to 1.0E-8.
653 *     17-AUG-2004 (DSB):
654 *        Correct GetFiducialNSC so that the stored values for longitude
655 *        parameters 1 and 2 are ignored unless the value of parameter 0 is
656 *        not zero.
657 *     19-AUG-2004 (DSB):
658 *        Modify SpecTrans to ignore any CDELT values if the header
659 *        includes some CDi_j values.
660 *     26-AUG-2004 (DSB):
661 *        Modify astSplit_ to allow floating point keyword values which
662 *        include an exponent to be specified with no decimal point
663 *        (e.g. "2E-4").
664 *     27-AUG-2004 (DSB):
665 *        Completed initial attempt at a FITS-CLASS encoding.
666 *     9-SEP-2004 (DSB):
667 *        Fixed usage of uninitialised values within ReadCrval.
668 *     13-SEP-2004 (DSB):
669 *        Check the "text" pointer can be used safely before using it in
670 *        DSSToStore.
671 *     27-SEP-2004 (DSB):
672 *        In SpecTrans, before creating new PCi_j values,  check that no
673 *        PCi_j values have been created via an earlier translation.
674 *     28-SEP-2004 (DSB):
675 *        In AIPSPPFromStore only get projection parameters values if there
676 *        are some celestialaxes. Also allow CROTA to describe rotation of
677 *        non-celestial axes (same for AIPSFromSTore).
678 *     4-OCT-2004 (DSB):
679 *        Correct rounding of CRPIX in AddVersion to avoid integer overflow.
680 *     11-NOV-2004 (DSB):
681 *        - WcsFcRead: Avoid issuing warnings about bad keywords which
682 *        have already been translated into equivalent good forms.
683 *        - SpecTrans: If both PROJP and PV keywords are present, use PV
684 *        in favour of PROJP only if the PV values look correct.
685 *     17-NOV-2004 (DSB):
686 *        - Make astSetFits<X> public.
687 *     16-MAR-2005 (DSB):
688 *        - Primary OBSGEO-X/Y/Z, MJD-AVG and MJDOBS keywords are associated
689 *        with all axis descriptions and should not have a trailing single
690 *        character indicating an alternate axis set.
691 *     9-AUG-2005 (DSB):
692 *        In WcsMapFrm, check reffrm is used before annulling it.
693 *     8-SEP-2005 (DSB):
694 *        - Change "if( a < b < c )" constructs to "if( a < b && b < c )"
695 *        - DSBSetup: correct test on FrameSet pointer state
696 *        - Ensure CLASS keywords written to a FitsChan do not come before
697 *        the final fixed position keyword.
698 *     9-SEP-2005 (DSB):
699 *        - Added "AZ--" and "EL--" as allowed axis types in FITS-WCS
700 *        ctype values.
701 *     12-SEP-2005 (DSB):
702 *        - Cast difference between two pointers to (int)
703 *        - CLASSFromStore:Check source velocity is defined before
704 *          storing it in the output header.
705 *     13-SEP-2005 (DSB):
706 *        - Corrected B1940 to B1950 in AddEncodingFrame. This bug
707 *        prevented some FrameSets being written out using FITS-CLASS.
708 *        - Rationalise the use of the "mapping" pointer in AddVersion.
709 *        - WcsCelestial: Modified so that the FITS reference point is
710 *        stored as the SkyFrame SkyRef attribute value.
711 *     7-OCT-2005 (DSB):
712 *        Make astGetFits<X> public.
713 *     30-NOV-2005 (DSB):
714 *        Add support for undefined FITS keyword values.
715 *     5-DEC-2005 (DSB):
716 *        - Include an IMAGFREQ keyword in the output when writing a
717 *        DSBSpecFrame out using FITS-WCS encoding.
718 *        - Correct test for constant values in FitOK.
719 *     7-DEC-2005 (DSB):
720 *        Free memory allocated by calls to astReadString.
721 *     30-JAN-2006 (DSB):
722 *        Modify astSplit so that it does no read the supplied card beyond
723 *        column 80.
724 *     14-FEB-2006 (DSB):
725 *        Override astGetObjSize.
726 *     28-FEB-2006 (DSB):
727 *        Correct documentation typo ("NCards" -> "Ncard").
728 *     5-APR-2006 (DSB):
729 *        Modify SpecTrans to convert CTYPE="LAMBDA" to CTYPE="WAVE".
730 *     26-MAY-2006 (DSB):
731 *        Guard against NULL comment pointer when converting RESTFREQ to
732 *        RESTFRQ in SpecTrans.
733 *     29-JUN-2006 (DSB):
734 *        - Added astRetainFits.
735 *        - Consume VELOSYS FITS-WCS keywords when reading an object.
736 *        - Write out VELOSYS FITS-WCS keywords when writing an object.
737 *     7-AUG-2006 (DSB):
738 *        Remove trailing spaces from the string returned by astGetFitsS
739 *        if the original string contains 8 or fewer characters.
740 *     16-AUG-2006 (DSB):
741 *        Document non-destructive nature of unsuccessful astRead calls.
742 *     17-AUG-2006 (DSB):
743 *        Fix bugs so that the value of the Clean attribute is honoured
744 *        even if an error has occurred.
745 *     4-SEP-2006 (DSB):
746 *        Modify GetClean so that it ignores the inherited status.
747 *     20-SEP-2006 (DSB):
748 *        Fix memory leak in WcsSpectral.
749 *     6-OCT-2006 (DSB):
750 *        Modify IsSpectral and IsAIPSSpectral to allow for CTYPE values that
751 *        are shorter than eight characters.
752 *     13-OCT-2006 (DSB):
753 *        - Ensure SpecFrames and SkyFrames created from a foreign FITS header
754 *        are consistent in their choice of Epoch.
755 *        - Convert MJD-OBS and MJD-AVG values from TIMESYS timescale to
756 *        TDB before using as the Epoch value in an AstFrame. Use UTC if
757 *        TIMESYS is absent.
758 *        - Convert Epoch values from TDB to UTC before storing as the
759 *        value of an MJD-OBS or MJD-AVG keyword (no TIMESYS keyword is
760 *        written).
761 *     23-OCT-2006 (DSB):
762 *        Prefer MJD-AVG over MJD-OBS.
763 *     30-OCT-2006 (DSB):
764 *        In FitOK: Changed lower limit on acceptbale correlation from
765 *        0.999999 to 0.99999.
766 *     1-NOV-2006 (DSB):
767 *        - When reading a foreign header that contains a DUT1 keyword,
768 *        use it to set the Dut1 attribute in the SkyFrame. Note, JACH
769 *        store DUT1 in units of days. This may clash with the FITS-WCS
770 *        standard (when its produced). Also note that DUT1 is not written
771 *        out as yet when writing a FrameSet to a foreign FITS header.
772 *        - Correct bug that prevented ZSOURCE keyword being added to the
773 *        output header if the source velocity was negative.
774 *     9-NOV-2006 (DSB):
775 *        Add STATUS argument to docs for F77 AST_SETx.
776 *     20-DEC-2006 (DSB):
777 *        Correct FK5 to ICRS in error message issued if no RADESYS or
778 *        EQUINOX is found.
779 *     16-JAN-2007 (DSB):
780 *        Cast ignored function return values to (void) to avoid compiler
781 *        warnings.
782 *     31-JAN-2007 (DSB):
783 *        Change SpecTrans to ignore blank unit strings (previously
784 *        converted them to "Hz").
785 *     16-APR-2007 (DSB):
786 *        In SplitMat, increase the allowed level of rounding erros from
787 *        1.0E-10 to 1.0E-7 (to avoid spurious low CDi_j values being
788 *        created that should be zero).
789 *     30-APR-2007 (DSB):
790 *        - Change DSBSetup so that the central DSBSpecFrame frequency is
791 *        CRVAL and the IF is the difference between CRVAL and LO.
792 *        - Change tolerance in FitOK from 0.99999 to 0.995 to handle data from Nicolas
793 *        Peretto.
794 *     1-MAY-2007 (DSB):
795 *        - In astSplit, if a keyword value looks like an int but is too long to
796 *         fit in an int, then treat it as a float instead.
797 *     18-MAY-2007 (DSB):
798 *        In CnvType, use input type rather than output type when checking
799 *        for a COMMENT card. Also, return a null data value buffer for a
800 *        COMMENT card.
801 *     4-JUN-2007 (DSB):
802 *        In CLASSFromStore, create a DELTAV header even if it is equal to
803 *        the spectral CDELT value. Also, convert spatial reference point
804 *        to (az,el) and write out as headers AZIMUTH and ELEVATIO.
805 *     9-JUL-2007 (DSB):
806 *        Fixed bug in DSBSetUp - previously, this function assumed that
807 *        the supplied DSBSpecFrame represented frequency, and so gave
808 *        incorrect values for IF and DSBCentre if the header described
809 *        velocity.
810 *     9-AUG-2007 (DSB):
811 *        Changed GetEncoding so that critcal keywords are ignored if
812 *        there are no CTYPE, CRPIX or CRVAL keywords in the header.
813 *     10-AUG-2007 (DSB):
814 *        - Changed GetEncoding so that FITS_PC is not returned if there are
815 *        any CDi_j or PCi_j keywords in the header.
816 *        - Added astPurgeWCS method.
817 *     13-AUG-2007 (DSB):
818 *        - Include the DSS keywords AMDX%d and AMDY%d in FindWCS.
819 *     16-AUG-2007 (DSB):
820 *        - Force all FITS-CLASS headers to contain frequency axes
821 *        (velocity axes seem not to be recognised properly by CLASS).
822 *        - Change the CLASS "VELO-LSR" header to be the velocity at the
823 *        reference channel, not the source velocity.
824 *     22-AUG-2007 (DSB):
825 *        - Remove debugging printf statements.
826 *     20-SEP-2007 (DSB):
827 *        Changed FitOK to check that the RMS residual is not more than
828 *        a fixed small fraction of the pixel size.
829 *     4-DEC-2007 (DSB):
830 *        Changed CreateKeyword so that it uses a KeyMap to search for
831 *        existing keywords. This is much faster than checking every
832 *        FitsCard in the FitsChan explicitly.
833 *     18-DEC-2007 (DSB):
834 *        Add keyword VLSR to the CLASS encoding. It holds the same value
835 *        as VELO-LSR, but different versions of class use different names.
836 *        Also write out the DELTAV keyword in the LSR rest frame rather
837 *        than the source rest frame.
838 *     31-JAN-2008 (DSB):
839 *        Correct calculation of redshift from radio velocity in ClassTrans.
840 *     25-FEB-2008 (DSB):
841 *        Ensure a SkyFrame represents absolute (rather than offset)
842 *        coords before writing it out in any non-native encoding.
843 *     28-FEB-2008 (DSB):
844 *        Test for existing of SkyRefIs attribute before accessing it.
845 *     2-APR-2008 (DSB):
846 *        In CLASSFromStore, adjust the spatial CRVAL and CRPIX values to be
847 *        the centre of the first pixel if the spatial axes are degenerate.
848 *     17-APR-2008 (DSB):
849 *        Ignore latitude axis PV terms supplied in a TAN header
850 *        (previously, such PV terms were used as polynomial correction
851 *        terms in a TPN projection).
852 *     30-APR-2008 (DSB):
853 *        SetValue changed so that new keywords are inserted before the
854 *        current card.
855 *     1-MAY-2008 (DSB):
856 *        Added UndefRead warning.
857 *     7-MAY-2008 (DSB):
858 *        Correct conversion of CDi_j to PCi_j/CDELT in SpecTrans.
859 *     8-MAY-2008 (DSB):
860 *        When writing out a FITS-WCS header, allow linear grid->WCS
861 *        mapping to be represented by a CAR projection.
862 *     9-MAY-2008 (DSB):
863 *        Make class variables IgnoreUsed and MarkNew static.
864 *     30-JUN-2008 (DSB):
865 *        Improve efficiency of FindWcs.
866 *     2-JUL-2008 (DSB):
867 *        FitsSof now returns non-zero if the FitsChan is empty.
868 *     16-JUL-2008 (DSB):
869 *        Plug memory leak caused by failure to free the Warnings
870 *        attribute string when a FitsChan is deleted.
871 *     24-JUL-2008 (TIMJ):
872 *        Fix buffer overrun in astGetFits when writing the keyword
873 *        to the buffer (occurred if the input string was 80 characters).
874 *     1-OCT-2008 (DSB):
875 *        When reading a FITS-WCS header, spurious PVi_j keywords no
876 *        longer generate an error. Instead they generate warnings via the
877 *        new "BadPV" warning type.
878 *     21-NOV-2008 (DSB):
879 *        Do not remove keywords from read headers if they may be of
880 *        relevance to things other than WCS (e.g. MJD-OBS, OBSGEO, etc).
881 *     2-DEC-2008 (DSB):
882 *        - astGetFits<X> now reports an error if the keyword value is undefined.
883 *        - Add new functions astTestFits and astSetFitsU.
884 *        - Remove use of AST__UNDEF<X> constants.
885 *        - Remove "undefread" warning.
886 *     16-JAN-2009 (DSB):
887 *        Use astAddWarning to store each warning in the parent Channel
888 *        structure.
889 *     4-MAR-2009 (DSB):
890 *        DATE-OBS and MJD-OBS cannot have an axis description character.
891 *     13-MAR-2009 (DSB):
892 *        The VELOSYS value read from the header is never used, so do not
893 *        report an error if VELOSYS has an undefined value.
894 *     11-JUN-2009 (DSB):
895 *        Delay reading cards from the source until they are actually
896 *        needed. Previously, the source function was called in the
897 *        FitsChan initialiser, but this means it is not possible for
898 *        application code to call astPutChannelData before the source
899 *        function is called. The ReadFromSource function is now called
900 *        at the start of each (nearly) public or protected function to
901 *        ensure the source function has been called (the source function
902 *        pointer in the FitsChan is then nullified to ensure it is not
903 *        called again).
904 *     18-JUN-2009 (DSB):
905 *        Include the effect of observer height (in the ObsAlt attribute)
906 *        when creating OBSGEO-X/Y/Z headers, and store a value for
907 *        ObsAlt when reading a set of OBSGEO-X/Y/Z headers.
908 *     2-JUL-2009 (DSB):
909 *        Check FitsChan is not empty at start of FindWcs.
910 *     7-JUL-2009 (DSB):
911 *        Add new function astSetFitsCM.
912 *     30-JUL-2009 (DSB):
913 *        Fix axis numbering in SkyPole.
914 *     12-FEB-2010 (DSB):
915 *        Use "<bad>" to represent AST__BAD externally.
916 *     25-JUN-2010 (DSB):
917 *        Fix problem rounding lots of 9's in RoundFString. The problem
918 *        only affected negative values, and could lead to an extra zero
919 *        being included in the integer part.
920 *     28-JUN-2010 (DSB):
921 *        Another problem in RoundFString! If the value has a series of
922 *        9's followed by a series of zeros, with no decimal point (e.g.
923 *        "260579999000"), then the trailing zeros were being lost.
924 *     16-JUL-2010 (DSB):
925 *        In SpecTrans, avoid over-writing the spatial projection code
926 *        with the spectral projection code.
927 *     20-JUL-2010 (DSB):
928 *        Correct interpretation of NCP projection code.
929 *     14-OCT-2010 (DSB):
930 *        - Correct loading of FitsChans that contain UNDEF keywords.
931 *        - Correct translation of spectral units with non-standard
932 *        capitalisation in SpecTrans.
933 *     10-JAN-2011 (DSB):
934 *        Fix memory leak in MakeIntWorld.
935 *     13-JAN-2011 (DSB):
936 *        Rename astEmpty ast astEmptyFits and make public.
937 *     20-JAN-2011 (DSB):
938 *        - Extensive changes to support -TAB algorithm
939 *        - Recovery from a major unrequested reformatting of whitespace by
940 *        my editor!
941 *     7-FEB-2011 (DSB):
942 *        Put a space between keyword value and slash that starts a comment
943 *        when formatting a FITS header card.
944 *     11-FEB-2011 (DSB):
945 *        Change meaning of TabOK attribute. It is no longer a simple
946 *        boolean indicating if the -TAB algorithm is supported. Instead
947 *        it gives the value to be used for the EXTVER header - i.e. the
948 *        version number to store with any binary table created as a
949 *        result of calling astWrite. If TabOK is zero or begative, then
950 *        the -TAB algorithm is not supported. This is so that there is
951 *        some way of having multiple binary table extensions with the same
952 *        name (but different EXTVER values).
953 *     14-FEB-2011 (DSB):
954 *        - Spectral reference point CRVAL records the obs. centre. So for -TAB
955 *        (when CRVAL is set to zero) we need to record the obs centre some
956 *        other way (use the AST-specific AXREF keywords, as for spatial axes).
957 *        - Whether to scale spatial axes from degs to rads depends on
958 *        whether the spatial axes are descirbed by -TAB or not.
959 *        - Relax the linearity requirement in IsMapLinear by a factor of
960 *        10 to prevent a change in rest frame resulting in a non-linear
961 *        mapping.
962 *     17-FEB-2011 (DSB):
963 *        Fix bug in axis linearity check (IsMapLinear).
964 *     22-FEB-2011 (DSB):
965 *        The translations of AIPS non-standard CTYPE values were always
966 *        stored as primary axis description keywords, even if the original
967 *        non-standard CTYPE values were read from an alternative axis
968 *        descriptions.
969 *     5-APR-2011 (DSB):
970 *        In SpecTrans, correct the MSX CAR projection translation. The
971 *        first pixel starts at GRID=0.5, not GRID=0.0. So the CRPIX value
972 *        needs to be reduced by 0.5 prior to normalisation, and then
973 *        increased by 0.5 after normalisation.
974 *     23-MAY-2011 (DSB):
975 *        Add support for TNX projections that use Chebyshev polynomials.
976 *     24-MAY-2011 (DSB):
977 *        - Add support for ZPX projections that include IRAF polynomial
978 *        corrections.
979 *        - Add PolyTan attribute.
980 *        - Fix interpretation of -SIP headers that have no inverse.
981 *     1-JUN-2011 (DSB):
982 *        In astInitFitsChanVtab, only create the two TimeFrames if they
983 *        have not already been created (fixes scuba2 trac ticket #666).
984 *     9-JUN-2011 (DSB):
985 *        In WCSFcRead, ignore trailing spaces when reading string values
986 *        for WCS keywords.
987 *     23-JUN-2011 (DSB):
988 *        - Override the parent astSetSourceFile method so that it reads
989 *        headers from the SourceFile and appends them to the end of the
990 *        FitsChan.
991 *        - On deletion, write out the FitsChan contents to the file
992 *        specified by the SinkFile attribute. If no file is specified,
993 *        use the sink function specified when the FitsChan was created.
994 *     30-AUG-2011 (DSB):
995 *        - Added astWriteFits and astReadFits.
996 *        - Move the deletion of tables and warnings from Delete to
997 *        EmptyFits.
998 *     21-SEP-2011 (DSB):
999 *        - In RoundFString, remember to update the pointer to the exponent.
1000 *        This bug caused parts of the exponent to dissappear when
1001 *        formatting a value that included some trailing zeros and a
1002 *        series of adjacent 9's.
1003 *        - Added Nkey attribute.
1004 *     22-SEP-2011 (DSB):
1005 *        - Added CardType attribute
1006 *        - Allow GetFits to be used to get/set the value of the current
1007 *        card.
1008 *     4-OCT-2011 (DSB):
1009 *        When reading a FITS-WCFS header, if the projection is TPV (as produced
1010 *        by SCAMP), change to TPN (the internal AST code for a distorted
1011 *        TAN projection).
1012 *     22-NOV-2011 (DSB):
1013 *        Allow the "-SIP" code to be used with non-celestial axes.
1014 *     1-FEB-2012 (DSB):
1015 *        Write out MJD-OBS in the timescale specified by any TIMESYS
1016 *        keyword in the FitsChan, and ensure the TIMESYS value is included
1017 *        in the output header.
1018 *     23-FEB-2012 (DSB):
1019 *        Use iauGd2gc in place of palGeoc where is saves some calculations.
1020 *     24-FEB-2012 (DSB):
1021 *        Move invocation of AddEncodingFrame from Write to end of
1022 *        MakeFitsFrameSet. This is so that AddEncodingFrame can take
1023 *        advantage of any standardisations (such as adding celestial axes)
1024 *        performed by MakeFItsFrameSet. Without this, a FRameSet contain
1025 *        a 1D SpecFrame (no celestial axes) would fail to be exported using
1026 *        FITS-CLASS encoding.
1027 *     29-FEB-2012 (DSB):
1028 *        Fix bug in CLASSFromStore that caused spatial axes added by
1029 *        MakeFitsFrameSet to be ignored.
1030 *     2-MAR-2012 (DSB):
1031 *        - In CLASSFromSTore, ensure NAXIS2/3 values are stored in teh FitsChan,
1032 *        and cater for FrameSets that have only a apectral axis and no celestial
1033 *        axes (this prevented the VELO_LSR keyword being created)..
1034 *     7-MAR-2012 (DSB):
1035 *        Use iauGc2gd in place of Geod.
1036 *     22-JUN-2012 (DSB):
1037 *        - Check for distorted TAN projections that have zero for all PVi_m
1038 *        coefficients. Issue a warning and ignore the distortion in such
1039 *        cases.
1040 *        - Remove all set but unused variables.
1041 *        - Convert SAO distorted TAN projections (which use COi_j keywords
1042 *        for polynomial coeffs) to TPN.
1043 *     26-JUN-2012 (DSB):
1044 *        Correct call to astKeyFields in SAOTrans (thanks to Bill Joye
1045 *        for pointing out this error).
1046 *     8-AUG-2012 (DSB):
1047 *        Correct assignment to lonpole within CLASSFromStore.
1048 *     10-AUG-2012 (DSB):
1049 *        Default DSS keywords CNPIX1 and CNPIX2 to zero if they are
1050 *        absent, rather than reporting an error.
1051 *     7-DEC-2012 (DSB):
1052 *        - When writing out a FrameSet that uses an SkyFrame to describe a
1053 *        generalised spherical coordinate system ("system=unknown"), ensure
1054 *        that the generated FITS CTYPE values use FITS-compliant codes
1055 *        for the axis type ( "xxLN/xxLT" or "xLON/xLAT" ).
1056 *        - Add support for reading and writing offset SkyFrames to
1057 *        FITS-WCS.
1058 *     30-JAN-2013 (DSB):
1059 *        When reading a FITS-CLASS header, use "VLSR" keyword if
1060 *        "VELO-..." is not available.
1061 *     15-APR-2013 (DSB):
1062 *        Correct initialisation of missing coefficients When reading a
1063 *        SAO plate solution header.
1064 *     16-APR-2013 (DSB):
1065 *        When determining default Encoding value, use "VLSR" keyword if
1066 *        "VELO-..." is not available.
1067 *     30-MAY-2013 (DSB):
1068 *        Prevent seg fault caused by overrunning the coeffs array in
1069 *        WATCoeffs in cases where the TNX/ZPX projection is found to be
1070 *        of a form that cannot be implemented as a TPN projection.
1071 *     11-JUN-2013 (DSB):
1072 *        Fix support for reading GLS projections, and add support for
1073 *        rotated GLS projections.
1074 *     28-AUG-2013 (DSB):
1075 *        In WcsCelestial, if celestial axes are found with no projection
1076 *        code in CTYPE, assume an old-fashioned CAR projection (i.e. no
1077 *        rotation from native to WCS coords). Before this change,
1078 *        CTYPE = "RA" | "DEC" axes got treated as radians, not degrees.
1079 *     16-SEP-2013 (DSB):
1080 *        When exporting alternate offset SkyFrames to FITS-WCS headers,
1081 *        correctly test the alternate Frame in the supplied FrameSet, rather
1082 *        than the current Frame.
1083 *     24-SEP-2013 (DSB):
1084 *        Fix bug in choosing default value for PolyTan attribute.
1085 *     19-OCT-2013 (DSB):
1086 *        - In SIPMapping, always ignore any inverse polynomial supplied in
1087 *        a SIP header as they seem often to be inaccurate. A new inverse is
1088 *        created to replace it.
1089 *        - In SIPMapping, only use a fit to the inverted SIP transformation
1090 *        if an accuracy of 0.01 pixel can be achieved over an area three
1091 *        times the dimensions of the image. Otherwise use an iterative
1092 *        inverse for each point. People were seeing bad round-trip errors
1093 *        when transforming points outside the image because the fit was
1094 *        being used when it was not very accurate.
1095 *     12-NOV-2013 (DSB):
1096 *        Added CardName and CardComm attributes.
1097 *     13-NOV-2013 (DSB):
1098 *        Use a zero-length string for the CardComm attribute if the card
1099 *        has no comment.
1100 *     15-NOV-2013 (DSB):
1101 *        - Added method astShowFits.
1102 *        - Ensure PurgeWcs removes WCS cards even if an error occurs when
1103 *        reading FrameSets from the FitsChan.
1104 *        - Change IsMapTab1D to improve chances of a -TAB mapping being found.
1105 *     6-JAN-2014 (DSB):
1106 *        - Allow default options for newly created FitsChans to be
1107 *        specified by the FITSCHAN_OPTIONS environment variable.
1108 *        - Ensure the used CarLin value is not changed by a trailing frequency axis.
1109 *     9-JUL-2014 (DSB):
1110 *        Added attribute FitsAxisOrder, which allows an order to be
1111 *        specified for WCS axis within FITS headers generated using astWrite.
1112 *     9-SEP-2014 (DSB):
1113 *        Modify Split so that any non-printing characters such as
1114 *        newlines at the end of the string are ignored.
1115 *class--
1116 */
1117 
1118 /* Module Macros. */
1119 /* ============== */
1120 
1121 /* Set the name of the class we are implementing. This indicates to
1122    the header files that define class interfaces that they should make
1123    "protected" symbols available. */
1124 #define astCLASS FitsChan
1125 
1126 /* A macro which tests a character to see if it can be used within a FITS
1127    keyword. We include lower case letters here, but they are considered
1128    as equivalent to upper case letter. */
1129 #define isFits(a) ( islower(a) || isupper(a) || isdigit(a) || (a)=='-' || (a)=='_' )
1130 
1131 /* Macros which return the maximum and minimum of two values. */
1132 #define MAX(aa,bb) ((aa)>(bb)?(aa):(bb))
1133 #define MIN(aa,bb) ((aa)<(bb)?(aa):(bb))
1134 
1135 /* Macro which takes a pointer to a FitsCard and returns non-zero if the
1136    card has been used and so should be ignored. */
1137 #define CARDUSED(card)  ( \
1138              ( ignore_used == 2 && \
1139                 ( (FitsCard *) (card) )->flags & PROVISIONALLY_USED ) || \
1140              ( ignore_used >= 1 && \
1141                 ( (FitsCard *) (card) )->flags & USED ) )
1142 
1143 /* Set of characters used to encode a "sequence number" at the end of
1144    FITS keywords in an attempt to make them unique.. */
1145 #define SEQ_CHARS "_ABCDEFGHIJKLMNOPQRSTUVWXYZ"
1146 
1147 /* A general tolerance for equality between floating point values. */
1148 #define TOL1 10.0*DBL_EPSILON
1149 
1150 /* A tolerance for equality between angular values in radians. */
1151 #define TOL2 1.0E-10
1152 
1153 /* Macro to check for equality of floating point values. We cannot
1154    compare bad values directory because of the danger of floating point
1155    exceptions, so bad values are dealt with explicitly. */
1156 #define EQUAL(aa,bb) (((aa)==AST__BAD)?(((bb)==AST__BAD)?1:0):(((bb)==AST__BAD)?0:(fabs((aa)-(bb))<=1.0E5*MAX((fabs(aa)+fabs(bb))*DBL_EPSILON,DBL_MIN))))
1157 
1158 /* Macro to check for equality of floating point angular values. We cannot
1159    compare bad values directory because of the danger of floating point
1160    exceptions, so bad values are dealt with explicitly. The smallest
1161    significant angle is assumed to be 1E-9 radians (0.0002 arc-seconds).*/
1162 #define EQUALANG(aa,bb) (((aa)==AST__BAD)?(((bb)==AST__BAD)?1:0):(((bb)==AST__BAD)?0:(fabs((aa)-(bb))<=MAX(1.0E5*(fabs(aa)+fabs(bb))*DBL_EPSILON,1.0E-9))))
1163 
1164 /* Macro to compare an angle in radians with zero, allowing some tolerance. */
1165 #define ZEROANG(aa) (fabs(aa)<1.0E-9)
1166 
1167 /* Constants: */
1168 #define UNKNOWN_ENCODING  -1
1169 #define NATIVE_ENCODING    0
1170 #define FITSPC_ENCODING    1
1171 #define DSS_ENCODING       2
1172 #define FITSWCS_ENCODING   3
1173 #define FITSIRAF_ENCODING  4
1174 #define FITSAIPS_ENCODING  5
1175 #define FITSAIPSPP_ENCODING 6
1176 #define FITSCLASS_ENCODING 7
1177 #define MAX_ENCODING       7
1178 #define UNKNOWN_STRING     "UNKNOWN"
1179 #define NATIVE_STRING      "NATIVE"
1180 #define FITSPC_STRING      "FITS-PC"
1181 #define FITSPC_STRING2     "FITS_PC"
1182 #define DSS_STRING         "DSS"
1183 #define FITSWCS_STRING     "FITS-WCS"
1184 #define FITSWCS_STRING2    "FITS_WCS"
1185 #define FITSIRAF_STRING    "FITS-IRAF"
1186 #define FITSIRAF_STRING2   "FITS_IRAF"
1187 #define FITSAIPS_STRING    "FITS-AIPS"
1188 #define FITSAIPS_STRING2   "FITS_AIPS"
1189 #define FITSAIPSPP_STRING  "FITS-AIPS++"
1190 #define FITSAIPSPP_STRING2 "FITS_AIPS++"
1191 #define FITSCLASS_STRING  "FITS-CLASS"
1192 #define FITSCLASS_STRING2 "FITS_CLASS"
1193 #define INDENT_INC         3
1194 #define PREVIOUS           0
1195 #define NEXT               1
1196 #define HEADER_TEXT        "Beginning of AST data for "
1197 #define FOOTER_TEXT        "End of AST data for "
1198 #define FITSNAMLEN         8
1199 #define FITSSTCOL          20
1200 #define FITSRLCOL          30
1201 #define FITSIMCOL          50
1202 #define FITSCOMCOL         32
1203 #define NORADEC            0
1204 #define FK4                1
1205 #define FK4NOE             2
1206 #define FK5                3
1207 #define GAPPT              4
1208 #define ICRS               5
1209 #define NOCEL              0
1210 #define RADEC              1
1211 #define ECLIP              2
1212 #define GALAC              3
1213 #define SUPER              4
1214 #define HECLIP             5
1215 #define AZEL               6
1216 #define LONAX             -1
1217 #define NONAX              0
1218 #define LATAX              1
1219 #define NDESC              9
1220 #define MXCTYPELEN        81
1221 #define ALLWARNINGS       " distortion noequinox noradesys nomjd-obs nolonpole nolatpole tnx zpx badcel noctype badlat badmat badval badctype badpv "
1222 #define NPFIT             10
1223 #define SPD               86400.0
1224 #define FL  1.0/298.257  /*  Reference spheroid flattening factor */
1225 #define A0  6378140.0    /*  Earth equatorial radius (metres) */
1226 
1227 /* String used to represent AST__BAD externally. */
1228 #define BAD_STRING "<bad>"
1229 
1230 /* Each card in the fitschan has a set of flags associated with it,
1231    stored in different bits of the "flags" item within each FitsCard
1232    structure (note, in AST V1.4 these flags were stored in the "del"
1233    item... Dump and LoadFitsChan will need to be changed to use a
1234    correspondingly changed name for the external representation of this
1235    item). The following flags are currently defined: */
1236 
1237 /* "USED" - This flag indicates that the the card has been used in the
1238    construction of an AST Object returned by astRead. Such cards should
1239    usually be treated as if they do not exist, i.e. they should not be
1240    used again by subsequent calls to astRead, they should not be recognised
1241    by public FitsChan methods which search the FitsChan for specified
1242    cards, and they should not be written out when the FitsChan is deleted.
1243    This flag was the only flag available in AST V1.4, and was called
1244    "Del" (for "deleted"). Used cards are retained in order to give an
1245    indication of where abouts within the header new cards should be placed
1246    when astWrite is called (i.e. new cards should usually be placed at
1247    the same point within the header as the cards which they replace). */
1248 #define USED 	1
1249 
1250 /* "PROVISIONALLY_USED" - This flag indicates that the the card is being
1251    considered as a candidate for inclusion in the construction of an AST
1252    Object. If the Object is constructed succesfully, cards flagged as
1253    "provisionally used" will be changed to be flagged as "definitely used"
1254    (using the USED flag). If the Object fails to be constructed
1255    succesfully (if some required cards are missing from the FitsChan
1256    for instance), then "provisionally used" cards will be returned to the
1257    former state which they had prior to the attempt to construct the
1258    object. */
1259 #define PROVISIONALLY_USED 2
1260 
1261 /* "NEW" - This flag indicates that the the card has just been added to
1262    the FitsChan and may yet proove to be unrequired. For instance if the
1263    supplied Object is not of an appropriate flavour to be stored using
1264    the requested encoding, all "new" cards which were added before the
1265    inappropriateness was discovered will be removed from the FitsChan.
1266    Two different levels of "newness" are available. */
1267 #define NEW1 4
1268 #define NEW2 8
1269 
1270 /* "PROTECTED" - This flag indicates that the the card should not be
1271    removed form the FitsChan when an Object is read using astRead. If
1272    this flag is not set, then the card will dehave as if it has been
1273    deleted if it was used in the construction of the returned AST Object. */
1274 #define PROTECTED 16
1275 
1276 /* Include files. */
1277 /* ============== */
1278 
1279 /* Interface definitions. */
1280 /* ---------------------- */
1281 #include "channel.h"
1282 #include "cmpframe.h"
1283 #include "cmpmap.h"
1284 #include "dssmap.h"
1285 #include "error.h"
1286 #include "fitschan.h"
1287 #include "frame.h"
1288 #include "frameset.h"
1289 #include "grismmap.h"
1290 #include "lutmap.h"
1291 #include "mathmap.h"
1292 #include "matrixmap.h"
1293 #include "memory.h"
1294 #include "object.h"
1295 #include "permmap.h"
1296 #include "pointset.h"
1297 #include "shiftmap.h"
1298 #include "skyframe.h"
1299 #include "timeframe.h"
1300 #include "keymap.h"
1301 #include "pal.h"
1302 #include "erfa.h"
1303 #include "slamap.h"
1304 #include "specframe.h"
1305 #include "dsbspecframe.h"
1306 #include "specmap.h"
1307 #include "sphmap.h"
1308 #include "unitmap.h"
1309 #include "polymap.h"
1310 #include "wcsmap.h"
1311 #include "winmap.h"
1312 #include "zoommap.h"
1313 #include "globals.h"
1314 #include "fitstable.h"
1315 
1316 /* Error code definitions. */
1317 /* ----------------------- */
1318 #include "ast_err.h"             /* AST error codes */
1319 
1320 /* C header files. */
1321 /* --------------- */
1322 #include <ctype.h>
1323 #include <float.h>
1324 #include <limits.h>
1325 #include <stdio.h>
1326 #include <stdlib.h>
1327 #include <string.h>
1328 #include <math.h>
1329 #include <errno.h>
1330 
1331 /* Type Definitions */
1332 /* ================ */
1333 
1334 /* This structure contains information describing a single FITS header card
1335    in a circular list of such structures. */
1336 typedef struct FitsCard {
1337    char name[ FITSNAMLEN + 1 ];/* Keyword name (plus terminating null). */
1338    int type;                  /* Data type. */
1339    void *data;                /* Pointer to the keyword's data value. */
1340    char *comment;             /* Pointer to a comment for the keyword. */
1341    int flags;                 /* Flags for each card */
1342    size_t size;               /* Size of data value */
1343    struct FitsCard *next;     /* Pointer to next structure in list. */
1344    struct FitsCard *prev;     /* Pointer to previous structure in list. */
1345 } FitsCard;
1346 
1347 /* Structure used to store information derived from the FITS WCS keyword
1348    values in a form more convenient to further processing. Conventions
1349    for units, etc, for values in a FitsStore follow FITS-WCS (e.g. angular
1350    values are stored in degrees, equinox is B or J depending on RADECSYS,
1351    etc). */
1352 typedef struct FitsStore {
1353    char ****cname;
1354    char ****ctype;
1355    char ****ctype_com;
1356    char ****cunit;
1357    char ****radesys;
1358    char ****wcsname;
1359    char ****specsys;
1360    char ****ssyssrc;
1361    char ****ps;
1362    char ****timesys;
1363    double ***pc;
1364    double ***cdelt;
1365    double ***crpix;
1366    double ***crval;
1367    double ***equinox;
1368    double ***latpole;
1369    double ***lonpole;
1370    double ***mjdobs;
1371    double ***dut1;
1372    double ***mjdavg;
1373    double ***pv;
1374    double ***wcsaxes;
1375    double ***obsgeox;
1376    double ***obsgeoy;
1377    double ***obsgeoz;
1378    double ***restfrq;
1379    double ***restwav;
1380    double ***zsource;
1381    double ***velosys;
1382    double ***asip;
1383    double ***bsip;
1384    double ***apsip;
1385    double ***bpsip;
1386    double ***imagfreq;
1387    double ***axref;
1388    int naxis;
1389    AstKeyMap *tables;
1390    double ***skyref;
1391    double ***skyrefp;
1392    char ****skyrefis;
1393 } FitsStore;
1394 
1395 /* Module Variables. */
1396 /* ================= */
1397 
1398 /* Address of this static variable is used as a unique identifier for
1399    member of this class. */
1400 static int class_check;
1401 
1402 /* Pointers to parent class methods which are extended by this class. */
1403 static void (* parent_setsourcefile)( AstChannel *, const char *, int * );
1404 static int (* parent_getobjsize)( AstObject *, int * );
1405 static const char *(* parent_getattrib)( AstObject *, const char *, int * );
1406 static int (* parent_getfull)( AstChannel *, int * );
1407 static int (* parent_getskip)( AstChannel *, int * );
1408 static int (* parent_testattrib)( AstObject *, const char *, int * );
1409 static void (* parent_clearattrib)( AstObject *, const char *, int * );
1410 static void (* parent_setattrib)( AstObject *, const char *, int * );
1411 static int (* parent_write)( AstChannel *, AstObject *, int * );
1412 static AstObject *(* parent_read)( AstChannel *, int * );
1413 #if defined(THREAD_SAFE)
1414 static int (* parent_managelock)( AstObject *, int, int, AstObject **, int * );
1415 #endif
1416 
1417 /* Strings to describe each data type. These should be in the order implied
1418    by the corresponding macros (eg AST__FLOAT, etc). */
1419 static const char *type_names[9] = {"comment", "integer", "floating point",
1420                                     "string", "complex floating point",
1421                                     "complex integer", "logical",
1422                                     "continuation string", "undef" };
1423 
1424 /* Text values used to represent Encoding values externally. */
1425 
1426 static const char *xencod[8] = { NATIVE_STRING, FITSPC_STRING,
1427                                  DSS_STRING, FITSWCS_STRING,
1428                                  FITSIRAF_STRING, FITSAIPS_STRING,
1429                                  FITSAIPSPP_STRING, FITSCLASS_STRING };
1430 /* Define two variables to hold TimeFrames which will be used for converting
1431    MJD values between time scales. */
1432 static AstTimeFrame *tdbframe = NULL;
1433 static AstTimeFrame *timeframe = NULL;
1434 
1435 /* Max number of characters in a formatted int */
1436 static int int_dig;
1437 
1438 /* Define macros for accessing each item of thread specific global data. */
1439 #ifdef THREAD_SAFE
1440 
1441 /* Define how to initialise thread-specific globals. */
1442 #define GLOBAL_inits \
1443    globals->Class_Init = 0; \
1444    globals->GetAttrib_Buff[ 0 ] = 0; \
1445    globals->Items_Written = 0; \
1446    globals->Write_Nest = -1; \
1447    globals->Current_Indent = 0; \
1448    globals->Ignore_Used = 1; \
1449    globals->Mark_New = 0; \
1450    globals->CnvType_Text[ 0 ] = 0; \
1451    globals->CnvType_Text0[ 0 ] = 0; \
1452    globals->CnvType_Text1[ 0 ] = 0; \
1453    globals->CreateKeyword_Seq_Nchars = -1; \
1454    globals->FormatKey_Buff[ 0 ] = 0; \
1455    globals->FitsGetCom_Sval[ 0 ] = 0; \
1456    globals->IsSpectral_Ret = NULL; \
1457    globals->Match_Fmt[ 0 ] = 0; \
1458    globals->Match_Template = NULL; \
1459    globals->Match_PA = 0; \
1460    globals->Match_PB = 0; \
1461    globals->Match_NA = 0; \
1462    globals->Match_NB = 0; \
1463    globals->Match_Nentry = 0; \
1464    globals->WcsCelestial_Type[ 0 ] = 0; \
1465    globals->Ignore_Used = 1; \
1466    globals->Mark_New = 0;
1467 
1468 /* Create the function that initialises global data for this module. */
1469 astMAKE_INITGLOBALS(FitsChan)
1470 
1471 /* Define macros for accessing each item of thread specific global data. */
1472 #define class_init astGLOBAL(FitsChan,Class_Init)
1473 #define class_vtab astGLOBAL(FitsChan,Class_Vtab)
1474 #define getattrib_buff astGLOBAL(FitsChan,GetAttrib_Buff)
1475 #define items_written astGLOBAL(FitsChan,Items_Written)
1476 #define write_nest astGLOBAL(FitsChan,Write_Nest)
1477 #define current_indent astGLOBAL(FitsChan,Current_Indent)
1478 #define ignore_used astGLOBAL(FitsChan,Ignore_Used)
1479 #define mark_new astGLOBAL(FitsChan,Mark_New)
1480 #define cnvtype_text astGLOBAL(FitsChan,CnvType_Text)
1481 #define cnvtype_text0 astGLOBAL(FitsChan,CnvType_Text0)
1482 #define cnvtype_text1 astGLOBAL(FitsChan,CnvType_Text1)
1483 #define createkeyword_seq_nchars astGLOBAL(FitsChan,CreateKeyword_Seq_Nchars)
1484 #define formatkey_buff astGLOBAL(FitsChan,FormatKey_Buff)
1485 #define fitsgetcom_sval astGLOBAL(FitsChan,FitsGetCom_Sval)
1486 #define isspectral_ret astGLOBAL(FitsChan,IsSpectral_Ret)
1487 #define match_fmt astGLOBAL(FitsChan,Match_Fmt)
1488 #define match_template astGLOBAL(FitsChan,Match_Template)
1489 #define match_pa astGLOBAL(FitsChan,Match_PA)
1490 #define match_pb astGLOBAL(FitsChan,Match_PB)
1491 #define match_na astGLOBAL(FitsChan,Match_NA)
1492 #define match_nb astGLOBAL(FitsChan,Match_NB)
1493 #define match_nentry  astGLOBAL(FitsChan,Match_Nentry)
1494 #define wcscelestial_type astGLOBAL(FitsChan,WcsCelestial_Type)
1495 static pthread_mutex_t mutex2 = PTHREAD_MUTEX_INITIALIZER;
1496 #define LOCK_MUTEX2 pthread_mutex_lock( &mutex2 );
1497 #define UNLOCK_MUTEX2 pthread_mutex_unlock( &mutex2 );
1498 static pthread_mutex_t mutex3 = PTHREAD_MUTEX_INITIALIZER;
1499 #define LOCK_MUTEX3 pthread_mutex_lock( &mutex3 );
1500 #define UNLOCK_MUTEX3 pthread_mutex_unlock( &mutex3 );
1501 static pthread_mutex_t mutex4 = PTHREAD_MUTEX_INITIALIZER;
1502 #define LOCK_MUTEX4 pthread_mutex_lock( &mutex4 );
1503 #define UNLOCK_MUTEX4 pthread_mutex_unlock( &mutex4 );
1504 
1505 /* If thread safety is not needed, declare and initialise globals at static
1506    variables. */
1507 #else
1508 
1509 /* Buffer returned by GetAttrib. */
1510 static char getattrib_buff[ AST__FITSCHAN_GETATTRIB_BUFF_LEN + 1 ];
1511 
1512 /* Buffer for returned text string in CnvType */
1513 static char cnvtype_text[ AST__FITSCHAN_FITSCARDLEN + 1 ];
1514 
1515 /* Buffer for real value in CnvType */
1516 static char cnvtype_text0[ AST__FITSCHAN_FITSCARDLEN + 1 ];
1517 
1518 /* Buffer for imaginary value in CnvType */
1519 static char cnvtype_text1[ AST__FITSCHAN_FITSCARDLEN + 1 ];
1520 
1521 /* Number of output items written since the last "Begin" or "IsA"
1522    output item, and level of Object nesting during recursive
1523    invocation of the astWrite method. */
1524 static int items_written = 0;
1525 static int write_nest = -1;
1526 
1527 /* Indentation level for indented comments when writing Objects to a
1528    FitsChan. */
1529 static int current_indent = 0;
1530 
1531 /* Ignore_Used: If 2, then cards which have been marked as either "definitely
1532    used" or "provisionally used" (see the USED flag above) will be ignored
1533    when searching the FitsChan, etc (i.e. they will be treated as if they
1534    have been removed from the FitsChan). If 1, then cards which have been
1535    "definitely used" will be skipped over. If zero then no cards will be
1536    skipped over. */
1537 static int ignore_used = 1;
1538 
1539 /* Mark_New: If non-zero, then all cards added to the FitsChan will be
1540    marked with both the NEW1 and NEW2 flags (see above). If zero then
1541    new cards will not be marked with either NEW1 or NEW2. */
1542 static int mark_new = 0;
1543 
1544 /* Number of characters used for encoding */
1545 static int createkeyword_seq_nchars = -1;
1546 
1547 /* Buffer for value returned by FormatKey */
1548 static char formatkey_buff[ 10 ];
1549 
1550 /* Buffer for value returned by FitsGetCom */
1551 static char fitsgetcom_sval[ AST__FITSCHAN_FITSCARDLEN + 1 ];
1552 
1553 /* Pointer returned by IsSpectral */
1554 static const char *isspectral_ret = NULL;
1555 
1556 /* Format specifier for reading an integer field in Match */
1557 static char match_fmt[ 10 ];
1558 
1559 /* Pointer to start of template in Match */
1560 static const char *match_template = NULL;
1561 
1562 /* Pointer to first returned field value in Match */
1563 static int *match_pa = 0;
1564 
1565 /* Pointer to last returned field value in Match */
1566 static int *match_pb = 0;
1567 
1568 /* No. of characters read from the test string in Match */
1569 static int match_na = 0;
1570 
1571 /* No. of characters read from the template string in Match */
1572 static int match_nb = 0;
1573 
1574 /* Number of recursive entries into Match */
1575 static int match_nentry = 0;
1576 
1577 /* Buffer for celestial system in WcsCelestial */
1578 static char wcscelestial_type[ 4 ];
1579 
1580 /* Define the class virtual function table and its initialisation flag
1581    as static variables. */
1582 static AstFitsChanVtab class_vtab;   /* Virtual function table */
1583 static int class_init = 0;       /* Virtual function table initialised? */
1584 #define LOCK_MUTEX2
1585 #define UNLOCK_MUTEX2
1586 #define LOCK_MUTEX3
1587 #define UNLOCK_MUTEX3
1588 #define LOCK_MUTEX4
1589 #define UNLOCK_MUTEX4
1590 #endif
1591 
1592 /* External Interface Function Prototypes. */
1593 /* ======================================= */
1594 
1595 /* The following functions have public prototypes only (i.e. no
1596    protected prototypes), so we must provide local prototypes for use
1597    within this module. */
1598 AstFitsChan *astFitsChanForId_( const char *(*)( void ),
1599                            char *(*)( const char *(*)( void ), int * ),
1600                            void (*)( const char * ),
1601                            void (*)( void (*)( const char * ), const char *, int * ),
1602                            const char *, ... );
1603 AstFitsChan *astFitsChanId_( const char *(* source)( void ),
1604                              void (* sink)( const char * ),
1605                              const char *options, ... );
1606 
1607 /* Prototypes for Private Member Functions. */
1608 /* ======================================== */
1609 static int GetObjSize( AstObject *, int * );
1610 static void ClearCard( AstFitsChan *, int * );
1611 static int GetCard( AstFitsChan *, int * );
1612 static int TestCard( AstFitsChan *, int * );
1613 static void SetCard( AstFitsChan *, int, int * );
1614 static void ClearEncoding( AstFitsChan *, int * );
1615 static int GetEncoding( AstFitsChan *, int * );
1616 static int TestEncoding( AstFitsChan *, int * );
1617 static void SetEncoding( AstFitsChan *, int, int * );
1618 static void ClearCDMatrix( AstFitsChan *, int * );
1619 static int GetCDMatrix( AstFitsChan *, int * );
1620 static int TestCDMatrix( AstFitsChan *, int * );
1621 static void SetCDMatrix( AstFitsChan *, int, int * );
1622 static void ClearFitsDigits( AstFitsChan *, int * );
1623 static int GetFitsDigits( AstFitsChan *, int * );
1624 static int TestFitsDigits( AstFitsChan *, int * );
1625 static void SetFitsDigits( AstFitsChan *, int, int * );
1626 static void ClearFitsAxisOrder( AstFitsChan *, int * );
1627 static const char *GetFitsAxisOrder( AstFitsChan *, int * );
1628 static int TestFitsAxisOrder( AstFitsChan *, int * );
1629 static void SetFitsAxisOrder( AstFitsChan *, const char *, int * );
1630 static void ClearDefB1950( AstFitsChan *, int * );
1631 static int GetDefB1950( AstFitsChan *, int * );
1632 static int TestDefB1950( AstFitsChan *, int * );
1633 static void SetDefB1950( AstFitsChan *, int, int * );
1634 static void ClearTabOK( AstFitsChan *, int * );
1635 static int GetTabOK( AstFitsChan *, int * );
1636 static int TestTabOK( AstFitsChan *, int * );
1637 static void SetTabOK( AstFitsChan *, int, int * );
1638 static void ClearCarLin( AstFitsChan *, int * );
1639 static int GetCarLin( AstFitsChan *, int * );
1640 static int TestCarLin( AstFitsChan *, int * );
1641 static void SetCarLin( AstFitsChan *, int, int * );
1642 static void ClearPolyTan( AstFitsChan *, int * );
1643 static int GetPolyTan( AstFitsChan *, int * );
1644 static int TestPolyTan( AstFitsChan *, int * );
1645 static void SetPolyTan( AstFitsChan *, int, int * );
1646 static void ClearIwc( AstFitsChan *, int * );
1647 static int GetIwc( AstFitsChan *, int * );
1648 static int TestIwc( AstFitsChan *, int * );
1649 static void SetIwc( AstFitsChan *, int, int * );
1650 static void ClearClean( AstFitsChan *, int * );
1651 static int GetClean( AstFitsChan *, int * );
1652 static int TestClean( AstFitsChan *, int * );
1653 static void SetClean( AstFitsChan *, int, int * );
1654 static void ClearWarnings( AstFitsChan *, int * );
1655 static const char *GetWarnings( AstFitsChan *, int * );
1656 static int TestWarnings( AstFitsChan *, int * );
1657 static void SetWarnings( AstFitsChan *, const char *, int * );
1658 
1659 static AstFitsChan *SpecTrans( AstFitsChan *, int, const char *, const char *, int * );
1660 static AstFitsTable *GetNamedTable( AstFitsChan *, const char *, int, int, int, const char *, int * );
1661 static AstFrameSet *MakeFitsFrameSet( AstFitsChan *, AstFrameSet *, int, int, int, const char *, const char *, int * );
1662 static AstGrismMap *ExtractGrismMap( AstMapping *, int, AstMapping **, int * );
1663 static AstKeyMap *GetTables( AstFitsChan *, int * );
1664 static AstMapping *AddUnitMaps( AstMapping *, int, int, int * );
1665 static AstMapping *CelestialAxes( AstFitsChan *this, AstFrameSet *, double *, int *, char, FitsStore *, int *, int, const char *, const char *, int * );
1666 static AstMapping *GrismSpecWcs( char *, FitsStore *, int, char, AstSpecFrame *, const char *, const char *, int * );
1667 static AstMapping *IsMapTab1D( AstMapping *, double, const char *, AstFrame *, double *, int, int, AstFitsTable **, int *, int *, int *, int * );
1668 static AstMapping *IsMapTab2D( AstMapping *, double, const char *, AstFrame *, double *, int, int, int, int, AstFitsTable **, int *, int *, int *, int *, int *, int *, int *, int *, int * );
1669 static AstMapping *LinearWcs( FitsStore *, int, char, const char *, const char *, int * );
1670 static AstMapping *LogAxis( AstMapping *, int, int, double *, double *, double, int * );
1671 static AstMapping *LogWcs( FitsStore *, int, char, const char *, const char *, int * );
1672 static AstMapping *MakeColumnMap( AstFitsTable *, const char *, int, int, const char *, const char *, int * );
1673 static AstMapping *NonLinSpecWcs( AstFitsChan *, char *, FitsStore *, int, char, AstSpecFrame *, const char *, const char *, int * );
1674 static AstMapping *OtherAxes( AstFitsChan *, AstFrameSet *, double *, int *, char, FitsStore *, double *, int *, const char *, const char *, int * );
1675 static AstMapping *SIPMapping( double *, FitsStore *, char, int, const char *, const char *, int * );
1676 static AstMapping *SpectralAxes( AstFitsChan *, AstFrameSet *, double *, int *, char, FitsStore *, double *, int *, const char *, const char *, int * );
1677 static AstMapping *TabMapping( AstFitsChan *, FitsStore *, char, int **, const char *, const char *, int *);
1678 static AstMapping *WcsCelestial( AstFitsChan *, FitsStore *, char, AstFrame **, AstFrame *, double *, double *, AstSkyFrame **, AstMapping **, int *, const char *, const char *, int * );
1679 static AstMapping *WcsIntWorld( AstFitsChan *, FitsStore *, char, int, const char *, const char *, int * );
1680 static AstMapping *WcsMapFrm( AstFitsChan *, FitsStore *, char, AstFrame **, const char *, const char *, int * );
1681 static AstMapping *WcsNative( AstFitsChan *, FitsStore *, char, AstWcsMap *, int, int, const char *, const char *, int * );
1682 static AstMapping *WcsOthers( AstFitsChan *, FitsStore *, char, AstFrame **, AstFrame *, const char *, const char *, int * );
1683 static AstMapping *WcsSpectral( AstFitsChan *, FitsStore *, char, AstFrame **, AstFrame *, double, double, AstSkyFrame *, const char *, const char *, int * );
1684 static AstMapping *ZPXMapping( AstFitsChan *, FitsStore *, char, int, int[2], const char *, const char *, int * );
1685 static AstMatrixMap *WcsCDeltMatrix( FitsStore *, char, int, const char *, const char *, int * );
1686 static AstMatrixMap *WcsPCMatrix( FitsStore *, char, int, const char *, const char *, int * );
1687 static AstObject *FsetFromStore( AstFitsChan *, FitsStore *, const char *, const char *, int * );
1688 static AstObject *Read( AstChannel *, int * );
1689 static AstSkyFrame *WcsSkyFrame( AstFitsChan *, FitsStore *, char, int, char *, int, int, const char *, const char *, int * );
1690 static AstTimeScaleType TimeSysToAst( AstFitsChan *, const char *, const char *, const char *, int * );
1691 static AstWinMap *WcsShift( FitsStore *, char, int, const char *, const char *, int * );
1692 static FitsCard *GetLink( FitsCard *, int, const char *, const char *, int * );
1693 static FitsStore *FitsToStore( AstFitsChan *, int, const char *, const char *, int * );
1694 static FitsStore *FreeStore( FitsStore *, int * );
1695 static FitsStore *FsetToStore( AstFitsChan *, AstFrameSet *, int, double *, int, const char *, const char *, int * );
1696 static char *CardComm( AstFitsChan *, int * );
1697 static char *CardName( AstFitsChan *, int * );
1698 static char *ConcatWAT( AstFitsChan *, int, const char *, const char *, int * );
1699 static char *FormatKey( const char *, int, int, char, int * );
1700 static char *GetItemC( char *****, int, int, char, char *, const char *method, const char *class, int * );
1701 static char *SourceWrap( const char *(*)( void ), int * );
1702 static char *UnPreQuote( const char *, int * );
1703 static char GetMaxS( double ****item, int * );
1704 static const char *GetAllWarnings( AstFitsChan *, int * );
1705 static const char *GetAttrib( AstObject *, const char *, int * );
1706 static const char *GetCardComm( AstFitsChan *, int * );
1707 static const char *GetCardName( AstFitsChan *, int * );
1708 static const char *GetFitsSor( const char *, int * );
1709 static const char *IsSpectral( const char *, char[5], char[5], int * );
1710 static double **OrthVectorSet( int, int, double **, int * );
1711 static double *Cheb2Poly( double *, int, int, double, double, double, double, int * );
1712 static double *FitLine( AstMapping *, double *, double *, double *, double, double *, int * );
1713 static double *OrthVector( int, int, double **, int * );
1714 static double *ReadCrval( AstFitsChan *, AstFrame *, char, const char *, const char *, int * );
1715 static double ChooseEpoch( AstFitsChan *, FitsStore *, char, const char *, const char *, int * );
1716 static double DateObs( const char *, int * );
1717 static double GetItem( double ****, int, int, char, char *, const char *method, const char *class, int * );
1718 static double NearestPix( AstMapping *, double, int, int * );
1719 static double TDBConv( double, int, int, const char *, const char *, int * );
1720 static int *CardFlags( AstFitsChan *, int * );
1721 static int AIPSFromStore( AstFitsChan *, FitsStore *, const char *, const char *, int * );
1722 static int AIPSPPFromStore( AstFitsChan *, FitsStore *, const char *, const char *, int * );
1723 static int AddEncodingFrame( AstFitsChan *, AstFrameSet *, int, const char *, const char *, int * );
1724 static int AddVersion( AstFitsChan *, AstFrameSet *, int, int, FitsStore *, double *, char, int, int, const char *, const char *, int * );
1725 static int CLASSFromStore( AstFitsChan *, FitsStore *, AstFrameSet *, double *, const char *, const char *, int * );
1726 static int CardType( AstFitsChan *, int * );
1727 static int CheckFitsName( const char *, const char *, const char *, int * );
1728 static int ChrLen( const char *, int * );
1729 static int CnvType( int, void *, size_t, int, int, void *, const char *, const char *, const char *, int * );
1730 static int CnvValue( AstFitsChan *, int , int, void *, const char *, int * );
1731 static int ComBlock( AstFitsChan *, int, const char *, const char *, int * );
1732 static int CountFields( const char *, char, const char *, const char *, int * );
1733 static int DSSFromStore( AstFitsChan *, FitsStore *, const char *, const char *, int * );
1734 static int EncodeFloat( char *, int, int, int, double, int * );
1735 static int EncodeValue( AstFitsChan *, char *, int, int, const char *, int * );
1736 static int FindBasisVectors( AstMapping *, int, int, double *, AstPointSet *, AstPointSet *, int * );
1737 static int FindFits( AstFitsChan *, const char *, char[ AST__FITSCHAN_FITSCARDLEN + 1 ], int, int * );
1738 static int FindKeyCard( AstFitsChan *, const char *, const char *, const char *, int * );
1739 static int FindLonLatSpecAxes( FitsStore *, char, int *, int *, int *, const char *, const char *, int * );
1740 static int FindString( int, const char *[], const char *, const char *, const char *, const char *, int * );
1741 static int FitOK( int, double *, double *, double, int * );
1742 static int FitsAxisOrder( AstFitsChan *this, int nwcs, AstFrame *wcsfrm, int *perm, int *status );
1743 static int FitsEof( AstFitsChan *, int * );
1744 static int FitsFromStore( AstFitsChan *, FitsStore *, int, double *, AstFrameSet *, const char *, const char *, int * );
1745 static int FitsGetCom( AstFitsChan *, const char *, char **, int * );
1746 static int FitsSof( AstFitsChan *, int * );
1747 static int FullForm( const char *, const char *, int, int * );
1748 static int GetCardType( AstFitsChan *, int * );
1749 static int GetFiducialWCS( AstWcsMap *, AstMapping *, int, int, double *, double *, int * );
1750 static int GetFitsCF( AstFitsChan *, const char *, double *, int * );
1751 static int GetFitsCI( AstFitsChan *, const char *, int *, int * );
1752 static int GetFitsCN( AstFitsChan *, const char *, char **, int * );
1753 static int GetFitsF( AstFitsChan *, const char *, double *, int * );
1754 static int GetFitsI( AstFitsChan *, const char *, int *, int * );
1755 static int GetFitsL( AstFitsChan *, const char *, int *, int * );
1756 static int GetFitsS( AstFitsChan *, const char *, char **, int * );
1757 static int GetFull( AstChannel *, int * );
1758 static int GetMaxI( double ****item, char, int * );
1759 static int GetMaxJM( double ****item, char, int * );
1760 static int GetMaxJMC( char *****item, char, int * );
1761 static int GetNcard( AstFitsChan *, int * );
1762 static int GetNkey( AstFitsChan *, int * );
1763 static int GetSkip( AstChannel *, int * );
1764 static int GetUsedPolyTan( AstFitsChan *, AstFitsChan *, int, int, char, const char *, const char *, int * );
1765 static int GetValue( AstFitsChan *, const char *, int, void *, int, int, const char *, const char *, int * );
1766 static int GetValue2( AstFitsChan *, AstFitsChan *, const char *, int, void *, int, const char *, const char *, int * );
1767 static int GoodWarns( const char *, int * );
1768 static int HasAIPSSpecAxis( AstFitsChan *, const char *, const char *, int * );
1769 static int HasCard( AstFitsChan *, const char *, const char *, const char *, int * );
1770 static int IRAFFromStore( AstFitsChan *, FitsStore *, const char *, const char *, int * );
1771 static int IsAIPSSpectral( const char *, char **, char **, int * );
1772 static int IsMapLinear( AstMapping *, const double [], const double [], int, int * );
1773 static int IsSkyOff( AstFrameSet *, int, int * );
1774 static int KeyFields( AstFitsChan *, const char *, int, int *, int *, int * );
1775 static int LooksLikeClass( AstFitsChan *, const char *, const char *, int * );
1776 static int MakeBasisVectors( AstMapping *, int, int, double *, AstPointSet *, AstPointSet *, int * );
1777 static int MakeIntWorld( AstMapping *, AstFrame *, int *, char, FitsStore *, double *, const char *, const char *, int * );
1778 static int Match( const char *, const char *, int, int *, int *, const char *, const char *, int * );
1779 static int MatchChar( char, char, const char *, const char *, const char *, int * );
1780 static int MatchFront( const char *, const char *, char *, int *, int *, int *, const char *, const char *, const char *, int * );
1781 static int MoveCard( AstFitsChan *, int, const char *, const char *, int * );
1782 static int PCFromStore( AstFitsChan *, FitsStore *, const char *, const char *, int * );
1783 static int SAOTrans( AstFitsChan *, AstFitsChan *, const char *, const char *, int * );
1784 static int SearchCard( AstFitsChan *, const char *, const char *, const char *, int * );
1785 static int SetFits( AstFitsChan *, const char *, void *, int, const char *, int, int * );
1786 static int Similar( const char *, const char *, int * );
1787 static int SkySys( AstFitsChan *, AstSkyFrame *, int, int, FitsStore *, int, int, char c, int, const char *, const char *, int * );
1788 static int Split( const char *, char **, char **, char **, const char *, const char *, int * );
1789 static int SplitMap( AstMapping *, int, int, int, AstMapping **, AstWcsMap **, AstMapping **, int * );
1790 static int SplitMap2( AstMapping *, int, AstMapping **, AstWcsMap **, AstMapping **, int * );
1791 static int SplitMat( int , double *, double *, int * );
1792 static int TestAttrib( AstObject *, const char *, int * );
1793 static int TestFits( AstFitsChan *, const char *, int *, int * );
1794 static int Use( AstFitsChan *, int, int, int * );
1795 static int Ustrcmp( const char *, const char *, int * );
1796 static int Ustrncmp( const char *, const char *, size_t, int * );
1797 static int WATCoeffs( const char *, int, double **, int **, int *, int * );
1798 static int WcsFromStore( AstFitsChan *, FitsStore *, const char *, const char *, int * );
1799 static int WcsNatPole( AstFitsChan *, AstWcsMap *, double, double, double, double *, double *, double *, int * );
1800 static int WorldAxes( AstFitsChan *this, AstMapping *, double *, int *, int * );
1801 static int Write( AstChannel *, AstObject *, int * );
1802 static void *CardData( AstFitsChan *, size_t *, int * );
1803 static void AdaptLut( AstMapping *, int, double, double, double, double, double, double **, double **, int *, int * );
1804 static void AddFrame( AstFitsChan *, AstFrameSet *, int, int, FitsStore *, char, const char *, const char *, int * );
1805 static void ChangePermSplit( AstMapping *, int * );
1806 static void CheckZero( char *, double, int, int * );
1807 static void Chpc1( double *, double *, int, int *, int *, int * );
1808 static void ClassTrans( AstFitsChan *, AstFitsChan *, int, int, const char *, const char *, int * );
1809 static void ClearAttrib( AstObject *, const char *, int * );
1810 static void Copy( const AstObject *, AstObject *, int * );
1811 static void CreateKeyword( AstFitsChan *, const char *, char [ FITSNAMLEN + 1 ], int * );
1812 static void DSBSetUp( AstFitsChan *, FitsStore *, AstDSBSpecFrame *, char, double, const char *, const char *, int * );
1813 static void DSSToStore( AstFitsChan *, FitsStore *, const char *, const char *, int * );
1814 static void DelFits( AstFitsChan *, int * );
1815 static void Delete( AstObject *, int * );
1816 static void DeleteCard( AstFitsChan *, const char *, const char *, int * );
1817 static void DistortMaps( AstFitsChan *, FitsStore *, char, int , AstMapping **, AstMapping **, AstMapping **, AstMapping **, const char *, const char *, int * );
1818 static void Dump( AstObject *, AstChannel *, int * );
1819 static void EmptyFits( AstFitsChan *, int * );
1820 static void FindWcs( AstFitsChan *, int, int, int, const char *, const char *, int * );
1821 static void FixNew( AstFitsChan *, int, int, const char *, const char *, int * );
1822 static void FixUsed( AstFitsChan *, int, int, int, const char *, const char *, int * );
1823 static void FormatCard( AstFitsChan *, char *, const char *, int * );
1824 static void FreeItem( double ****, int * );
1825 static void FreeItemC( char *****, int * );
1826 static void GetFiducialNSC( AstWcsMap *, double *, double *, int * );
1827 static void GetFiducialPPC( AstWcsMap *, double *, double *, int * );
1828 static void GetNextData( AstChannel *, int, char **, char **, int * );
1829 static void InsCard( AstFitsChan *, int, const char *, int, void *, const char *, const char *, const char *, int * );
1830 static void MakeBanner( const char *, const char *, const char *, char [ AST__FITSCHAN_FITSCARDLEN - FITSNAMLEN + 1 ], int * );
1831 static void MakeIndentedComment( int, char, const char *, const char *, char [ AST__FITSCHAN_FITSCARDLEN - FITSNAMLEN + 1], int * );
1832 static void MakeIntoComment( AstFitsChan *, const char *, const char *, int * );
1833 static void MakeInvertable( double **, int, double *, int * );
1834 static void MarkCard( AstFitsChan *, int * );
1835 static void NewCard( AstFitsChan *, const char *, int, const void *, const char *, int, int * );
1836 static void PreQuote( const char *, char [ AST__FITSCHAN_FITSCARDLEN - FITSNAMLEN - 3 ], int * );
1837 static void PurgeWCS( AstFitsChan *, int * );
1838 static void PutCards( AstFitsChan *, const char *, int * );
1839 static void PutFits( AstFitsChan *, const char [ AST__FITSCHAN_FITSCARDLEN + 1 ], int, int * );
1840 static void PutTable( AstFitsChan *, AstFitsTable *, const char *, int * );
1841 static void PutTables( AstFitsChan *, AstKeyMap *, int * );
1842 static void ReadFits( AstFitsChan *, int * );
1843 static void ReadFromSource( AstFitsChan *, int * );
1844 static void RemoveTables( AstFitsChan *, const char *, int * );
1845 static void RetainFits( AstFitsChan *, int * );
1846 static void RoundFString( char *, int, int * );
1847 static void SetAlgCode( char *, const char *, int * );
1848 static void SetAttrib( AstObject *, const char *, int * );
1849 static void SetFitsCF( AstFitsChan *, const char *, double *, const char *, int, int * );
1850 static void SetFitsCI( AstFitsChan *, const char *, int *, const char *, int, int * );
1851 static void SetFitsCM( AstFitsChan *, const char *, int, int * );
1852 static void SetFitsCN( AstFitsChan *, const char *, const char *, const char *, int, int * );
1853 static void SetFitsCom( AstFitsChan *, const char *, const char *, int, int * );
1854 static void SetFitsF( AstFitsChan *, const char *, double, const char *, int, int * );
1855 static void SetFitsI( AstFitsChan *, const char *, int, const char *, int, int * );
1856 static void SetFitsL( AstFitsChan *, const char *, int, const char *, int, int * );
1857 static void SetFitsS( AstFitsChan *, const char *, const char *, const char *, int, int * );
1858 static void SetFitsU( AstFitsChan *, const char *, const char *, int, int * );
1859 static void SetItem( double ****, int, int, char, double, int * );
1860 static void SetItemC( char *****, int, int, char, const char *, int * );
1861 static void SetSourceFile( AstChannel *, const char *, int * );
1862 static void SetValue( AstFitsChan *, const char *, void *, int, const char *, int * );
1863 static void ShowFits( AstFitsChan *, int * );
1864 static void Shpc1( double, double, int, double *, double *, int * );
1865 static void SinkWrap( void (*)( const char * ), const char *, int * );
1866 static void SkyPole( AstWcsMap *, AstMapping *, int, int, int *, char, FitsStore *, const char *, const char *, int * );
1867 static void TableSource( AstFitsChan *, void (*)( AstFitsChan *, const char *, int, int, int * ), int * );
1868 static void TidyOffsets( AstFrameSet *, int * );
1869 static void Warn( AstFitsChan *, const char *, const char *, const char *, const char *, int * );
1870 static void WcsFcRead( AstFitsChan *, AstFitsChan *, FitsStore *, const char *, const char *, int * );
1871 static void WcsToStore( AstFitsChan *, AstFitsChan *, FitsStore *, const char *, const char *, int * );
1872 static void WriteBegin( AstChannel *, const char *, const char *, int * );
1873 static void WriteDouble( AstChannel *, const char *, int, int, double, const char *, int * );
1874 static void WriteEnd( AstChannel *, const char *, int * );
1875 static void WriteFits( AstFitsChan *, int * );
1876 static void WriteInt( AstChannel *, const char *, int, int, int, const char *, int * );
1877 static void WriteIsA( AstChannel *, const char *, const char *, int * );
1878 static void WriteObject( AstChannel *, const char *, int, int, AstObject *, const char *, int * );
1879 static void WriteString( AstChannel *, const char *, int, int, const char *, const char *, int * );
1880 static void WriteToSink( AstFitsChan *, int * );
1881 static void SetTableSource( AstFitsChan *,
1882                             void (*)( void ),
1883                             void (*)( void (*)( void ),
1884                                       AstFitsChan *, const char *, int, int, int * ), int * );
1885 static void TabSourceWrap( void (*)( void ),
1886                            AstFitsChan *, const char *, int, int, int * );
1887 #if defined(THREAD_SAFE)
1888 static int ManageLock( AstObject *, int, int, AstObject **, int * );
1889 #endif
1890 
1891 /* Member functions. */
1892 /* ================= */
1893 
AdaptLut(AstMapping * map,int npos,double eps,double x0,double x1,double v0,double v1,double ** xtab,double ** vtab,int * nsamp,int * status)1894 static void AdaptLut( AstMapping *map, int npos, double eps, double x0,
1895                       double x1, double v0, double v1, double **xtab,
1896                       double **vtab, int *nsamp, int *status ){
1897 /*
1898 *  Name:
1899 *     AdaptLut
1900 
1901 *  Purpose:
1902 *     Create a table of optimally sampled values for a Mapping.
1903 
1904 *  Type:
1905 *     Private function.
1906 
1907 *  Synopsis:
1908 *     void AdaptLut( AstMapping *map, int npos, double eps, double x0,
1909 *                    double x1, double v0, double v1, double **xtab,
1910 *                    double **vtab, int *nsamp, int *status )
1911 
1912 *  Class Membership:
1913 *     FitsChan
1914 
1915 *  Description:
1916 *     This function returns a look-up table holding samples of the supplied
1917 *     1D mapping. The input values at which the samples are taken are
1918 *     returned in the "xtab" array, and the Mapping output values at
1919 *     these input values are returned in the "vtab" array. The sample
1920 *     spacing is smaller at positions where the output gradient is
1921 *     changing more rapidly (i.e. where the output is more non-linear).
1922 
1923 *  Parameters:
1924 *     map
1925 *        Pointer to the Mapping. Should have 1 input and 1 output.
1926 *     npos
1927 *        The minimum number of samples to place within the interval to be
1928 *         sampled, excluding the two end points (which are always sampeld
1929 *         anyway). These samples are placed evenly through the [x0,x1]
1930           interval. The interval between adjacent samples will be further
1931 *         subdivided if necessary by calling this function recursively.
1932 *     eps
1933 *        The maximum error in X (i.e. the Mapping input) allowed before
1934 *        the supplied interval is subdivided further by a recursive call
1935 *        to this function.
1936 *     x0
1937 *        The Mapping input value at the start of the interval to be sampled.
1938 *        It is assumed that this value is already stored in (*xtab)[0] on
1939 *        entry.
1940 *     x1
1941 *        The Mapping input value at the end of the interval to be sampled.
1942 *     v0
1943 *        The Mapping output value at the start of the interval to be sampled.
1944 *        It is assumed that this value is already stored in (*vtab)[0] on
1945 *        entry.
1946 *     v1
1947 *        The Mapping output value at the end of the interval to be sampled.
1948 *     xtab
1949 *        Address of a pointer to the array in which to store the Mapping
1950 *        input values at which samples were taken. The supplied pointer
1951 *        may be changed on exit to point to a larger array. New values
1952 *        are added to the end of this array. The initial size of the array
1953 *        is given by the supplied value for "*nsamp"
1954 *     vtab
1955 *        Address of a pointer to the array in which to store the Mapping
1956 *        output value at each sample. The supplied pointer may be changed
1957 *        on exit to point to a larger array. New values are added to the
1958 *        end of this array. The initial size of the array is given by the
1959 *        supplied value for "*nsamp".
1960 *     nsamp
1961 *        Address of an int holding the number of values in the "*xtab"
1962 *        and "*ytab" arrays. Updated on exit to include the new values
1963 *        added to the arrays by this function.
1964 *     status
1965 *        Pointer to the inherited status variable.
1966 
1967 *  Returned Value:
1968 *     The size of the returned xtab and vtab arrays.
1969 */
1970 
1971 /* Local Variables: */
1972    double *vv;               /* Pointer to Mapping output values */
1973    double *xx;               /* Pointer to Mapping input values */
1974    double dx;                /* Step between sample positions */
1975    double rg;                /* Reciprocal of gradient of (x0,v0)->(x1,v1) line */
1976    double xx0;               /* X at first new sample position */
1977    int ipos;                 /* Interior sample index */
1978    int isamp;                /* Index into extended xtab and vtab arrays. */
1979    int subdivide;            /* Subdivide each subinterval? */
1980 
1981 /* Check the inherited status. */
1982    if( !astOK ) return;
1983 
1984 /* Allocate work space. */
1985    xx = astMalloc( sizeof( double )*npos );
1986    vv = astMalloc( sizeof( double )*npos );
1987    if( astOK ) {
1988 
1989 /* Set up the evenly spaced interior sample positions. */
1990       dx = ( x1 - x0 )/( npos + 1 );
1991       xx0 = x0 + dx;
1992       for( ipos = 0; ipos < npos; ipos++ ) {
1993          xx[ ipos ] = xx0 + ipos*dx;
1994       }
1995 
1996 /* Find the Mapping output values at these input values. */
1997       astTran1( map, npos, xx, 1, vv );
1998 
1999 /* See if any of these samples deviate significantly from the straight line
2000    defined by (x0,v0) and (x1,v1). If any such sample is found, we call
2001    this function recursively to sample the subdivided intervals. First
2002    handle cases where the straight line has zero gradient. */
2003       subdivide = 0;
2004       if( v0 == v1 ) {
2005 
2006 /* Subdivide if any of the interior sample values are different to the
2007    end values. */
2008          for( ipos = 0; ipos < npos; ipos++ ) {
2009             if( vv[ ipos ] != v0 ) {
2010                subdivide = 1;
2011                break;
2012             }
2013          }
2014 
2015 /* Now handle cases where the line has non-zero gradient. Subdivide if any
2016    of the interior sample input positions are further than "eps" from the
2017    input position that would give the same output value if the mapping was
2018    linear. */
2019       } else {
2020          rg = ( x1 - x0 )/( v1 - v0 );
2021          for( ipos = 0; ipos < npos; ipos++ ) {
2022             if( vv[ ipos ] == AST__BAD ||
2023                 fabs( rg*( vv[ ipos ] - v0 ) - ( xx[ ipos ] - x0 ) ) > eps ) {
2024                subdivide = 1;
2025                break;
2026             }
2027          }
2028       }
2029 
2030 /* If required, call this function recursively to subdivide each section
2031    of the supplied input interval, and append samples to the returned
2032    arrays. */
2033       if( subdivide ) {
2034 
2035 /* Do each sub-interval, except the last one. The number of subintervals
2036    is one more than the number of interior samples. */
2037          for( ipos = 0; ipos < npos; ipos++ ) {
2038 
2039 /* Append samples covering the current subinterval to the ends of the
2040    arrays. */
2041             AdaptLut( map, npos, eps, x0, xx[ ipos ], v0, vv[ ipos ],
2042                       xtab, vtab, nsamp, status );
2043 
2044 /* Store the starting position for the next sub-interval. */
2045             x0 = xx[ ipos ];
2046             v0 = vv[ ipos ];
2047          }
2048 
2049 /* Now do the final sub-interval. */
2050          AdaptLut( map, npos, eps, x0, x1, v0, v1, xtab, vtab, nsamp, status );
2051 
2052 /* If we do not need to subdivide, store the samples in the returned
2053    array, together with the supplied final point. */
2054       } else {
2055 
2056 /* Extend the arrays. */
2057          isamp = *nsamp;
2058          *nsamp += npos + 1;
2059          *xtab = astGrow( *xtab, *nsamp, sizeof( double ) );
2060          *vtab = astGrow( *vtab, *nsamp, sizeof( double ) );
2061          if( astOK ) {
2062 
2063 /* Store the sample positions and values at the end of the extended
2064    arrays. */
2065             for( ipos = 0; ipos < npos; ipos++, isamp++ ) {
2066                (*xtab)[ isamp ] = xx[ ipos ];
2067                (*vtab)[ isamp ] = vv[ ipos ];
2068             }
2069             (*xtab)[ isamp ] = x1;
2070             (*vtab)[ isamp ] = v1;
2071          }
2072       }
2073    }
2074 
2075 /* Free resources. */
2076    xx = astFree( xx );
2077    vv= astFree( vv );
2078 }
2079 
AddEncodingFrame(AstFitsChan * this,AstFrameSet * fs,int encoding,const char * method,const char * class,int * status)2080 static int AddEncodingFrame( AstFitsChan *this, AstFrameSet *fs, int encoding,
2081                              const char *method, const char *class, int *status ){
2082 
2083 /*
2084 *  Name:
2085 *     AddEncodingFrame
2086 
2087 *  Purpose:
2088 *     Add a Frame which conforms to the requirements of the specified encoding.
2089 
2090 *  Type:
2091 *     Private function.
2092 
2093 *  Synopsis:
2094 *     int AddEncodingFrame( AstFitsChan *this, AstFrameSet *fs, int encoding,
2095 *                           const char *method, const char *class, int *status )
2096 
2097 *  Class Membership:
2098 *     FitsChan
2099 
2100 *  Description:
2101 *     This function attempts to create a Frame based on the current Frame
2102 *     of the supplied FrameSet, which conforms to the requirements of the
2103 *     specified Encoding. If created, this Frame is added into the
2104 *     FrameSet as the new current Frame, and the index of the original current
2105 *     Frame is returned.
2106 
2107 *  Parameters:
2108 *     this
2109 *        Pointer to the FitsChan.
2110 *     fs
2111 *        Pointer to the FrameSet.
2112 *     encoding
2113 *        The encoding in use.
2114 *     method
2115 *        Pointer to a string holding the name of the calling method.
2116 *        This is only for use in constructing error messages.
2117 *     class
2118 *        Pointer to a string holding the name of the supplied object class.
2119 *        This is only for use in constructing error messages.
2120 *     status
2121 *        Pointer to the inherited status variable.
2122 
2123 *  Returned Value:
2124 *     The index of the original current Frame in the FrameSet. A value of
2125 *     AST__NOFRAME is returned if no new Frame is added to the FrameSet,
2126 *     or if an error occurs.
2127 */
2128 
2129 /* Local Variables: */
2130    AstCmpFrame *cmpfrm;   /* Pointer to spectral cube frame */
2131    AstFrame *cfrm;        /* Pointer to original current Frame */
2132    AstFrame *newfrm;      /* Frame describing coord system to be used */
2133    AstFrame *pfrm;        /* Pointer to primary Frame containing axis */
2134    AstFrameSet *fsconv;   /* FrameSet converting what we have to what we want */
2135    AstMapping *map;       /* Mapping from what we have to what we want */
2136    AstSkyFrame *skyfrm;   /* Pointer to SkyFrame */
2137    AstSpecFrame *specfrm; /* Pointer to SpecFrame */
2138    AstSystemType sys;     /* Frame coordinate system */
2139    int i;                 /* Axis index */
2140    int naxc;              /* No. of axes in original current Frame */
2141    int paxis;             /* Axis index in primary frame */
2142    int result;            /* Returned value */
2143 
2144 /* Initialise */
2145    result = AST__NOFRAME;
2146 
2147 /* Check the inherited status. */
2148    if( !astOK ) return result;
2149 
2150 /* Get a pointer to the current Frame and note how many axes it has. */
2151    cfrm = astGetFrame( fs, AST__CURRENT );
2152    naxc = astGetNaxes( cfrm );
2153 
2154 /* FITS-CLASS */
2155 /* ========== */
2156    if( encoding == FITSCLASS_ENCODING ) {
2157 
2158 /* Try to locate a SpecFrame and a SkyFrame in the current Frame. */
2159       specfrm = NULL;
2160       skyfrm = NULL;
2161       for( i = 0; i < naxc; i++ ) {
2162          astPrimaryFrame( cfrm, i, &pfrm, &paxis );
2163          if( astIsASpecFrame( pfrm ) ) {
2164             if( !specfrm ) specfrm = astCopy( pfrm );
2165          } else if( astIsASkyFrame( pfrm ) ) {
2166             if( !skyfrm ) skyfrm = astCopy( pfrm );
2167          }
2168          pfrm = astAnnul( pfrm );
2169       }
2170 
2171 /* Cannot do anything if either is missing. */
2172       if( specfrm && skyfrm ) {
2173 
2174 /* If the spectral axis is not frequency, set it to frequency. Also set
2175    spectral units of "Hz". */
2176          sys = astGetSystem( specfrm );
2177          if( sys != AST__FREQ ) {
2178             astSetSystem( specfrm, AST__FREQ );
2179             sys = AST__FREQ;
2180          }
2181 
2182 /* Ensure the standard of rest is Source and units are "Hz". */
2183          astSetUnit( specfrm, 0, "Hz" );
2184          astSetStdOfRest( specfrm, AST__SCSOR );
2185 
2186 /* The celestial axes must be either FK4, FK5 or galactic. */
2187          sys = astGetSystem( skyfrm );
2188          if( sys != AST__FK4 && sys != AST__FK5 && sys != AST__GALACTIC ) {
2189             astSetSystem( skyfrm, AST__FK5 );
2190             sys = AST__FK5;
2191          }
2192 
2193 /* FK5 systems must be J2000, and FK4 must be B1950. */
2194          if( sys == AST__FK5 ) {
2195             astSetC( skyfrm, "Equinox", "J2000.0" );
2196          } else if( sys == AST__FK4 ) {
2197             astSetC( skyfrm, "Equinox", "B1950.0" );
2198          }
2199 
2200 /* Combine the spectral and celestial Frames into a single CmpFrame with
2201    the spectral axis being the first axis. */
2202          cmpfrm = astCmpFrame( specfrm, skyfrm, "", status );
2203 
2204 /* Attempt to obtain the current Frame of the supplied FrameSet to this
2205    new Frame. */
2206          fsconv = astConvert( cfrm, cmpfrm, "" );
2207          if( fsconv ) {
2208 
2209 /* Get the Mapping and current Frame from the rconversion FrameSet. */
2210             newfrm = astGetFrame( fsconv, AST__CURRENT );
2211             map = astGetMapping( fsconv, AST__BASE, AST__CURRENT );
2212 
2213 /* Save the original current Frame index. */
2214             result = astGetCurrent( fs );
2215 
2216 /* Add the new Frame into the supplied FrameSet using the above Mapping
2217    to connect it to the original current Frame. The new Frame becomes the
2218    current Frame. */
2219             astAddFrame( fs, AST__CURRENT, map, newfrm );
2220 
2221 /* Free resources */
2222             map = astAnnul( map );
2223             newfrm = astAnnul( newfrm );
2224             fsconv = astAnnul( fsconv );
2225          }
2226 
2227 /* Free resources */
2228          cmpfrm = astAnnul( cmpfrm );
2229       }
2230 
2231 /* Release resources. */
2232       if( specfrm ) specfrm = astAnnul( specfrm );
2233       if( skyfrm ) skyfrm = astAnnul( skyfrm );
2234    }
2235 
2236 /* Free reources. */
2237    cfrm = astAnnul( cfrm );
2238 
2239 /* Return the result */
2240    return result;
2241 }
2242 
AddFrame(AstFitsChan * this,AstFrameSet * fset,int pixel,int npix,FitsStore * store,char s,const char * method,const char * class,int * status)2243 static void AddFrame( AstFitsChan *this, AstFrameSet *fset, int pixel,
2244                       int npix, FitsStore *store, char s, const char *method,
2245                       const char *class, int *status ){
2246 /*
2247 *  Name:
2248 *     AddFrame
2249 
2250 *  Purpose:
2251 *     Create a Frame describing a set of axes with a given co-ordinate
2252 *     version, and add it to the supplied FrameSet.
2253 
2254 *  Type:
2255 *     Private function.
2256 
2257 *  Synopsis:
2258 *     #include "fitschan.h"
2259 *     void AddFrame( AstFitsChan *this, AstFrameSet *fset, int pixel,
2260 *                    int npix, FitsStore *store, char s, const char *method,
2261 *                    const char *class, int *status )
2262 
2263 *  Class Membership:
2264 *     FitsChan member function.
2265 
2266 *  Description:
2267 *     A Frame is created describing axis with a specific co-ordinate
2268 *     version character, reading information from the supplied FitsStore.
2269 *     A suitable Mapping is created to connect the new Frame to the pixel
2270 *     (GRID) Frame in the supplied FrameSet, and the Frame is added into
2271 *     the FrameSet using this Mapping.
2272 
2273 *  Parameters:
2274 *     this
2275 *        The FitsChan from which the keywords were read. Warning messages
2276 *        are added to this FitsChan if the celestial co-ordinate system is
2277 *        not recognized.
2278 *     fset
2279 *        Pointer to the FrameSet to be extended.
2280 *     pixel
2281 *        The index of the pixel (GRID) Frame within fset.
2282 *     npix
2283 *        The number of pixel axes.
2284 *     store
2285 *        The FitsStore containing the required information extracted from
2286 *        the FitsChan.
2287 *     s
2288 *        The co-ordinate version character. A space means the primary
2289 *        axis descriptions. Otherwise the supplied character should be
2290 *        an upper case alphabetical character ('A' to 'Z').
2291 *     method
2292 *        Pointer to a string holding the name of the calling method.
2293 *        This is only for use in constructing error messages.
2294 *     class
2295 *        Pointer to a string holding the name of the supplied object class.
2296 *        This is only for use in constructing error messages.
2297 *     status
2298 *        Pointer to the inherited status variable.
2299 */
2300 
2301 /* Local Variables: */
2302    AstFrame *frame;            /* Requested Frame */
2303    AstMapping *mapping;        /* Mapping from pixel to requested Frame */
2304    AstMapping *tmap;           /* Temporary Mapping pointer */
2305    AstPermMap *pmap;           /* PermMap pointer to add or remove axes */
2306    double con;                 /* Value to be assigned to missing axes */
2307    int *inperm;                /* Pointer to input axis permutation array */
2308    int *outperm;               /* Pointer to output axis permutation array */
2309    int i;                      /* Axis index */
2310    int nwcs;                   /* Number of wcs axes */
2311 
2312 /* Check the inherited status. */
2313    if( !astOK ) return;
2314 
2315 /* Get a Mapping between pixel coordinates and physical coordinates, using
2316    the requested axis descriptions. Also returns a Frame describing the
2317    physical coordinate system. */
2318    mapping = WcsMapFrm( this, store, s, &frame, method, class, status );
2319 
2320 /* Add the Frame into the FrameSet, and annul the mapping and frame. If
2321    the new Frame has more axes than the pixel Frame, use a PermMap which
2322    assigns constant value 1.0 to the extra axes. If the new Frame has less
2323    axes than the pixel Frame, use a PermMap which throws away the extra
2324    axes. */
2325    if( mapping != NULL ) {
2326       nwcs = astGetNin( mapping );
2327       if( nwcs != npix ) {
2328          inperm = astMalloc( sizeof(int)*(size_t)npix );
2329          outperm = astMalloc( sizeof(int)*(size_t)nwcs );
2330          if( astOK ) {
2331             for( i = 0; i < npix; i++ ) {
2332                inperm[ i ] = ( i < nwcs ) ? i : -1;
2333             }
2334             for( i = 0; i < nwcs; i++ ) {
2335                outperm[ i ] = ( i < npix ) ? i : -1;
2336             }
2337             con = 1.0;
2338             pmap = astPermMap( npix, inperm, nwcs, outperm, &con, "", status );
2339             tmap = (AstMapping *) astCmpMap( pmap, mapping, 1, "", status );
2340             pmap = astAnnul( pmap );
2341             (void) astAnnul( mapping );
2342             mapping = tmap;
2343          }
2344          inperm = astFree( inperm );
2345          outperm = astFree( outperm );
2346       }
2347       astAddFrame( fset, pixel, mapping, frame );
2348 
2349 /* Annul temporary resources. */
2350       mapping = astAnnul( mapping );
2351    }
2352    frame = astAnnul( frame );
2353 }
2354 
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)2355 static int AddVersion( AstFitsChan *this, AstFrameSet *fs, int ipix, int iwcs,
2356                        FitsStore *store, double *dim, char s, int encoding,
2357                        int isoff, const char *method, const char *class,
2358                        int *status ){
2359 
2360 /*
2361 *  Name:
2362 *     AddVersion
2363 
2364 *  Purpose:
2365 *     Add values to a FitsStore describing a specified Frame in a FrameSet.
2366 
2367 *  Type:
2368 *     Private function.
2369 
2370 *  Synopsis:
2371 *     #include "fitschan.h"
2372 *     int AddVersion( AstFitsChan *this, AstFrameSet *fs, int ipix, int iwcs,
2373 *                     FitsStore *store, double *dim, char s, int encoding,
2374 *                     int isoff, const char *method, const char *class,
2375 *                     int *status )
2376 
2377 *  Class Membership:
2378 *     FitsChan member function.
2379 
2380 *  Description:
2381 *     Values are added to the supplied FitsStore describing the specified
2382 *     WCS Frame, and its relationship to the specified pixel Frame. These
2383 *     values are based on the standard FITS-WCS conventions.
2384 
2385 *  Parameters:
2386 *     this
2387 *        Pointer to the FitsChan.
2388 *     fs
2389 *        Pointer to the FrameSet.
2390 *     ipix
2391 *        The index of the pixel (GRID) Frame within fset.
2392 *     iwcs
2393 *        The index of the Frame within fset to use as the WCS co-ordinate
2394 *        Frame.
2395 *     store
2396 *        The FitsStore in which to store the information extracted from
2397 *        the FrameSet.
2398 *     dim
2399 *        Pointer to an array of pixel axis dimensions. Individual elements
2400 *        will be AST__BAD if dimensions are not known. The number of
2401 *        elements should equal the number of axes in the base Frame of the
2402 *        supplied FrameSet.
2403 *     s
2404 *        The co-ordinate version character. A space means the primary
2405 *        axis descriptions. Otherwise the supplied character should be
2406 *        an upper case alphabetical character ('A' to 'Z').
2407 *     encoding
2408 *        The encoding being used.
2409 *     isoff
2410 *        If greater than zero, the Frame is an offset SkyFrame and the
2411 *        description added to the FitsStore should describe offset coordinates.
2412 *        If less than than zero, the Frame is an offset SkyFrame and the
2413 *        description added to the FitsStore should describe absolute coordinates.
2414 *        If zero, the Frame is an absolute SkyFrame and the description added
2415 *        to the FitsSTore should (by  necessity) describe absolute coordinates.
2416 *     method
2417 *        Pointer to a string holding the name of the calling method.
2418 *        This is only for use in constructing error messages.
2419 *     class
2420 *        Pointer to a string holding the name of the supplied object class.
2421 *        This is only for use in constructing error messages.
2422 *     status
2423 *        Pointer to the inherited status variable.
2424 
2425 *  Retuned Value:
2426 *     A value of 1 is returned if the WCS Frame was succesfully added to
2427 *     the FitsStore. A value of zero is returned otherwise.
2428 */
2429 
2430 /* Local Variables: */
2431    AstFrame *wcsfrm;        /* WCS Frame */
2432    AstFrameSet *fset;       /* Temporary FrameSet */
2433    AstMapping *iwcmap;      /* Mapping from WCS to IWC Frame */
2434    AstMapping *mapping;     /* Mapping from pixel to WCS Frame */
2435    AstMapping *pixiwcmap;   /* Mapping from pixel to IWC Frame */
2436    AstMapping *tmap2;       /* Temporary Mapping */
2437    AstMapping *tmap;        /* Temporary Mapping */
2438    const char *old_skyrefis;/* Old value of SkyRefIs attribute */
2439    double *crvals;          /* Pointer to array holding default CRVAL values */
2440    double cdelt2;           /* Sum of squared PC values */
2441    double cdelt;            /* CDELT value for axis */
2442    double crpix;            /* CRPIX value for axis */
2443    double crval;            /* CRVAL value for axis */
2444    double pc;               /* Element of the PC array */
2445    int *axis_done;          /* Flags indicating which axes have been done */
2446    int *wperm;              /* FITS axis for each Mapping output (Frame axis) */
2447    int fits_i;              /* FITS WCS axis index */
2448    int fits_j;              /* FITS pixel axis index */
2449    int iax;                 /* Frame axis index */
2450    int icurr;               /* Index of current Frame */
2451    int nwcs;                /* No. of axes in WCS frame */
2452    int ret;                 /* Returned value */
2453 
2454 /* Initialise */
2455    ret = 0;
2456 
2457 /* Check the inherited status. */
2458    if( !astOK ) return ret;
2459 
2460 /* If the frame is a SkyFrame describing offset coordinates, but the
2461    description added to the FitsStore should be for absolute coordinates,
2462    temporarily clear the SkyFrame SkyRefIs attribute. We need to make it
2463    the current Frame first so that we can use the FrameSet to clear the
2464    attribte, so that the SkyFrame will be re-mapped within the FrameSet
2465    to take account of the clearing. For negative isoff values, set the
2466    specific negative value to indicate the original SkyRefIs value. */
2467    if( isoff < 0 ) {
2468       icurr = astGetCurrent( fs );
2469       astSetCurrent( fs, iwcs );
2470       old_skyrefis = astGetC( fs, "SkyRefIs" );
2471       if( astOK ) {
2472          if( !Ustrcmp( old_skyrefis, "POLE", status ) ) {
2473             isoff = -1;
2474          } else if( !Ustrcmp( old_skyrefis, "ORIGIN", status ) ) {
2475             isoff = -2;
2476          } else {
2477             isoff = -3;
2478          }
2479       }
2480       astClear( fs, "SkyRefIs" );
2481       astSetCurrent( fs, icurr );
2482    } else {
2483       old_skyrefis = AST__BAD_REF;
2484    }
2485 
2486 /* Construct a new FrameSet holding the pixel and WCS Frames from the
2487    supplied FrameSet, but in which the current Frame is a copy of the
2488    supplied WCS Frame, but optionally extended to include any extra axes
2489    needed to conform to the FITS model. For instance, if the WCS Frame
2490    consists of a single 1D SpecFrame with a defined celestial reference
2491    position (SpecFrame attributes RefRA and RefDec), then FITS-WCS paper
2492    III requires there to be a pair of celestial axes in the WCS Frame in
2493    which the celestial reference point for the spectral axis is defined. */
2494    fset = MakeFitsFrameSet( this, fs, ipix, iwcs, encoding, method, class, status );
2495 
2496 /* If required, re-instate the original value of the SkyRefIs attribute
2497    in the supplied FrameSet. */
2498    if( old_skyrefis != AST__BAD_REF ) {
2499       astSetCurrent( fs, iwcs );
2500       astSetC( fs, "SkyRefIs", old_skyrefis );
2501       astSetCurrent( fs, icurr );
2502    }
2503 
2504 /* Abort if the FrameSet could not be produced. */
2505    if( !fset ) return ret;
2506 
2507 /* Get the Mapping from base to current Frame and check its inverse is
2508    defined. Return if not. Note, we can handle non-invertable Mappings if
2509    we are allowed to use the -TAB algorithm. */
2510    mapping = astGetMapping( fset, AST__BASE, AST__CURRENT );
2511    wcsfrm = astGetFrame( fset, AST__CURRENT );
2512    if( !astGetTranInverse( mapping ) && astGetTabOK( this ) <= 0 ) {
2513       mapping = astAnnul( mapping );
2514       wcsfrm = astAnnul( wcsfrm );
2515       fset = astAnnul( fset );
2516       return ret;
2517    }
2518 
2519 /* We now need to choose the "FITS WCS axis" (i.e. the number that is included
2520    in FITS keywords such as CRVAL2) for each axis of the output Frame.
2521    Allocate memory to store these indices. */
2522    nwcs= astGetNout( mapping );
2523    wperm = astMalloc( sizeof(int)*(size_t) nwcs );
2524 
2525 /* Attempt to use the FitsAxisOrder attribute to determine the order. If
2526    this is set to "<auto>", then for each WCS axis, we use the index of
2527    the pixel axis which is most closely aligned with it. */
2528    if( !FitsAxisOrder( this, nwcs, wcsfrm, wperm, status ) &&
2529        !WorldAxes( this, mapping, dim, wperm, status ) ) {
2530       wperm = astFree( wperm );
2531       mapping = astAnnul( mapping );
2532       wcsfrm = astAnnul( wcsfrm );
2533       fset = astAnnul( fset );
2534       return ret;
2535    }
2536 
2537 /* Allocate an array of flags, one for each axis, which indicate if a
2538    description of the corresponding axis has yet been stored in the
2539    FitsStore. Initialise them to indicate that no axes have yet been
2540    described. */
2541    axis_done = astMalloc( sizeof(int)*(size_t) nwcs );
2542    if( astOK ) for( iax = 0; iax < nwcs; iax++ ) axis_done[ iax ] = 0;
2543 
2544 /* Get the original reference point from the FitsChan and convert it into
2545    the require WCS Frame. This is used as the default reference point (some
2546    algorithms may choose to ignore this default reference point ). */
2547    crvals = ReadCrval( this, wcsfrm, s, method, class, status );
2548 
2549 /* For each class of FITS conventions (celestial, spectral, others),
2550    identify any corresponding axes within the WCS Frame and add
2551    descriptions of them to the FitsStore. These descriptions are in terms
2552    of the FITS keywords defined in the corresponding FITS-WCS paper. Note,
2553    the keywords which descirbed the pixel->IWC mapping (CRPIX, CD, PC,
2554    CDELT) are not stored by these functions, instead each function
2555    returns a Mapping from WCS to IWC coords (these Mappings
2556    pass on axes of the wrong class without change). These Mappings are
2557    combined in series to get the final WCS->IWC Mapping. First do
2558    celestial axes. */
2559    iwcmap = CelestialAxes( this, fset, dim, wperm, s, store, axis_done,
2560                            isoff, method, class, status );
2561 
2562 /* Now look for spectral axes, and update the iwcmap. */
2563    tmap = SpectralAxes( this, fset, dim, wperm, s, store, crvals, axis_done,
2564                         method, class, status );
2565    tmap2 = (AstMapping *) astCmpMap( iwcmap, tmap, 1, "", status );
2566    tmap = astAnnul( tmap );
2567    (void) astAnnul( iwcmap );
2568    iwcmap = tmap2;
2569 
2570 /* Finally add descriptions of any axes not yet described (they are
2571    assumed to be linear), and update the iwcmap. */
2572    tmap = OtherAxes( this, fset, dim, wperm, s, store, crvals, axis_done,
2573                      method, class, status );
2574    tmap2 = (AstMapping *) astCmpMap( iwcmap, tmap, 1, "", status );
2575    tmap = astAnnul( tmap );
2576    (void) astAnnul( iwcmap );
2577    iwcmap = tmap2;
2578 
2579 /* The "iwcmap" Mapping found above converts from the WCS Frame to the IWC
2580    Frame. Combine the pixel->WCS Mapping with this WCS->IWC Mapping to
2581    get the pixel->IWC Mapping. */
2582    pixiwcmap = (AstMapping *) astCmpMap( mapping, iwcmap, 1, "", status );
2583    mapping = astAnnul( mapping );
2584    iwcmap = astAnnul( iwcmap );
2585 
2586 /* Now attempt to store values for the keywords describing the pixel->IWC
2587    Mapping (CRPIX, CD, PC, CDELT). This tests that the iwcmap is linear.
2588    Zero is returned if the test fails. */
2589    ret = MakeIntWorld( pixiwcmap, wcsfrm, wperm, s, store, dim, method, class,
2590                        status );
2591 
2592 /* If succesfull... */
2593    if( ret ) {
2594 
2595 /* Store the Domain name as the WCSNAME keyword (if set). */
2596       if( astTestDomain( wcsfrm ) ) {
2597          SetItemC( &(store->wcsname), 0, 0, s, (char *) astGetDomain( wcsfrm ),
2598                    status );
2599       }
2600 
2601 /* Store the UT1-UTC correction, if set, converting from seconds to days
2602    (as used by JACH). */
2603       if( astTestDut1( wcsfrm ) && s == ' ' ) {
2604          SetItem( &(store->dut1), 0, 0, ' ', astGetDut1( wcsfrm )/SPD, status );
2605       }
2606 
2607 /* Set CRVAL values which are very small compared to the pixel size to
2608    zero. */
2609       for( iax = 0; iax < nwcs; iax++ ) {
2610          fits_i = wperm[ iax ];
2611          crval = GetItem( &(store->crval), fits_i, 0, s, NULL, method, class,
2612                           status );
2613          if( crval != AST__BAD ) {
2614             cdelt2 = 0.0;
2615             for( fits_j = 0; fits_j < nwcs; fits_j++ ){
2616                pc = GetItem( &(store->pc), fits_i, fits_j, s, NULL, method, class, status );
2617                if( pc == AST__BAD ) pc = ( fits_i == fits_j ) ? 1.0 : 0.0;
2618                cdelt2 += pc*pc;
2619             }
2620             cdelt = GetItem( &(store->cdelt), fits_i, 0, s, NULL, method, class, status );
2621             if( cdelt == AST__BAD ) cdelt = 1.0;
2622             cdelt2 *= ( cdelt*cdelt );
2623             if( fabs( crval ) < sqrt( DBL_EPSILON*cdelt2 ) ) {
2624                SetItem( &(store->crval), fits_i, 0, s, 0.0, status );
2625             }
2626          }
2627       }
2628 
2629 /* Round CRPIX values to the nearest millionth of a pixel. */
2630       for( iax = 0; iax < nwcs; iax++ ) {
2631          crpix = GetItem( &(store->crpix), 0, iax, s, NULL, method, class, status );
2632          if( crpix != AST__BAD ) {
2633             SetItem( &(store->crpix), 0, iax, s,
2634                      floor( crpix*1.0E6 + 0.5 )*1.0E-6, status );
2635          }
2636       }
2637    }
2638 
2639 /* Free remaining resources. */
2640    if( crvals ) crvals = astFree( crvals );
2641    wcsfrm = astAnnul( wcsfrm );
2642    pixiwcmap = astAnnul( pixiwcmap );
2643    axis_done = astFree( axis_done );
2644    wperm = astFree( wperm );
2645    fset = astAnnul( fset );
2646 
2647 /* If an error has occurred, return zero */
2648    return astOK ? ret : 0;
2649 }
2650 
AddUnitMaps(AstMapping * map,int iax,int nax,int * status)2651 static AstMapping *AddUnitMaps( AstMapping *map, int iax, int nax, int *status ) {
2652 /*
2653 *  Name:
2654 *     AddUnitMaps
2655 
2656 *  Purpose:
2657 *     Embed a Mapping within a pair of parallel UnitMaps.
2658 
2659 *  Type:
2660 *     Private function.
2661 
2662 *  Synopsis:
2663 *     #include "fitschan.h"
2664 *     AstMapping *AddUnitMaps( AstMapping *map, int iax, int nax, int *status )
2665 
2666 *  Class Membership:
2667 *     FitsChan member function.
2668 
2669 *  Description:
2670 *     This function returns a Mapping which consists of the supplied Mapping
2671 *     in parallel with a pair of UnitMaps so that the first axis of the
2672 *     supplied Mapping is at a specified axis number in the returned Mapping.
2673 
2674 *  Parameters:
2675 *     map
2676 *        Pointer to the Mapping. The Mapping must have equal numbers of
2677 *        input and output coordinates.
2678 *     iax
2679 *        The index for the first input of "map" within the returned
2680 *        Mapping.
2681 *     nax
2682 *        The number of axes for the returned Mapping.
2683 *     status
2684 *        Pointer to the inherited status variable.
2685 
2686 *  Returned Value:
2687 *     A Mapping which has "nax" axes, and in which the "iax" axis
2688 *     corresponds to the first axis of "map". Axes lower than "iax" are
2689 *     transformed using a UnitMap, and axes higher than the last axis of
2690 *     "map" are transformed using a UnitMap.
2691 */
2692 
2693 /* Local Variables: */
2694    AstMapping *ret;      /* Returned Mapping */
2695    AstMapping *tmap0;    /* Temporary Mapping */
2696    AstMapping *tmap1;    /* Temporary Mapping */
2697    AstMapping *tmap2;    /* Temporary Mapping */
2698    int nmap;             /* Number of supplied Mapping inputs */
2699 
2700 /* Initialise */
2701    ret = NULL;
2702 
2703 /* Check the inherited status. */
2704    if( !astOK ) return ret;
2705 
2706 /* Initialise the returned Mapping to be a clone of the supplied Mapping. */
2707    ret = astClone( map );
2708 
2709 /* Note the number of inputs of the supplied Mapping (assumed to be equal
2710    to the number of outputs). */
2711    nmap = astGetNin( map );
2712 
2713 /* If necessary produce a parallel CmpMap which combines the Mapping with a
2714    UnitMap representing the axes lower than "iax". */
2715    if( iax > 0 ) {
2716       tmap0 = (AstMapping *) astUnitMap( iax, "", status );
2717       tmap1 = (AstMapping *) astCmpMap( tmap0, ret, 0, "", status );
2718       ret = astAnnul( ret );
2719       tmap0 = astAnnul( tmap0 );
2720       ret = tmap1;
2721    }
2722 
2723 /* If necessary produce a parallel CmpMap which combines the Mapping with a
2724    UnitMap representing the axes higher than "iax+nmap". */
2725    if( iax + nmap < nax ) {
2726       tmap1 = (AstMapping *) astUnitMap( nax - iax - nmap, "", status );
2727       tmap2 = (AstMapping *) astCmpMap( ret, tmap1, 0, "", status );
2728       ret = astAnnul( ret );
2729       tmap1 = astAnnul( tmap1 );
2730       ret = tmap2;
2731    }
2732 
2733 /* Return the result. */
2734    return ret;
2735 }
2736 
AIPSFromStore(AstFitsChan * this,FitsStore * store,const char * method,const char * class,int * status)2737 static int AIPSFromStore( AstFitsChan *this, FitsStore *store,
2738                           const char *method, const char *class, int *status ){
2739 
2740 /*
2741 *  Name:
2742 *     AIPSFromStore
2743 
2744 *  Purpose:
2745 *     Store WCS keywords in a FitsChan using FITS-AIPS encoding.
2746 
2747 *  Type:
2748 *     Private function.
2749 
2750 *  Synopsis:
2751 
2752 *     int AIPSFromStore( AstFitsChan *this, FitsStore *store,
2753 *                        const char *method, const char *class, int *status )
2754 
2755 *  Class Membership:
2756 *     FitsChan
2757 
2758 *  Description:
2759 *     A FitsStore is a structure containing a generalised represention of
2760 *     a FITS WCS FrameSet. Functions exist to convert a FitsStore to and
2761 *     from a set of FITS header cards (using a specified encoding), or
2762 *     an AST FrameSet. In other words, a FitsStore is an encoding-
2763 *     independant intermediary staging post between a FITS header and
2764 *     an AST FrameSet.
2765 *
2766 *     This function copies the WCS information stored in the supplied
2767 *     FitsStore into the supplied FitsChan, using FITS-AIPS encoding.
2768 *
2769 *     AIPS encoding is like FITS-WCS encoding but with the following
2770 
2771 *     restrictions:
2772 *
2773 *     1) The celestial projection must not have any projection parameters
2774 *     which are not set to their default values. The one exception to this
2775 *     is that SIN projections are acceptable if the associated projection
2776 *     parameter PV<axlat>_1 is zero and PV<axlat>_2 = cot( reference point
2777 *     latitude). This is encoded using the string "-NCP". The SFL projection
2778 *     is encoded using the string "-GLS". Note, the original AIPS WCS
2779 *     system only recognised a small subset of the currently available
2780 *     projections, but some more recent AIPS-like software recognizes some
2781 *     of the new projections included in the FITS-WCS encoding. The AIT,
2782 *     GLS and MER can only be written if the CRVAL keywords are zero for
2783 *     both longitude and latitude axes.
2784 *
2785 *     2) The celestial axes must be RA/DEC, galactic or ecliptic.
2786 *
2787 *     3) LONPOLE and LATPOLE must take their default values.
2788 *
2789 *     4) Only primary axis descriptions are written out.
2790 *
2791 *     5) EPOCH is written instead of EQUINOX & RADECSYS, and uses the
2792 *        IAU 1984 rule ( EPOCH < 1984.0 is treated as a Besselian epoch
2793 *        and implies RADECSYS=FK4,  EPOCH >= 1984.0 is treated as a
2794 *        Julian epoch and implies RADECSYS=FK5). The RADECSYS & EQUINOX
2795 *        values in the FitsStore must be consistent with this rule.
2796 *
2797 *     6) Any rotation produced by the PC matrix must be restricted to
2798 *        the celestial plane, and must involve no shear. A CROTA keyword
2799 *        with associated CDELT values are produced instead of the PC
2800 *        matrix.
2801 *
2802 *     7) ICRS is not supported.
2803 *
2804 *     8) Spectral axes can be created only for FITS-WCS CTYPE values of "FREQ"
2805 *        "VRAD" and "VOPT-F2W" and with standards of rest of LSRK, LSRD,
2806 *        BARYCENT and GEOCENTR.
2807 
2808 *  Parameters:
2809 *     this
2810 *        Pointer to the FitsChan.
2811 *     store
2812 *        Pointer to the FitsStore.
2813 *     method
2814 *        Pointer to a string holding the name of the calling method.
2815 *        This is only for use in constructing error messages.
2816 *     class
2817 *        Pointer to a string holding the name of the supplied object class.
2818 *        This is only for use in constructing error messages.
2819 *     status
2820 *        Pointer to the inherited status variable.
2821 
2822 *  Returned Value:
2823 *     A value of 1 is returned if succesfull, and zero is returned
2824 *     otherwise.
2825 */
2826 
2827 /* Local Variables: */
2828    char *comm;         /* Pointer to comment string */
2829    const char *cval;   /* Pointer to string keyword value */
2830    const char *specunit;/* Pointer to corrected spectral units string */
2831    char combuf[80];    /* Buffer for FITS card comment */
2832    char lattype[MXCTYPELEN];/* Latitude axis CTYPE */
2833    char lontype[MXCTYPELEN];/* Longitude axis CTYPE */
2834    char s;             /* Co-ordinate version character */
2835    char sign[2];       /* Fraction's sign character */
2836    char spectype[MXCTYPELEN];/* Spectral axis CTYPE */
2837    double *cdelt;      /* Pointer to CDELT array */
2838    double cdl;         /* CDELT term */
2839    double cdlat_lon;   /* Off-diagonal CD element */
2840    double cdlon_lat;   /* Off-diagonal CD element */
2841    double coscro;      /* Cos( CROTA ) */
2842    double crota;       /* CROTA value to use */
2843    double epoch;       /* Epoch of reference equinox */
2844    double fd;          /* Fraction of a day */
2845    double latval;      /* CRVAL for latitude axis */
2846    double lonval;      /* CRVAL for longitude axis */
2847    double mjd99;       /* MJD at start of 1999 */
2848    double p1, p2;      /* Projection parameters */
2849    double rho_a;       /* First estimate of CROTA */
2850    double rho_b;       /* Second estimate of CROTA */
2851    double sincro;      /* Sin( CROTA ) */
2852    double specfactor;  /* Factor for converting internal spectral units */
2853    double val;         /* General purpose value */
2854    int axlat;          /* Index of latitude FITS WCS axis */
2855    int axlon;          /* Index of longitude FITS WCS axis */
2856    int axrot1;         /* Index of first CROTA rotation axis */
2857    int axrot2;         /* Index of second CROTA rotation axis */
2858    int axspec;         /* Index of spectral FITS WCS axis */
2859    int i;              /* Axis index */
2860    int ihmsf[ 4 ];     /* Hour, minute, second, fractional second */
2861    int iymdf[ 4 ];     /* Year, month, date, fractional day */
2862    int j;              /* Axis index */
2863    int jj;             /* SlaLib status */
2864    int naxis;          /* No. of axes */
2865    int ok;             /* Is FitsSTore OK for IRAF encoding? */
2866    int prj;            /* Projection type */
2867 
2868 /* Check the inherited status. */
2869    if( !astOK ) return 0;
2870 
2871 /* Initialise */
2872    specunit = "";
2873    specfactor = 1.0;
2874 
2875 /* First check that the values in the FitsStore conform to the
2876    requirements of the AIPS encoding. Assume they do to begin with. */
2877    ok = 1;
2878 
2879 /* Just do primary axes. */
2880    s = ' ';
2881 
2882 /* Look for the primary celestial axes. */
2883    FindLonLatSpecAxes( store, s, &axlon, &axlat, &axspec, method, class, status );
2884 
2885 /* If both longitude and latitude axes are present ...*/
2886    if( axlon >= 0 && axlat >= 0 ) {
2887 
2888 /* Get the CRVAL values for both axes. */
2889       latval = GetItem( &( store->crval ), axlat, 0, s, NULL, method, class, status );
2890       if( latval == AST__BAD ) ok = 0;
2891       lonval = GetItem( &( store->crval ), axlon, 0, s, NULL, method, class, status );
2892       if( lonval == AST__BAD ) ok = 0;
2893 
2894 /* Get the CTYPE values for both axes. Extract the projection type as
2895    specified by the last 4 characters in the latitude CTYPE keyword value. */
2896       cval = GetItemC( &(store->ctype), axlon, 0, s, NULL, method, class, status );
2897       if( !cval ) {
2898          ok = 0;
2899       } else {
2900          strcpy( lontype, cval );
2901       }
2902       cval = GetItemC( &(store->ctype), axlat, 0, s, NULL, method, class, status );
2903       if( !cval ) {
2904          ok = 0;
2905          prj = AST__WCSBAD;
2906       } else {
2907          strcpy( lattype, cval );
2908          prj = astWcsPrjType( cval + 4 );
2909       }
2910 
2911 /* Check the projection type is OK. */
2912       if( prj == AST__WCSBAD ){
2913          ok = 0;
2914       } else if( prj != AST__SIN ){
2915 
2916 /* There must be no projection parameters. */
2917          if( GetMaxJM( &(store->pv), ' ', status ) >= 0 ) {
2918             ok = 0;
2919 
2920 /* FITS-AIPS cannot handle the AST-specific TPN projection. */
2921          } else if( prj == AST__TPN ) {
2922             ok = 0;
2923 
2924 /* For AIT, MER and GLS, check that the reference point is the origin of
2925    the celestial co-ordinate system. */
2926          } else if( prj == AST__MER ||
2927                     prj == AST__AIT ||
2928                     prj == AST__SFL ) {
2929             if( latval != 0.0 || lonval != 0.0 ){
2930                ok = 0;
2931 
2932 /* Change the new SFL projection code to to the older equivalent GLS */
2933             } else if( prj == AST__SFL ){
2934                (void) strcpy( lontype + 4, "-GLS" );
2935                (void) strcpy( lattype + 4, "-GLS" );
2936             }
2937          }
2938 
2939 /* SIN projections are only acceptable if the associated projection
2940    parameters are both zero, or if the first is zero and the second
2941    = cot( reference point latitude )  (the latter case is equivalent to
2942    the old NCP projection). */
2943       } else {
2944          p1 = GetItem( &( store->pv ), axlat, 1, s, NULL, method, class, status );
2945          p2 = GetItem( &( store->pv ), axlat, 2, s, NULL, method, class, status );
2946          if( p1 == AST__BAD ) p1 = 0.0;
2947          if( p2 == AST__BAD ) p2 = 0.0;
2948          ok = 0;
2949          if( p1 == 0.0 ) {
2950             if( p2 == 0.0 ) {
2951                ok = 1;
2952             } else if( fabs( p2 ) >= 1.0E14 && latval == 0.0 ){
2953                ok = 1;
2954                (void) strcpy( lontype + 4, "-NCP" );
2955                (void) strcpy( lattype + 4, "-NCP" );
2956             } else if( fabs( p2*tan( AST__DD2R*latval ) - 1.0 )
2957                        < 0.01 ){
2958                ok = 1;
2959                (void) strcpy( lontype + 4, "-NCP" );
2960                (void) strcpy( lattype + 4, "-NCP" );
2961             }
2962          }
2963       }
2964 
2965 /* Identify the celestial coordinate system from the first 4 characters of the
2966    longitude CTYPE value. Only RA, galactic longitude, and ecliptic
2967    longitude can be stored using FITS-AIPS. */
2968       if( ok && strncmp( lontype, "RA--", 4 ) &&
2969                strncmp( lontype, "GLON", 4 ) &&
2970                strncmp( lontype, "ELON", 4 ) ) ok = 0;
2971 
2972 /* If the physical Frame requires a LONPOLE or LATPOLE keyword, it cannot
2973    be encoded using FITS-IRAF. */
2974       if( GetItem( &(store->latpole), 0, 0, s, NULL, method, class, status )
2975           != AST__BAD ||
2976           GetItem( &(store->lonpole), 0, 0, s, NULL, method, class, status )
2977           != AST__BAD ) ok = 0;
2978    }
2979 
2980 /* If a spectral axis is present ...*/
2981    if( ok && axspec >= 0 ) {
2982 
2983 /* Get the CTYPE values for the axis, and find the AIPS equivalent, if
2984    possible. */
2985       cval = GetItemC( &(store->ctype), axspec, 0, s, NULL, method, class, status );
2986       if( !cval ) {
2987          ok = 0;
2988       } else {
2989          if( !strncmp( cval, "FREQ", astChrLen( cval ) ) ) {
2990             strcpy( spectype, "FREQ" );
2991          } else if( !strncmp( cval, "VRAD", astChrLen( cval ) ) ) {
2992             strcpy( spectype, "VELO" );
2993          } else if( !strncmp( cval, "VOPT-F2W", astChrLen( cval ) ) ) {
2994             strcpy( spectype, "FELO" );
2995          } else {
2996             ok = 0;
2997          }
2998       }
2999 
3000 /* If OK, check the SPECSYS value and add the AIPS equivalent onto the
3001    end of the CTYPE value.*/
3002       cval = GetItemC( &(store->specsys), 0, 0, s, NULL, method, class, status );
3003       if( !cval ) {
3004          ok = 0;
3005       } else if( ok ) {
3006          if( !strncmp( cval, "LSRK", astChrLen( cval ) ) ) {
3007             strcpy( spectype+4, "-LSR" );
3008          } else if( !strncmp( cval, "LSRD", astChrLen( cval ) ) ) {
3009             strcpy( spectype+4, "-LSD" );
3010          } else if( !strncmp( cval, "BARYCENT", astChrLen( cval ) ) ) {
3011             strcpy( spectype+4, "-HEL" );
3012          } else if( !strncmp( cval, "GEOCENTR", astChrLen( cval ) ) ) {
3013             strcpy( spectype+4, "-GEO" );
3014          } else {
3015             ok = 0;
3016          }
3017       }
3018 
3019 /* If still OK, ensure the spectral axis units are Hz or m/s. */
3020       cval = GetItemC( &(store->cunit), axspec, 0, s, NULL, method, class, status );
3021       if( !cval ) {
3022          ok = 0;
3023       } else if( ok ) {
3024          if( !strcmp( cval, "Hz" ) ) {
3025             specunit = "HZ";
3026             specfactor = 1.0;
3027          } else if( !strcmp( cval, "kHz" ) ) {
3028             specunit = "HZ";
3029             specfactor = 1.0E3;
3030          } else if( !strcmp( cval, "MHz" ) ) {
3031             specunit = "HZ";
3032             specfactor = 1.0E6;
3033          } else if( !strcmp( cval, "GHz" ) ) {
3034             specunit = "HZ";
3035             specfactor = 1.0E9;
3036          } else if( !strcmp( cval, "m/s" ) ) {
3037             specunit = "m/s";
3038             specfactor = 1.0;
3039          } else if( !strcmp( cval, "km/s" ) ) {
3040             specunit = "m/s";
3041             specfactor = 1.0E3;
3042          } else {
3043             ok = 0;
3044          }
3045       }
3046    }
3047 
3048 /* Save the number of axes */
3049    naxis = GetMaxJM( &(store->crpix), ' ', status ) + 1;
3050 
3051 /* If this is different to the value of NAXIS abort since this encoding
3052    does not support WCSAXES keyword. */
3053    if( naxis != store->naxis ) ok = 0;
3054 
3055 /* Allocate memory to store the CDELT values */
3056    if( ok ) {
3057       cdelt = (double *) astMalloc( sizeof(double)*naxis );
3058       if( !cdelt ) ok = 0;
3059    } else {
3060       cdelt = NULL;
3061    }
3062 
3063 /* Check that rotation is restricted to the celestial plane, and extract
3064    the CDELT (diagonal) terms, etc. If there are no celestial
3065    axes, restrict rotation to the first two non-spectral axes. */
3066    if( axlat < 0 && axlon < 0 ) {
3067       if( axspec >= 0 && naxis > 2 ) {
3068          axrot2 = ( axspec == 0 ) ? 1 : 0;
3069          axrot1 = axrot2 + 1;
3070          if( axrot1 == axspec ) axrot1++;
3071       } else if( naxis > 1 ){
3072          axrot2 = 0;
3073          axrot1 = 1;
3074       } else {
3075          axrot2 = -1;
3076          axrot1 = -1;
3077       }
3078    } else {
3079       axrot1 = axlon;
3080       axrot2 = axlat;
3081    }
3082    cdlat_lon = 0.0;
3083    cdlon_lat = 0.0;
3084    for( i = 0; i < naxis && ok; i++ ){
3085       cdl = GetItem( &(store->cdelt), i, 0, s, NULL, method, class, status );
3086       if( cdl == AST__BAD ) cdl = 1.0;
3087       for( j = 0; j < naxis && ok; j++ ){
3088           val = GetItem( &(store->pc), i, j, s, NULL, method, class, status );
3089           if( val == AST__BAD ) val = ( i == j ) ? 1.0 : 0.0;
3090           val *= cdl;
3091           if( i == j ){
3092              cdelt[ i ] = val;
3093           } else if( i == axrot2 && j == axrot1 ){
3094              cdlat_lon = val;
3095           } else if( i == axrot1 && j == axrot2 ){
3096              cdlon_lat = val;
3097           } else if( val != 0.0 ){
3098              ok = 0;
3099           }
3100       }
3101    }
3102 
3103 /* Find the CROTA and CDELT values for the celestial axes. */
3104    if( ok && axrot1 >= 0 && axrot2 >= 0 ) {
3105       if( cdlat_lon > 0.0 ) {
3106          rho_a = atan2( cdlat_lon, cdelt[ axrot1 ] );
3107       } else if( cdlat_lon == 0.0 ) {
3108          rho_a = 0.0;
3109       } else {
3110          rho_a = atan2( -cdlat_lon, -cdelt[ axrot1 ] );
3111       }
3112       if( cdlon_lat > 0.0 ) {
3113          rho_b = atan2( cdlon_lat, -cdelt[ axrot2 ] );
3114       } else if( cdlon_lat == 0.0 ) {
3115          rho_b = 0.0;
3116       } else {
3117          rho_b = atan2( -cdlon_lat, cdelt[ axrot2 ] );
3118       }
3119       if( fabs( palDrange( rho_a - rho_b ) ) < 1.0E-2 ){
3120          crota = 0.5*( palDranrm( rho_a ) + palDranrm( rho_b ) );
3121          coscro = cos( crota );
3122          sincro = sin( crota );
3123          if( fabs( coscro ) > fabs( sincro ) ){
3124             cdelt[ axrot2 ] /= coscro;
3125             cdelt[ axrot1 ] /= coscro;
3126          } else {
3127             cdelt[ axrot2 ] = -cdlon_lat/sincro;
3128             cdelt[ axrot1 ] = cdlat_lon/sincro;
3129          }
3130          crota *= AST__DR2D;
3131       } else {
3132          ok = 0;
3133       }
3134    } else {
3135       crota = 0.0;
3136    }
3137 
3138 /* Get RADECSYS and the reference equinox (called EPOCH in FITS-AIPS). */
3139    cval = GetItemC( &(store->radesys), 0, 0, s, NULL, method, class, status );
3140    epoch = GetItem( &(store->equinox), 0, 0, s, NULL, method, class, status );
3141 
3142 /* If RADECSYS was available... */
3143    if( cval ){
3144 
3145 /* ICRS is not supported in this encoding. */
3146       if( !strcmp( "ICRS", cval ) ) ok = 0;
3147 
3148 /* If epoch was not available, set a default epoch. */
3149       if( epoch == AST__BAD ){
3150          if( !strcmp( "FK4", cval ) ){
3151             epoch = 1950.0;
3152          } else if( !strcmp( "FK5", cval ) ){
3153             epoch = 2000.0;
3154          } else {
3155             ok = 0;
3156          }
3157 
3158 /* If an epoch was supplied, check it is consistent with the IAU 1984
3159    rule. */
3160       } else {
3161          if( !strcmp( "FK4", cval ) ){
3162             if( epoch >= 1984.0 ) ok = 0;
3163          } else if( !strcmp( "FK5", cval ) ){
3164             if( epoch < 1984.0 ) ok = 0;
3165          } else {
3166             ok = 0;
3167          }
3168       }
3169    }
3170 
3171 /* Only create the keywords if the FitsStore conforms to the requirements
3172    of the FITS-AIPS encoding. */
3173    if( ok ) {
3174 
3175 /* Get and save CRPIX for all pixel axes. These are required, so break
3176    if they are not available. */
3177       for( j = 0; j < naxis && ok; j++ ){
3178          val = GetItem( &(store->crpix), 0, j, s, NULL, method, class, status );
3179          if( val == AST__BAD ) {
3180             ok = 0;
3181          } else {
3182             sprintf( combuf, "Reference pixel on axis %d", j + 1 );
3183             SetValue( this, FormatKey( "CRPIX", j + 1, -1, s, status ), &val,
3184                       AST__FLOAT, combuf, status );
3185          }
3186       }
3187 
3188 /* Get and save CRVAL for all intermediate axes. These are required, so
3189    break if they are not available. */
3190       for( i = 0; i < naxis && ok; i++ ){
3191          val = GetItem( &(store->crval), i, 0, s, NULL, method, class, status );
3192          if( val == AST__BAD ) {
3193             ok = 0;
3194          } else {
3195             if( i == axspec ) val *= specfactor;
3196             sprintf( combuf, "Value at ref. pixel on axis %d", i + 1 );
3197             SetValue( this, FormatKey( "CRVAL", i + 1, -1, s, status ), &val,
3198                       AST__FLOAT, combuf, status );
3199          }
3200       }
3201 
3202 /* Get and save CTYPE for all intermediate axes. These are required, so
3203    break if they are not available. Use the potentially modified versions
3204    saved above for the celestial axes. */
3205       for( i = 0; i < naxis && ok; i++ ){
3206          if( i == axlat ) {
3207             cval = lattype;
3208          } else if( i == axlon ) {
3209             cval = lontype;
3210          } else if( i == axspec ) {
3211             cval = spectype;
3212          } else {
3213             cval = GetItemC( &(store->ctype), i, 0, s, NULL, method, class, status );
3214          }
3215          if( cval && strcmp( cval + 4, "-TAB" ) ) {
3216             comm = GetItemC( &(store->ctype_com), i, 0, s, NULL, method, class, status );
3217             if( !comm ) {
3218                sprintf( combuf, "Type of co-ordinate on axis %d", i + 1 );
3219                comm = combuf;
3220             }
3221             SetValue( this, FormatKey( "CTYPE", i + 1, -1, s, status ), &cval,
3222                       AST__STRING, comm, status );
3223          } else {
3224             ok = 0;
3225          }
3226       }
3227 
3228 /* CDELT values */
3229       if( axspec != -1 ) cdelt[ axspec ] *= specfactor;
3230       for( i = 0; i < naxis; i++ ){
3231          SetValue( this, FormatKey( "CDELT", i + 1, -1, s, status ), cdelt + i,
3232                    AST__FLOAT, "Pixel size", status );
3233       }
3234 
3235 /* CUNIT values. */
3236       for( i = 0; i < naxis; i++ ) {
3237          cval = GetItemC( &(store->cunit), i, 0, s, NULL, method, class, status );
3238          if( cval ) {
3239             if( i == axspec ) cval = specunit;
3240             sprintf( combuf, "Units for axis %d", i + 1 );
3241             SetValue( this, FormatKey( "CUNIT", i + 1, -1, s, status ), &cval, AST__STRING,
3242                       combuf, status );
3243          }
3244       }
3245 
3246 /* CROTA */
3247       if( axrot2 != -1 ){
3248          SetValue( this, FormatKey( "CROTA", axrot2 + 1, -1, s, status ), &crota,
3249                    AST__FLOAT, "Axis rotation", status );
3250       } else if( ( axspec == -1 && naxis > 1 ) ||
3251                   ( axspec != -1 && naxis > 2 ) )  {
3252          SetValue( this, "CROTA1", &crota, AST__FLOAT, "Axis rotation", status );
3253       }
3254 
3255 /* Reference equinox */
3256       if( epoch != AST__BAD ) SetValue( this, "EPOCH", &epoch, AST__FLOAT,
3257                                         "Epoch of reference equinox", status );
3258 
3259 /* Date of observation. */
3260       val = GetItem( &(store->mjdobs), 0, 0, ' ', NULL, method, class, status );
3261       if( val != AST__BAD ) {
3262 
3263 /* The format used for the DATE-OBS keyword depends on the value of the
3264    keyword. For DATE-OBS < 1999.0, use the old "dd/mm/yy" format.
3265    Otherwise, use the new "ccyy-mm-ddThh:mm:ss[.ssss]" format. */
3266          palCaldj( 99, 1, 1, &mjd99, &jj );
3267          if( val < mjd99 ) {
3268             palDjcal( 0, val, iymdf, &jj );
3269             sprintf( combuf, "%2.2d/%2.2d/%2.2d", iymdf[ 2 ], iymdf[ 1 ],
3270                      iymdf[ 0 ] - ( ( iymdf[ 0 ] > 1999 ) ? 2000 : 1900 ) );
3271          } else {
3272             palDjcl( val, iymdf, iymdf+1, iymdf+2, &fd, &jj );
3273             palDd2tf( 3, fd, sign, ihmsf );
3274             sprintf( combuf, "%4.4d-%2.2d-%2.2dT%2.2d:%2.2d:%2.2d.%3.3d",
3275                      iymdf[0], iymdf[1], iymdf[2], ihmsf[0], ihmsf[1],
3276                      ihmsf[2], ihmsf[3] );
3277          }
3278 
3279 /* Now store the formatted string in the FitsChan. */
3280          cval = combuf;
3281          SetValue( this, "DATE-OBS", (void *) &cval, AST__STRING,
3282                    "Date of observation", status );
3283       }
3284 
3285 /* Spectral stuff.. */
3286       if( axspec >= 0 ) {
3287 
3288 /* Rest frequency */
3289          val = GetItem( &(store->restfrq), 0, 0, s, NULL, method, class, status );
3290          if( val != AST__BAD ) SetValue( this, FormatKey( "RESTFREQ", -1, -1, s, status ),
3291                                          &val, AST__FLOAT, "[Hz] Rest frequency", status );
3292       }
3293    }
3294 
3295 /* Release CDELT workspace */
3296    if( cdelt ) cdelt = (double *) astFree( (void *) cdelt );
3297 
3298 /* Return zero or ret depending on whether an error has occurred. */
3299    return astOK ? ok : 0;
3300 }
3301 
AIPSPPFromStore(AstFitsChan * this,FitsStore * store,const char * method,const char * class,int * status)3302 static int AIPSPPFromStore( AstFitsChan *this, FitsStore *store,
3303                             const char *method, const char *class, int *status ){
3304 
3305 /*
3306 *  Name:
3307 *     AIPSPPFromStore
3308 
3309 *  Purpose:
3310 *     Store WCS keywords in a FitsChan using FITS-AIPS++ encoding.
3311 
3312 *  Type:
3313 *     Private function.
3314 
3315 *  Synopsis:
3316 
3317 *     int AIPSPPFromStore( AstFitsChan *this, FitsStore *store,
3318 *                        const char *method, const char *class, int *status )
3319 
3320 *  Class Membership:
3321 *     FitsChan
3322 
3323 *  Description:
3324 *     A FitsStore is a structure containing a generalised represention of
3325 *     a FITS WCS FrameSet. Functions exist to convert a FitsStore to and
3326 *     from a set of FITS header cards (using a specified encoding), or
3327 *     an AST FrameSet. In other words, a FitsStore is an encoding-
3328 *     independant intermediary staging post between a FITS header and
3329 *     an AST FrameSet.
3330 *
3331 *     This function copies the WCS information stored in the supplied
3332 *     FitsStore into the supplied FitsChan, using FITS-AIPS++ encoding.
3333 *
3334 *     AIPS++ encoding is like FITS-WCS encoding but with the following
3335 
3336 *     restrictions:
3337 *
3338 *     1) The celestial axes must be RA/DEC, galactic or ecliptic.
3339 *
3340 *     2) Only primary axis descriptions are written out.
3341 *
3342 *     3) RADESYS is not written and so the RADECSYS & EQUINOX values in the
3343 *        FitsStore must be consistent with the "1984" rule.
3344 *
3345 *     4) Any rotation produced by the PC matrix must be restricted to
3346 *        the celestial plane, and must involve no shear. A CROTA keyword
3347 *        with associated CDELT values are produced instead of the PC
3348 *        matrix.
3349 *
3350 *     5) ICRS is not supported.
3351 *
3352 *     6) Spectral axes can be created only for FITS-WCS CTYPE values of "FREQ"
3353 *        "VRAD" and "VOPT-F2W" and with standards of rest of LSRK, LSRD,
3354 *        BARYCENT and GEOCENTR.
3355 
3356 *  Parameters:
3357 *     this
3358 *        Pointer to the FitsChan.
3359 *     store
3360 *        Pointer to the FitsStore.
3361 *     method
3362 *        Pointer to a string holding the name of the calling method.
3363 *        This is only for use in constructing error messages.
3364 *     class
3365 *        Pointer to a string holding the name of the supplied object class.
3366 *        This is only for use in constructing error messages.
3367 *     status
3368 *        Pointer to the inherited status variable.
3369 
3370 *  Returned Value:
3371 *     A value of 1 is returned if succesfull, and zero is returned
3372 *     otherwise.
3373 */
3374 
3375 /* Local Variables: */
3376    char *comm;         /* Pointer to comment string */
3377    const char *cval;   /* Pointer to string keyword value */
3378    const char *specunit;/* Pointer to corrected spectral units string */
3379    char combuf[80];    /* Buffer for FITS card comment */
3380    char lattype[MXCTYPELEN];/* Latitude axis CTYPE */
3381    char lontype[MXCTYPELEN];/* Longitude axis CTYPE */
3382    char s;             /* Co-ordinate version character */
3383    char sign[2];       /* Fraction's sign character */
3384    char spectype[MXCTYPELEN];/* Spectral axis CTYPE */
3385    double *cdelt;      /* Pointer to CDELT array */
3386    double cdl;         /* CDELT term */
3387    double cdlat_lon;   /* Off-diagonal CD element */
3388    double cdlon_lat;   /* Off-diagonal CD element */
3389    double coscro;      /* Cos( CROTA ) */
3390    double crota;       /* CROTA value to use */
3391    double epoch;       /* Epoch of reference equinox */
3392    double fd;          /* Fraction of a day */
3393    double mjd99;       /* MJD at start of 1999 */
3394    double rho_a;       /* First estimate of CROTA */
3395    double rho_b;       /* Second estimate of CROTA */
3396    double sincro;      /* Sin( CROTA ) */
3397    double specfactor;  /* Factor for converting internal spectral units */
3398    double val;         /* General purpose value */
3399    int axlat;          /* Index of latitude FITS WCS axis */
3400    int axlon;          /* Index of longitude FITS WCS axis */
3401    int axrot1;         /* Index of first CROTA rotation axis */
3402    int axrot2;         /* Index of second CROTA rotation axis */
3403    int axspec;         /* Index of spectral FITS WCS axis */
3404    int i;              /* Axis index */
3405    int ihmsf[ 4 ];     /* Hour, minute, second, fractional second */
3406    int iymdf[ 4 ];     /* Year, month, date, fractional day */
3407    int j;              /* Axis index */
3408    int jj;             /* SlaLib status */
3409    int m;              /* Projection parameter index */
3410    int maxm;           /* Max projection parameter index */
3411    int naxis;          /* No. of axes */
3412    int ok;             /* Is FitsSTore OK for IRAF encoding? */
3413    int prj;            /* Projection type */
3414 
3415 /* Check the inherited status. */
3416    if( !astOK ) return 0;
3417 
3418 /* Initialise */
3419    specunit = "";
3420    specfactor = 1.0;
3421    maxm = 0;
3422 
3423 /* First check that the values in the FitsStore conform to the
3424    requirements of the AIPS++ encoding. Assume they do to begin with. */
3425    ok = 1;
3426 
3427 /* Just do primary axes. */
3428    s = ' ';
3429 
3430 /* Save the number of axes */
3431    naxis = GetMaxJM( &(store->crpix), ' ', status ) + 1;
3432 
3433 /* Look for the primary celestial and spectral axes. */
3434    FindLonLatSpecAxes( store, s, &axlon, &axlat, &axspec, method, class, status );
3435 
3436 /* If both longitude and latitude axes are present ...*/
3437    if( axlon >= 0 && axlat >= 0 ) {
3438 
3439 /* Get the CTYPE values for both axes. Extract the projection type as
3440    specified by the last 4 characters in the latitude CTYPE keyword value. */
3441       cval = GetItemC( &(store->ctype), axlon, 0, s, NULL, method, class, status );
3442       if( !cval ) {
3443          ok = 0;
3444       } else {
3445          strcpy( lontype, cval );
3446       }
3447       cval = GetItemC( &(store->ctype), axlat, 0, s, NULL, method, class, status );
3448       if( !cval ) {
3449          ok = 0;
3450          prj = AST__WCSBAD;
3451       } else {
3452          strcpy( lattype, cval );
3453          prj = astWcsPrjType( cval + 4 );
3454       }
3455 
3456 /* FITS-AIPS++ cannot handle the AST-specific TPN projection. */
3457       if( prj == AST__TPN || prj == AST__WCSBAD ) ok = 0;
3458 
3459 /* Projection parameters. FITS-AIPS++ encoding ignores projection parameters
3460    associated with the longitude axis. The number of parameters is limited to
3461    10. */
3462       maxm = GetMaxJM( &(store->pv), ' ', status );
3463       for( i = 0; i < naxis && ok; i++ ){
3464          if( i != axlon ) {
3465             for( m = 0; m <= maxm; m++ ){
3466                val = GetItem( &(store->pv), i, m, s, NULL, method, class, status );
3467                if( val != AST__BAD ) {
3468                   if( i != axlat || m >= 10 ){
3469                      ok = 0;
3470                      break;
3471                   }
3472                }
3473             }
3474          }
3475       }
3476 
3477 /* Identify the celestial coordinate system from the first 4 characters of the
3478    longitude CTYPE value. Only RA, galactic longitude, and ecliptic
3479    longitude can be stored using FITS-AIPS++. */
3480       if( ok && strncmp( lontype, "RA--", 4 ) &&
3481                 strncmp( lontype, "GLON", 4 ) &&
3482                 strncmp( lontype, "ELON", 4 ) ) ok = 0;
3483    }
3484 
3485 /* If a spectral axis is present ...*/
3486    if( axspec >= 0 ) {
3487 
3488 /* Get the CTYPE values for the axis, and find the AIPS equivalent, if
3489    possible. */
3490       cval = GetItemC( &(store->ctype), axspec, 0, s, NULL, method, class, status );
3491       if( !cval ) {
3492          ok = 0;
3493       } else {
3494          if( !strncmp( cval, "FREQ", astChrLen( cval ) ) ) {
3495             strcpy( spectype, "FREQ" );
3496          } else if( !strncmp( cval, "VRAD", astChrLen( cval ) ) ) {
3497             strcpy( spectype, "VELO" );
3498          } else if( !strncmp( cval, "VOPT-F2W", astChrLen( cval ) ) ) {
3499             strcpy( spectype, "FELO" );
3500          } else {
3501             ok = 0;
3502          }
3503       }
3504 
3505 /* If OK, check the SPECSYS value and add the AIPS equivalent onto the
3506    end of the CTYPE value.*/
3507       cval = GetItemC( &(store->specsys), 0, 0, s, NULL, method, class, status );
3508       if( !cval ) {
3509          ok = 0;
3510       } else {
3511          if( !strncmp( cval, "LSRK", astChrLen( cval ) ) ) {
3512             strcpy( spectype+4, "-LSR" );
3513          } else if( !strncmp( cval, "LSRD", astChrLen( cval ) ) ) {
3514             strcpy( spectype+4, "-LSD" );
3515          } else if( !strncmp( cval, "BARYCENT", astChrLen( cval ) ) ) {
3516             strcpy( spectype+4, "-HEL" );
3517          } else if( !strncmp( cval, "GEOCENTR", astChrLen( cval ) ) ) {
3518             strcpy( spectype+4, "-GEO" );
3519          } else {
3520             ok = 0;
3521          }
3522       }
3523 
3524 /* If still OK, ensure the spectral axis units are Hz or m/s. */
3525       cval = GetItemC( &(store->cunit), axspec, 0, s, NULL, method, class, status );
3526       if( !cval ) {
3527          ok = 0;
3528       } else if( ok ) {
3529          if( !strcmp( cval, "Hz" ) ) {
3530             specunit = "HZ";
3531             specfactor = 1.0;
3532          } else if( !strcmp( cval, "kHz" ) ) {
3533             specunit = "HZ";
3534             specfactor = 1.0E3;
3535          } else if( !strcmp( cval, "MHz" ) ) {
3536             specunit = "HZ";
3537             specfactor = 1.0E6;
3538          } else if( !strcmp( cval, "GHz" ) ) {
3539             specunit = "HZ";
3540             specfactor = 1.0E9;
3541          } else if( !strcmp( cval, "m/s" ) ) {
3542             specunit = "m/s";
3543             specfactor = 1.0;
3544          } else if( !strcmp( cval, "km/s" ) ) {
3545             specunit = "m/s";
3546             specfactor = 1.0E3;
3547          } else {
3548             ok = 0;
3549          }
3550       }
3551    }
3552 
3553 /* If this is different to the value of NAXIS abort since this encoding
3554    does not support WCSAXES keyword. */
3555    if( naxis != store->naxis ) ok = 0;
3556 
3557 /* Allocate memory to store the CDELT values */
3558    if( ok ) {
3559       cdelt = (double *) astMalloc( sizeof(double)*naxis );
3560       if( !cdelt ) ok = 0;
3561    } else {
3562       cdelt = NULL;
3563    }
3564 
3565 /* Check that rotation is restricted to the celestial plane, and extract
3566    the CDELT (diagonal) terms, etc. If there are no celestial
3567    axes, restrict rotation to the first two non-spectral axes. */
3568    if( axlat < 0 && axlon < 0 ) {
3569       if( axspec >= 0 && naxis > 2 ) {
3570          axrot2 = ( axspec == 0 ) ? 1 : 0;
3571          axrot1 = axrot2 + 1;
3572          if( axrot1 == axspec ) axrot1++;
3573       } else if( naxis > 1 ){
3574          axrot2 = 0;
3575          axrot1 = 1;
3576       } else {
3577          axrot2 = -1;
3578          axrot1 = -1;
3579       }
3580    } else {
3581       axrot1 = axlon;
3582       axrot2 = axlat;
3583    }
3584    cdlat_lon = 0.0;
3585    cdlon_lat = 0.0;
3586    for( i = 0; i < naxis && ok; i++ ){
3587       cdl = GetItem( &(store->cdelt), i, 0, s, NULL, method, class, status );
3588       if( cdl == AST__BAD ) cdl = 1.0;
3589       for( j = 0; j < naxis && ok; j++ ){
3590           val = GetItem( &(store->pc), i, j, s, NULL, method, class, status );
3591           if( val == AST__BAD ) val = ( i == j ) ? 1.0 : 0.0;
3592           val *= cdl;
3593           if( i == j ){
3594              cdelt[ i ] = val;
3595           } else if( i == axrot2 && j == axrot1 ){
3596              cdlat_lon = val;
3597           } else if( i == axrot1 && j == axrot2 ){
3598              cdlon_lat = val;
3599           } else if( val != 0.0 ){
3600              ok = 0;
3601           }
3602       }
3603    }
3604 
3605 /* Find the CROTA and CDELT values for the celestial axes. */
3606    if( ok && axrot1 >= 0 && axrot2 >= 0 ) {
3607       if( cdlat_lon > 0.0 ) {
3608          rho_a = atan2( cdlat_lon, cdelt[ axrot1 ] );
3609       } else if( cdlat_lon == 0.0 ) {
3610          rho_a = 0.0;
3611       } else {
3612          rho_a = atan2( -cdlat_lon, -cdelt[ axrot1 ] );
3613       }
3614       if( cdlon_lat > 0.0 ) {
3615          rho_b = atan2( cdlon_lat, -cdelt[ axrot2 ] );
3616       } else if( cdlon_lat == 0.0 ) {
3617          rho_b = 0.0;
3618       } else {
3619          rho_b = atan2( -cdlon_lat, cdelt[ axrot2 ] );
3620       }
3621       if( fabs( palDrange( rho_a - rho_b ) ) < 1.0E-2 ){
3622          crota = 0.5*( palDranrm( rho_a ) + palDranrm( rho_b ) );
3623          coscro = cos( crota );
3624          sincro = sin( crota );
3625          if( fabs( coscro ) > fabs( sincro ) ){
3626             cdelt[ axrot2 ] /= coscro;
3627             cdelt[ axrot1 ] /= coscro;
3628          } else {
3629             cdelt[ axrot2 ] = -cdlon_lat/sincro;
3630             cdelt[ axrot1 ] = cdlat_lon/sincro;
3631          }
3632          crota *= AST__DR2D;
3633 
3634 /* Use AST__BAD to indicate that CDi_j values should be produced
3635    instead of CROTA/CDELT. (I am told AIPS++ can understand CD matrices) */
3636       } else {
3637          crota = AST__BAD;
3638       }
3639    } else {
3640       crota = 0.0;
3641    }
3642 
3643 /* Get RADECSYS and the reference equinox. */
3644    cval = GetItemC( &(store->radesys), 0, 0, s, NULL, method, class, status );
3645    epoch = GetItem( &(store->equinox), 0, 0, s, NULL, method, class, status );
3646 
3647 /* If RADECSYS was available... */
3648    if( cval ){
3649 
3650 /* ICRS is not supported in this encoding. */
3651       if( !strcmp( "ICRS", cval ) ) ok = 0;
3652 
3653 /* If epoch was not available, set a default epoch. */
3654       if( epoch == AST__BAD ){
3655          if( !strcmp( "FK4", cval ) ){
3656             epoch = 1950.0;
3657          } else if( !strcmp( "FK5", cval ) ){
3658             epoch = 2000.0;
3659          } else {
3660             ok = 0;
3661          }
3662 
3663 /* If an equinox was supplied, check it is consistent with the IAU 1984
3664    rule. */
3665       } else {
3666          if( !strcmp( "FK4", cval ) ){
3667             if( epoch >= 1984.0 ) ok = 0;
3668          } else if( !strcmp( "FK5", cval ) ){
3669             if( epoch < 1984.0 ) ok = 0;
3670          } else {
3671             ok = 0;
3672          }
3673       }
3674    }
3675 
3676 /* Only create the keywords if the FitsStore conforms to the requirements
3677    of the FITS-AIPS++ encoding. */
3678    if( ok ) {
3679 
3680 /* Get and save CRPIX for all pixel axes. These are required, so break
3681    if they are not available. */
3682       for( j = 0; j < naxis && ok; j++ ){
3683          val = GetItem( &(store->crpix), 0, j, s, NULL, method, class, status );
3684          if( val == AST__BAD ) {
3685             ok = 0;
3686          } else {
3687             sprintf( combuf, "Reference pixel on axis %d", j + 1 );
3688             SetValue( this, FormatKey( "CRPIX", j + 1, -1, s, status ), &val,
3689                       AST__FLOAT, combuf, status );
3690          }
3691       }
3692 
3693 /* Get and save CRVAL for all intermediate axes. These are required, so
3694    break if they are not available. */
3695       for( i = 0; i < naxis && ok; i++ ){
3696          val = GetItem( &(store->crval), i, 0, s, NULL, method, class, status );
3697          if( val == AST__BAD ) {
3698             ok = 0;
3699          } else {
3700             if( i == axspec ) val *= specfactor;
3701             sprintf( combuf, "Value at ref. pixel on axis %d", i + 1 );
3702             SetValue( this, FormatKey( "CRVAL", i + 1, -1, s, status ), &val,
3703                       AST__FLOAT, combuf, status );
3704          }
3705       }
3706 
3707 /* Get and save CTYPE for all intermediate axes. These are required, so
3708    break if they are not available. Use the potentially modified versions
3709    saved above for the celestial axes. */
3710       for( i = 0; i < naxis && ok; i++ ){
3711          if( i == axlat ) {
3712             cval = lattype;
3713          } else if( i == axlon ) {
3714             cval = lontype;
3715          } else if( i == axspec ) {
3716             cval = spectype;
3717          } else {
3718             cval = GetItemC( &(store->ctype), i, 0, s, NULL, method, class, status );
3719          }
3720          if( cval && strcmp( cval + 4, "-TAB" ) ) {
3721             comm = GetItemC( &(store->ctype_com), i, 0, s, NULL, method, class, status );
3722             if( !comm ) {
3723                sprintf( combuf, "Type of co-ordinate on axis %d", i + 1 );
3724                comm = combuf;
3725             }
3726             SetValue( this, FormatKey( "CTYPE", i + 1, -1, s, status ), &cval,
3727                       AST__STRING, comm, status );
3728          } else {
3729             ok = 0;
3730          }
3731       }
3732 
3733 /* CDELT values */
3734       if( axspec != -1 ) cdelt[ axspec ] *= specfactor;
3735       for( i = 0; i < naxis; i++ ){
3736          SetValue( this, FormatKey( "CDELT", i + 1, -1, s, status ), cdelt + i,
3737                    AST__FLOAT, "Pixel size", status );
3738       }
3739 
3740 /* CUNIT values. [Spectral axis units should be upper-case] */
3741       for( i = 0; i < naxis; i++ ) {
3742          cval = GetItemC( &(store->cunit), i, 0, s, NULL, method, class, status );
3743          if( cval ) {
3744             if( i == axspec ) cval = specunit;
3745             sprintf( combuf, "Units for axis %d", i + 1 );
3746             SetValue( this, FormatKey( "CUNIT", i + 1, -1, s, status ), &cval, AST__STRING,
3747                       combuf, status );
3748          }
3749       }
3750 
3751 /* CD matrix. Multiply the row of the PC matrix by the CDELT value. */
3752       if( crota == AST__BAD ) {
3753          for( i = 0; i < naxis; i++ ) {
3754             cdl = GetItem( &(store->cdelt), i, 0, s, NULL, method, class, status );
3755             if( cdl == AST__BAD ) cdl = 1.0;
3756             for( j = 0; j < naxis; j++ ){
3757                val = GetItem( &(store->pc), i, j, s, NULL, method, class, status );
3758                if( val == AST__BAD ) val = ( i == j ) ? 1.0 : 0.0;
3759                val *= cdl;
3760                if( val != 0.0 ) {
3761                    SetValue( this, FormatKey( "CD", i + 1, j + 1, s, status ), &val,
3762                              AST__FLOAT, "Transformation matrix element", status );
3763                }
3764             }
3765          }
3766 
3767 /* CROTA */
3768       } else if( crota != 0.0 ) {
3769          if( axrot2 != -1 ){
3770             SetValue( this, FormatKey( "CROTA", axrot2 + 1, -1, s, status ), &crota,
3771                       AST__FLOAT, "Axis rotation", status );
3772          } else if( ( axspec == -1 && naxis > 1 ) ||
3773                     ( axspec != -1 && naxis > 2 ) ) {
3774             SetValue( this, "CROTA1", &crota, AST__FLOAT, "Axis rotation", status );
3775          }
3776       }
3777 
3778 /* Reference equinox */
3779       if( epoch != AST__BAD ) SetValue( this, "EPOCH", &epoch, AST__FLOAT,
3780                                         "Epoch of reference equinox", status );
3781 
3782 /* Latitude of native north pole. */
3783       val = GetItem( &(store->latpole), 0, 0, s, NULL, method, class, status );
3784       if( val != AST__BAD ) SetValue( this, "LATPOLE", &val, AST__FLOAT,
3785                                       "Latitude of native north pole", status );
3786 
3787 /* Longitude of native north pole. */
3788       val = GetItem( &(store->lonpole), 0, 0, s, NULL, method, class, status );
3789       if( val != AST__BAD ) SetValue( this, "LONPOLE", &val, AST__FLOAT,
3790                                       "Longitude of native north pole", status );
3791 
3792 /* Date of observation. */
3793       val = GetItem( &(store->mjdobs), 0, 0, ' ', NULL, method, class, status );
3794       if( val != AST__BAD ) {
3795 
3796 /* The format used for the DATE-OBS keyword depends on the value of the
3797    keyword. For DATE-OBS < 1999.0, use the old "dd/mm/yy" format.
3798    Otherwise, use the new "ccyy-mm-ddThh:mm:ss[.ssss]" format. */
3799          palCaldj( 99, 1, 1, &mjd99, &jj );
3800          if( val < mjd99 ) {
3801             palDjcal( 0, val, iymdf, &jj );
3802             sprintf( combuf, "%2.2d/%2.2d/%2.2d", iymdf[ 2 ], iymdf[ 1 ],
3803                      iymdf[ 0 ] - ( ( iymdf[ 0 ] > 1999 ) ? 2000 : 1900 ) );
3804          } else {
3805             palDjcl( val, iymdf, iymdf+1, iymdf+2, &fd, &jj );
3806             palDd2tf( 3, fd, sign, ihmsf );
3807             sprintf( combuf, "%4.4d-%2.2d-%2.2dT%2.2d:%2.2d:%2.2d.%3.3d",
3808                      iymdf[0], iymdf[1], iymdf[2], ihmsf[0], ihmsf[1],
3809                      ihmsf[2], ihmsf[3] );
3810          }
3811 
3812 /* Now store the formatted string in the FitsChan. */
3813          cval = combuf;
3814          SetValue( this, "DATE-OBS", (void *) &cval, AST__STRING,
3815                    "Date of observation", status );
3816       }
3817 
3818 /* Projection parameters. */
3819       if( axlat >= 0 && axlon >= 0 ) {
3820          for( m = 0; m <= maxm; m++ ){
3821             val = GetItem( &(store->pv), axlat, m, s, NULL, method, class, status );
3822             if( val != AST__BAD ) SetValue( this, FormatKey( "PROJP", m, -1, ' ', status ),
3823                                             &val, AST__FLOAT, "Projection parameter", status );
3824          }
3825       }
3826 
3827 /* Spectral stuff.. */
3828       if( axspec >= 0 ) {
3829 
3830 /* Rest frequency */
3831          val = GetItem( &(store->restfrq), 0, 0, s, NULL, method, class, status );
3832          if( val != AST__BAD ) SetValue( this, FormatKey( "RESTFREQ", -1, -1, s, status ),
3833                                          &val, AST__FLOAT, "[Hz] Rest frequency", status );
3834       }
3835    }
3836 
3837 /* Release CDELT workspace */
3838    if( cdelt ) cdelt = (double *) astFree( (void *) cdelt );
3839 
3840 /* Return zero or ret depending on whether an error has occurred. */
3841    return astOK ? ok : 0;
3842 }
3843 
CardComm(AstFitsChan * this,int * status)3844 static char *CardComm( AstFitsChan *this, int *status ){
3845 
3846 /*
3847 *  Name:
3848 *     CardComm
3849 
3850 *  Purpose:
3851 *     Return the keyword comment from the current card.
3852 
3853 *  Type:
3854 *     Private function.
3855 
3856 *  Synopsis:
3857 *     #include "fitschan.h"
3858 *     char *CardComm( AstFitsChan *this, int *status )
3859 
3860 *  Class Membership:
3861 *     FitsChan member function.
3862 
3863 *  Description:
3864 *     Returns a pointer to a string holding the keyword comment from the
3865 *     current card.
3866 
3867 *  Parameters:
3868 *     this
3869 *        Pointer to the FitsChan.
3870 *     status
3871 *        Pointer to the inherited status variable.
3872 
3873 *  Returned Value:
3874 *     A pointer to the keyword comment, or NULL if the FitsChan is at
3875 *     end-of-file, or does not have a comment.
3876 
3877 *  Notes:
3878 *     -  The current card is not changed by this function.
3879 *     -  This function attempts to execute even if an error has occurred.
3880 */
3881 
3882 /* Local Variables: */
3883    char *ret;
3884 
3885 /* Check the supplied object. */
3886    if( !this ) return NULL;
3887 
3888 /* If the current card is defined, store a pointer to its keyword comment. */
3889    if( this->card ){
3890       ret = ( (FitsCard *) this->card )->comment;
3891 
3892 /* Otherwise store a NULL pointer. */
3893    } else {
3894       ret =  NULL;
3895    }
3896 
3897 /* Return the answer. */
3898    return ret;
3899 }
3900 
CardData(AstFitsChan * this,size_t * size,int * status)3901 static void *CardData( AstFitsChan *this, size_t *size, int *status ){
3902 
3903 /*
3904 *  Name:
3905 *     CardData
3906 
3907 *  Purpose:
3908 *     Return a pointer to the keyword data value for the current card.
3909 
3910 *  Type:
3911 *     Private function.
3912 
3913 *  Synopsis:
3914 *     #include "fitschan.h"
3915 
3916 *     void *CardData( AstFitsChan *this, size_t *size, int *status )
3917 
3918 *  Class Membership:
3919 *     FitsChan member function.
3920 
3921 *  Description:
3922 *     Returns a pointer to keyword data value from the current card.
3923 
3924 *  Parameters:
3925 *     this
3926 *        Pointer to the FitsChan.
3927 *     size
3928 *        A pointer to a location at which to return the number of bytes
3929 *        occupied by the data value. NULL can be supplied if this
3930 *        information is not required.
3931 *     status
3932 *        Pointer to the inherited status variable.
3933 
3934 *  Returned Value:
3935 *     A pointer to the keyword data, or NULL if the FitsChan is at
3936 *     end-of-file, or if the keyword does not have any data.
3937 
3938 *  Notes:
3939 *     -  For text data, the returned value for "size" includes the
3940 *     terminating null character.
3941 *     -  The current card is not changed by this function.
3942 *     -  This function attempts to execute even if an error has occurred.
3943 */
3944 
3945 /* Local Variables: */
3946    void *ret;
3947 
3948 /* Check the supplied object. */
3949    if( !this ) return NULL;
3950 
3951 /* If the current card is defined, store a pointer to its keyword data. */
3952    if( this->card ){
3953       ret = ( (FitsCard *) this->card )->data;
3954       if( size ) *size = ( (FitsCard *) this->card )->size;
3955 
3956 /* Otherwise store a NULL pointer. */
3957    } else {
3958       ret =  NULL;
3959       if( size ) *size = 0;
3960    }
3961 
3962 /* Return the answer. */
3963    return ret;
3964 }
3965 
CardFlags(AstFitsChan * this,int * status)3966 static int *CardFlags( AstFitsChan *this, int *status ){
3967 
3968 /*
3969 *  Name:
3970 *     CardFlags
3971 
3972 *  Purpose:
3973 *     Return a pointer to the flags mask for the current card.
3974 
3975 *  Type:
3976 *     Private function.
3977 
3978 *  Synopsis:
3979 *     #include "fitschan.h"
3980 
3981 *     int *CardFlags( AstFitsChan *this, int *status )
3982 
3983 *  Class Membership:
3984 *     FitsChan member function.
3985 
3986 *  Description:
3987 *     Returns a pointer to the flags mask for the current card.
3988 
3989 *  Parameters:
3990 *     this
3991 *        Pointer to the FitsChan.
3992 *     status
3993 *        Pointer to the inherited status variable.
3994 
3995 *  Returned Value:
3996 *     The pointer to the flags mask.
3997 
3998 *  Notes:
3999 *     -  The current card is not changed by this function.
4000 *     -  NULL is returned if the current card is not defined.
4001 *     -  This function attempts to execute even if an error has occurred.
4002 */
4003 
4004 /* Local Variables: */
4005    int *ret;
4006 
4007 /* Check the supplied object. */
4008    if( !this ) return NULL;
4009 
4010 /* If the current card is defined, store its deletion flag. */
4011    if( this->card ){
4012       ret = &( ( (FitsCard *) this->card )->flags );
4013 
4014 /* Otherwise store zero. */
4015    } else {
4016       ret =  NULL;
4017    }
4018 
4019 /* Return the answer. */
4020    return ret;
4021 }
4022 
CardName(AstFitsChan * this,int * status)4023 static char *CardName( AstFitsChan *this, int *status ){
4024 /*
4025 *  Name:
4026 *     CardName
4027 
4028 *  Purpose:
4029 *     Return the keyword name from the current card.
4030 
4031 *  Type:
4032 *     Private function.
4033 
4034 *  Synopsis:
4035 *     #include "fitschan.h"
4036 *     char *CardName( AstFitsChan *this, int *status )
4037 
4038 *  Class Membership:
4039 *     FitsChan member function.
4040 
4041 *  Description:
4042 *     Returns a pointer to a string holding the keyword name from the
4043 *     current card.
4044 
4045 *  Parameters:
4046 *     this
4047 *        Pointer to the FitsChan.
4048 *     status
4049 *        Pointer to the inherited status variable.
4050 
4051 *  Returned Value:
4052 *     A pointer to the keyword name, or NULL if the FitsChan is at
4053 *     end-of-file.
4054 
4055 *  Notes:
4056 *     -  The current card is not changed by this function.
4057 *     -  This function attempts to execute even if an error has occurred.
4058 */
4059 
4060 /* Local Variables: */
4061    char *ret;
4062 
4063 /* Check the supplied object. */
4064    if( !this ) return NULL;
4065 
4066 /* If the current card is defined, store a pointer to its keyword name. */
4067    if( this->card ){
4068       ret = ( (FitsCard *) this->card )->name;
4069 
4070 /* Otherwise store a NULL pointer. */
4071    } else {
4072       ret =  NULL;
4073    }
4074 
4075 /* Return the answer. */
4076    return ret;
4077 }
4078 
CardType(AstFitsChan * this,int * status)4079 static int CardType( AstFitsChan *this, int *status ){
4080 /*
4081 *  Name:
4082 *     CardType
4083 
4084 *  Purpose:
4085 *     Return the keyword type from the current card.
4086 
4087 *  Type:
4088 *     Private function.
4089 
4090 *  Synopsis:
4091 *     #include "fitschan.h"
4092 *     int CardType( AstFitsChan *this, int *status )
4093 
4094 *  Class Membership:
4095 *     FitsChan member function.
4096 
4097 *  Description:
4098 *     Returns the keyword type from the current card.
4099 
4100 *  Parameters:
4101 *     this
4102 *        Pointer to the FitsChan.
4103 *     status
4104 *        Pointer to the inherited status variable.
4105 
4106 *  Returned Value:
4107 *     The keyword type.
4108 
4109 *  Notes:
4110 *     -  The current card is not changed by this function.
4111 *     -  AST__NOTYPE is returned if the current card is not defined.
4112 *     -  This function attempts to execute even if an error has occurred.
4113 */
4114 
4115 /* Local Variables: */
4116    int ret;
4117 
4118 /* Check the supplied object. */
4119    if( !this ) return AST__NOTYPE;
4120 
4121 /* If the current card is defined, store the keyword type. */
4122    if( this->card ){
4123       ret = ( (FitsCard *) this->card )->type;
4124 
4125 /* Otherwise store AST__NOTYPE. */
4126    } else {
4127       ret =  AST__NOTYPE;
4128    }
4129 
4130 /* Return the answer. */
4131    return ret;
4132 }
4133 
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)4134 static AstMapping *CelestialAxes( AstFitsChan *this, AstFrameSet *fs, double *dim,
4135                                   int *wperm, char s, FitsStore *store, int *axis_done,
4136                                   int isoff, const char *method, const char *class, int *status ){
4137 
4138 /*
4139 *  Name:
4140 *     CelestialAxes
4141 
4142 *  Purpose:
4143 *     Add values to a FitsStore describing celestial axes in a Frame.
4144 
4145 *  Type:
4146 *     Private function.
4147 
4148 *  Synopsis:
4149 *     #include "fitschan.h"
4150 
4151 *     AstMapping *CelestialAxes( AstFitsChan *this, AstFrameSet *fs, double *dim,
4152 *                                int *wperm, char s, FitsStore *store, int *axis_done,
4153 *                                int isoff, const char *method, const char *class, int *status )
4154 
4155 *  Class Membership:
4156 *     FitsChan member function.
4157 
4158 *  Description:
4159 *     The current Frame of the supplied FrameSet is searched for celestial
4160 *     axes. If any are found, FITS WCS keyword values describing the axis
4161 *     are added to the supplied FitsStore, if possible (the conventions
4162 *     of FITS-WCS paper II are used). Note, this function does not store
4163 *     values for keywords which define the transformation from pixel
4164 *     coords to Intermediate World Coords (CRPIX, PC and CDELT), but a
4165 *     Mapping is returned which embodies these values. This Mapping is
4166 *     from the current Frame in the FrameSet (WCS coords) to a Frame
4167 *     representing IWC. The IWC Frame has the same number of axes as the
4168 *     WCS Frame which may be greater than the number of base Frame (i.e.
4169 *     pixel) axes.
4170 
4171 *  Parameters:
4172 *     this
4173 *        Pointer to the FitsChan.
4174 *     fs
4175 *        Pointer to the FrameSet. The base Frame should represent FITS pixel
4176 *        coordinates, and the current Frame should represent FITS WCS
4177 *        coordinates. The number of base Frame axes should not exceed the
4178 *        number of current Frame axes.
4179 *     dim
4180 *        An array holding the image dimensions in pixels. AST__BAD can be
4181 *        supplied for any unknown dimensions.
4182 *     wperm
4183 *        Pointer to an array of integers with one element for each axis of
4184 *        the current Frame. Each element holds the zero-based
4185 *        index of the FITS-WCS axis (i.e. the value of "i" in the keyword
4186 *        names "CTYPEi", "CRVALi", etc) which describes the Frame axis.
4187 *     s
4188 *        The co-ordinate version character. A space means the primary
4189 *        axis descriptions. Otherwise the supplied character should be
4190 *        an upper case alphabetical character ('A' to 'Z').
4191 *     store
4192 *        The FitsStore in which to store the FITS WCS keyword values.
4193 *     axis_done
4194 *        An array of flags, one for each Frame axis, which indicate if a
4195 *        description of the corresponding axis has yet been stored in the
4196 *        FitsStore.
4197 *     isoff
4198 *        If greater than zero, the description to add to the FitsStore
4199 *        should describe offset coordinates. If less than zero, the
4200 *        description to add to the FitsStore should describe absolute
4201 *        coordinates but should include the SkyRefIs, SkyRef and SkyRefP
4202 *        attributes. If zero, ignore all offset coordinate info. The
4203 *        absolute value indicates the nature of the reference point:
4204 *        1 == "pole", 2 == "origin", otherwise "ignored".
4205 *     method
4206 *        Pointer to a string holding the name of the calling method.
4207 *        This is only for use in constructing error messages.
4208 *     class
4209 *        Pointer to a string holding the name of the supplied object class.
4210 *        This is only for use in constructing error messages.
4211 *     status
4212 *        Pointer to the inherited status variable.
4213 
4214 *  Returned Value:
4215 *     If celestial axes were found which can be described using the
4216 *     conventions of FITS-WCS paper II, then a Mapping from the current Frame
4217 *     of the supplied FrameSet, to the IWC Frame is returned. Otherwise,
4218 *     a UnitMap is returned. Note, the Mapping only defines the IWC
4219 *     transformation for celestial axes. Any non-celestial axes are passed
4220 *     unchanged by the returned Mapping.
4221 */
4222 
4223 /* Local Variables: */
4224    AstFitsTable *table;    /* Pointer to structure holding -TAB table info */
4225    AstFrame *pframe;       /* Primary Frame containing current WCS axis*/
4226    AstFrame *wcsfrm;       /* WCS Frame within FrameSet */
4227    AstMapping *map1;       /* Pointer to pre-WcsMap Mapping */
4228    AstMapping *map3;       /* Pointer to post-WcsMap Mapping */
4229    AstMapping *map;        /* Pixel -> WCS mapping */
4230    AstMapping *ret;        /* Returned Mapping */
4231    AstMapping *tmap0;      /* A temporary Mapping */
4232    AstMapping *tmap1;      /* A temporary Mapping */
4233    AstMapping *tmap2;      /* A temporary Mapping */
4234    AstMapping *tmap3;      /* A temporary Mapping */
4235    AstMapping *tmap4;      /* A temporary Mapping */
4236    AstSkyFrame *skyfrm;    /* The SkyFrame defining current WCS axis */
4237    AstWcsMap *map2;        /* Pointer to WcsMap */
4238    AstWcsMap *map2b;       /* Pointer to WcsMap with cleared lat/lonpole */
4239    char *cval;             /* Pointer to keyword value */
4240    char *temp;             /* Pointer to temporary string */
4241    double *mat;            /* Pointer to matrix diagonal elements */
4242    double *ppcfid;         /* Pointer to array holding PPC at fiducial point */
4243    double con;             /* Constant value for unassigned axes */
4244    double crval[ 2 ];      /* Psi coords of reference point */
4245    double pv;              /* Projection parameter value */
4246    double skyfid[ 2 ];     /* Sky coords of fiducial point */
4247    double val;             /* Keyword value */
4248    int *inperm;            /* Input axis permutation array */
4249    int *outperm;           /* Output axis permutation array */
4250    int *tperm;             /* Pointer to new FITS axis numbering array */
4251    int axlat;              /* Index of latitude output from WcsMap */
4252    int axlon;              /* Index of longitude output from WcsMap */
4253    int extver;             /* Table version number for -TAB headers */
4254    int fits_ilat;          /* FITS WCS axis index for latitude axis */
4255    int fits_ilon;          /* FITS WCS axis index for longitude axis */
4256    int i;                  /* Loop index */
4257    int iax;                /* Axis index */
4258    int icolindexlat;       /* Index of table column holding lat index vector */
4259    int icolindexlon;       /* Index of table column holding lon index vector */
4260    int icolmainlat;        /* Index of table column holding main lat coord array */
4261    int icolmainlon;        /* Index of table column holding main lon coord array */
4262    int interplat;          /* INterpolation method for latitude look-up tables */
4263    int interplon;          /* INterpolation method for longitude look-up tables */
4264    int ilat;               /* Index of latitude axis within total WCS Frame */
4265    int ilon;               /* Index of longitude axis within total WCS Frame */
4266    int j;                  /* Loop index */
4267    int m;                  /* Projection parameter index */
4268    int maxm;               /* Largest used "m" value */
4269    int mlat;               /* Index of latitude axis in main lat coord array */
4270    int mlon;               /* Index of longitude axis in main lon coord array */
4271    int nwcs;               /* Number of WCS axes */
4272    int nwcsmap;            /* Number of inputs/outputs for the WcsMap */
4273    int paxis;              /* Axis index within primary Frame */
4274    int skylataxis;         /* Index of latitude axis within SkyFrame */
4275    int skylonaxis;         /* Index of longitude axis within SkyFrame */
4276    int tpn;                /* Is the WCS projectiona TPN projection? */
4277 
4278 /* Initialise */
4279    ret = NULL;
4280 
4281 /* Other initialisation to avoid compiler warnings. */
4282    mlon = 0;
4283    mlat = 0;
4284 
4285 /* Check the inherited status. */
4286    if( !astOK ) return ret;
4287 
4288 /* Get a pointer to the WCS Frame. */
4289    wcsfrm = astGetFrame( fs, AST__CURRENT );
4290 
4291 /* Store the number of WCS axes. */
4292    nwcs = astGetNout( fs );
4293 
4294 /* Check each axis in the WCS Frame to see if it is a celestial axis. */
4295    skyfrm = NULL;
4296    map = NULL;
4297    ilon = -1;
4298    ilat = -1;
4299    for( iax = 0; iax < nwcs; iax++ ) {
4300 
4301 /* Obtain a pointer to the primary Frame containing the current WCS axis. */
4302       astPrimaryFrame( wcsfrm, iax, &pframe, &paxis );
4303 
4304 /* If the current axis belongs to a SkyFrame, we have found a celestial
4305    axis. Keep a pointer to it, and note the indices of the celestial axes
4306    within the complete WCS Frame. The MakeFitsFrameSet function will have
4307    ensured that the WCS Frame only contains at most a single SkyFrame. */
4308       if( astIsASkyFrame( pframe ) ) {
4309          if( !skyfrm ) skyfrm = astClone( pframe );
4310          if( paxis == 0 ) {
4311             ilon = iax;
4312          } else {
4313             ilat = iax;
4314          }
4315 
4316 /* Indicate that this axis has been classified. */
4317          axis_done[ iax ] = 1;
4318       }
4319 
4320 /* Release resources. */
4321       pframe = astAnnul( pframe );
4322    }
4323 
4324 /* Only proceed if we found celestial axes. */
4325    if( ilon != -1 && ilat != -1 ) {
4326 
4327 /* Note the FITS WCS axis indices for the longitude and latitude axes */
4328       fits_ilon = wperm[ ilon ];
4329       fits_ilat = wperm[ ilat ];
4330 
4331 /* Create an array to hold the Projection Plane Coords corresponding to the
4332    CRVALi keywords. */
4333       ppcfid = (double *) astMalloc( sizeof( double )*nwcs );
4334 
4335 /* Get the pixel->wcs Mapping. */
4336       map = astGetMapping( fs, AST__BASE, AST__CURRENT );
4337 
4338 /* Get the table version number to use if we end up using the -TAB
4339    algorithm. This is the set value of the TabOK attribute (if positive). */
4340       extver = astGetTabOK( this );
4341 
4342 /* Some of the required FITS Keyword values are defined by the WcsMap
4343    contained within the Mapping. Split the mapping up into a list of serial
4344    component mappings, and locate the first WcsMap in this list. The first
4345    Mapping returned by this call is the result of compounding all the
4346    Mappings up to (but not including) the WcsMap, the second returned Mapping
4347    is the (inverted) WcsMap, and the third returned Mapping is anything
4348    following the WcsMap. Only proceed if one and only one WcsMap is found. */
4349       if( SplitMap( map, astGetInvert( map ), ilon, ilat, &map1, &map2, &map3,
4350                     status ) ){
4351 
4352 /* Get the indices of the latitude and longitude axes within the SkyFrame
4353    (not necessarily (1,0) because they may have been permuted). */
4354          skylataxis = astGetLatAxis( skyfrm );
4355          skylonaxis = astGetLonAxis( skyfrm );
4356 
4357 /* The reference point in the celestial coordinate system is found by
4358    transforming the fiducial point in native spherical co-ordinates
4359    into WCS coordinates using map3. */
4360          if( GetFiducialWCS( map2, map3, ilon,  ilat, skyfid + skylonaxis,
4361                          skyfid + skylataxis, status ) ){
4362 
4363 /* We also need to find the indices of the longitude and latitude outputs
4364    from the WcsMap. These may not be the same as ilat and ilon because of
4365    axis permutations in "map3". */
4366             axlon = astGetWcsAxis( map2, 0 );
4367             axlat = astGetWcsAxis( map2, 1 );
4368 
4369 /* Normalise the latitude and longitude values at the fiducial point. The
4370    longitude and latitude values found above will be in radians, but after
4371    normalization we convert them to degrees, as expected by other functions
4372    which handle FitsStores. */
4373             if( skyfid[ skylonaxis ] == AST__BAD ) skyfid[ skylonaxis ] = 0.0;
4374             if( skyfid[ skylataxis ] == AST__BAD ) skyfid[ skylataxis ] = 0.0;
4375             if( ZEROANG( skyfid[ 0 ] ) ) skyfid[ 0 ] = 0.0;
4376             if( ZEROANG( skyfid[ 1 ] ) ) skyfid[ 1 ] = 0.0;
4377             astNorm( skyfrm, skyfid );
4378             SetItem( &(store->crval), fits_ilon, 0, s, AST__DR2D*skyfid[ skylonaxis ], status );
4379             SetItem( &(store->crval), fits_ilat, 0, s, AST__DR2D*skyfid[ skylataxis ], status );
4380 
4381 /* Set a flag if we have a TPN projection. This is an AST-specific
4382    projection which mimicks the old "TAN with correction terms" projection
4383    which was removed from the final version of the FITS-WCS paper II. */
4384             tpn = ( astGetWcsType( map2 ) == AST__TPN );
4385 
4386 /* Store the WCS projection parameters. Except for TPN projections, always
4387    exclude parameters 3 and 4 on the longitude axis since these are
4388    reserved to hold copies of LONPOLE and LATPOLE. */
4389             for( m = 0; m < WCSLIB_MXPAR; m++ ){
4390                if( astTestPV( map2, axlon, m ) ) {
4391                   if( m < 3 || m > 4 || tpn ) {
4392                      pv = astGetPV( map2, axlon, m );
4393                      if( pv != AST__BAD ) SetItem( &(store->pv), fits_ilon, m,
4394                                                    s, pv, status );
4395                   }
4396                }
4397                if( astTestPV( map2, axlat, m ) ) {
4398                   pv = astGetPV( map2, axlat, m );
4399                   if( pv != AST__BAD ) SetItem( &(store->pv), fits_ilat, m,
4400                                                 s, pv, status );
4401                }
4402             }
4403 
4404 /* If PVi_0 (for the longitude axis) is non-zero, the Cartesian coordinates
4405    used by the WcsMap (Projection Plane Coordinates, PPC) need to be shifted
4406    to produce Intermediate World Coordinates (IWC). This shift results in
4407    the pixel reference position specified by the CRPIXi values (and which
4408    corresponds to the origin of IWC) mapping on to the fiducial position
4409    specified by the CRVALi values. The required shifts are just the PPC
4410    coordinates of the fiducial point. The AST-specific "TPN" projection uses
4411    longitude projection parameters to define correction terms, and so cannot
4412    use the above convention (which is part of FITS-WCS paper II). Therefore
4413    TPN projections always use zero shift between PPC and IWC. */
4414             for( iax = 0; iax < nwcs; iax++ ) ppcfid[ iax ] = 0.0;
4415             if( !tpn && astGetPV( map2, axlon, 0 ) != 0.0 ) {
4416                GetFiducialPPC( (AstWcsMap *) map2, ppcfid + ilon, ppcfid + ilat, status );
4417                if( ppcfid[ ilon ] == AST__BAD ) ppcfid[ ilon ] = 0.0;
4418                if( ppcfid[ ilat ] == AST__BAD ) ppcfid[ ilat ] = 0.0;
4419                ppcfid[ ilon ] *= AST__DR2D;
4420                ppcfid[ ilat ] *= AST__DR2D;
4421             }
4422 
4423 /* Store the CTYPE, CNAME, EQUINOX, MJDOBS, and RADESYS values. */
4424             SkySys( this, skyfrm, 1, astGetWcsType( map2 ), store, fits_ilon,
4425                     fits_ilat, s, isoff, method, class, status );
4426 
4427 /* Store the LONPOLE and LATPOLE values in the FitsStore. */
4428             SkyPole( map2, map3, ilon, ilat, wperm, s, store, method, class, status );
4429 
4430 /* The values of LONPOLE and LATPOLE stored above (in the FitsStore) will be
4431    ignored by WcsNative if the WcsMap contains set values for projection
4432    parameters PVi_3a and/or PVi_4a (these will be used in preference to
4433    the values in the FitsStore). To avoid this happening we take a copy
4434    of the WcsMap and clear the relevant parameters (but not if the WcsMap is
4435    for a TPN projection because TPN uses PVi_3a and PVi_4a for other
4436    purposes). */
4437             if( astGetWcsType( map2 ) != AST__TPN ) {
4438                map2b = astCopy( map2 );
4439                astClearPV( map2b, axlon, 3 );
4440                astClearPV( map2b, axlon, 4 );
4441             } else {
4442                map2b = astClone( map2 );
4443             }
4444 
4445 /* We will now create the Mapping from WCS coords to IWC coords. In fact,
4446    we produce the Mapping from IWC to WCS and then invert it. Create the
4447    first component of this Mapping which implements any shift of origin
4448    from IWC to PPC. */
4449             tmap0 = (AstMapping *) astShiftMap( nwcs, ppcfid, "", status );
4450 
4451 /* The next component of this Mapping scales the PPC coords from degrees
4452    to radians on the celestial axes. */
4453             mat = astMalloc( sizeof( double )*(size_t) nwcs );
4454             if( astOK ) {
4455                for( iax = 0; iax < nwcs; iax++ ) mat[ iax ] = 1.0;
4456                mat[ ilon ] = AST__DD2R;
4457                mat[ ilat ] = AST__DD2R;
4458                tmap1 = (AstMapping *) astMatrixMap( nwcs, nwcs, 1, mat, "", status );
4459                mat = astFree( mat );
4460             } else {
4461                tmap1 = NULL;
4462             }
4463 
4464 /* Now create the Mapping from Native Spherical Coords to WCS. */
4465             tmap2 = WcsNative( NULL, store, s, map2b, fits_ilon, fits_ilat,
4466                                method, class, status );
4467 
4468 /* Combine the WcsMap with the above Mapping, to get the Mapping from PPC
4469    to WCS. */
4470             tmap3 = (AstMapping *) astCmpMap( map2b, tmap2, 1, "", status );
4471             tmap2 = astAnnul( tmap2 );
4472 
4473 /* If there are more WCS axes than IWC axes, create a UnitMap for the extra
4474    WCS axes and add it in parallel with tmap3. */
4475             nwcsmap = astGetNin( map3 );
4476             if( nwcsmap < nwcs ) {
4477                tmap2 = (AstMapping *) astUnitMap( nwcs - nwcsmap, "", status );
4478                tmap4 = (AstMapping *) astCmpMap( tmap3, tmap2, 0, "", status );
4479                tmap3 = astAnnul( tmap3 );
4480                tmap2 = astAnnul( tmap2 );
4481                tmap3 = tmap4;
4482                nwcsmap = nwcs;
4483             }
4484 
4485 /* The pixel->wcs mapping may include a PermMap which selects some sub-set
4486    or super-set of the orignal WCS axes. In this case the number of inputs
4487    and outputs for "tmap3" created above may not equal "nwcs". To avoid this,
4488    we embed "tmap3" between 2 PermMaps which select the required axes. */
4489             if( nwcsmap != nwcs || ilon != axlon || ilat != axlat ) {
4490                inperm = astMalloc( sizeof( int )*(size_t) nwcs );
4491                outperm = astMalloc( sizeof( int )*(size_t) nwcsmap );
4492                if( astOK ) {
4493 
4494 /* Indicate that no inputs of the PermMap have yet been assigned to any
4495    outputs */
4496                   for( i = 0; i < nwcs; i++ ) inperm[ i ] = -1;
4497 
4498 /* Assign the WcsMap long/lat axes to the WCS Frame long/lat axes */
4499                   inperm[ ilon ] = axlon;
4500                   inperm[ ilat ] = axlat;
4501 
4502 /* Assign the remaining inputs arbitrarily (doesn't matter how we do this
4503    since the WcsMap is effectively a UnitMap on all non-celestial axes). */
4504                   iax = 0;
4505                   for( i = 0; i < nwcs; i++ ) {
4506                      while( iax == axlon || iax == axlat ) iax++;
4507                      if( inperm[ i ] == -1 ) inperm[ i ] = iax++;
4508                   }
4509 
4510 /* Do the same for the outputs. */
4511                   for( i = 0; i < nwcsmap; i++ ) outperm[ i ] = -1;
4512                   outperm[ axlon ] = ilon;
4513                   outperm[ axlat ] = ilat;
4514                   iax = 0;
4515                   for( i = 0; i < nwcsmap; i++ ) {
4516                      while( iax == ilon || iax == ilat ) iax++;
4517                      if( outperm[ i ] == -1 ) outperm[ i ] = iax++;
4518                   }
4519 
4520 /* Create the PermMap. */
4521                   con = AST__BAD;
4522                   tmap2 = (AstMapping *) astPermMap( nwcs, inperm, nwcsmap,
4523                                                      outperm, &con, "", status );
4524 
4525 /* Sandwich the WcsMap between the PermMap and its inverse. */
4526                   tmap4 = (AstMapping *) astCmpMap( tmap2, tmap3, 1, "", status );
4527                   tmap3 = astAnnul( tmap3 );
4528                   astInvert( tmap2 );
4529                   tmap3 = (AstMapping *) astCmpMap( tmap4, tmap2, 1, "", status );
4530                   tmap2 = astAnnul( tmap2 );
4531                   tmap4 = astAnnul( tmap4 );
4532                }
4533                inperm = astFree( inperm );
4534                outperm = astFree( outperm );
4535             }
4536 
4537 /* Combine these Mappings together. */
4538             tmap4 = (AstMapping *) astCmpMap( tmap0, tmap1, 1, "", status );
4539             tmap0 = astAnnul( tmap0 );
4540             tmap1 = astAnnul( tmap1 );
4541             ret = (AstMapping *) astCmpMap( tmap4, tmap3, 1, "", status );
4542             tmap3 = astAnnul( tmap3 );
4543             tmap4 = astAnnul( tmap4 );
4544 
4545 /* Invert this Mapping to get the Mapping from WCS to IWC. */
4546             astInvert( ret );
4547 
4548 /* The spherical rotation involved in converting WCS to IWC can result in
4549    inappropriate numbering of the FITS axes. For instance, a LONPOLE
4550    value of 90 degrees causes the IWC axes to be transposed. For this
4551    reason we re-asses the FITS axis numbers assigned to the celestial
4552    axes in order to make the IWC axes as close as possible to the pixel
4553    axes with the same number (but only if the axis order is being
4554    determined automatically). To do this, we need the Mapping from
4555    pixel to IWC, which is formed by concatenating the pixel->WCS
4556    Mapping with the WCS->IWC Mapping. */
4557             if( astChrMatch( astGetFitsAxisOrder( this ), "<auto>" ) ) {
4558                tmap0 = (AstMapping *) astCmpMap( map, ret, 1, "", status );
4559 
4560 /* Find the outputs of this Mapping which should be associated with each
4561    input. */
4562                tperm = astMalloc( sizeof(int)*(size_t) nwcs );
4563                if( ! WorldAxes( this, tmap0, dim, tperm, status ) ) {
4564                   ret = astAnnul( ret );
4565                }
4566 
4567 /* If the index associated with the celestial axes appear to have been
4568    swapped... */
4569                if( ret && astOK && fits_ilon == tperm[ ilat ] &&
4570                             fits_ilat == tperm[ ilon ] ) {
4571 
4572 /* Swap the fits axis indices associated with each WCS axis to match. */
4573                   wperm[ ilon ] = fits_ilat;
4574                   wperm[ ilat ] = fits_ilon;
4575 
4576 /* Swap the stored CRVAL value for the longitude and latitude axis. */
4577                   val = GetItem( &(store->crval), fits_ilat, 0, s, NULL, method, class, status );
4578                   SetItem( &(store->crval), fits_ilat, 0, s,
4579                            GetItem( &(store->crval), fits_ilon, 0, s, NULL,
4580                            method, class, status ), status );
4581                   SetItem( &(store->crval), fits_ilon, 0, s, val, status );
4582 
4583 /* Swap the stored CTYPE value for the longitude and latitude axis. */
4584                   cval = GetItemC( &(store->ctype), fits_ilat, 0, s, NULL, method, class, status );
4585                   if( cval ) {
4586                      temp = astStore( NULL, (void *) cval, strlen( cval ) + 1 );
4587                      cval = GetItemC( &(store->ctype), fits_ilon, 0, s, NULL, method, class, status );
4588                      if( cval ) {
4589                         SetItemC( &(store->ctype), fits_ilat, 0, s, cval, status );
4590                         SetItemC( &(store->ctype), fits_ilon, 0, s, temp, status );
4591                      }
4592                      temp = astFree( temp );
4593                   }
4594 
4595 /* Swap the stored CNAME value for the longitude and latitude axis. */
4596                   cval = GetItemC( &(store->cname), fits_ilat, 0, s, NULL, method, class, status );
4597                   if( cval ) {
4598                      temp = astStore( NULL, (void *) cval, strlen( cval ) + 1 );
4599                      cval = GetItemC( &(store->cname), fits_ilon, 0, s, NULL, method, class, status );
4600                      if( cval ) {
4601                         SetItemC( &(store->cname), fits_ilat, 0, s, cval, status );
4602                         SetItemC( &(store->cname), fits_ilon, 0, s, temp, status );
4603                      }
4604                      temp = astFree( temp );
4605                   }
4606 
4607 /* Swap the projection parameters asociated with the longitude and latitude
4608    axes. */
4609                   maxm = GetMaxJM( &(store->pv), s, status );
4610                   for( m = 0; m <= maxm; m++ ){
4611                      val = GetItem( &(store->pv), fits_ilat, m, s, NULL, method, class, status );
4612                      SetItem( &(store->pv), fits_ilat, m, s,
4613                               GetItem( &(store->pv), fits_ilon, m, s, NULL,
4614                               method, class, status ), status );
4615                      SetItem( &(store->pv), fits_ilon, m, s, val, status );
4616                   }
4617                }
4618 
4619 /* Release resources. */
4620                tperm = astFree( tperm );
4621                tmap0 = astAnnul( tmap0 );
4622             }
4623             map2b = astAnnul( map2b );
4624          }
4625 
4626 /* Release resources. */
4627          map1 = astAnnul( map1 );
4628          map2 = astAnnul( map2 );
4629          map3 = astAnnul( map3 );
4630 
4631 /* If no WcsMap was found in the pixel->WCS Mapping, it may be possible
4632    to describe the celestial axes using a tabular look-up table (i.e. the
4633    FITS-WCS "_TAB" algorithm). Only do this if the -TAB algorithm is to
4634    be supported. */
4635       } else if( extver > 0 ) {
4636 
4637 /* Get any pre-existing FitsTable from the FitsStore. This is the table
4638    in which the tabular data will be stored (if the Mapping can be expressed
4639    in -TAB form). */
4640          if( !astMapGet0A( store->tables, AST_TABEXTNAME, &table ) ) table = NULL;
4641 
4642 /* See if the transformations for the celestial axes can be expressed in -TAB
4643    form. The returned Mapping (if any) is the Mapping from (lon,lat)
4644    (rads) to (psi_lon,psi_lat) (pixels). See FITS-WCS paper III section 6.1.2
4645    for definition of psi. Scale the values stored in the table from radians
4646    to degrees. */
4647          tmap0 = IsMapTab2D( map, AST__DR2D, "deg", wcsfrm, dim, ilon, ilat,
4648                              fits_ilon, fits_ilat, &table, &icolmainlon,
4649                              &icolmainlat, &icolindexlon, &icolindexlat,
4650                              &mlon, &mlat, &interplon, &interplat, status );
4651          if( tmap0 ) {
4652 
4653 /* Store the CTYPE, CNAME, EQUINOX, MJDOBS, and RADESYS values. */
4654             SkySys( this, skyfrm, 0, 0, store, fits_ilon, fits_ilat, s, isoff,
4655                     method, class, status );
4656 
4657 /* If possible, choose the two CRVAL values (which are values on the psi
4658    axes) so that transforming them using the Mapping returned by
4659    IsMapTab2D gives the sky reference position stored in the SkyFrame.
4660    Check the SkyFrame has a defined reference position. */
4661             if( astTestSkyRef( skyfrm, 0 ) && astTestSkyRef( skyfrm, 1 ) ){
4662 
4663 /* Get the longitude and latitude at the reference point in radians. */
4664                skyfid[ 0 ] = astGetSkyRef( skyfrm, astGetLonAxis( skyfrm ));
4665                skyfid[ 1 ] = astGetSkyRef( skyfrm, astGetLatAxis( skyfrm ));
4666 
4667 /* We use the WCS->psi Mapping to convert the reference point WCS coords
4668    (rads) into psi coords (pixels). We can only do this if the WCS->psi
4669    Mapping has a defined forward transformation. */
4670                if( astGetTranForward( tmap0 ) ) {
4671                   astTran2( tmap0, 1, skyfid, skyfid + 1, 1, crval,
4672                             crval + 1 );
4673 
4674 /* If the WCS->psi mapping has an undefined forward transformation, then
4675    just store the sky reference point coords (in degs) in keywords
4676    AXREFn, and use 1.0 for the CRVAL values, so that IWC becomes equal
4677    to (psi-1) i.e. (grid coords - 1). This means the reference point is
4678    at grid coords (1.0,1.0). Note this choice of 1.0 for CRVAL is not
4679    arbitrary since it is required by the trick used to create invertable CD
4680    matrix in function MakeInvertable. */
4681                } else {
4682                   SetItem( &(store->axref), fits_ilon, 0, s,
4683                            AST__DR2D*skyfid[ 0 ], status );
4684                   SetItem( &(store->axref), fits_ilat, 0, s,
4685                            AST__DR2D*skyfid[ 1 ], status );
4686                   crval[ 0 ] = 1.0;
4687                   crval[ 1 ] = 1.0;
4688                }
4689 
4690 /* If the SkyFrame has no reference position, use 1.0 for the CRVAL values. */
4691             } else {
4692                crval[ 0 ] = 1.0;
4693                crval[ 1 ] = 1.0;
4694             }
4695 
4696 /* Create a Mapping that describes the transformation from the lon and lat
4697    psi axes to the lon and lat IWC axes (i.e. a ShiftMap that just subtracts
4698    the CRVAL values from each axis). */
4699             crval[ 0 ] = -crval[ 0 ];
4700             crval[ 1 ] = -crval[ 1 ];
4701             tmap1 = (AstMapping *) astShiftMap( 2, crval, " ", status );
4702             crval[ 0 ] = -crval[ 0 ];
4703             crval[ 1 ] = -crval[ 1 ];
4704 
4705 /* Create a series compound Mapping that applies the Mapping returned
4706    by IsMapTab2D first (the Mapping from WCS to psi), followed by the
4707    Mapping from psi to IWC created above. There-after, use this compound
4708    Mapping in place of the Mapping returned by IsMapTab2D. It maps WCS to
4709    IWC. */
4710             tmap2 = (AstMapping *) astCmpMap( tmap0, tmap1, 1, " ", status );
4711             (void) astAnnul( tmap0 );
4712             tmap1 = astAnnul( tmap1 );
4713             tmap0 = tmap2;
4714 
4715 /* Store the CRVAL values */
4716             SetItem( &(store->crval), fits_ilon, 0, s, crval[ 0 ], status );
4717             SetItem( &(store->crval), fits_ilat, 0, s, crval[ 1 ], status );
4718 
4719 /* Store TAB-specific values in the FitsStore. First the name of the
4720    FITS binary table extension holding the coordinate info. */
4721             SetItemC( &(store->ps), fits_ilon, 0, s, AST_TABEXTNAME, status );
4722             SetItemC( &(store->ps), fits_ilat, 0, s, AST_TABEXTNAME, status );
4723 
4724 /* Next the table version number. This is the set (positive) value for the
4725    TabOK attribute. */
4726             SetItem( &(store->pv), fits_ilon, 1, s, extver, status );
4727             SetItem( &(store->pv), fits_ilat, 1, s, extver, status );
4728 
4729 /* Also store the table version in the binary table header. */
4730             astSetFitsI( table->header, "EXTVER", extver, "Table version number",
4731                          0 );
4732 
4733 /* Next the name of the table column containing the main coords array. */
4734             SetItemC( &(store->ps), fits_ilon, 1, s,
4735                       astColumnName( table, icolmainlon ), status );
4736             SetItemC( &(store->ps), fits_ilat, 1, s,
4737                       astColumnName( table, icolmainlat ), status );
4738 
4739 /* Next the name of the column containing the index array. */
4740             if( icolindexlon >= 0 ) SetItemC( &(store->ps), fits_ilon, 2, s,
4741                              astColumnName( table, icolindexlon ), status );
4742             if( icolindexlat >= 0 ) SetItemC( &(store->ps), fits_ilat, 2, s,
4743                              astColumnName( table, icolindexlat ), status );
4744 
4745 /* The one-based index of the axes within the coordinate array that
4746    describes FITS WCS axes "fits_ilon" and "fits_ilat". */
4747             SetItem( &(store->pv), fits_ilon, 3, s, mlon, status );
4748             SetItem( &(store->pv), fits_ilat, 3, s, mlat, status );
4749 
4750 /* The interpolation method (an AST extension to the published -TAB
4751    algorithm, communicated through the QVi_4a keyword). */
4752             SetItem( &(store->pv), fits_ilon, 4, s, interplon, status );
4753             SetItem( &(store->pv), fits_ilat, 4, s, interplat, status );
4754 
4755 /* Also store the FitsTable itself in the FitsStore. */
4756             astMapPut0A( store->tables, AST_TABEXTNAME, table, NULL );
4757 
4758 /* Allocate space for the arrays that define the permutations required
4759    for the inputs and outputs of a PermMap. */
4760             inperm = astMalloc( sizeof( double )*nwcs );
4761             outperm = astMalloc( sizeof( double )*nwcs );
4762             if( astOK ) {
4763 
4764 /* Create the WCS -> IWC Mapping. First create a parallel CmpMap that
4765    combines the Mapping returned by IsMapTab2D (which transforms the celestial
4766    axes), with a UnitMap which transforms the non-celestial axes. */
4767                if( nwcs > 2 ) {
4768                   tmap1 = (AstMapping *) astUnitMap( nwcs - 2, " ", status );
4769                   tmap2 = (AstMapping *) astCmpMap( tmap0, tmap1, 0, " ", status );
4770                   tmap1 = astAnnul( tmap1 );
4771                } else {
4772                   tmap2 = astClone( tmap0 );
4773                }
4774 
4775 /* Now create a PermMap that permutes the inputs of this CmpMap into the
4776    order of the axes in the WCS Frame. */
4777                outperm[ 0 ] = ilon;
4778                outperm[ 1 ] = ilat;
4779                j = 0;
4780                for( i = 2; i < nwcs; i++ ) {
4781                   while( j == ilon || j == ilat ) j++;
4782                   outperm[ i ] = j++;
4783                }
4784                for( i = 0; i < nwcs; i++ ) inperm[ outperm[ i ] ] = i;
4785                tmap1 = (AstMapping *) astPermMap( nwcs, inperm, nwcs, outperm,
4786                                                   NULL, " ", status );
4787 
4788 /* Use this PermMap (and its inverse) to permute the inputs (and outputs)
4789    of the parallel CmpMap created above. */
4790                tmap3 = (AstMapping *) astCmpMap( tmap1, tmap2, 1, " ", status );
4791                tmap2 = astAnnul( tmap2 );
4792                astInvert( tmap1 );
4793                tmap2 = (AstMapping *) astCmpMap( tmap3, tmap1, 1, " ", status );
4794                tmap1 = astAnnul( tmap1 );
4795                tmap3 = astAnnul( tmap3 );
4796 
4797 /* Now create a PermMap that permutes the WCS axes into the FITS axis order. */
4798                for( i = 0; i < nwcs; i++ ) {
4799                   inperm[ i ] = wperm[ i ];
4800                   outperm[ wperm[ i ] ] = i;
4801                }
4802                tmap1 = (AstMapping *) astPermMap( nwcs, inperm, nwcs, outperm,
4803                                                   NULL, "", status );
4804 
4805 /* Use this PermMap to permute the outputs of the "tmap2" Mapping. The
4806    resulting Mapping is the Mapping from the current Frame to IWC and is
4807    the Mapping to be returned as the function value. */
4808                ret = (AstMapping *) astCmpMap( tmap2, tmap1, 1, " ", status );
4809                tmap1 = astAnnul( tmap1 );
4810                tmap2 = astAnnul( tmap2 );
4811             }
4812 
4813 /* Free remaining resources. */
4814             inperm = astFree( inperm );
4815             outperm = astFree( outperm );
4816             tmap0 = astAnnul( tmap0 );
4817          }
4818          if( table ) table = astAnnul( table );
4819       }
4820 
4821 /* Release resources. */
4822       ppcfid = astFree( ppcfid );
4823    }
4824 
4825 /* Release resources. */
4826    wcsfrm = astAnnul( wcsfrm );
4827    if( skyfrm ) skyfrm = astAnnul( skyfrm );
4828    if( map ) map = astAnnul( map );
4829 
4830 /* If we have a Mapping to return, simplify it. Otherwise, create
4831    a UnitMap to return. */
4832    if( ret ) {
4833       tmap0 = ret;
4834       ret = astSimplify( tmap0 );
4835       tmap0 =  astAnnul( tmap0 );
4836    } else {
4837       ret = (AstMapping *) astUnitMap( nwcs, "", status );
4838    }
4839 
4840 /* Return the result. */
4841    return ret;
4842 }
4843 
ChangePermSplit(AstMapping * map,int * status)4844 static void ChangePermSplit( AstMapping *map, int *status ){
4845 /*
4846 *  Name:
4847 *     ChangePermSplit
4848 
4849 *  Purpose:
4850 *     Change all PermMaps in a Mapping to use the alternate
4851 *     implementation of the astMapSplit method.
4852 
4853 *  Type:
4854 *     Private function.
4855 
4856 *  Synopsis:
4857 *     #include "fitschan.h"
4858 *     void ChangePermSplit( AstMapping *map, int *status )
4859 
4860 *  Class Membership:
4861 *     FitsChan member function.
4862 
4863 *  Description:
4864 *     The PemMap class provides two implementations of the astMapSplit
4865 *     method. The implementation used by each PermMap is determined by
4866 *     the value of the PermMap's "PermSplit" attribute. This function
4867 *     searches the supplied Mapping for any PermMaps, and set their
4868 *     PermSplit attribute to 1, indicating that the alternate
4869 *     implementation of astMapSplit should be used.
4870 
4871 *  Parameters:
4872 *     map
4873 *        Pointer to the Mapping. Modified on exit by setting all
4874 *        PermSplit attributes to 1.
4875 *     status
4876 *        Pointer to the inherited status variable.
4877 */
4878 
4879 /* Local Variables: */
4880    AstMapping *map1;
4881    AstMapping *map2;
4882    int series;
4883    int invert1;
4884    int invert2;
4885 
4886 /* Check inherited status */
4887    if( !astOK ) return;
4888 
4889 /* If the supplied Mapping is a PermMap, set its PermSplit attribute
4890    non-zero. */
4891    if( astIsAPermMap( map ) ) {
4892       astSetPermSplit( map, 1 );
4893 
4894 /* If the supplied Mapping is not a PermMap, attempt to decompose the
4895    Mapping into two component Mappings. */
4896    } else {
4897       astDecompose( map, &map1, &map2, &series, &invert1, &invert2 );
4898 
4899 /* If the Mapping could be decomposed, use this function recursively to
4900    set the PermSplit attributes in each component Mapping. */
4901       if( map1 && map2 ) {
4902          ChangePermSplit( map1, status );
4903          ChangePermSplit( map2, status );
4904 
4905 /* Annul the component Mappings. */
4906          map1 = astAnnul( map1 );
4907          map2 = astAnnul( map2 );
4908       } else if( map1 ) {
4909          map1 = astAnnul( map1 );
4910       } else if( map2 ) {
4911          map2 = astAnnul( map2 );
4912       }
4913    }
4914 }
4915 
Cheb2Poly(double * c,int nx,int ny,double xmin,double xmax,double ymin,double ymax,int * status)4916 static double *Cheb2Poly( double *c, int nx, int ny, double xmin, double xmax,
4917                           double ymin, double ymax, int *status ){
4918 /*
4919 *  Name:
4920 *     Cheb2Poly
4921 
4922 *  Purpose:
4923 *     Converts a two-dimensional Chebyshev polynomial to standard form and
4924 *     scale the arguments.
4925 
4926 *  Type:
4927 *     Private function.
4928 
4929 *  Synopsis:
4930 *     #include "fitschan.h"
4931 *     double *Cheb2Poly( double *c, int nx, int ny, double xmin, double xmax,
4932 *                        double ymin, double ymax, int *status )
4933 
4934 *  Class Membership:
4935 *     FitsChan
4936 
4937 *  Description:
4938 *     Given the coefficients of a two-dimensional Chebychev polynomial P(u,v),
4939 *     find the coefficients of the equivalent standard two-dimensional
4940 *     polynomial Q(x,y). The allowed range of u and v is assumed to be the
4941 *     unit square, and this maps on to the rectangle in (x,y) given by
4942 *     (xmin:xmax,ymin:ymax).
4943 
4944 *  Parameters:
4945 *     c
4946 *        An array of (nx,ny) elements supplied holding the coefficients of
4947 *        P, such that the coefficient of (Ti(u)*Tj(v)) is held in element
4948 *        (i + j*nx), where "Ti(u)" is the Chebychev polynomial (of the
4949 *        first kind) of order "i" evaluated at "u", and "Tj(v)" is the
4950 *        Chebychev polynomial of order "j" evaluated at "v".
4951 *     nx
4952 *        One more than the maximum power of u within P.
4953 *     ny
4954 *        One more than the maximum power of v within P.
4955 *     xmin
4956 *        X value corresponding to u = -1
4957 *     xmax
4958 *        X value corresponding to u = +1
4959 *     ymin
4960 *        Y value corresponding to v = -1
4961 *     ymax
4962 *        Y value corresponding to v = +1
4963 *     status
4964 *        Pointer to the inherited status variable.
4965 
4966 *  Returned Value:
4967 *     Pointer to a dynamically allocated array of (nx,ny) elements holding
4968 *     the coefficients of Q, such that the coefficient of (x^i*y^j) is held
4969 *     in element (i + j*nx). Free it using astFree when no longer needed.
4970 */
4971 
4972 /* Local Variables: */
4973    double *d;
4974    double *pa;
4975    double *pw;
4976    double *work1;
4977    double *work2;
4978    double *work3;
4979    int *iw1;
4980    int *iw2;
4981    int i;
4982    int j;
4983 
4984 /* Check the status and supplied value pointer. */
4985    if( !astOK ) return NULL;
4986 
4987 /* Allocate returned array. */
4988    d = astMalloc( sizeof( *d )*nx*ny );
4989 
4990 /* Allocate workspace. */
4991    work1 = astMalloc( sizeof( *work1 )*ny );
4992    work2 = astMalloc( sizeof( *work2 )*ny );
4993    work3 = astMalloc( sizeof( *work2 )*nx );
4994    iw1 = astMalloc( sizeof(int)*( nx > ny ? nx : ny ) );
4995    iw2 = astMalloc( sizeof(int)*( nx > ny ? nx : ny ) );
4996    if( astOK ) {
4997 
4998 /* Thinking of P as a 1D polynomial in v, each coefficient would itself then
4999    be a 1D polynomial in u:
5000 
5001    P = (   c[0] +      c[1]*T1(u) +      c[2]*T2(u) + ... ) +
5002        (  c[nx] +   c[nx+1]*T1(u) +   c[nx+2]*T2(u) + ... )*T1(v) +
5003        (c[2*nx] + c[2*nx+1]*T1(u) + c[2*nx+2]*T2(u) + ... )*T2(v) +
5004        ...
5005        (c[(ny-1)*nx] + c[(ny-1)*nx+1]*T1(u) + c[(ny-1)*nx+2]*T2(u) + ... )T{ny-1}(v)
5006 
5007    Use Chpc1 to convert these "polynomial coefficients" to standard
5008    form, storing the result in the corresponding row of "d" . Also,
5009    convert them from u to x. */
5010 
5011       for( j = 0; j < ny; j++ ) {
5012          Chpc1( c + j*nx, work3, nx, iw1, iw2, status );
5013          Shpc1( xmin, xmax, nx, work3, d + j*nx, status );
5014       }
5015 
5016 /* The polynomial value is now:
5017 
5018     (   d[0] +      d[1]*x +      d[2]*x*x + ... ) +
5019     (  d[nx] +   d[nx+1]*x +   d[nx+2]*x*x + ... )*T1(v) +
5020     (d[2*nx] + d[2*nx+1]*x + d[2*nx+2]*x*x + ... )*T2(v) +
5021     ...
5022     (d[(ny-1)*nx] + d[(ny-1)*nx+1]*x + d[(ny-1)*nx+2]*x*x + ... )*T{ny-1}(v)
5023 
5024    If we rearrange this expression to view it as a 1D polynomial in x,
5025    rather than v, each coefficient of the new 1D polynomial is then
5026    itself a polynomial in v:
5027 
5028     ( d[0] +   d[nx]*T1(v) +   d[2*nx]*T2(v) + ... d[(ny-1)*nx]*T{ny-1}(v) ) +
5029     ( d[1] + d[nx+1]*T1(v) + d[2*nx+1]*T2(v) + ... d[(ny-1)*nx+1]T{ny-1}(v)... )*x +
5030     ( d[2] + d[nx+2]*T1(v) + d[2*nx+2]*T2(v) + ... d[(ny-1)*nx+2]T{ny-1}(v)... )*x*x +
5031     ...
5032     ( 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*...
5033 
5034 
5035    Now use Chpc1 to convert each of these "polynomial coefficients"
5036    to standard form. We copy each column of the d array into a 1D work array,
5037    use Shpc1 to modify the values in the work array, and then write
5038    the modified values back into the current column of d. Also convert
5039    from v to y. */
5040 
5041       for( i = 0; i < nx; i++ ) {
5042          pa = d + i;
5043          pw = work1;
5044          for( j = 0; j < ny; j++ ) {
5045             *(pw++) = *pa;
5046             pa += nx;
5047          }
5048 
5049          Chpc1( work1, work2, ny, iw1, iw2, status );
5050          Shpc1( ymin, ymax, ny, work2, work1, status );
5051 
5052          pa = d + i;
5053          pw = work1;
5054          for( j = 0; j < ny; j++ ) {
5055             *pa = *(pw++);
5056             pa += nx;
5057          }
5058       }
5059 
5060 /* So the polynomial is now:
5061 
5062     ( d[0] +   d[nx]*y +   d[2*nx]*y*y + ... d[(ny-1)*nx]*y*y*... ) +
5063     ( d[1] + d[nx+1]*y + d[2*nx+1]*y*y + ... d[(ny-1)*nx+1]*y*y*... )*x +
5064     ( d[2] + d[nx+2]*y + d[2*nx+2]*y*y + ... d[(ny-1)*nx+2]*y*y*... )*x*x +
5065     ...
5066     ( d[nx-1] + d[2*nx-1]*y + d[3*nx-1]*y*y + ... d[ny*nx-1]*y*y*... )*x*x*...
5067 
5068   Re-arranging, this is:
5069 
5070     (   d[0] +      d[1]*x +      d[2]*x*x + ... ) +
5071     (  d[nx] +   d[nx+1]*x +   d[nx+2]*x*x + ... )*y +
5072     (d[2*nx] + d[2*nx+1]*x + d[2*nx+2]*x*x + ... )*y*y +
5073     ...
5074     (d[(ny-1)*nx] + d[(ny-1)*nx+1]*x + d[(ny-1)*nx+2]*x*x + ... )*y*y*...
5075 
5076    as required. */
5077 
5078    }
5079 
5080 /* Free the workspace. */
5081    work1 = astFree( work1 );
5082    work2 = astFree( work2 );
5083    work3 = astFree( work3 );
5084    iw1 = astFree( iw1 );
5085    iw2 = astFree( iw2 );
5086 
5087 /* Return the result. */
5088    return d;
5089 }
5090 
CheckFitsName(const char * name,const char * method,const char * class,int * status)5091 static int CheckFitsName( const char *name, const char *method,
5092                           const char *class, int *status ){
5093 /*
5094 *  Name:
5095 *     CheckFitsName
5096 
5097 *  Purpose:
5098 *     Check a keyword name conforms to FITS standards.
5099 
5100 *  Type:
5101 *     Private function.
5102 
5103 *  Synopsis:
5104 *     #include "fitschan.h"
5105 *     int CheckFitsName( const char *name, const char *method,
5106 *                        const char *class, int *status )
5107 
5108 *  Class Membership:
5109 *     FitsChan member function.
5110 
5111 *  Description:
5112 *     FITS keywords must contain between 1 and 8 characters, and each
5113 *     character must be an upper-case Latin alphabetic character, a digit,
5114 *     an underscore, or a hyphen. Leading, trailing or embedded white space
5115 *     is not allowed, with the exception of totally blank or null keyword
5116 *     names.
5117 
5118 *  Parameters:
5119 *     name
5120 *        Pointer to a string holding the name to check.
5121 *     method
5122 *        Pointer to a string holding the name of the calling method.
5123 *        This is only for use in constructing error messages.
5124 *     class
5125 *        Pointer to a string holding the name of the supplied object class.
5126 *        This is only for use in constructing error messages.
5127 *     status
5128 *        Pointer to the inherited status variable.
5129 
5130 *  Returned Value:
5131 *     A value of 0 is returned if the supplied name was blank. A value of 1
5132 *     is returned otherwise.
5133 
5134 *  Notes:
5135 *     -  An error is reported if the supplied keyword name does not
5136 *     conform to FITS requirements, and zero is returned.
5137 */
5138 
5139 /* Local Variables: */
5140    const char *c;     /* Pointer to next character in name */
5141    size_t n;          /* No. of characters in supplied name */
5142    int ret;           /* Returned value */
5143 
5144 /* Check the global status. */
5145    if( !astOK ) return 0;
5146 
5147 /* Initialise the returned value to indicate that the supplied name was
5148    blank. */
5149    ret = 0;
5150 
5151 /* Check that the supplied pointer is not NULL. */
5152    if( name ){
5153 
5154 /* Get the number of characters in the name. */
5155       n = strlen( name );
5156 
5157 /* Report an error if the name has too many characters in it. */
5158       if( n > FITSNAMLEN ){
5159          astError( AST__BDFTS, "%s(%s): The supplied FITS keyword name ('%s') "
5160                    "has %d characters. FITS only allows up to %d.", status, method,
5161                    class, name, (int) n, FITSNAMLEN );
5162 
5163 /* If the name has no characters in it, then assume it is a legal blank
5164    keyword name. Otherwise, check that no illegal characters occur in the
5165    name. */
5166       } else if( n != 0 ) {
5167 
5168 /* Whitespace is only allowed in the special case of a name consisting
5169    entirely of whitespace. Such keywords are used to indicate that the rest
5170    of the card is a comment. Find the first non-whitespace character in the
5171    name. */
5172          c = name;
5173          while( isspace( ( int ) *(c++) ) );
5174 
5175 /* If the name is filled entirely with whitespace, then the name is acceptable
5176    as the special case. Otherwise, we need to do more checks. */
5177          if( c - name - 1 < n ){
5178 
5179 /* Indicate that the supplied name is not blank. */
5180             ret = 1;
5181 
5182 /* Loop round every character checking that it is one of the legal characters.
5183    Report an error if any illegal characters are found. */
5184             c = name;
5185             while( *c ){
5186                if( !isFits( (int) *c ) ){
5187                   if( *c == '=' ){
5188                      astError( AST__BDFTS, "%s(%s): An equals sign ('=') was found "
5189                                "before column %d within a FITS keyword name or header "
5190                                "card.", status, method, class, FITSNAMLEN + 1 );
5191                   } else if( *c < ' ' ) {
5192                      astError( AST__BDFTS, "%s(%s): The supplied FITS keyword "
5193                                "name ('%s') contains an illegal non-printing "
5194                                "character (ascii value %d).", status, method, class,
5195                                name, *c );
5196                   } else if( *c < ' ' ) {
5197                      astError( AST__BDFTS, "%s(%s): The supplied FITS keyword "
5198                                "name ('%s') contains an illegal character ('%c').",
5199                                status, method, class, name, *c );
5200                   }
5201                   break;
5202                }
5203                c++;
5204             }
5205          }
5206       }
5207 
5208 /* Report an error if no pointer was supplied. */
5209    } else if( astOK ){
5210       astError( AST__INTER, "CheckFitsName(fitschan): AST internal "
5211                 "error; a NULL pointer was supplied for the keyword name. ",
5212                 status );
5213    }
5214 
5215 /* If an error has occurred, return 0. */
5216    if( !astOK ) ret = 0;
5217 
5218 /* Return the answer. */
5219    return ret;
5220 }
5221 
CheckZero(char * text,double value,int width,int * status)5222 static void CheckZero( char *text, double value, int width, int *status ){
5223 /*
5224 *  Name:
5225 *     CheckZero
5226 
5227 *  Purpose:
5228 *     Ensure that the formatted value zero has no minus sign.
5229 
5230 *  Type:
5231 *     Private function.
5232 
5233 *  Synopsis:
5234 *     #include "fitschan.h"
5235 *     void CheckZero( char *text, double value, int width, int *status )
5236 
5237 *  Class Membership:
5238 *     FitsChan member function.
5239 
5240 *  Description:
5241 *     There is sometimes a problem (perhaps only on DEC UNIX) when formatting
5242 *     the floating-point value 0.0 using C. Sometimes it gives the string
5243 *     "-0". This function fixed this by checking the first character of
5244 *     the supplied string (if the supplied value is zero), and shunting the
5245 *     remaining text one character to the right if it is a minus sign. It
5246 *     returns without action if the supplied value is not zero.
5247 *
5248 *     In addition, this function also rounds out long sequences of
5249 *     adjacent zeros or nines in the number.
5250 
5251 *  Parameters:
5252 *     text
5253 *        The formatted value.
5254 *     value
5255 *        The floating value which was formatted.
5256 *     width
5257 *        The minimum field width to use. The value is right justified in
5258 *        this field width. Ignored if zero.
5259 *     status
5260 *        Pointer to the inherited status variable.
5261 
5262 *  Notes:
5263 *     -  This function attempts to execute even if an error has occurred.
5264 */
5265 
5266 /* Local Variables: */
5267    char *c;
5268 
5269 /* Return if no text was supplied. */
5270    if( !text ) return;
5271 
5272 /* If the numerical value is zero, check for the leading minus sign. */
5273    if( value == 0.0 ) {
5274 
5275 /* Find the first non-space character. */
5276       c = text;
5277       while( *c && isspace( (int) *c ) ) c++;
5278 
5279 /* If the first non-space character is a minus sign, replace it with a
5280       space. */
5281       if( *c == '-' ) *c = ' ';
5282 
5283 /* Otherwise, round out sequences of zeros or nines. */
5284    } else {
5285       RoundFString( text, width, status );
5286    }
5287 }
5288 
ChooseEpoch(AstFitsChan * this,FitsStore * store,char s,const char * method,const char * class,int * status)5289 static double ChooseEpoch( AstFitsChan *this, FitsStore *store, char s,
5290                            const char *method, const char *class, int *status ){
5291 /*
5292 *  Name:
5293 *     ChooseEpoch
5294 
5295 *  Purpose:
5296 *     Choose a FITS keyword value to use for the AST Epoch attribute.
5297 
5298 *  Type:
5299 *     Private function.
5300 
5301 *  Synopsis:
5302 *     double ChooseEpoch( AstFitsChan *this, FitsStore *store, char s,
5303 *                         const char *method, const char *class, int *status )
5304 
5305 *  Class Membership:
5306 *     FitsChan
5307 
5308 *  Description:
5309 *     This function returns an MJD value in the TDB timescale, which can
5310 *     be used as the Epoch value in an AST Frame. It uses the following
5311 *     preference order: secondary MJD-AVG, primary MJD-AVG, secondary MJD-OBS,
5312 *     primary MJD-OBS. Note, DATE-OBS keywords are converted into MJD-OBS
5313 *     keywords by the SpecTrans function before this function is called.
5314 
5315 *  Parameters:
5316 *     this
5317 *        Pointer to the FitsChan.
5318 *     store
5319 *        A structure containing values for FITS keywords relating to
5320 *        the World Coordinate System.
5321 *     s
5322 *        A character identifying the co-ordinate version to use. A space
5323 *        means use primary axis descriptions. Otherwise, it must be an
5324 *        upper-case alphabetical characters ('A' to 'Z').
5325 *     method
5326 *        The calling method. Used only in error messages.
5327 *     class
5328 *        The object class. Used only in error messages.
5329 *     status
5330 *        Pointer to the inherited status variable.
5331 
5332 *  Returned Value:
5333 *     The MJD value.
5334 
5335 *  Notes:
5336 *     -  A value of AST__BAD is returned if an error occurs, or if none
5337 *     of the required keywords can be found in the FitsChan.
5338 */
5339 
5340 /* Local Variables: */
5341    const char *timesys;  /* The TIMESYS value in the FitsStore */
5342    double mjd;           /* The returned MJD */
5343 
5344 /* Initialise the returned value. */
5345    mjd = AST__BAD;
5346 
5347 /* Check the global status. */
5348    if( !astOK ) return mjd;
5349 
5350 /* Otherwise, try to get the secondary MJD-AVG value. */
5351    mjd = GetItem( &(store->mjdavg), 0, 0, s, NULL, method, class, status );
5352 
5353 /* Otherwise, try to get the primary MJD-AVG value. */
5354    if( mjd == AST__BAD ) mjd = GetItem( &(store->mjdavg), 0, 0, ' ', NULL,
5355                                         method, class, status );
5356 
5357 /* If the secondary MJD-OBS keyword is present in the FitsChan, gets its
5358    value. */
5359    if( mjd == AST__BAD ) mjd = GetItem( &(store->mjdobs), 0, 0, s, NULL,
5360                                         method, class, status );
5361 
5362 /* Otherwise, try to get the primary MJD-OBS value. */
5363    if( mjd == AST__BAD ) mjd = GetItem( &(store->mjdobs), 0, 0, ' ', NULL,
5364                                         method, class, status );
5365 
5366 /* Now convert the MJD value to the TDB timescale. */
5367    timesys = GetItemC( &(store->timesys), 0, 0, ' ', NULL, method, class, status );
5368    mjd = TDBConv( mjd, TimeSysToAst( this, timesys, method, class, status ),
5369                   0, method, class, status );
5370 
5371 /* Return the answer. */
5372    return mjd;
5373 }
5374 
Chpc1(double * c,double * d,int n,int * w0,int * w1,int * status)5375 static void Chpc1( double *c, double *d, int n, int *w0, int *w1, int *status ){
5376 /*
5377 *  Name:
5378 *     Chpc1
5379 
5380 *  Purpose:
5381 *     Converts a one-dimensional Chebyshev polynomial to standard form.
5382 
5383 *  Type:
5384 *     Private function.
5385 
5386 *  Synopsis:
5387 *     #include "fitschan.h"
5388 *     void Chpc1( double *c, double *d, int n, int *w0, int *w1, int *status )
5389 
5390 *  Class Membership:
5391 *     FitsChan
5392 
5393 *  Description:
5394 *     Given the coefficients of a one-dimensional Chebychev polynomial P(u),
5395 *     find the coefficients of the equivalent standard 1D polynomial Q(u).
5396 *     The allowed range of u is assumed to be the unit interval.
5397 
5398 *  Parameters:
5399 *     c
5400 *        An array of n elements supplied holding the coefficients of
5401 *        P, such that the coefficient of (Ti(u)) is held in element
5402 *        (i), where "Ti(u)" is the Chebychev polynomial (of the
5403 *        first kind) of order "i" evaluated at "u".
5404 *     d
5405 *        An array of n elements returned holding the coefficients of
5406 *        Q, such that the coefficient of (u^i) is held in element (i).
5407 *     n
5408 *        One more than the highest power of u in P.
5409 *     w0
5410 *        Pointer to a work array of n elements.
5411 *     w1
5412 *        Pointer to a work array of n elements.
5413 *     status
5414 *        Inherited status value
5415 
5416 *  Notes:
5417 *    - Vaguely inspired by the Numerical Recipes routine "chebpc". But the
5418 *    original had bugs, so I wrote this new version from first principles.
5419 
5420 */
5421 
5422 /* Local Variables: */
5423    int sv;
5424    int j;
5425    int k;
5426 
5427 /* Check inherited status */
5428    if( !astOK ) return;
5429 
5430 /* Initialise the returned coefficients array. */
5431    for( j = 0; j < n; j++ ) d[ j ] = 0.0;
5432 
5433 /* Use the recurrence relation
5434 
5435    T{k+1}(x) = 2.x.T{k}(x) - T{k-1}(x).
5436 
5437    w0[i] holds the coefficient of x^i in T{k-1}. w1[i] holds the
5438    coefficient of x^i in T{k}. Initialise them for T0 (="1") and
5439    T1 (="x"). */
5440    for( j = 0; j < n; j++ ) w0[ j ] = w1[ j ] = 0;
5441    w0[ 0 ] = 1;
5442    w1[ 1 ] = 1;
5443 
5444 /* Update the returned coefficients array to include the T0 and T1 terms. */
5445    d[ 0 ] = c[ 0 ];
5446    d[ 1 ] = c[ 1 ];
5447 
5448 /* Loop round using the above recurrence relation until we have found
5449    T{n-1}. */
5450    for( k = 1; k < n - 1; k++ ){
5451 
5452 /* To get the coefficients of T{k+1} shift the contents of w1 up one
5453    element, introducing a zero at the low end, and then double all the
5454    values in w1. Finally subtract off the values in w0. This implements
5455    the above recurrence relationship. Starting at the top end and working
5456    down to the bottom, store a new value for each element of w1. */
5457       for( j = n - 1; j > 0; j-- ) {
5458 
5459 /* First save the original element of w1 in w0 for use next time. But we
5460    also need the original w0 element later on so save it first. */
5461          sv = w0[ j ];
5462          w0[ j ] = w1[ j ];
5463 
5464 /* Double the lower neighbouring w1 element and subtract off the w0
5465    element saved above. This forms the new value for w1. */
5466          w1[ j ] = 2*w1[ j - 1 ] - sv;
5467       }
5468 
5469 /* Introduce a zero into the lowest element of w1, saving the original
5470    value first in w0. Then subtract off the original value of w0. */
5471       sv = w0[ 0 ];
5472       w0[ 0 ] = w1[ 0 ];
5473       w1[ 0 ] = -sv;
5474 
5475 /* W1 now contains the coefficients of T{k+1} in w1, and the coefficients
5476    of T{k} in w0. Multiply these by the supplied coefficient for T{k+1},
5477    and add them into the returned array. */
5478       for( j = 0; j <= k + 1; j++ ){
5479          d[ j ] += c[ k + 1 ]*w1[ j ];
5480       }
5481    }
5482 }
5483 
ChrLen(const char * string,int * status)5484 static int ChrLen( const char *string, int *status ){
5485 /*
5486 *  Name:
5487 *     ChrLen
5488 
5489 *  Purpose:
5490 *     Return the length of a string excluding any trailing white space.
5491 
5492 *  Type:
5493 *     Private function.
5494 
5495 *  Synopsis:
5496 *     int ChrLen( const char *string, int *status )
5497 
5498 *  Class Membership:
5499 *     FitsChan
5500 
5501 *  Description:
5502 *     This function returns the length of a string excluding any trailing
5503 *     white space, or non-printable characters.
5504 
5505 *  Parameters:
5506 *     string
5507 *        Pointer to the string.
5508 *     status
5509 *        Pointer to the inherited status variable.
5510 
5511 *  Returned Value:
5512 *     The length of a string excluding any trailing white space and
5513 *     non-printable characters.
5514 
5515 *  Notes:
5516 *     -  A value of zero is returned if a NULL pointer is supplied, or if an
5517 *     error has already occurred.
5518 */
5519 
5520 /* Local Variables: */
5521    const char *c;      /* Pointer to the next character to check */
5522    int ret;            /* The returned string length */
5523 
5524 /* Check the global status. */
5525    if( !astOK ) return 0;
5526 
5527 /* Initialise the returned string length. */
5528    ret = 0;
5529 
5530 /* Check a string has been supplied. */
5531    if( string ){
5532 
5533 /* Check each character in turn, starting with the last one. */
5534       ret = strlen( string );
5535       c = string + ret - 1;
5536       while( ret ){
5537          if( isprint( (int) *c ) && !isspace( (int) *c ) ) break;
5538          c--;
5539          ret--;
5540       }
5541    }
5542 
5543 /* Return the answer. */
5544    return ret;
5545 }
5546 
CLASSFromStore(AstFitsChan * this,FitsStore * store,AstFrameSet * fs,double * dim,const char * method,const char * class,int * status)5547 static int CLASSFromStore( AstFitsChan *this, FitsStore *store,
5548                            AstFrameSet *fs, double *dim, const char *method,
5549                            const char *class, int *status ){
5550 
5551 /*
5552 *  Name:
5553 *     CLASSFromStore
5554 
5555 *  Purpose:
5556 *     Store WCS keywords in a FitsChan using FITS-CLASS encoding.
5557 
5558 *  Type:
5559 *     Private function.
5560 
5561 *  Synopsis:
5562 
5563 *     int CLASSFromStore( AstFitsChan *this, FitsStore *store,
5564 *                         AstFrameSet *fs, double *dim, const char *method,
5565 *                         const char *class, int *status )
5566 
5567 *  Class Membership:
5568 *     FitsChan
5569 
5570 *  Description:
5571 *     A FitsStore is a structure containing a generalised represention of
5572 *     a FITS WCS FrameSet. Functions exist to convert a FitsStore to and
5573 *     from a set of FITS header cards (using a specified encoding), or
5574 *     an AST FrameSet. In other words, a FitsStore is an encoding-
5575 *     independant intermediary staging post between a FITS header and
5576 *     an AST FrameSet.
5577 *
5578 *     This function copies the WCS information stored in the supplied
5579 *     FitsStore into the supplied FitsChan, using FITS-CLASS encoding.
5580 
5581 *  Parameters:
5582 *     this
5583 *        Pointer to the FitsChan.
5584 *     store
5585 *        Pointer to the FitsStore.
5586 *     fs
5587 *        Pointer to the FrameSet from which the values in the FitsStore
5588 *        were derived.
5589 *     dim
5590 *        Pointer to an array holding the main array dimensions (AST__BAD
5591 *        if a dimension is not known).
5592 *     method
5593 *        Pointer to a string holding the name of the calling method.
5594 *        This is only for use in constructing error messages.
5595 *     class
5596 *        Pointer to a string holding the name of the supplied object class.
5597 *        This is only for use in constructing error messages.
5598 *     status
5599 *        Pointer to the inherited status variable.
5600 
5601 *  Returned Value:
5602 *     A value of 1 is returned if succesfull, and zero is returned
5603 *     otherwise.
5604 */
5605 
5606 /* Local Variables: */
5607    AstFrame *azelfrm;  /* (az,el) frame */
5608    AstFrame *curfrm;   /* Current Frame in supplied FrameSet */
5609    AstFrame *freqfrm;  /* Frame for reference frequency value */
5610    AstFrame *radecfrm; /* Spatial frame for CRVAL values */
5611    AstFrame *velofrm;  /* Frame for reference velocity value */
5612    AstFrameSet *fsconv1;/* FrameSet connecting "curfrm" & "radecfrm" */
5613    AstFrameSet *fsconv2;/* FrameSet connecting "curfrm" & "azelfrm" */
5614    AstMapping *map1;   /* Axis permutation to get (lonaxis,lataxis) = (0,1) */
5615    AstMapping *map2;   /* Mapping from FITS CTYPE to (az,el) */
5616    AstMapping *map3;   /* Mapping from (lon,lat) to (az,el) */
5617    char *comm;         /* Pointer to comment string */
5618    char *cval;         /* Pointer to string keyword value */
5619    char attbuf[20];    /* Buffer for AST attribute name */
5620    char combuf[80];    /* Buffer for FITS card comment */
5621    char lattype[MXCTYPELEN];/* Latitude axis CTYPE */
5622    char lontype[MXCTYPELEN];/* Longitude axis CTYPE */
5623    char s;             /* Co-ordinate version character */
5624    char sign[2];       /* Fraction's sign character */
5625    char spectype[MXCTYPELEN];/* Spectral axis CTYPE */
5626    double *cdelt;      /* Pointer to CDELT array */
5627    double aval[ 2 ];   /* General purpose array */
5628    double azel[ 2 ];   /* Reference (az,el) values */
5629    double cdl;         /* CDELT term */
5630    double crval[ 3 ];  /* CRVAL values converted to rads, etc */
5631    double delta;       /* Spectral axis increment */
5632    double equ;         /* Epoch of reference equinox */
5633    double fd;          /* Fraction of a day */
5634    double latval;      /* CRVAL for latitude axis */
5635    double lonpole;     /* LONPOLE value */
5636    double lonval;      /* CRVAL for longitude axis */
5637    double mjd99;       /* MJD at start of 1999 */
5638    double p1, p2;      /* Projection parameters */
5639    double radec[ 2 ];  /* Reference (lon,lat) values */
5640    double rf;          /* Rest freq (Hz) */
5641    double specfactor;  /* Factor for converting internal spectral units */
5642    double val;         /* General purpose value */
5643    double xin[ 3 ];    /* Grid coords at centre of first pixel */
5644    double xout[ 3 ];   /* WCS coords at centre of first pixel */
5645    int axlat;          /* Index of latitude FITS WCS axis */
5646    int axlon;          /* Index of longitude FITS WCS axis */
5647    int axspec;         /* Index of spectral FITS WCS axis */
5648    int i;              /* Axis index */
5649    int ihmsf[ 4 ];     /* Hour, minute, second, fractional second */
5650    int iymdf[ 4 ];     /* Year, month, date, fractional day */
5651    int j;              /* Axis index */
5652    int jj;             /* SlaLib status */
5653    int naxis2;         /* Length of pixel axis 2 */
5654    int naxis3;         /* Length of pixel axis 3 */
5655    int naxis;          /* No. of axes */
5656    int ok;             /* Is FitsSTore OK for IRAF encoding? */
5657    int prj;            /* Projection type */
5658 
5659 /* Other initialisation to avoid compiler warnings. */
5660    lonval = 0.0;
5661    latval = 0.0;
5662 
5663 /* Check the inherited status. */
5664    if( !astOK ) return 0;
5665 
5666 /* Initialise */
5667    specfactor = 1.0;
5668 
5669 /* First check that the values in the FitsStore conform to the
5670    requirements of the CLASS encoding. Assume they do not to begin with. */
5671    ok = 0;
5672 
5673 /* Just do primary axes. */
5674    s = ' ';
5675 
5676 /* Look for the primary celestial axes. */
5677    FindLonLatSpecAxes( store, s, &axlon, &axlat, &axspec, method, class, status );
5678 
5679 /* Get the current Frame from the supplied FrameSet. */
5680    curfrm = astGetFrame( fs, AST__CURRENT );
5681 
5682 /* Spectral and celestial axes must be present in axes 1,2 and 3. */
5683    if( axspec >= 0 && axspec < 3 &&
5684        axlon >= 0 && axlon < 3 &&
5685        axlat >= 0 && axlat < 3 ) {
5686       ok = 1;
5687 
5688 /* If the spatial pixel axes are degenerate (i.e. span only a single
5689    pixel), modify the CRPIX and CRVAL values in the FitsStore to put
5690    the reference point at the centre of the one and only spatial pixel. */
5691       if( store->naxis >= 3 && dim[ axlon ] == 1.0 && dim[ axlat ] == 1.0 ){
5692          xin[ 0 ] = 1.0;
5693          xin[ 1 ] = 1.0;
5694          xin[ 2 ] = 1.0;
5695          astTranN( fs, 1, 3, 1, xin, 1, 3, 1, xout );
5696          if( xout[ axlon ] != AST__BAD && xout[ axlat ] != AST__BAD ) {
5697 
5698 /* The indices of the spatial axes in the FITS header may not be the same
5699    as the indices of the spatial axes in the WCS Frame of the supplied
5700    FrameSet. So search the current Frame for longitude and latitude axes,
5701    and store the corresponding elements of the "xout" array for later use. */
5702             for( i = 0; i < 3; i++ ) {
5703                sprintf( attbuf, "IsLonAxis(%d)", i + 1 );
5704                if( astHasAttribute( curfrm, attbuf ) ) {
5705                   if( astGetI( curfrm, attbuf ) ) {
5706                      lonval = xout[ i ];
5707                   } else {
5708                      latval = xout[ i ];
5709                   }
5710                }
5711             }
5712 
5713 /* Store them in the FitsStore. */
5714             SetItem( &(store->crval), axlon, 0, ' ', lonval*AST__DR2D, status );
5715             SetItem( &(store->crval), axlat, 0, ' ', latval*AST__DR2D, status );
5716             SetItem( &(store->crpix), 0, axlon, ' ', 1.0, status );
5717             SetItem( &(store->crpix), 0, axlat, ' ', 1.0, status );
5718          }
5719       }
5720 
5721 /* Get the CRVAL values for both spatial axes. */
5722       latval = GetItem( &( store->crval ), axlat, 0, s, NULL, method, class, status );
5723       if( latval == AST__BAD ) ok = 0;
5724       lonval = GetItem( &( store->crval ), axlon, 0, s, NULL, method, class, status );
5725       if( lonval == AST__BAD ) ok = 0;
5726 
5727 /* Get the CTYPE values for both axes. Extract the projection type as
5728    specified by the last 4 characters in the latitude CTYPE keyword value. */
5729       cval = GetItemC( &(store->ctype), axlon, 0, s, NULL, method, class, status );
5730       if( !cval ) {
5731          ok = 0;
5732       } else {
5733          strcpy( lontype, cval );
5734       }
5735       cval = GetItemC( &(store->ctype), axlat, 0, s, NULL, method, class, status );
5736       if( !cval ) {
5737          ok = 0;
5738          prj = AST__WCSBAD;
5739       } else {
5740          strcpy( lattype, cval );
5741          prj = astWcsPrjType( cval + 4 );
5742       }
5743 
5744 /* Check the projection type is OK. */
5745       if( prj == AST__WCSBAD ){
5746          ok = 0;
5747       } else if( prj != AST__SIN ){
5748 
5749 /* Check the projection code is OK. */
5750          ok = 0;
5751          if( prj == AST__TAN ||
5752              prj == AST__ARC ||
5753              prj == AST__STG ||
5754              prj == AST__AIT ||
5755              prj == AST__SFL ) {
5756             ok = 1;
5757 
5758 /* For AIT, and SFL, check that the reference point is the origin of
5759    the celestial co-ordinate system. */
5760             if( prj == AST__AIT ||
5761                 prj == AST__SFL ) {
5762                if( latval != 0.0 || lonval != 0.0 ){
5763                   ok = 0;
5764 
5765 /* Change the new SFL projection code to to the older equivalent GLS */
5766                } else if( prj == AST__SFL ){
5767                   (void) strcpy( lontype + 4, "-GLS" );
5768                   (void) strcpy( lattype + 4, "-GLS" );
5769 
5770 /* Change the new AIT projection code to to the older equivalent ATF */
5771                } else if( prj == AST__AIT ){
5772                   (void) strcpy( lontype + 4, "-ATF" );
5773                   (void) strcpy( lattype + 4, "-ATF" );
5774                }
5775             }
5776          }
5777 
5778 /* SIN projections are only acceptable if the associated projection
5779    parameters are both zero. */
5780       } else {
5781          p1 = GetItem( &( store->pv ), axlat, 1, s, NULL, method, class, status );
5782          p2 = GetItem( &( store->pv ), axlat, 2, s, NULL, method, class, status );
5783          if( p1 == AST__BAD ) p1 = 0.0;
5784          if( p2 == AST__BAD ) p2 = 0.0;
5785          ok = ( p1 == 0.0 && p2 == 0.0 );
5786       }
5787 
5788 /* Identify the celestial coordinate system from the first 4 characters of the
5789    longitude CTYPE value. Only RA and galactic longitude can be stored using
5790    FITS-CLASS. */
5791       if( ok && strncmp( lontype, "RA--", 4 ) &&
5792                strncmp( lontype, "GLON", 4 ) ) ok = 0;
5793 
5794 /* Get the CTYPE values for the spectral axis, and find the CLASS equivalent,
5795    if possible. */
5796       cval = GetItemC( &(store->ctype), axspec, 0, s, NULL, method, class, status );
5797       if( !cval ) {
5798          ok = 0;
5799       } else {
5800          if( !strncmp( cval, "FREQ", astChrLen( cval ) ) ) {
5801             strcpy( spectype, "FREQ" );
5802          } else {
5803             ok = 0;
5804          }
5805       }
5806 
5807 /* If OK, check the SPECSYS value is SOURCE. */
5808       cval = GetItemC( &(store->specsys), 0, 0, s, NULL, method, class, status );
5809       if( !cval ) {
5810          ok = 0;
5811       } else if( ok ) {
5812          if( strncmp( cval, "SOURCE", astChrLen( cval ) ) ) ok = 0;
5813       }
5814 
5815 /* If still OK, ensure the spectral axis units are Hz. */
5816       cval = GetItemC( &(store->cunit), axspec, 0, s, NULL, method, class, status );
5817       if( !cval ) {
5818          ok = 0;
5819       } else if( ok ) {
5820          if( !strcmp( cval, "Hz" ) ) {
5821             specfactor = 1.0;
5822          } else if( !strcmp( cval, "kHz" ) ) {
5823             specfactor = 1.0E3;
5824          } else if( !strcmp( cval, "MHz" ) ) {
5825             specfactor = 1.0E6;
5826          } else if( !strcmp( cval, "GHz" ) ) {
5827             specfactor = 1.0E9;
5828          } else {
5829             ok = 0;
5830          }
5831       }
5832    }
5833 
5834 /* Save the number of WCS axes */
5835    naxis = GetMaxJM( &(store->crpix), ' ', status ) + 1;
5836 
5837 /* If this is larger than 3, ignore the surplus WCS axes. Note, the
5838    above code has checked that the spatial and spectral axes are
5839    WCS axes 0, 1 and 2. */
5840    if( naxis > 3 ) naxis = 3;
5841 
5842 /* Allocate memory to store the CDELT values */
5843    if( ok ) {
5844       cdelt = (double *) astMalloc( sizeof(double)*naxis );
5845       if( !cdelt ) ok = 0;
5846    } else {
5847       cdelt = NULL;
5848    }
5849 
5850 /* Check that there is no rotation, and extract the CDELT (diagonal) terms,
5851    etc. If the spatial axes are degenerate (i.e. cover only a single pixel)
5852    then ignore any rotation. */
5853    if( !GetValue( this, FormatKey( "NAXIS", axlon + 1, -1, s, status ), AST__INT,
5854                   &naxis2, 0, 0, method, class, status ) ) {
5855       naxis2 = 0;
5856    }
5857    if( !GetValue( this, FormatKey( "NAXIS", axlat + 1, -1, s, status ), AST__INT,
5858                   &naxis3, 0, 0, method, class, status ) ) {
5859       naxis3 = 0;
5860    }
5861    for( i = 0; i < naxis && ok; i++ ){
5862       cdl = GetItem( &(store->cdelt), i, 0, s, NULL, method, class, status );
5863       if( cdl == AST__BAD ) cdl = 1.0;
5864       for( j = 0; j < naxis && ok; j++ ){
5865           val = GetItem( &(store->pc), i, j, s, NULL, method, class, status );
5866           if( val == AST__BAD ) val = ( i == j ) ? 1.0 : 0.0;
5867           val *= cdl;
5868           if( i == j ){
5869              cdelt[ i ] = val;
5870           } else if( val != 0.0 ){
5871              if( naxis2 != 1 || naxis3 != 1 ) ok = 0;
5872           }
5873       }
5874    }
5875 
5876 /* Get RADECSYS and the reference equinox. */
5877    cval = GetItemC( &(store->radesys), 0, 0, s, NULL, method, class, status );
5878    equ = GetItem( &(store->equinox), 0, 0, s, NULL, method, class, status );
5879 
5880 /* If RADECSYS was available... */
5881    if( cval ){
5882 
5883 /* Only FK4 and FK5 are supported in this encoding. */
5884       if( strcmp( "FK4", cval ) && strcmp( "FK5", cval ) ) ok = 0;
5885 
5886 /* If epoch was not available, set a default epoch. */
5887       if( equ == AST__BAD ){
5888          if( !strcmp( "FK4", cval ) ){
5889             equ = 1950.0;
5890          } else if( !strcmp( "FK5", cval ) ){
5891             equ = 2000.0;
5892          } else {
5893             ok = 0;
5894          }
5895 
5896 /* If an epoch was supplied, check it is consistent with the IAU 1984
5897    rule. */
5898       } else {
5899          if( !strcmp( "FK4", cval ) ){
5900             if( equ >= 1984.0 ) ok = 0;
5901          } else if( !strcmp( "FK5", cval ) ){
5902             if( equ < 1984.0 ) ok = 0;
5903          } else {
5904             ok = 0;
5905          }
5906       }
5907 
5908 /* Check we have a rest frequency */
5909       rf = GetItem( &(store->restfrq), 0, 0, s, NULL, method, class, status );
5910       if( rf == AST__BAD ) ok = 0;
5911    }
5912 
5913 /* If the spatial Frame covers more than a single Frame and requires a LONPOLE
5914    or LATPOLE keyword, it cannot be encoded using FITS-CLASS. However since
5915    FITS-CLASS imposes a no rotation restriction, it can tolerate lonpole
5916    values of +/- 180 degrees. */
5917    if( ok && ( naxis2 != 1 || naxis3 != 1 ) ) {
5918       lonpole =  GetItem( &(store->lonpole), 0, 0, s, NULL, method, class, status );
5919       if( lonpole != AST__BAD && lonpole != -180.0 && lonpole == 180 ) ok = 0;
5920       if( GetItem( &(store->latpole), 0, 0, s, NULL, method, class, status )
5921           != AST__BAD ) ok = 0;
5922    }
5923 
5924 /* Only create the keywords if the FitsStore conforms to the requirements
5925    of the FITS-CLASS encoding. */
5926    if( ok ) {
5927 
5928 /* If celestial axes were added by MakeFitsFrameSet, we need to ensure
5929    the header contains 3 main array axes. This is because the CLASS
5930    encoding does not support the WCSAXES keyword. */
5931       if( store->naxis == 1 ) {
5932 
5933 /* Update the "NAXIS" value to 3 or put a new card in at the start. */
5934          astClearCard( this );
5935          i = 3;
5936          SetValue( this, "NAXIS", &i, AST__INT, NULL, status );
5937 
5938 /* Put NAXIS2/3 after NAXIS1, or after NAXIS if the FitsChan does not contain
5939    NAXIS1. These are set to 1 since the spatial axes are degenerate. */
5940          if( FindKeyCard( this, "NAXIS1",  method, class, status ) ) {
5941             MoveCard( this, 1, method, class, status );
5942          }
5943          i = 1;
5944          SetValue( this, "NAXIS2", &i, AST__INT, NULL, status );
5945          SetValue( this, "NAXIS3", &i, AST__INT, NULL, status );
5946       }
5947 
5948 /* Find the last WCS related card. */
5949       FindWcs( this, 1, 1, 0, method, class, status );
5950 
5951 /* Get and save CRPIX for all pixel axes. These are required, so break
5952    if they are not available. */
5953       for( j = 0; j < naxis && ok; j++ ){
5954          val = GetItem( &(store->crpix), 0, j, s, NULL, method, class, status );
5955          if( val == AST__BAD ) {
5956             ok = 0;
5957          } else {
5958             sprintf( combuf, "Reference pixel on axis %d", j + 1 );
5959             SetValue( this, FormatKey( "CRPIX", j + 1, -1, s, status ), &val,
5960                       AST__FLOAT, combuf, status );
5961          }
5962       }
5963 
5964 /* Get and save CRVAL for all intermediate axes. These are required, so
5965    break if they are not available. Note, the frequency axis CRVAL is
5966    redefined by FITS-CLASS by reducing it by the RESTFREQ value. */
5967       for( i = 0; i < naxis && ok; i++ ){
5968          val = GetItem( &(store->crval), i, 0, s, NULL, method, class, status );
5969          if( val == AST__BAD ) {
5970             ok = 0;
5971          } else {
5972             crval[ i ] = val;
5973             if( i == axspec ) {
5974                val *= specfactor;
5975                val -= rf;
5976             }
5977             sprintf( combuf, "Value at ref. pixel on axis %d", i + 1 );
5978             SetValue( this, FormatKey( "CRVAL", i + 1, -1, s, status ), &val,
5979                       AST__FLOAT, combuf, status );
5980          }
5981       }
5982 
5983 /* Get and save CTYPE for all intermediate axes. These are required, so
5984    break if they are not available. Use the potentially modified versions
5985    saved above for the celestial axes. */
5986       for( i = 0; i < naxis && ok; i++ ){
5987          if( i == axlat ) {
5988             cval = lattype;
5989          } else if( i == axlon ) {
5990             cval = lontype;
5991          } else if( i == axspec ) {
5992             cval = spectype;
5993          } else {
5994             cval = GetItemC( &(store->ctype), i, 0, s, NULL, method, class, status );
5995          }
5996          if( cval && strcmp( cval + 4, "-TAB" ) ) {
5997             comm = GetItemC( &(store->ctype_com), i, 0, s, NULL, method, class, status );
5998             if( !comm ) {
5999                sprintf( combuf, "Type of co-ordinate on axis %d", i + 1 );
6000                comm = combuf;
6001             }
6002             SetValue( this, FormatKey( "CTYPE", i + 1, -1, s, status ), &cval,
6003                       AST__STRING, comm, status );
6004          } else {
6005             ok = 0;
6006          }
6007       }
6008 
6009 /* CDELT values */
6010       if( axspec != -1 ) cdelt[ axspec ] *= specfactor;
6011       for( i = 0; i < naxis; i++ ){
6012          SetValue( this, FormatKey( "CDELT", i + 1, -1, s, status ), cdelt + i,
6013                    AST__FLOAT, "Pixel size", status );
6014       }
6015 
6016 /* Reference equinox */
6017       if( equ != AST__BAD ) SetValue( this, "EQUINOX", &equ, AST__FLOAT,
6018                                         "Epoch of reference equinox", status );
6019 
6020 /* Date of observation. */
6021       val = GetItem( &(store->mjdobs), 0, 0, ' ', NULL, method, class, status );
6022       if( val != AST__BAD ) {
6023 
6024 /* The format used for the DATE-OBS keyword depends on the value of the
6025    keyword. For DATE-OBS < 1999.0, use the old "dd/mm/yy" format.
6026    Otherwise, use the new "ccyy-mm-ddThh:mm:ss[.ssss]" format. */
6027          palCaldj( 99, 1, 1, &mjd99, &jj );
6028          if( val < mjd99 ) {
6029             palDjcal( 0, val, iymdf, &jj );
6030             sprintf( combuf, "%2.2d/%2.2d/%2.2d", iymdf[ 2 ], iymdf[ 1 ],
6031                      iymdf[ 0 ] - ( ( iymdf[ 0 ] > 1999 ) ? 2000 : 1900 ) );
6032          } else {
6033             palDjcl( val, iymdf, iymdf+1, iymdf+2, &fd, &jj );
6034             palDd2tf( 3, fd, sign, ihmsf );
6035             sprintf( combuf, "%4.4d-%2.2d-%2.2dT%2.2d:%2.2d:%2.2d.%3.3d",
6036                      iymdf[0], iymdf[1], iymdf[2], ihmsf[0], ihmsf[1],
6037                      ihmsf[2], ihmsf[3] );
6038          }
6039 
6040 /* Now store the formatted string in the FitsChan. */
6041          cval = combuf;
6042          SetValue( this, "DATE-OBS", (void *) &cval, AST__STRING,
6043                    "Date of observation", status );
6044       }
6045 
6046 /* Rest frequency */
6047       SetValue( this, "RESTFREQ", &rf, AST__FLOAT, "[Hz] Rest frequency", status );
6048 
6049 /* The image frequency corresponding to the rest frequency (only used for
6050    double sideband data). */
6051       val = GetItem( &(store->imagfreq), 0, 0, s, NULL, method, class, status );
6052       if( val != AST__BAD ) {
6053          SetValue( this, "IMAGFREQ", &val, AST__FLOAT, "[Hz] Image frequency", status );
6054       }
6055 
6056 /* Ensure the FitsChan contains OBJECT and LINE headers */
6057       if( !HasCard( this, "OBJECT", method, class, status ) ) {
6058          cval = " ";
6059          SetValue( this, "OBJECT", &cval, AST__STRING, NULL, status );
6060       }
6061       if( !HasCard( this, "LINE", method, class, status ) ) {
6062          cval = " ";
6063          SetValue( this, "LINE", &cval, AST__STRING, NULL, status );
6064       }
6065 
6066 /* CLASS expects the VELO-LSR keyword to hold the radio velocity of the
6067    reference channel (NOT of the source as I was told!!) with respect to
6068    the LSRK rest frame. The "crval" array holds the frequency of the
6069    reference channel in the source rest frame, so we need to convert this
6070    to get the value for VELO-LSR. Create a SpecFrame describing the
6071    required frame (other attributes such as Epoch etc are left unset and
6072    so will be picked up from the supplied FrameSet). We set MinAxes
6073    and MaxAxes so that the Frame can be used as a template to match the
6074    1D or 3D current Frame in the supplied FrameSet. */
6075       velofrm = (AstFrame *) astSpecFrame( "System=vrad,StdOfRest=lsrk,"
6076                                            "Unit=m/s,MinAxes=1,MaxAxes=3", status );
6077 
6078 /* Find the spectral axis within the current Frame of the supplied
6079    FrameSet, using the above "velofrm" as a template. */
6080       fsconv1 = astFindFrame( curfrm, velofrm, "" );
6081 
6082 /* If OK, extract the SpecFrame from the returned FraneSet (this will
6083    have the attribute values that were assigned explicitly to "velofrm"
6084    and will have inherited all unset attributes from the supplied
6085    FrameSet). */
6086       if( fsconv1 ) {
6087          velofrm = astAnnul( velofrm );
6088          velofrm = astGetFrame( fsconv1, AST__CURRENT );
6089          fsconv1 = astAnnul( fsconv1 );
6090 
6091 /* Take a copy of the velofrm and modify its attributes so that it
6092    describes frequency in the sources rest frame in units of Hz. This is
6093    the system that CLASS expects for the CRVAL3 keyword. */
6094          freqfrm = astCopy( velofrm );
6095          astSet( freqfrm, "System=freq,StdOfRest=Source,Unit=Hz", status );
6096 
6097 /* Get a Mapping from frequency to velocity. */
6098          fsconv1 = astConvert( freqfrm, velofrm, "" );
6099          if( fsconv1 ) {
6100 
6101 /* Use this Mapping to convert the spectral crval value from frequency to
6102    velocity. Also convert the value for the neighbouring channel. */
6103             aval[ 0 ] = crval[ axspec ]*specfactor;
6104             aval[ 1 ] = aval[ 0 ] + cdelt[ axspec ]*specfactor;
6105             astTran1( fsconv1, 2, aval, 1, aval );
6106 
6107 /* Store the value. Also store it as VLSR since this keyword seems to be
6108    used for the same thing. */
6109             SetValue( this, "VELO-LSR", aval, AST__FLOAT, "[m/s] Reference velocity", status );
6110             SetValue( this, "VLSR", aval, AST__FLOAT, "[m/s] Reference velocity", status );
6111 
6112 /* The DELTAV keyword holds the radio velocity channel spacing in the
6113    LSR. */
6114             delta = aval[ 1 ] - aval[ 0 ];
6115             SetValue( this, "DELTAV", &delta, AST__FLOAT, "[m/s] Velocity resolution", status );
6116 
6117 /* Free remaining resources. */
6118             fsconv1 = astAnnul( fsconv1 );
6119          }
6120       }
6121       velofrm = astAnnul( velofrm );
6122 
6123 /* AZIMUTH and ELEVATIO - the (az,el) equivalent of CRVAL. We need a
6124    Mapping from the CTYPE spatial system to (az,el). This depends on all
6125    the extra info like telescope position, epoch, etc.  This info is in
6126    the current Frame in the supplied FrameSet. First get a conversion
6127    from a sky frame with default axis ordering to the supplied Frame. All
6128    the extra info is picked up from the supplied Frame since it is not set
6129    in the template. */
6130       radecfrm = (AstFrame *) astSkyFrame( "Permute=0,MinAxes=3,MaxAxes=3", status );
6131       fsconv1 = astFindFrame( curfrm, radecfrm, "" );
6132 
6133 /* Now get conversion from the an (az,el) Frame to the supplied Frame. */
6134       azelfrm = (AstFrame *) astSkyFrame( "System=AZEL,Permute=0,MinAxes=3,MaxAxes=3", status );
6135       fsconv2 = astFindFrame( curfrm, azelfrm, "" );
6136 
6137 /* If both conversions werew possible, concatenate their Mappings to get
6138    a Mapping from (lon,lat) in the CTYPE system, to (az,el). */
6139       if( fsconv1 && fsconv2 ) {
6140          map1 = astGetMapping( fsconv1, AST__CURRENT, AST__BASE );
6141          map2 = astGetMapping( fsconv2, AST__BASE, AST__CURRENT );
6142          map3 = (AstMapping *) astCmpMap( map1, map2, 1, "", status );
6143 
6144 /* Store the CRVAL (ra,dec) values in the default order. */
6145          radec[ 0 ] = crval[ axlon ]*AST__DD2R;
6146          radec[ 1 ] = crval[ axlat ]*AST__DD2R;
6147 
6148 /* Transform to (az,el), normalise, convert to degrees and store. */
6149          astTranN( map3, 1, 2, 1, radec, 1, 2, 1, azel );
6150          if( azel[ 0 ] != AST__BAD && azel[ 1 ] != AST__BAD ) {
6151             astNorm( azelfrm, azel );
6152             azel[ 0 ] *= AST__DR2D;
6153             azel[ 1 ] *= AST__DR2D;
6154             SetValue( this, "AZIMUTH", azel, AST__FLOAT, "[Deg] Telescope azimuth", status );
6155             SetValue( this, "ELEVATIO", azel + 1, AST__FLOAT, "[Deg] Telescope elevation", status );
6156          }
6157 
6158 /* Free resources */
6159          map1 = astAnnul( map1 );
6160          map2 = astAnnul( map2 );
6161          map3 = astAnnul( map3 );
6162          fsconv1 = astAnnul( fsconv1 );
6163          fsconv2 = astAnnul( fsconv2 );
6164       }
6165       radecfrm = astAnnul( radecfrm );
6166       azelfrm = astAnnul( azelfrm );
6167    }
6168    curfrm = astAnnul( curfrm );
6169 
6170 /* Release CDELT workspace */
6171    if( cdelt ) cdelt = (double *) astFree( (void *) cdelt );
6172 
6173 /* Return zero or ret depending on whether an error has occurred. */
6174    return astOK ? ok : 0;
6175 }
6176 
ClassTrans(AstFitsChan * this,AstFitsChan * ret,int axlat,int axlon,const char * method,const char * class,int * status)6177 static void ClassTrans( AstFitsChan *this, AstFitsChan *ret, int axlat,
6178                         int axlon, const char *method, const char *class, int *status ){
6179 
6180 /*
6181 *  Name:
6182 *     ClassTrans
6183 
6184 *  Purpose:
6185 *     Translated non-standard FITS-CLASS headers into equivalent standard
6186 *     ones.
6187 
6188 *  Type:
6189 *     Private function.
6190 
6191 *  Synopsis:
6192 *     #include "fitschan.h"
6193 *     void ClassTrans( AstFitsChan *this, AstFitsChan *ret, int axlat,
6194 *                      int axlon, const char *method, const char *class )
6195 
6196 *  Class Membership:
6197 *     FitsChan member function.
6198 
6199 *  Description:
6200 *     This function extends the functionality of the SpecTrans function,
6201 *     by converting non-standard WCS keywords into standard FITS-WCS
6202 *     keywords, using the conventions of the FITS-CLASS encoding.
6203 
6204 *  Parameters:
6205 *     this
6206 *        Pointer to the FitsChan containing the original header cards.
6207 *     ret
6208 *        Pointer to a FitsChan in which to return the standardised header
6209 *        cards.
6210 *     axlat
6211 *        Zero-based index of the celestial latitude axis.
6212 *     axlon
6213 *        Zero-based index of the celestial longitude axis.
6214 *     method
6215 *        Pointer to string holding name of calling method.
6216 *     class
6217 *        Pointer to a string holding the name of the supplied object class.
6218 */
6219 
6220 /* Local Variables: */
6221    char *cval;                    /* Pointer to character string */
6222    char newtype[ 10 ];            /* New CTYPE value */
6223    const char *keyname;           /* Pointer to keyword name */
6224    const char *ssyssrc;           /* Pointer to SSYSSRC keyword value string */
6225    double crval;                  /* CRVAL value */
6226    double restfreq;               /* Rest frequency (Hz) */
6227    double v0;                     /* Ref channel velocity in source frame */
6228    double vref;                   /* Ref channel velocity in LSR or whatever */
6229    double vsource;                /* Source velocity */
6230    double zsource;                /* Source redshift */
6231    int axspec;                    /* Index of spectral axis */
6232 
6233 /* Check the global error status. */
6234    if ( !astOK ) return;
6235 
6236 /* Get the rest frequency. */
6237    restfreq = AST__BAD;
6238    if( !GetValue2( ret, this, "RESTFRQ", AST__FLOAT, (void *) &restfreq, 0,
6239                   method, class, status ) ){
6240       GetValue2( ret, this, "RESTFREQ", AST__FLOAT, (void *) &restfreq, 0,
6241                   method, class, status );
6242    }
6243    if( restfreq == AST__BAD ) {
6244       astError( AST__BDFTS, "%s(%s): Keyword RESTFREQ not found in CLASS "
6245                 "FITS header.", status, method, class );
6246    }
6247 
6248 /* Get the index of the spectral axis. */
6249    if( axlat + axlon == 1 ) {
6250       axspec = 2;
6251    } else if( axlat + axlon == 3 ) {
6252       axspec = 0;
6253    } else {
6254       axspec = 1;
6255    }
6256 
6257 /* Get the spectral CTYPE value */
6258    if( GetValue2( ret, this, FormatKey( "CTYPE", axspec + 1, -1, ' ', status ),
6259                   AST__STRING, (void *) &cval, 0, method, class, status ) ){
6260 
6261 /* We can only handle frequency axes at the moment. */
6262       if( !astChrMatch( "FREQ", cval ) ) {
6263          astError( AST__BDFTS, "%s(%s): FITS-CLASS keyword %s has value "
6264                    "\"%s\" - CLASS support in AST only includes \"FREQ\" axes.", status,
6265                    method, class, FormatKey( "CTYPE", axspec + 1, -1, ' ', status ),
6266                    cval );
6267 
6268 /* CRVAL for the spectral axis needs to be incremented by RESTFREQ if the
6269    axis represents frequency. */
6270       } else {
6271          keyname = FormatKey( "CRVAL", axspec + 1, -1, ' ', status );
6272          if( GetValue2( ret, this, keyname, AST__FLOAT, (void *) &crval, 1,
6273                         method, class, status ) ) {
6274             crval += restfreq;
6275             SetValue( ret, keyname, (void *) &crval, AST__FLOAT, NULL, status );
6276          }
6277       }
6278 
6279 /* CLASS frequency axes describe source frame frequencies. */
6280       cval = "SOURCE";
6281       SetValue( ret, "SPECSYS", (void *) &cval, AST__STRING, NULL, status );
6282    }
6283 
6284 /* If no projection code is supplied for the longitude and latitude axes,
6285    use "-GLS". This will be translated to "-SFL" by SpecTrans. */
6286    keyname = FormatKey( "CTYPE", axlon + 1, -1, ' ', status );
6287    if( GetValue2( ret, this, keyname, AST__STRING, (void *) &cval, 0, method,
6288                   class, status ) ){
6289       if( !strncmp( "    ", cval + 4, 4 ) ) {
6290          strncpy( newtype, cval, 4 );
6291          strcpy( newtype + 4, "-GLS" );
6292          cval = newtype;
6293          SetValue( ret, keyname, (void *) &cval, AST__STRING, NULL, status );
6294       }
6295    }
6296    keyname = FormatKey( "CTYPE", axlat + 1, -1, ' ', status );
6297    if( GetValue2( ret, this, keyname, AST__STRING, (void *) &cval, 0, method,
6298                   class, status ) ){
6299       if( !strncmp( "    ", cval + 4, 4 ) ) {
6300          strncpy( newtype, cval, 4 );
6301          strcpy( newtype + 4, "-GLS" );
6302          cval = newtype;
6303          SetValue( ret, keyname, (void *) &cval, AST__STRING, NULL, status );
6304       }
6305    }
6306 
6307 /* Look for a keyword with name "VELO-...". This specifies the radio velocity
6308    at the reference channel, in a standard of rest specified by the "..."
6309    in the keyword name. If "VELO-..." is not found, look for "VLSR",
6310    which is the same as "VELO-LSR". */
6311    if( GetValue2( ret, this, "VELO-%3c", AST__FLOAT, (void *) &vref, 0,
6312                   method, class, status ) ||
6313        GetValue2( ret, this, "VLSR", AST__FLOAT, (void *) &vref, 0,
6314                   method, class, status ) ){
6315 
6316 /* Calculate the radio velocity (in the rest frame of the source) corresponding
6317    to the frequency at the reference channel. */
6318       v0 = AST__C*( restfreq - crval )/restfreq;
6319 
6320 /* Assume that the source velocity is the difference between this velocity
6321    and the reference channel velocity given by "VELO-..." */
6322       vsource = vref - v0;
6323 
6324 /* Get the keyword name and find the corresponding SSYSSRC keyword value. */
6325       keyname = CardName( this, status );
6326       if( !strcmp( keyname, "VELO-HEL" ) ) {
6327          ssyssrc = "BARYCENT";
6328       } else if( !strcmp( keyname, "VELO-OBS" ) || !strcmp( keyname, "VELO-TOP" ) ) {
6329          ssyssrc = "TOPOCENT";
6330       } else if( !strcmp( keyname, "VELO-EAR" ) || !strcmp( keyname, "VELO-GEO" ) ) {
6331          ssyssrc = "GEOCENTR";
6332       } else {
6333          ssyssrc = "LSRK";
6334       }
6335       SetValue( ret, "SSYSSRC", (void *) &ssyssrc, AST__STRING, NULL, status );
6336 
6337 /* Convert from radio velocity to redshift and store as ZSOURCE */
6338       zsource = ( AST__C / (AST__C - vsource) ) - 1.0;
6339       SetValue( ret, "ZSOURCE", (void *) &zsource, AST__FLOAT, NULL, status );
6340    }
6341 }
6342 
ClearAttrib(AstObject * this_object,const char * attrib,int * status)6343 static void ClearAttrib( AstObject *this_object, const char *attrib, int *status ) {
6344 /*
6345 *  Name:
6346 *     ClearAttrib
6347 
6348 *  Purpose:
6349 *     Clear an attribute value for a FitsChan.
6350 
6351 *  Type:
6352 *     Private function.
6353 
6354 *  Synopsis:
6355 *     #include "fitschan.h"
6356 *     void ClearAttrib( AstObject *this, const char *attrib, int *status )
6357 
6358 *  Class Membership:
6359 *     FitsChan member function (over-rides the astClearAttrib protected
6360 *     method inherited from the Channel class).
6361 
6362 *  Description:
6363 *     This function clears the value of a specified attribute for a
6364 *     FitsChan, so that the default value will subsequently be used.
6365 
6366 *  Parameters:
6367 *     this
6368 *        Pointer to the FitsChan.
6369 *     attrib
6370 *        Pointer to a null-terminated string specifying the attribute
6371 *        name.  This should be in lower case with no surrounding white
6372 *        space.
6373 *     status
6374 *        Pointer to the inherited status variable.
6375 */
6376 
6377 /* Local Variables: */
6378    AstFitsChan *this;            /* Pointer to the FitsChan structure */
6379 
6380 /* Check the global error status. */
6381    if ( !astOK ) return;
6382 
6383 /* Obtain a pointer to the FitsChan structure. */
6384    this = (AstFitsChan *) this_object;
6385 
6386 /* Check the attribute name and clear the appropriate attribute. */
6387 
6388 /* Card. */
6389 /* ----- */
6390    if ( !strcmp( attrib, "card" ) ) {
6391       astClearCard( this );
6392 
6393 /* Encoding. */
6394 /* --------- */
6395    } else if ( !strcmp( attrib, "encoding" ) ) {
6396       astClearEncoding( this );
6397 
6398 /* CDMatrix */
6399 /* -------- */
6400    } else if ( !strcmp( attrib, "cdmatrix" ) ) {
6401       astClearCDMatrix( this );
6402 
6403 /* FitsAxisOrder. */
6404 /* ----------- */
6405    } else if ( !strcmp( attrib, "fitsaxisorder" ) ) {
6406       astClearFitsAxisOrder( this );
6407 
6408 /* FitsDigits. */
6409 /* ----------- */
6410    } else if ( !strcmp( attrib, "fitsdigits" ) ) {
6411       astClearFitsDigits( this );
6412 
6413 /* DefB1950 */
6414 /* -------- */
6415    } else if ( !strcmp( attrib, "defb1950" ) ) {
6416       astClearDefB1950( this );
6417 
6418 /* TabOK */
6419 /* ----- */
6420    } else if ( !strcmp( attrib, "tabok" ) ) {
6421       astClearTabOK( this );
6422 
6423 /* CarLin */
6424 /* ------ */
6425    } else if ( !strcmp( attrib, "carlin" ) ) {
6426       astClearCarLin( this );
6427 
6428 /* PolyTan */
6429 /* ------- */
6430    } else if ( !strcmp( attrib, "polytan" ) ) {
6431       astClearPolyTan( this );
6432 
6433 /* Iwc */
6434 /* --- */
6435    } else if ( !strcmp( attrib, "iwc" ) ) {
6436       astClearIwc( this );
6437 
6438 /* Clean */
6439 /* ----- */
6440    } else if ( !strcmp( attrib, "clean" ) ) {
6441       astClearClean( this );
6442 
6443 /* Warnings. */
6444 /* -------- */
6445    } else if ( !strcmp( attrib, "warnings" ) ) {
6446       astClearWarnings( this );
6447 
6448 /* If the name was not recognised, test if it matches any of the
6449    read-only attributes of this class. If it does, then report an
6450    error. */
6451    } else if ( astOK && ( !strcmp( attrib, "ncard" ) ||
6452                           !strcmp( attrib, "allwarnings" ) ) ){
6453       astError( AST__NOWRT, "astClear: Invalid attempt to clear the \"%s\" "
6454                 "value for a %s.", status, attrib, astGetClass( this ) );
6455       astError( AST__NOWRT, "This is a read-only attribute." , status);
6456 
6457 /* If the attribute is still not recognised, pass it on to the parent
6458    method for further interpretation. */
6459    } else {
6460       (*parent_clearattrib)( this_object, attrib, status );
6461    }
6462 }
6463 
ClearCard(AstFitsChan * this,int * status)6464 static void ClearCard( AstFitsChan *this, int *status ){
6465 
6466 /*
6467 *+
6468 *  Name:
6469 *     astClearCard
6470 
6471 *  Purpose:
6472 *     Clear the Card attribute.
6473 
6474 *  Type:
6475 *     Protected virtual function.
6476 
6477 *  Synopsis:
6478 *     #include "fitschan.h"
6479 *     void astClearCard( AstFitsChan *this )
6480 
6481 *  Class Membership:
6482 *     FitsChan method.
6483 
6484 *  Description:
6485 *     This function clears the Card attribute for the supplied FitsChan by
6486 *     setting it to the index of the first un-used card in the FitsChan.
6487 *     This causes the next read operation performed on the FitsChan to
6488 *     read the first card. Thus, it is equivalent to "rewinding" the FitsChan.
6489 
6490 *  Parameters:
6491 *     this
6492 *        Pointer to the FitsChan.
6493 
6494 *  Notes:
6495 *     -  This function attempts to execute even if an error has occurred.
6496 *-
6497 */
6498 
6499 /* Local Variables; */
6500    astDECLARE_GLOBALS            /* Declare the thread specific global data */
6501 
6502 /* Ensure the source function has been called */
6503    ReadFromSource( this, status );
6504 
6505 /* Check the supplied FitsChan. If its is empty, return. */
6506    if ( !this || !(this->head) ) return;
6507 
6508 /* Get a pointer to the structure holding thread-specific global data. */
6509    astGET_GLOBALS(this);
6510 
6511 /* Set the pointer to the current card so that it points to the card at
6512    the head of the list. */
6513    this->card = this->head;
6514 
6515 /* If the current card has been read into an AST object, move on to the
6516    first card which has not, unless we are not skipping such cards. */
6517    if( CARDUSED(this->card) ){
6518       MoveCard( this, 1, "astClearCard", astGetClass( this ), status );
6519    }
6520 }
6521 
CnvValue(AstFitsChan * this,int type,int undef,void * buff,const char * method,int * status)6522 static int CnvValue( AstFitsChan *this, int type, int undef, void *buff,
6523                      const char *method, int *status ){
6524 
6525 /*
6526 *
6527 *  Name:
6528 *     CnvValue
6529 
6530 *  Purpose:
6531 *     Convert a data value into a given FITS data type.
6532 
6533 *  Type:
6534 *     Private function.
6535 
6536 *  Synopsis:
6537 *     #include "fitschan.h"
6538 *     int CnvValue( AstFitsChan *this, int type, int undef, void *buff,
6539 *                   const char *method, int *status )
6540 
6541 *  Class Membership:
6542 *     FitsChan method.
6543 
6544 *  Description:
6545 *     This function produces a copy of the data value for the current card
6546 *     converted from its stored data type to the supplied data type.
6547 
6548 *  Parameters:
6549 *     this
6550 *        Pointer to the FitsChan.
6551 *     type
6552 *        The FITS data type in which to return the data value of the
6553 *        current card.
6554 *     undef
6555 *        Determines what happens if the current card has an undefined
6556 *        value. If "undef" is zero, an error will be reported identifying
6557 *        the undefined keyword value. If "undef" is non-zero, no error is
6558 *        reported and the contents of the output buffer are left unchanged.
6559 *     buf
6560 *        A pointer to a buffer to recieve the converted value. It is the
6561 *        responsibility of the caller to ensure that a suitable buffer is
6562 *        supplied.
6563 *     method
6564 *        Pointer to a string holding the name of the calling method.
6565 *        This is only for use in constructing error messages.
6566 *     status
6567 *        Pointer to the inherited status variable.
6568 
6569 *  Returned Value:
6570 *     Zero if the conversion was not possible (in which case NO error is
6571 *     reported), one otherwise.
6572 
6573 *  Notes:
6574 *     -  When converting from floating point to integer, the  floating
6575 *     point value is truncated using a C cast.
6576 *     -  Non-zero numerical values are considered TRUE, and zero
6577 *     numerical values are considered FALSE. Any string starting with a
6578 *     'T' or a 'Y' (upper or lower case) is considered TRUE, and anything
6579 *     starting with an 'F' or an 'N' (upper or lower case) is considered
6580 *     FALSE. In addition, a dot ('.') may be placed in front of a 'T' or an
6581 *     'F'.
6582 *     -  A logical TRUE value is represented as a real numerical value of
6583 *     one and the character string "Y". A logical FALSE value is represented
6584 *     by a real numerical value of zero and the character string "N".
6585 *     -  When converting from a string to any numerical value, zero is
6586 *     returned if the string is not a formatted value which can be converted
6587 *     into the corresponding type using astSscanf.
6588 *     - Real and imaginary parts of a complex value should be separated by
6589 *     spaces within strings. If a string does contains only a single numerical
6590 *     value, it is assumed to be the real part, and the imaginary part is
6591 *     assumed to be zero.
6592 *     -  When converting a complex numerical type to a non-complex numerical
6593 *     type, the returned value is derived from the real part only, the
6594 *     imaginary part is ignored.
6595 *     -  Zero is returned if an error has occurred, or if this function
6596 *     should fail for any reason.
6597 *     - If the supplied value is undefined an error will be reported.
6598 */
6599 
6600 /* Local Variables: */
6601    int otype;               /* Stored data type */
6602    size_t osize;            /* Size of stored data */
6603    void *odata;             /* Pointer to stored data */
6604 
6605 /* Check the global error status, and the supplied buffer. */
6606    if ( !astOK || !buff ) return 0;
6607 
6608 /* Get the type in which the data value is stored. */
6609    otype = CardType( this, status );
6610 
6611 /* Get a pointer to the stored data value, and its size. */
6612    osize = 0;
6613    odata = CardData( this, &osize, status );
6614 
6615 /* Do the conversion. */
6616    return CnvType( otype, odata, osize, type, undef, buff,
6617                    CardName( this, status ), method, astGetClass( this ),
6618                    status );
6619 }
6620 
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)6621 static int CnvType( int otype, void *odata, size_t osize, int type, int undef,
6622                      void *buff, const char *name, const char *method,
6623                      const char *class, int *status ){
6624 /*
6625 *
6626 *  Name:
6627 *     CnvType
6628 
6629 *  Purpose:
6630 *     Convert a data value into a given FITS data type.
6631 
6632 *  Type:
6633 *     Private function.
6634 
6635 *  Synopsis:
6636 *     #include "fitschan.h"
6637 *     int CnvType( int otype, void *odata, size_t osize, int type, int undef,
6638 *                   void *buff, const char *name, const char *method,
6639 *                   const char *class, int *status )
6640 
6641 *  Class Membership:
6642 *     FitsChan method.
6643 
6644 *  Description:
6645 *     This function produces a copy of the data value for the current card
6646 *     converted from its stored data type to the supplied data type.
6647 
6648 *  Parameters:
6649 *     otype
6650 *        The type of the supplied data value.
6651 *     odata
6652 *        Pointer to a buffer holding the supplied data value.
6653 *     osize
6654 *        The size of the data value (in bytes - strings include the
6655 *        terminating null).
6656 *     type
6657 *        The FITS data type in which to return the data value of the
6658 *        current card.
6659 *     undef
6660 *        Determines what happens if the supplied data value type is
6661 *        undefined If "undef" is zero, an error will be reported identifying
6662 *        the undefined keyword value. If "undef" is non-zero, no error is
6663 *        reported and the contents of the output buffer are left unchanged.
6664 *     buff
6665 *        A pointer to a buffer to recieve the converted value. It is the
6666 *        responsibility of the caller to ensure that a suitable buffer is
6667 *        supplied.
6668 *     name
6669 *        A pointer to a string holding a keyword name to include in error
6670 *        messages.
6671 *     method
6672 *        Pointer to a string holding the name of the calling method.
6673 *        This is only for use in constructing error messages.
6674 *     class
6675 *        Pointer to a string holding the name of the object class.
6676 *        This is only for use in constructing error messages.
6677 *     status
6678 *        Pointer to the inherited status variable.
6679 
6680 *  Returned Value:
6681 *     Zero if the conversion was not possible (in which case NO error is
6682 *     reported), one otherwise.
6683 
6684 *  Notes:
6685 *     -  When converting from floating point to integer, the  floating
6686 *     point value is truncated using a C cast.
6687 *     -  Non-zero numerical values are considered TRUE, and zero
6688 *     numerical values are considered FALSE. Any string starting with a
6689 *     'T' or a 'Y' (upper or lower case) is considered TRUE, and anything
6690 *     starting with an 'F' or an 'N' (upper or lower case) is considered
6691 *     FALSE. In addition, a dot ('.') may be placed in front of a 'T' or an
6692 *     'F'.
6693 *     -  A logical TRUE value is represented as a real numerical value of
6694 *     one and the character string "Y". A logical FALSE value is represented
6695 *     by a real numerical value of zero and the character string "N".
6696 *     -  When converting from a string to any numerical value, zero is
6697 *     returned if the string isn not a formatted value which can be converted
6698 *     into the corresponding type using astSscanf.
6699 *     - Real and imaginary parts of a complex value should be separated by
6700 *     spaces within strings. If a string does contains only a single numerical
6701 *     value, it is assumed to be the real part, and the imaginary part is
6702 *     assumed to be zero.
6703 *     -  When converting a complex numerical type to a non-complex numerical
6704 *     type, the returned value is derived from the real part only, the
6705 *     imaginary part is ignored.
6706 *     -  Zero is returned if an error has occurred, or if this function
6707 *     should fail for any reason.
6708 */
6709 
6710 /* Local Variables: */
6711    astDECLARE_GLOBALS            /* Declare the thread specific global data */
6712    const char *c;           /* Pointer to next character */
6713    const char *ostring;     /* String data value */
6714    double odouble;          /* Double data value */
6715    int oint;                /* Integer data value */
6716    int ival;                /* Integer value read from string */
6717    int len;                 /* Length of character string */
6718    int nc;                  /* No. of characetsr used */
6719    int ret;                 /* Returned success flag */
6720 
6721 /* Check the global error status, and the supplied buffer. */
6722    if ( !astOK || !buff ) return 0;
6723 
6724 /* Get a pointer to the structure holding thread-specific global data. */
6725    astGET_GLOBALS(NULL);
6726 
6727 /* Assume success. */
6728    ret = 1;
6729 
6730 /* If the supplied data type is undefined, report an error unless the
6731    returned data type is also undefined or an undefined value is
6732    acceptable for the keyword. */
6733    if( otype == AST__UNDEF ) {
6734       if( type != AST__UNDEF && !undef ) {
6735          ret = 0;
6736          astError( AST__FUNDEF, "The FITS keyword '%s' has an undefined "
6737                    "value.", status, name );
6738       }
6739 
6740 /* If the returned data type is undefined, the returned value is
6741    immaterial, so leave the buffer contents unchanged. */
6742    } else if( type == AST__UNDEF ) {
6743 
6744 /* If there is no data value and this is not a COMMENT keyword, or if
6745    there is a data value and this is a COMMENT card, conversion is not
6746    possible. */
6747    } else if( ( odata && otype == AST__COMMENT ) ||
6748               ( !odata && otype != AST__COMMENT ) ) {
6749       ret = 0;
6750 
6751 /* If there is no data (and therefore this is a comment card), leave the
6752    supplied buffers unchanged. */
6753    } else if( odata ) {
6754 
6755 /* Do each possible combination of supplied and stored data types... */
6756 
6757 /* Convert a AST__FLOAT data value to ... */
6758       if( otype == AST__FLOAT ){
6759          odouble = *( (double *) odata );
6760          if( type == AST__FLOAT ){
6761             (void) memcpy( buff, odata, osize );
6762          } else if( type == AST__STRING || type == AST__CONTINUE  ){
6763             if( odouble != AST__BAD ) {
6764                (void) sprintf( cnvtype_text, "%.*g", DBL_DIG, odouble );
6765                CheckZero( cnvtype_text, odouble, 0, status );
6766             } else {
6767                strcpy( cnvtype_text, BAD_STRING );
6768             }
6769             *( (char **) buff ) = cnvtype_text;
6770          } else if( type == AST__INT      ){
6771             *( (int *) buff ) = (int) odouble;
6772          } else if( type == AST__LOGICAL  ){
6773             *( (int *) buff ) = ( odouble == 0.0 ) ? 0 : 1;
6774          } else if( type == AST__COMPLEXF ){
6775             ( (double *) buff )[ 0 ] = odouble;
6776             ( (double *) buff )[ 1 ] = 0.0;
6777          } else if( type == AST__COMPLEXI ){
6778             ( (int *) buff )[ 0 ] = (int) odouble;
6779             ( (int *) buff )[ 1 ] = 0;
6780          } else if( astOK ){
6781             ret = 0;
6782             astError( AST__INTER, "CnvType: AST internal programming error - "
6783                       "FITS data-type no. %d not yet supported.", status, type );
6784          }
6785 
6786 /* Convert a AST__STRING data value to ... */
6787       } else if( otype == AST__STRING || type == AST__CONTINUE ){
6788          ostring = (char *) odata;
6789          len = (int) strlen( ostring );
6790          if( type == AST__FLOAT ){
6791             if( nc = 0,
6792                     ( 0 == astSscanf( ostring, BAD_STRING " %n", &nc ) )
6793                   && (nc >= len ) ){
6794                *( (double *) buff ) = AST__BAD;
6795             } else if( nc = 0,
6796                      ( 1 != astSscanf( ostring, "%lf %n", (double *) buff, &nc ) )
6797                   || (nc < len ) ){
6798                ret = 0;
6799             }
6800          } else if( type == AST__STRING || type == AST__CONTINUE  ){
6801             strncpy( cnvtype_text, (char *) odata, AST__FITSCHAN_FITSCARDLEN );
6802             *( (char **) buff ) = cnvtype_text;
6803          } else if( type == AST__INT      ){
6804             if( nc = 0,
6805                      ( 1 != astSscanf( ostring, "%d %n", (int *) buff, &nc ) )
6806                   || (nc < len ) ){
6807                ret = 0;
6808             }
6809          } else if( type == AST__LOGICAL  ){
6810             if( nc = 0,
6811                      ( 1 == astSscanf( ostring, "%d %n", &ival, &nc ) )
6812                   && (nc >= len ) ){
6813                *( (int *) buff ) = ival ? 1 : 0;
6814             } else {
6815                c = ostring;
6816                while( *c && isspace( (int) *c ) ) c++;
6817                if( *c == 'y' || *c == 'Y' || *c == 't' || *c == 'T' ||
6818                    ( *c == '.' && ( c[1] == 't' || c[1] == 'T' ) ) ){
6819                   *( (int *) buff ) = 1;
6820                } else if( *c == 'n' || *c == 'N' || *c == 'f' || *c == 'F' ||
6821                    ( *c == '.' && ( c[1] == 'f' || c[1] == 'F' ) ) ){
6822                   *( (int *) buff ) = 0;
6823                } else {
6824                   ret = 0;
6825                }
6826             }
6827          } else if( type == AST__COMPLEXF ){
6828             if( nc = 0,
6829                      ( 1 != astSscanf( ostring, "%lf %lf %n", (double *) buff,
6830                                     (double *) buff + 1, &nc ) )
6831                   || (nc < len ) ){
6832                if( nc = 0,
6833                         ( 1 != astSscanf( ostring, "%lf %n", (double *) buff,
6834                                        &nc ) )
6835                      || (nc < len ) ){
6836                   ret = 0;
6837                } else {
6838                   ( (double *) buff )[ 1 ] = 0.0;
6839                }
6840             }
6841          } else if( type == AST__COMPLEXI ){
6842             if( nc = 0,
6843                     ( 1 != astSscanf( ostring, "%d %d %n", (int *) buff,
6844                                    (int *) buff + 1, &nc ) )
6845                    || (nc < len ) ){
6846                if( nc = 0,
6847                         ( 1 != astSscanf( ostring, "%d %n", (int *) buff, &nc ) )
6848                      || (nc < len ) ){
6849                   ret = 0;
6850                } else {
6851                   ( (int *) buff )[ 1 ] = 0;
6852                }
6853             }
6854          } else if( astOK ){
6855             ret = 0;
6856             astError( AST__INTER, "CnvType: AST internal programming error - "
6857                       "FITS data-type no. %d not yet supported.", status, type );
6858          }
6859 
6860 /* Convert an AST__INT data value to ... */
6861       } else if( otype == AST__INT      ){
6862          oint = *( (int *) odata );
6863          if( type == AST__FLOAT ){
6864             *( (double *) buff ) = (double) oint;
6865          } else if( type == AST__STRING || type == AST__CONTINUE  ){
6866             (void) sprintf( cnvtype_text, "%d", oint );
6867             *( (char **) buff ) = cnvtype_text;
6868          } else if( type == AST__INT      ){
6869             (void) memcpy( buff, odata, osize );
6870          } else if( type == AST__LOGICAL  ){
6871             *( (int *) buff ) = oint ? 1 : 0;
6872          } else if( type == AST__COMPLEXF ){
6873             ( (double *) buff )[ 0 ] = (double) oint;
6874             ( (double *) buff )[ 1 ] = 0.0;
6875          } else if( type == AST__COMPLEXI ){
6876             ( (int *) buff )[ 0 ] = oint;
6877             ( (int *) buff )[ 1 ] = 0;
6878          } else if( astOK ){
6879             ret = 0;
6880             astError( AST__INTER, "CnvType: AST internal programming error - "
6881                       "FITS data-type no. %d not yet supported.", status, type );
6882          }
6883 
6884 /* Convert a LOGICAL data value to ... */
6885       } else if( otype == AST__LOGICAL  ){
6886          oint = *( (int *) odata );
6887          if( type == AST__FLOAT ){
6888             *( (double *) buff ) = oint ? 1.0 : 0.0;
6889          } else if( type == AST__STRING || type == AST__CONTINUE  ){
6890             if( oint ){
6891                strcpy( cnvtype_text, "Y" );
6892             } else {
6893                strcpy( cnvtype_text, "N" );
6894             }
6895             *( (char **) buff ) = cnvtype_text;
6896          } else if( type == AST__INT      ){
6897             *( (int *) buff ) = oint;
6898          } else if( type == AST__LOGICAL  ){
6899             (void) memcpy( buff, odata, osize );
6900          } else if( type == AST__COMPLEXF ){
6901             ( (double *) buff )[ 0 ] = oint ? 1.0 : 0.0;
6902             ( (double *) buff )[ 1 ] = 0.0;
6903          } else if( type == AST__COMPLEXI ){
6904             ( (int *) buff )[ 0 ] = oint ? 1 : 0;
6905             ( (int *) buff )[ 1 ] = 0;
6906          } else if( astOK ){
6907             ret = 0;
6908             astError( AST__INTER, "CnvType: AST internal programming error - "
6909                       "FITS data-type no. %d not yet supported.", status, type );
6910          }
6911 
6912 /* Convert a AST__COMPLEXF data value to ... */
6913       } else if( otype == AST__COMPLEXF ){
6914          odouble = ( (double *) odata )[ 0 ];
6915          if( type == AST__FLOAT ){
6916             *( (double *) buff ) = odouble;
6917          } else if( type == AST__STRING || type == AST__CONTINUE  ){
6918             (void) sprintf( cnvtype_text0, "%.*g", DBL_DIG, ( (double *) odata )[ 0 ] );
6919             CheckZero( cnvtype_text0, ( (double *) odata )[ 0 ], 0, status );
6920             (void) sprintf( cnvtype_text1, "%.*g", DBL_DIG, ( (double *) odata )[ 1 ] );
6921             CheckZero( cnvtype_text1, ( (double *) odata )[ 1 ], 0, status );
6922             (void) sprintf( cnvtype_text, "%s %s", cnvtype_text0, cnvtype_text1 );
6923             *( (char **) buff ) = cnvtype_text;
6924          } else if( type == AST__INT      ){
6925             *( (int *) buff ) = (int) odouble;
6926          } else if( type == AST__LOGICAL  ){
6927             *( (int *) buff ) = ( odouble == 0.0 ) ? 0 : 1;
6928          } else if( type == AST__COMPLEXF ){
6929             (void) memcpy( buff, odata, osize );
6930          } else if( type == AST__COMPLEXI ){
6931             ( (int *) buff )[ 0 ] = (int) odouble;
6932             ( (int *) buff )[ 1 ] = (int) ( (double *) odata )[ 1 ];
6933          } else if( astOK ){
6934             ret = 0;
6935             astError( AST__INTER, "CnvType: AST internal programming error - "
6936                       "FITS data-type no. %d not yet supported.", status, type );
6937          }
6938 
6939 /* Convert a AST__COMPLEXI data value to ... */
6940       } else if( otype == AST__COMPLEXI ){
6941          oint = ( (int *) odata )[ 0 ];
6942          if( type == AST__FLOAT ){
6943             *( (double *) buff ) = (double) oint;
6944          } else if( type == AST__STRING || type == AST__CONTINUE  ){
6945             (void) sprintf( cnvtype_text, "%d %d", ( (int *) odata )[ 0 ],
6946                                            ( (int *) odata )[ 1 ] );
6947             *( (char **) buff ) = cnvtype_text;
6948          } else if( type == AST__INT      ){
6949             *( (int *) buff ) = oint;
6950          } else if( type == AST__LOGICAL  ){
6951             *( (int *) buff ) = oint ? 1 : 0;
6952          } else if( type == AST__COMPLEXF ){
6953             ( (double *) buff )[ 0 ] = (double) oint;
6954             ( (double *) buff )[ 1 ] = (double) ( (int *) odata )[ 1 ];
6955          } else if( type == AST__COMPLEXI ){
6956             (void) memcpy( buff, odata, osize );
6957          } else if( astOK ){
6958             ret = 0;
6959             astError( AST__INTER, "CnvType: AST internal programming error - "
6960                       "FITS data-type no. %d not yet supported.", status, type );
6961          }
6962       } else if( astOK ){
6963          ret = 0;
6964          astError( AST__INTER, "CnvType: AST internal programming error - "
6965                    "FITS data-type no. %d not yet supported.", status, type );
6966       }
6967    }
6968    return ret;
6969 }
6970 
ComBlock(AstFitsChan * this,int incr,const char * method,const char * class,int * status)6971 static int ComBlock( AstFitsChan *this, int incr, const char *method,
6972                      const char *class, int *status ){
6973 
6974 /*
6975 *  Name:
6976 *     ComBlock
6977 
6978 *  Purpose:
6979 *     Delete a AST comment block in a Native-encoded FitsChan.
6980 
6981 *  Type:
6982 *     Private function.
6983 
6984 *  Synopsis:
6985 *     #include "fitschan.h"
6986 
6987 *     int ComBlock( AstFitsChan *this, int incr, const char *method,
6988 *                   const char *class, int *status )
6989 
6990 *  Class Membership:
6991 *     FitsChan member function.
6992 
6993 *  Description:
6994 *     This function looks for a block of comment cards as defined below,
6995 *     and deletes all the cards in the block, if a suitable block is found.
6996 *
6997 *     Comment blocks consist of a contiguous sequence of COMMENT cards. The
6998 *     text of each card should start and end with the 3 characters "AST".
6999 *     The block is delimited above by a card containing all +'s (except
7000 *     for the two "AST" strings), and below by a card containing all -'s.
7001 *
7002 *     The block is assumed to start on the card which is adjacent to the
7003 *     current card on entry.
7004 
7005 *  Parameters:
7006 *     this
7007 *        Pointer to the FitsChan.
7008 *     incr
7009 *        This should be either +1 or -1, and is the increment between
7010 *        adjacent cards in the comment block. A value of +1 means
7011 *        that the card following the current card is taken as the first in
7012 *        the block, and subsequent cards are checked. The block must then
7013 *        end with a line of -'s. If -1 is supplied, then the card
7014 *        preceding the current card is taken as the first in the block,
7015 *        and preceding cards are checked. The block must then end with
7016 *        a row of +'s.
7017 *     method
7018 *        Pointer to a string holding the name of the calling method.
7019 *        This is only for use in constructing error messages.
7020 *     class
7021 *        Pointer to a string holding the name of the supplied object class.
7022 *        This is only for use in constructing error messages.
7023 *     status
7024 *        Pointer to the inherited status variable.
7025 
7026 *  Returned Value:
7027 *     1 if a block was found and deleted, 0 otherwise.
7028 
7029 *  Notes:
7030 *     -  The pointer to the current card is returned unchanged.
7031 */
7032 
7033 /* Local Variables: */
7034    FitsCard *card0;              /* Pointer to current FitsCard on entry */
7035    char del;                     /* Delimiter character */
7036    char *text;                   /* Pointer to the comment text */
7037    int i;                        /* Card index within the block */
7038    int ncard;                    /* No. of cards in the block */
7039    int ret;                      /* The returned flag */
7040    size_t len;                   /* Length of the comment text */
7041 
7042 /* Check the global status. */
7043    if( !astOK ) return 0;
7044 
7045 /* Save the pointer to the current card. */
7046    card0 = this->card;
7047 
7048 /* Initialise the returned flag to indicate that we have not found a
7049    comment block. */
7050    ret = 0;
7051 
7052 /* Move on to the first card in the block. If this is not possible (due to
7053    us already being at the start or end of the FitsChan), then return. */
7054    if( MoveCard( this, incr, method, class, status ) == 1 ) {
7055 
7056 /* Store the character which is used in the delimiter line for the
7057    comment block. */
7058       del = ( incr == 1 ) ? '-' : '+';
7059 
7060 /* Initialise the number of cards in the comment block to zero. */
7061       ncard = 0;
7062 
7063 /* Loop round until the end (or start) of the comment block is found.
7064    Leave the loop if an error occurs.  */
7065       while( astOK ) {
7066 
7067 /* Is this card a comment card? If not, then we have failed to find a
7068    complete comment block. Break out of the loop. */
7069          if( CardType( this, status ) != AST__COMMENT ) break;
7070 
7071 /* Increment the number of cards in the comment block. */
7072          ncard++;
7073 
7074 /* Get the text of the comment, and its length. */
7075          text = CardComm( this, status );
7076          if( text ){
7077             len = strlen( text );
7078 
7079 /* Check the first 3 characters. Break out of the loop if they are not
7080    "AST". */
7081             if( strncmp( "AST", text, 3 ) ) break;
7082 
7083 /* Check the last 3 characters. Break out of the loop if they are not
7084    "AST". */
7085             if( strcmp( "AST", text + len - 3 ) ) break;
7086 
7087 /* If the comment is the appropriate block delimiter (a line of +'s or
7088    -'s depending on the direction), then set the flag to indicate that we
7089    have a complete comment block and leave the loop. Allow spaces to be
7090    included. Exclude the "AST" strings at begining and end from the check. */
7091             ret = 1;
7092             for( i = 3; i < len - 3; i++ ) {
7093                if( text[ i ] != del && text[ i ] != ' ' ) {
7094                   ret = 0;
7095                   break;
7096                }
7097             }
7098          }
7099          if( ret ) break;
7100 
7101 /* Move on to the next card. If this is not possible (due to us already
7102    being at the start or end of the FitsChan), then break out of the loop. */
7103          if( MoveCard( this, incr, method, class, status ) == 0 ) break;
7104       }
7105 
7106 /* Re-instate the original current card. */
7107       this->card = card0;
7108 
7109 /* If we found a complete comment block, mark it (which is equivalent to
7110    deleting it except that memory of the cards location within the
7111    FitsChan is preserved for future use), and then re-instate the original
7112    current card. */
7113       if( ret && astOK ) {
7114          for( i = 0; i < ncard; i++ ) {
7115             MoveCard( this, incr, method, class, status );
7116             MarkCard( this, status );
7117          }
7118          this->card = card0;
7119       }
7120    }
7121 
7122 /* If an error occurred, indicate that coment block has been deleted. */
7123    if( !astOK ) ret = 0;
7124    return ret;
7125 }
7126 
ConcatWAT(AstFitsChan * this,int iaxis,const char * method,const char * class,int * status)7127 static char *ConcatWAT( AstFitsChan *this, int iaxis, const char *method,
7128                         const char *class, int *status ){
7129 /*
7130 *  Name:
7131 *     ConcatWAT
7132 
7133 *  Purpose:
7134 *     Concatenate all the IRAF "WAT" keywords for an axis.
7135 
7136 *  Type:
7137 *     Private function.
7138 
7139 *  Synopsis:
7140 *     #include "fitschan.h"
7141 *     char *ConcatWAT( AstFitsChan *this, int iaxis, const char *method,
7142 *                      const char *class, int *status )
7143 
7144 *  Class Membership:
7145 *     FitsChan member function.
7146 
7147 *  Description:
7148 *     This function searches the supplied FitsChan for any keywords of
7149 *     the form "WATi_j", where i and j are integers and i is equal to the
7150 *     supplied "iaxis" value plus one, and concatenates their string
7151 *     values into a single string. Such keywords are created by IRAF to
7152 *     describe their non-standard ZPX and TNX projections.
7153 
7154 *  Parameters:
7155 *     this
7156 *        The FistChan.
7157 *     iaxis
7158 *        The zero-based index of the axis to be retrieved.
7159 *     method
7160 *         The name of the calling method to include in error messages.
7161 *     class
7162 *         The object type to include in error messages.
7163 *     status
7164 *        Pointer to the inherited status variable.
7165 
7166 *  Returned Value:
7167 *     A pointer to a dynamically allocated, null terminated string
7168 *     containing a copy of the concatentated WAT values. This string must
7169 *     be freed by the caller (using astFree) when no longer required.
7170 *
7171 *     A NULL pointer will be returned if there are no WAT kewyords for
7172 *     the requested axis in the FitsChan.
7173 
7174 *  Notes:
7175 *     - A NULL pointer value will be returned if this function is
7176 *     invoked with the global error status set or if it should fail
7177 *     for any reason.
7178 */
7179 
7180 /* Local Variables: */
7181    char keyname[ FITSNAMLEN + 5 ];/* Keyword name */
7182    char *wat;                     /* Pointer to a single WAT string */
7183    char *result;                  /* Returned string */
7184    int watlen;                    /* Length of total WAT string (inc. term null)*/
7185    int j;                         /* WAT index */
7186    size_t size;                   /* Length of string value */
7187 
7188 /* Initialise returned value. */
7189    result = NULL;
7190 
7191 /* Check inherited status */
7192    if( !astOK ) return result;
7193 
7194 /* Rewind the FitsChan. */
7195    astClearCard( this );
7196 
7197 /* Concatenate all the IRAF "WAT" keywords together for this axis. These
7198    keywords are marked as having been used, so that they are not written
7199    out when the FitsChan is deleted. */
7200    watlen = 1;
7201    j = 1;
7202    size = 0;
7203    sprintf( keyname, "WAT%d_%.3d", iaxis + 1, j );
7204    while( astOK ) {
7205 
7206 /* Search forward from the current card for the next WAT card. If no
7207    found, try searching again from the start of the FitsChan. If not found
7208    evenm then, break. */
7209       if( ! FindKeyCard( this, keyname, method, class, status ) ) {
7210          astClearCard( this );
7211          if( ! FindKeyCard( this, keyname, method, class, status ) ) break;
7212       }
7213 
7214       wat = (char *) CardData( this, &size, status );
7215       result = (char *) astRealloc( (void *) result,
7216                                     watlen - 1 + size );
7217       if( result ) {
7218          strcpy( result + watlen - 1, wat );
7219          watlen += size - 1;
7220          MarkCard( this, status );
7221          MoveCard( this, 1, method, class, status );
7222          j++;
7223          sprintf( keyname, "WAT%d_%.3d", iaxis + 1, j );
7224       } else {
7225          break;
7226       }
7227    }
7228 
7229 /* Return the result. */
7230    return result;
7231 }
7232 
CountFields(const char * temp,char type,const char * method,const char * class,int * status)7233 static int CountFields( const char *temp, char type, const char *method,
7234                         const char *class, int *status ){
7235 /*
7236 *  Name:
7237 *     CountFields
7238 
7239 *  Purpose:
7240 *     Count the number of field specifiers in a template string.
7241 
7242 *  Type:
7243 *     Private function.
7244 
7245 *  Synopsis:
7246 *     #include "fitschan.h"
7247 *     int CountFields( const char *temp, char type, const char *method,
7248 *                      const char *class, int *status )
7249 
7250 *  Class Membership:
7251 *     FitsChan member function.
7252 
7253 *  Description:
7254 *     This function returns the number of fields which include the
7255 *     specified character type in the supplied string.
7256 
7257 *  Parameters:
7258 *     temp
7259 *        Pointer to a null terminated string holding the template.
7260 *     type
7261 *        A single character giving the field type to be counted (e.g.
7262 *        'd', 'c' or 'f').
7263 *     method
7264 *        Pointer to a string holding the name of the calling method.
7265 *        This is only for use in constructing error messages.
7266 *     class
7267 *        Pointer to a string holding the name of the supplied object class.
7268 *        This is only for use in constructing error messages.
7269 *     status
7270 *        Pointer to the inherited status variable.
7271 
7272 *  Returned Value:
7273 *     The number of fields.
7274 
7275 *  Notes:
7276 *     -  No error is reported if the parameter "type" is not a valid
7277 *     field type specifier, but zero will be returned.
7278 *     -  An error is reported if the template has any invalid field
7279 *     specifiers in it.
7280 *     -  A value of zero is returned if an error has already occurred,
7281 *     or if this function should fail for any reason.
7282 */
7283 
7284 /* Local Variables: */
7285    const char *b;         /* Pointer to next template character */
7286    int nf;                /* No. of fields found so far */
7287 
7288 /* Check global status. */
7289    if( !astOK ) return 0;
7290 
7291 /* Initialise a pointer to the start of the template string. */
7292    b = temp;
7293 
7294 /* Initialise the number of fields found so far. */
7295    nf = 0;
7296 
7297 /* Go through the string. */
7298    while( *b && astOK ){
7299 
7300 /* If the current character is a '%', a field is starting. */
7301       if( *b == '%' ){
7302 
7303 /* Skip over the field width (if supplied). */
7304          if( isdigit( (int) *(++b) ) ) b++;
7305 
7306 /* Report an error if the end of the string occurs within the field. */
7307          if( !*b ) {
7308             astError( AST__BDFMT, "%s(%s): Incomplete field specifier found "
7309                       "at end of filter template '%s'.", status, method, class,
7310                       temp );
7311             break;
7312 
7313 /* Report an error if the field type is illegal. */
7314          } else if( *b != 'd' && *b != 'c' && *b != 'f' ) {
7315             astError( AST__BDFMT, "%s(%s): Illegal field type or width "
7316                       "specifier '%c' found in filter template '%s'.", status,
7317                       method, class, *b, temp );
7318             break;
7319          }
7320 
7321 /* Compare the field type with the supplied type, and increment the
7322    number of fields found if it is the correct type. */
7323          if( *b == type ) nf++;
7324       }
7325 
7326 /* Move on to the next character. */
7327       b++;
7328    }
7329 
7330 /* If an error has occurred, return 0. */
7331    if( !astOK ) nf = 0;
7332 
7333 /* Return the answer. */
7334    return nf;
7335 }
7336 
CreateKeyword(AstFitsChan * this,const char * name,char keyword[FITSNAMLEN+1],int * status)7337 static void CreateKeyword( AstFitsChan *this, const char *name,
7338                            char keyword[ FITSNAMLEN + 1 ], int *status ){
7339 
7340 /*
7341 *  Name:
7342 *     CreateKeyword
7343 
7344 *  Purpose:
7345 *     Create a unique un-used keyword for a FitsChan.
7346 
7347 *  Type:
7348 *     Private function.
7349 
7350 *  Synopsis:
7351 *     #include "fitschan.h"
7352 
7353 *     void CreateKeyword( AstFitsChan *this, const char *name,
7354 *                         char keyword[ FITSNAMLEN + 1 ], int *status )
7355 
7356 *  Class Membership:
7357 *     FitsChan member function.
7358 
7359 *  Description:
7360 *     This function takes a name which forms the basis of a FITS
7361 *     keyword and appends a sequence number (encoded as a pair of
7362 *     legal FITS keyword characters) so as to generate a unique FITS
7363 *     keyword which has not previously been used in the FitsChan
7364 *     supplied.
7365 *
7366 *     It is intended for use when several keywords with the same name
7367 *     must be stored in a FitsChan, since to comply strictly with the
7368 *     FITS standard keywords should normally be unique (otherwise
7369 *     external software which processes the keywords might omit one or
7370 *     other of the values).
7371 *
7372 *     An attempt is also made to generate keywords in a form that is
7373 *     unlikely to clash with those from other sources (in as far as
7374 *     this is possible with FITS). In any event, a keyword that
7375 *     already appears in the FitsChan will not be re-used.
7376 
7377 *  Parameters:
7378 *     this
7379 *        Pointer to the FitsChan.
7380 *     name
7381 *        Pointer to a constant null-terminated string containing the
7382 *        name on which the new keyword should be based. This should be
7383 *        a legal FITS keyword in itself, except that it should be at
7384 *        least two characters shorter than the maximum length, in
7385 *        order to accommodate the sequence number characters.
7386 *
7387 *        If this string is too long, it will be silently
7388 *        truncated. Mixed case is permitted, as all characters
7389 *        supplied are converted to upper case before use.
7390 *     keyword
7391 *        A character array in which the generated unique keyword will
7392 *        be returned, null terminated.
7393 *     status
7394 *        Pointer to the inherited status variable.
7395 */
7396 
7397 /* Local Variables: */
7398    astDECLARE_GLOBALS            /* Declare the thread specific global data */
7399    const char *seq_chars = SEQ_CHARS;/* Pointer to characters used for encoding */
7400    char seq_char;                /* The first sequence character */
7401    const char *class;            /* Object clas */
7402    int found;                    /* Keyword entry found in list? */
7403    int limit;                    /* Sequence number has reached limit? */
7404    int nc;                       /* Number of basic keyword characters */
7405    int seq;                      /* The sequence number */
7406 
7407 /* Check the global error status. */
7408    if( !astOK ) return;
7409 
7410 /* Get a pointer to the structure holding thread-specific global data. */
7411    astGET_GLOBALS(this);
7412 
7413 /* Store the object class. */
7414    class = astGetClass( this );
7415 
7416 /* On the first invocation only, determine the number of characters
7417    being used to encode sequence number information and save this
7418    value. */
7419    if( createkeyword_seq_nchars < 0 ) createkeyword_seq_nchars = (int) strlen( seq_chars );
7420 
7421 /* Copy the name supplied into the output array, converting to upper
7422    case. Leave space for two characters to encode a sequence
7423    number. Terminate the resulting string. */
7424    for( nc = 0; ( nc < ( FITSNAMLEN - 2 ) ) && name[ nc ]; nc++ ) {
7425       keyword[ nc ] = toupper( name[ nc ] );
7426    }
7427    keyword[ nc ] = '\0';
7428 
7429 /* We now search the list of sequence numbers already allocated to
7430    find the next one to use for this keyword. */
7431    if( this->keyseq ) {
7432       found = astMapGet0I( this->keyseq, keyword, &seq );
7433    } else {
7434       found = 0;
7435       this->keyseq = astKeyMap( " ", status );
7436    }
7437 
7438 /* If the keyword was not found in the list, create a new list entry
7439    to describe it. */
7440    if( !found ) seq = 0;
7441 
7442 /* If OK, loop to find a new sequence number which results in a FITS
7443    keyword that hasn't already been used to store data in the
7444    FitsChan. */
7445    if( astOK ) {
7446       while( 1 ) {
7447 
7448 /* Determine if the sequence number just obtained has reached the
7449    upper limit. This is unlikely to happen in practice, but if it
7450    does, we simply re-use this maximum value. Otherwise, we increment
7451    the sequence number last used for this keyword to obtain a new
7452    one. */
7453          limit = ( seq >= ( createkeyword_seq_nchars * createkeyword_seq_nchars - 1 ) );
7454          if( !limit ) seq++;
7455 
7456 /* Encode the sequence number into two characters and append them to
7457    the original keyword (with a terminating null). */
7458          seq_char = seq_chars[ seq / createkeyword_seq_nchars ];
7459          keyword[ nc ] = seq_char;
7460          keyword[ nc + 1 ] = seq_chars[ seq % createkeyword_seq_nchars ];
7461          keyword[ nc + 2 ] = '\0';
7462 
7463 /* If the upper sequence number limit has not been reached, try to
7464    look up the resulting keyword in the FitsChan to see if it has
7465    already been used. Quit searching when a suitable keyword is
7466    found. */
7467          if ( limit || !HasCard( this, keyword, "astWrite", class, status ) ) break;
7468       }
7469 
7470 /* Store the update sequence number in the keymap. The keys into this
7471    keymap are the base keyword name without the appended sequence string, so
7472    temporaily terminate the returned keyword name to exclude the sequence
7473    string. */
7474       keyword[ nc ] = '\0';
7475       astMapPut0I( this->keyseq, keyword, seq, NULL );
7476       keyword[ nc ] = seq_char;
7477    }
7478 }
7479 
DateObs(const char * dateobs,int * status)7480 static double DateObs( const char *dateobs, int *status ) {
7481 /*
7482 *  Name:
7483 *     DateObs
7484 
7485 *  Purpose:
7486 *     Convert a FITS DATE-OBS keyword value to a MJD.
7487 
7488 *  Type:
7489 *     Private function.
7490 
7491 *  Synopsis:
7492 *     #include "fitschan.h"
7493 *     double DateObs( const char *dateobs, int *status )
7494 
7495 *  Class Membership:
7496 *     FitsChan member function.
7497 
7498 *  Description:
7499 *     Extracts the date and time fields from the supplied string and converts
7500 *     them into a modified Julian Date. Supports both old "dd/mm/yy"
7501 *     format, and the new "ccyy-mm-ddThh:mm:ss[.sss...]" format.
7502 
7503 *  Parameters:
7504 *     dateobs
7505 *        Pointer to the DATE-OBS string.
7506 *     status
7507 *        Pointer to the inherited status variable.
7508 
7509 *  Returned Value:
7510 *     The Modified Julian Date corresponding to the supplied DATE-OBS
7511 *     string.
7512 
7513 *  Notes:
7514 *     -  The value AST__BAD is returned (without error) if the supplied
7515 *     string does not conform to the requirements of a FITS DATE-OBS value,
7516 *     or if an error has already occurred.
7517 */
7518 
7519 /* Local Variables: */
7520    double days;               /* The hours, mins and secs as a fraction of a day */
7521    double ret;                /* The returned MJD value */
7522    double secs;               /* The total value of the two seconds fields */
7523    int dd;                    /* The day field from the supplied string */
7524    int fsc;                   /* The fractional seconds field from the supplied string */
7525    int hr;                    /* The hour field from the supplied string */
7526    int j;                     /* SLALIB status */
7527    int len;                   /* The length of the supplied string */
7528    int mm;                    /* The month field from the supplied string */
7529    int mn;                    /* The minute field from the supplied string */
7530    int nc;                    /* Number of characters used */
7531    int ok;                    /* Was the string of a legal format? */
7532    int rem;                   /* The least significant digit in fsc */
7533    int sc;                    /* The whole seconds field from the supplied string */
7534    int yy;                    /* The year field from the supplied string */
7535 
7536 /* Check the global status. */
7537    if( !astOK ) return AST__BAD;
7538 
7539 /* Initialise the returned value. */
7540    ret = AST__BAD;
7541 
7542 /* Save the length of the supplied string. */
7543    len = (int) strlen( dateobs );
7544 
7545 /* Extract the year, month, day, hour, minute, second and fractional
7546    seconds fields from the supplied string. Assume initially that the
7547    string does not match any format. */
7548    ok = 0;
7549 
7550 /* First check for the old "dd/mm/yy" format. */
7551    if( nc = 0,
7552         ( astSscanf( dateobs, " %2d/%2d/%d %n", &dd, &mm, &yy, &nc ) == 3 ) &&
7553         ( nc >= len )  ){
7554       ok = 1;
7555       hr = 0;
7556       mn = 0;
7557       sc = 0;
7558       fsc = 0;
7559 
7560 /* Otherwise, check for the new short format "ccyy-mm-dd". */
7561    } else if( nc = 0,
7562         ( astSscanf( dateobs, " %4d-%2d-%2d %n", &yy, &mm, &dd, &nc ) == 3 ) &&
7563         ( nc >= len )  ){
7564       ok = 1;
7565       hr = 0;
7566       mn = 0;
7567       sc = 0;
7568       fsc = 0;
7569 
7570 /* Otherwise, check for the new format "ccyy-mm-ddThh:mm:ss" without a
7571    fractional seconds field or the trailing Z. */
7572    } else if( nc = 0,
7573         ( astSscanf( dateobs, " %4d-%2d-%2dT%2d:%2d:%2d %n", &yy, &mm, &dd,
7574                   &hr, &mn, &sc, &nc ) == 6 ) && ( nc >= len )  ){
7575       ok = 1;
7576       fsc = 0;
7577 
7578 /* Otherwise, check for the new format "ccyy-mm-ddThh:mm:ss.sss" with a
7579    fractional seconds field but without the trailing Z. */
7580    } else if( nc = 0,
7581         ( astSscanf( dateobs, " %4d-%2d-%2dT%2d:%2d:%2d.%d %n", &yy, &mm, &dd,
7582                   &hr, &mn, &sc, &fsc, &nc ) == 7 ) && ( nc >= len )  ){
7583       ok = 1;
7584 
7585 /* Otherwise, check for the new format "ccyy-mm-ddThh:mm:ssZ" without a
7586    fractional seconds field but with the trailing Z. */
7587    } else if( nc = 0,
7588         ( astSscanf( dateobs, " %4d-%2d-%2dT%2d:%2d:%2dZ %n", &yy, &mm, &dd,
7589                   &hr, &mn, &sc, &nc ) == 6 ) && ( nc >= len )  ){
7590       ok = 1;
7591       fsc = 0;
7592 
7593 /* Otherwise, check for the new format "ccyy-mm-ddThh:mm:ss.sssZ" with a
7594    fractional seconds field and the trailing Z. */
7595    } else if( nc = 0,
7596         ( astSscanf( dateobs, " %4d-%2d-%2dT%2d:%2d:%2d.%dZ %n", &yy, &mm, &dd,
7597                   &hr, &mn, &sc, &fsc, &nc ) == 7 ) && ( nc >= len )  ){
7598       ok = 1;
7599    }
7600 
7601 /* If the supplied string was legal, create a MJD from the separate fields. */
7602    if( ok ) {
7603 
7604 /* Get the MJD at the start of the day. */
7605       palCaldj( yy, mm, dd, &ret, &j );
7606 
7607 /* If succesful, convert the hours, minutes and seconds to a fraction of
7608     a day, and add it onto the MJD found above. */
7609       if( j == 0 ) {
7610 
7611 /* Obtain a floating point representation of the fractional seconds
7612    field. */
7613          secs = 0.0;
7614          while ( fsc > 0 ) {
7615              rem = ( fsc % 10  );
7616              fsc /= 10;
7617              secs = 0.1 * ( secs + (double) rem );
7618          }
7619 
7620 /* Add on the whole seconds field. */
7621          secs += (double) sc;
7622 
7623 /*Convert the hours, minutes and seconds to a fractional day. */
7624          palDtf2d( hr, mn, secs, &days, &j );
7625 
7626 /* If succesful, add this onto the returned MJD. */
7627          if( j == 0 ) {
7628             ret = ret + days;
7629 
7630 /* If the conversion to MJD failed, return AST__BAD. */
7631          } else {
7632             ret = AST__BAD;
7633          }
7634       } else {
7635          ret = AST__BAD;
7636       }
7637    }
7638 
7639 /* Return the result. */
7640    return ret;
7641 }
7642 
DeleteCard(AstFitsChan * this,const char * method,const char * class,int * status)7643 static void DeleteCard( AstFitsChan *this, const char *method,
7644                         const char *class, int *status ){
7645 /*
7646 *  Name:
7647 *     DeleteCard
7648 
7649 *  Purpose:
7650 *     Delete the current card from a FitsChan.
7651 
7652 *  Type:
7653 *     Private function.
7654 
7655 *  Synopsis:
7656 *     #include "fitschan.h"
7657 *     void DeleteCard( AstFitsChan *this, const char *method,
7658 *                      const char *class )
7659 
7660 *  Class Membership:
7661 *     FitsChan member function.
7662 
7663 *  Description:
7664 *     The current card is removed from the circular linked list of structures
7665 *     stored in the supplied FitsChan, and the memory used to store the
7666 *     structure is then freed.
7667 
7668 *  Parameters:
7669 *     this
7670 *        Pointer to the FitsChan containing the list.
7671 *     method
7672 *        Name of calling method.
7673 *     class
7674 *        Object class.
7675 
7676 *  Notes:
7677 *     -  This function returns without action if the FitsChan is
7678 *     currently at "end-of-file".
7679 *     -  The next card becomes the current card.
7680 *     -  This function attempts to execute even if an error has occurred.
7681 */
7682 
7683 /* Local Variables: */
7684    FitsCard *card;            /* Pointer to the current card */
7685    FitsCard *next;            /* Pointer to next card in list */
7686    FitsCard *prev;            /* Pointer to previous card in list */
7687 
7688 /* Return if the supplied object or current card is NULL. */
7689    if( !this || !this->card ) return;
7690 
7691 /* Get a pointer to the card to be deleted (the current card). */
7692    card = (FitsCard *) this->card;
7693 
7694 /* Remove it from the KeyMap holding all keywords. */
7695    astMapRemove( this->keywords, card->name );
7696 
7697 /* Move the current card on to the next card. */
7698    MoveCard( this, 1, method, class, status );
7699 
7700 /* Save pointers to the previous and next cards in the list. */
7701    prev = GetLink( card, PREVIOUS, method, class, status );
7702    next = GetLink( card, NEXT, method, class, status );
7703 
7704 /* If the backwards link points back to the supplied card, then it must
7705    be the only one left on the list. */
7706    if( prev == card ) prev = NULL;
7707    if( next == card ) next = NULL;
7708 
7709 /* If the list head is to be deleted, store a value for the new list
7710    head. */
7711    if( this->head == (void *) card ) this->head = (void *) next;
7712 
7713 /* Free the memory used to hold the data value. */
7714    (void) astFree( card->data );
7715 
7716 /* Free the memory used to hold any comment. */
7717    if( card->comment ) (void) astFree( (void *) card->comment );
7718 
7719 /* Free the memory used to hold the whole structure. */
7720    (void) astFree( (void *) card );
7721 
7722 /* Fix up the links between the two adjacent cards in the list, unless the
7723    supplied card was the last one in the list. */
7724    if( prev && next ){
7725       next->prev = prev;
7726       prev->next = next;
7727    } else {
7728       this->head = NULL;
7729       this->card = NULL;
7730    }
7731 
7732 /* Return. */
7733    return;
7734 }
7735 
DelFits(AstFitsChan * this,int * status)7736 static void DelFits( AstFitsChan *this, int *status ){
7737 
7738 /*
7739 *++
7740 *  Name:
7741 c     astDelFits
7742 f     AST_DELFITS
7743 
7744 *  Purpose:
7745 *     Delete the current FITS card in a FitsChan.
7746 
7747 *  Type:
7748 *     Public virtual function.
7749 
7750 *  Synopsis:
7751 c     #include "fitschan.h"
7752 c     void astDelFits( AstFitsChan *this )
7753 f     CALL AST_DELFITS( THIS, STATUS )
7754 
7755 *  Class Membership:
7756 *     FitsChan method.
7757 
7758 *  Description:
7759 c     This function deletes the current FITS card from a FitsChan. The
7760 f     This routine deletes the current FITS card from a FitsChan. The
7761 *     current card may be selected using the Card attribute (if its index
7762 c     is known) or by using astFindFits (if only the FITS keyword is
7763 f     is known) or by using AST_FINDFITS (if only the FITS keyword is
7764 *     known).
7765 *
7766 *     After deletion, the following card becomes the current card.
7767 
7768 *  Parameters:
7769 c     this
7770 f     THIS = INTEGER (Given)
7771 *        Pointer to the FitsChan.
7772 f     STATUS = INTEGER (Given and Returned)
7773 f        The global status.
7774 
7775 *  Notes:
7776 *     - This function returns without action if the FitsChan is
7777 *     initially positioned at the "end-of-file" (i.e. if the Card
7778 *     attribute exceeds the number of cards in the FitsChan).
7779 *     - If there are no subsequent cards in the FitsChan, then the
7780 *     Card attribute is left pointing at the "end-of-file" after
7781 *     deletion (i.e. is set to one more than the number of cards in
7782 *     the FitsChan).
7783 *--
7784 */
7785 
7786 /* Check the global error status. */
7787    if ( !astOK ) return;
7788 
7789 /* Ensure the source function has been called */
7790    ReadFromSource( this, status );
7791 
7792 /* Delete the current card. The next card will be made the current card. */
7793    DeleteCard( this, "astDelFits", astGetClass( this ), status );
7794 }
7795 
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)7796 static void DistortMaps( AstFitsChan *this, FitsStore *store, char s,
7797                          int naxes, AstMapping **map1, AstMapping **map2,
7798                          AstMapping **map3, AstMapping **map4,
7799                          const char *method, const char *class, int *status ){
7800 /*
7801 *  Name:
7802 *     DistortMap
7803 
7804 *  Purpose:
7805 *     Create a Mapping representing a FITS-WCS Paper IV distortion code.
7806 
7807 *  Type:
7808 *     Private function.
7809 
7810 *  Synopsis:
7811 *     void DistortMaps( AstFitsChan *this, FitsStore *store, char s,
7812 *                       int naxes, AstMapping **map1, AstMapping **map2,
7813 *                       AstMapping **map3, AstMapping **map4,
7814 *                       const char *method, const char *class )
7815 
7816 *  Class Membership:
7817 *     FitsChan
7818 
7819 *  Description:
7820 *     This function checks the CTYPE keywords in the supplied FitsStore to see
7821 *     if they contain a known distortion code (following the syntax described
7822 *     in FITS-WCS paper IV). If so, Mappings are returned which represent the
7823 *     distortions to be applied at each stage in the pixel->IWC chain. If
7824 *     any distortion codes are found in the FitsStore CTYPE values, whether
7825 *     recognised or not, the CTYPE values in the FitsStore are modified to
7826 *     remove the distortion code. Warnings about any unknown or inappropriate
7827 *     distortion codes are added to the FitsChan.
7828 
7829 *  Parameters:
7830 *     this
7831 *        The FitsChan. ASTWARN cards may be added to this FitsChan if any
7832 *        anomalies are found in the keyword values in the FitsStore.
7833 *     store
7834 *        A structure containing information about the requested axis
7835 *        descriptions derived from a FITS header.
7836 *     s
7837 *        A character identifying the co-ordinate version to use. A space
7838 *        means use primary axis descriptions. Otherwise, it must be an
7839 *        upper-case alphabetical characters ('A' to 'Z').
7840 *     naxes
7841 *        The number of intermediate world coordinate axes (WCSAXES).
7842 *     map1
7843 *        Address of a location at which to store a pointer to a Mapping
7844 *        which describes any distortion to be applied to pixel
7845 *        coordinates, prior to performing the translation specified by the
7846 *        CRPIXj keywords. NULL is returned if no distortion is necessary.
7847 *     map2
7848 *        Address of a location at which to store a pointer to a Mapping
7849 *        which describes any distortion to be applied to translated pixel
7850 *        coordinates, prior to performing the PC matrix multiplication.
7851 *        NULL is returned if no distortion is necessary.
7852 *     map3
7853 *        Address of a location at which to store a pointer to a Mapping
7854 *        which describes any distortion to be applied to unscaled IWC
7855 *        coordinates, prior to performing the CDELT matrix multiplication.
7856 *        NULL is returned if no distortion is necessary.
7857 *     map4
7858 *        Address of a location at which to store a pointer to a Mapping
7859 *        which describes any distortion to be applied to scaled IWC
7860 *        coordinates, after performing the CDELT matrix multiplication.
7861 *        NULL is returned if no distortion is necessary.
7862 *     method
7863 *        A pointer to a string holding the name of the calling method.
7864 *        This is used only in the construction of error messages.
7865 *     class
7866 *        A pointer to a string holding the class of the object being
7867 *        read. This is used only in the construction of error messages.
7868 */
7869 
7870 /* Local Variables: */
7871    AstMapping *tmap1;        /* Mapping pointer */
7872    AstMapping *tmap2;        /* Mapping pointer */
7873    char *ctype;              /* Pointer to CTYPE value */
7874    char code[ 4 ];           /* Projection code extracted from CTYPE */
7875    char dist[ 4 ];           /* Distortion code extracted from CTYPE */
7876    char msgbuf[ 250 ];       /* Buffer for warning message */
7877    char type[ 5 ];           /* Axis type extracted from CTYPE */
7878    double *dim;              /* Array holding array dimensions */
7879    int found_axes[ 2 ];      /* Index of axes with the distortion code */
7880    int i;                    /* FITS axis index */
7881    int nc;                   /* No. of characters in CTYPE without "-SIP" */
7882    int nfound;               /* No. of axes with the distortion code */
7883    int warned;               /* Have any ASTWARN cards been issued? */
7884 
7885 /* Initialise pointers to the returned Mappings. */
7886    *map1 = NULL;
7887    *map2 = NULL;
7888    *map3 = NULL;
7889    *map4 = NULL;
7890 
7891 /* Check the global status. */
7892    if ( !astOK ) return;
7893 
7894 /* Allocate memory to hold the image dimensions. */
7895    dim = (double *) astMalloc( sizeof(double)*naxes );
7896    if( dim ){
7897 
7898 /* Note the image dimensions, if known. If not, store AST__BAD values. */
7899       for( i = 0; i < naxes; i++ ){
7900          if( !astGetFitsF( this, FormatKey( "NAXIS", i + 1, -1, ' ', status ),
7901                            dim + i ) ) dim[ i ] = AST__BAD;
7902       }
7903 
7904 /* First check each known distortion type... */
7905 
7906 /* "-SIP": Spitzer (http://irsa.ipac.caltech.edu/data/SPITZER/docs/files/spitzer/shupeADASS.pdf)
7907    ============= */
7908 
7909 /* Spitzer distortion is limited to 2D. Check the first two axes to see if
7910    they have "-SIP" codes at the end of their CTYPE values. If they do,
7911    terminate the ctype string in order to exclude the distortion code (this
7912    is so that later functions do not need to allow for the possibility of a
7913    distortion code being present in the CTYPE value). */
7914       ctype = GetItemC( &(store->ctype), 0, 0, s, NULL, method, class, status );
7915       if( ctype ){
7916          nc = astChrLen( ctype ) - 4;
7917          if( nc >= 0 && !strcmp( ctype + nc, "-SIP" ) ) {
7918             ctype[ nc ] = 0;
7919             ctype = GetItemC( &(store->ctype), 1, 0, s, NULL, method, class, status );
7920             if( ctype ) {
7921                nc = astChrLen( ctype ) - 4;
7922                if( nc >= 0 && !strcmp( ctype + nc, "-SIP" ) ) {
7923                   ctype[ nc ] = 0;
7924 
7925 /* Create a Mapping describing the distortion (other axes are passed
7926    unchanged by this Mapping), and add it in series with the returned map2
7927    (Spitzer distortion is applied to the translated pixel coordinates). */
7928                   tmap1 = SIPMapping( dim, store, s, naxes, method, class, status );
7929                   if( ! *map2 ) {
7930                      *map2 = tmap1;
7931                   } else {
7932                      tmap2 = (AstMapping *) astCmpMap( *map2, tmap1, 1, "", status );
7933                      *map2 = astAnnul( *map2 );
7934                      tmap1 = astAnnul( tmap1 );
7935                      *map2 = tmap2;
7936                   }
7937                }
7938             }
7939          }
7940       }
7941 
7942 /* Check that the "-SIP" code is not included in any axes other than axes
7943    0 and 1. Issue a warning if it is, and remove it. */
7944       warned = 0;
7945       for( i = 2; i < naxes; i++ ){
7946          ctype = GetItemC( &(store->ctype), i, 0, s, NULL, method, class, status );
7947          if( ctype ){
7948             nc = astChrLen( ctype ) - 4;
7949             if( nc >= 0 && !strcmp( ctype + nc, "-SIP" ) ) {
7950                if( !warned ){
7951                   warned = 1;
7952                   sprintf( msgbuf, "The \"-SIP\" distortion code can only be "
7953                            "used on axes 1 and 2, but was found in keyword "
7954                            "%s (='%s'). The distortion will be ignored.",
7955                            FormatKey( "CTYPE", i + 1, -1, ' ', status ),  ctype );
7956                   Warn( this, "distortion", msgbuf, method, class, status );
7957                }
7958                ctype[ nc ] = 0;
7959             }
7960          }
7961       }
7962 
7963 /* "-ZPX": IRAF (http://iraf.noao.edu/projects/ccdmosaic/zpx.html)
7964    ============= */
7965 
7966 /* An IRAF ZPX header uses a ZPX projection within each CTYPE value in place
7967    of the basic ZPN projection. The SpecTrans function converts -ZPX" to
7968    "-ZPN-ZPX" (i.e. a basic projection of ZPN with a distortion code of
7969    "-ZPX"). This function then traps and processes the "-ZPX" distortion
7970    code. */
7971 
7972 /* Look for axes that have the "-ZPX" code in their CTYPE values. If any
7973    are found, check that there are exactly two such axes, and terminate the
7974    ctype strings in order to exclude the distortion code (this is so that
7975    later functions do not need to allow for the possibility of a distortion
7976    code  being present in the CTYPE value)*/
7977       nfound = 0;
7978       for( i = 0; i < naxes; i++ ){
7979          ctype = GetItemC( &(store->ctype), i, 0, s, NULL, method, class, status );
7980          if( ctype && 3 == astSscanf( ctype, "%4s-%3s-%3s", type, code, dist ) ){
7981             if( !strcmp( "ZPX", dist ) ){
7982                if( nfound < 2 ) found_axes[ nfound ] = i;
7983                nfound++;
7984                ctype[ 8 ] = 0;
7985             }
7986          }
7987       }
7988 
7989 /* Issue a warning if more than two ZPX axes were found. */
7990       if( nfound > 2 ) {
7991          Warn( this, "distortion", "More than two axes were found "
7992                "with the \"-ZPX\" projection code. A ZPN projection "
7993                "will be used instead.", method, class, status );
7994 
7995 /* Otherwise, create a Mapping describing the distortion (other axes are passed
7996    unchanged by this Mapping), and add it in series with the returned map4
7997    (ZPX distortion is applied to the translated, rotated, scaled IWC
7998    coordinates). */
7999       } else if( nfound == 2 ){
8000          tmap1 = ZPXMapping( this, store, s, naxes,  found_axes, method,
8001                              class, status );
8002          if( ! *map4 ) {
8003             *map4 = tmap1;
8004          } else {
8005             tmap2 = (AstMapping *) astCmpMap( *map4, tmap1, 1, "", status );
8006             *map4 = astAnnul( *map4 );
8007             tmap1 = astAnnul( tmap1 );
8008             *map4 = tmap2;
8009          }
8010       }
8011 
8012 /* (There are currently no other supported distortion codes.) */
8013 
8014 /* Finally, check all axes looking for any remaining (and therefore
8015    unsupported) distortion codes. Issue a warning about them and remove
8016    them.
8017    =================================================================== */
8018 
8019 /* Indicate that we have not yet issued a warning. */
8020       warned = 0;
8021 
8022 /* Do each IWC axis. */
8023       for( i = 0; i < naxes; i++ ){
8024 
8025 /* Get the CTYPE value for this axis. */
8026          ctype = GetItemC( &(store->ctype), i, 0, s, NULL, method, class, status );
8027          if( ctype ) {
8028 
8029 /* See if has the "4-3-3" form described in FITS-WCS paper IV. */
8030             if( 3 == astSscanf( ctype, "%4s-%3s-%3s", type, code, dist ) ){
8031 
8032 /* Add an ASTWARN card to the FitsChan. Only issue one warning (this avoids
8033    multiple warnings about the same distortion code in multiple CTYPE values). */
8034                if( !warned ){
8035                   warned = 1;
8036                   sprintf( msgbuf, "The header contains CTYPE values (e.g. "
8037                            "%s = '%s') which "
8038                            "include a distortion code \"-%s\". AST "
8039                            "currently ignores this distortion. The code "
8040                            "has been removed from the CTYPE values.",
8041                            FormatKey( "CTYPE", i + 1, -1, ' ', status ),  ctype, dist );
8042                   Warn( this, "distortion", msgbuf, method, class, status );
8043                }
8044 
8045 /* Terminate the CTYPE value in the FitsStore in order to exclude the distortion
8046    code. This means that later functions will not need to take account of
8047    distortion codes. */
8048                ctype[ 8 ] = 0;
8049             }
8050          }
8051       }
8052    }
8053 
8054 /* Free resources. */
8055    dim = astFree( dim );
8056 }
8057 
DSBSetUp(AstFitsChan * this,FitsStore * store,AstDSBSpecFrame * dsb,char s,double crval,const char * method,const char * class,int * status)8058 static void DSBSetUp( AstFitsChan *this, FitsStore *store,
8059                       AstDSBSpecFrame *dsb, char s, double crval,
8060                       const char *method, const char *class, int *status ){
8061 
8062 /*
8063 *  Name:
8064 *     DSBSetUp
8065 
8066 *  Purpose:
8067 *     Modify an AstDSBSpecFrame object to reflect the contents of a FitsStore.
8068 
8069 *  Type:
8070 *     Private function.
8071 
8072 *  Synopsis:
8073 
8074 *     void DSBSetUp( AstFitsChan *this, FitsStore *store,
8075 *                    AstDSBSpecFrame *dsb, char s, double crval,
8076 *                    const char *method, const char *class, int *status  )
8077 
8078 *  Class Membership:
8079 *     FitsChan
8080 
8081 *  Description:
8082 *     This function sets the attributes of the supplied DSBSpecFrame to
8083 *     reflect the values in the supplied FitsStore.
8084 
8085 *  Parameters:
8086 *     this
8087 *        The FitsChan.
8088 *     store
8089 *        A structure containing information about the requested axis
8090 *        descriptions derived from a FITS header.
8091 *     dsb
8092 *        Pointer to the DSBSpecFrame.
8093 *     s
8094 *        Alternate axis code.
8095 *     crval
8096 *        The spectral CRVAL value, in the spectral system represented by
8097 *        the supplied DSBSPecFrame.
8098 *     method
8099 *        Pointer to a string holding the name of the calling method.
8100 *        This is only for use in constructing error messages.
8101 *     class
8102 *        Pointer to a string holding the name of the supplied object class.
8103 *        This is only for use in constructing error messages.
8104 *     status
8105 *        Pointer to the inherited status variable.
8106 
8107 *  Notes:
8108 *     - This implementation follows the conventions of the FITS-CLASS encoding.
8109 */
8110 
8111 /* Local Variables: */
8112    AstDSBSpecFrame *dsb_src; /* New DSBSpecFrame in which StdOfRest is source */
8113    AstDSBSpecFrame *dsb_topo;/* New DSBSpecFrame in which StdOfRest is topo */
8114    AstFrameSet *fs;        /* FrameSet connecting two standards of rest */
8115    double dsbcentre;       /* Topocentric reference (CRVAL) frequency */
8116    double in[2];           /* Source rest and image frequencies */
8117    double lo;              /* Topocentric Local Oscillator frequency */
8118    double out[2];          /* Topocentric rest and image frequencies */
8119 
8120 /* Check the global status. */
8121    if ( !astOK ) return;
8122 
8123 /* In order to determine the topocentric IF, we need the topocentric
8124    frequencies corresponding to the RESTFREQ and IMAGFREQ values in the
8125    FITS header. The values stored in the FITS header are measured in Hz,
8126    in the source's rest frame, so we need a mapping from frequency in the
8127    source rest frame to topocentric frequency. Take a copy of the supplied
8128    DSBSpecFrame and then set its attributes to represent frequency in the
8129    sources rest frame. */
8130    dsb_src = astCopy( dsb );
8131    astSetStdOfRest( dsb_src, AST__SCSOR );
8132    astSetSystem( dsb_src, AST__FREQ );
8133    astSetUnit( dsb_src, 0, "Hz" );
8134 
8135 /* Take a copy of this DSBSpecFrame and set its standard of rest to
8136    topocentric. */
8137    dsb_topo = astCopy( dsb_src );
8138    astSetStdOfRest( dsb_topo, AST__TPSOR );
8139 
8140 /* Now get the Mapping between these. */
8141    fs = astConvert( dsb_src, dsb_topo, "" );
8142    dsb_src = astAnnul( dsb_src );
8143    dsb_topo = astAnnul( dsb_topo );
8144 
8145 /* Check a conversion was found. */
8146    if( fs != NULL ) {
8147 
8148 /* Use this Mapping to transform the rest frequency and the image
8149    frequency from the standard of rest of the source to that of the
8150    observer. */
8151       in[ 0 ] = astGetRestFreq( dsb );
8152       in[ 1 ] = GetItem( &(store->imagfreq), 0, 0, s, NULL, method, class, status );
8153       astTran1( fs, 2, in, 1, out );
8154 
8155 /* The intermediate frequency is half the distance between these two
8156    frequencies. Note, the IF value is signed so as to put the rest
8157    frequency in the observed sideband. */
8158       if( out[ 0 ] != AST__BAD && out[ 1 ] != AST__BAD ) {
8159 
8160 /* Store the spectral CRVAL value as the centre frequency of the
8161    DSBSpecFrame. The public astSetD method interprets the supplied value
8162    as a value in the spectral system described by the other SpecFrame
8163    attributes. */
8164          astSetD( dsb, "DSBCentre", crval );
8165 
8166 /* To calculate the topocentric IF we need the topocentric frequency
8167    equivalent of CRVAL. So take a copy of the DSBSpecFrame, then set it to
8168    represent topocentric frequency, and read back the DSBCentre value. */
8169          dsb_topo = astCopy( dsb );
8170          astSetStdOfRest( dsb_topo, AST__TPSOR );
8171          astSetSystem( dsb_topo, AST__FREQ );
8172          astSetUnit( dsb_topo, 0, "Hz" );
8173          dsbcentre = astGetD( dsb_topo, "DSBCentre" );
8174          dsb_topo = astAnnul( dsb_topo );
8175 
8176 /* We also need the topocentric Local Oscillator frequency. This is
8177    assumed to be half way between the topocentric IMAGFREQ and RESTFREQ
8178    values. */
8179          lo = 0.5*( out[ 1 ] + out[ 0 ] );
8180 
8181 /* Set the IF to be the difference between the Local Oscillator frequency
8182    and the CRVAL frequency. */
8183          astSetIF( dsb, lo - dsbcentre );
8184 
8185 /* Set the DSBSpecFrame to represent the observed sideband */
8186          astSetC( dsb, "SideBand", "observed" );
8187       }
8188 
8189 /* Free resources. */
8190       fs = astAnnul( fs );
8191    }
8192 }
8193 
DSSFromStore(AstFitsChan * this,FitsStore * store,const char * method,const char * class,int * status)8194 static int DSSFromStore( AstFitsChan *this, FitsStore *store,
8195                          const char *method, const char *class, int *status ){
8196 
8197 /*
8198 *  Name:
8199 *     DSSFromStore
8200 
8201 *  Purpose:
8202 *     Store WCS keywords in a FitsChan using DSS encoding.
8203 
8204 *  Type:
8205 *     Private function.
8206 
8207 *  Synopsis:
8208 
8209 *     int DSSFromStore( AstFitsChan *this, FitsStore *store,
8210 *                       const char *method, const char *class, int *status )
8211 
8212 *  Class Membership:
8213 *     FitsChan
8214 
8215 *  Description:
8216 *     A FitsStore is a structure containing a generalised represention of
8217 *     a FITS WCS FrameSet. Functions exist to convert a FitsStore to and
8218 *     from a set of FITS header cards (using a specified encoding), or
8219 *     an AST FrameSet. In other words, a FitsStore is an encoding-
8220 *     independant intermediary staging post between a FITS header and
8221 *     an AST FrameSet.
8222 *
8223 *     This function copies the WCS information stored in the supplied
8224 *     FitsStore into the supplied FitsChan, using DSS encoding.
8225 
8226 *  Parameters:
8227 *     this
8228 *        Pointer to the FitsChan.
8229 *     store
8230 *        Pointer to the FitsStore.
8231 *     method
8232 *        Pointer to a string holding the name of the calling method.
8233 *        This is only for use in constructing error messages.
8234 *     class
8235 *        Pointer to a string holding the name of the supplied object class.
8236 *        This is only for use in constructing error messages.
8237 *     status
8238 *        Pointer to the inherited status variable.
8239 
8240 *  Returned Value:
8241 *     A value of 1 is returned if succesfull, and zero is returned
8242 *     otherwise.
8243 */
8244 
8245 /* Local Variables: */
8246    const char *comm;   /* Pointer to comment string */
8247    char *cval;         /* Pointer to string keyword value */
8248    const char *pltdecsn;/* PLTDECSN keyword value */
8249    double amdx[20];    /* AMDXi keyword value */
8250    double amdy[20];    /* AMDYi keyword value */
8251    double cdelt;       /* CDELT element */
8252    double cnpix1;      /* CNPIX1 keyword value */
8253    double cnpix2;      /* CNPIX2 keyword value */
8254    double pc;          /* PC element */
8255    double pltdecd;     /* PLTDECD keyword value */
8256    double pltdecm;     /* PLTDECM keyword value */
8257    double pltdecs;     /* PLTDECS keyword value */
8258    double pltrah;      /* PLTRAH keyword value */
8259    double pltram;      /* PLTRAM keyword value */
8260    double pltras;      /* PLTRAS keyword value */
8261    double pltscl;      /* PLTSCL keyword value */
8262    double ppo1;        /* PPO1 keyword value */
8263    double ppo2;        /* PPO2 keyword value */
8264    double ppo3;        /* PPO3 keyword value */
8265    double ppo4;        /* PPO4 keyword value */
8266    double ppo5;        /* PPO5 keyword value */
8267    double ppo6;        /* PPO6 keyword value */
8268    double pvx[22];     /* X projection parameter values */
8269    double pvy[22];     /* Y projection parameter values */
8270    double val;         /* General purpose value */
8271    double xpixelsz;    /* XPIXELSZ keyword value */
8272    double ypixelsz;    /* YPIXELSZ keyword value */
8273    int i;              /* Loop count */
8274    int gottpn;         /* Is the projection a "TPN" projection? */
8275    int m;              /* Parameter index */
8276    int ret;            /* Returned value. */
8277 
8278 /* Initialise */
8279    ret = 0;
8280 
8281 /* Check the inherited status. */
8282    if( !astOK ) return ret;
8283 
8284 /* Check the image is 2 dimensional. */
8285    if( GetMaxJM( &(store->crpix), ' ', status ) != 1 ) return ret;
8286 
8287 /* Check the first axis is RA with a TAN or TPN projection. */
8288    cval = GetItemC( &(store->ctype), 0, 0, ' ', NULL, method, class, status );
8289    if( !cval ) return ret;
8290    gottpn = !strcmp( "RA---TPN", cval );
8291    if( strcmp( "RA---TAN", cval ) && !gottpn ) return ret;
8292 
8293 /* Check the second axis is DEC with a TAN or TPN projection. */
8294    cval = GetItemC( &(store->ctype), 1, 0, ' ', NULL, method, class, status );
8295    if( !cval ) return ret;
8296    if( gottpn ) {
8297       if( strcmp( "DEC--TPN", cval ) ) return ret;
8298    } else {
8299       if( strcmp( "DEC--TAN", cval ) ) return ret;
8300    }
8301 
8302 /* Check that LONPOLE is undefined or is 180 degrees. */
8303    val = GetItem( &(store->lonpole), 0, 0, ' ', NULL, method, class, status );
8304    if( val != AST__BAD && val != 180.0 ) return ret;
8305 
8306 /* Check that the RA/DEC system is FK5. */
8307    cval = GetItemC( &(store->radesys), 0, 0, ' ', NULL, method, class, status );
8308    if( !cval || strcmp( "FK5", cval ) ) return ret;
8309 
8310 /* Check that equinox is not defined or is 2000.0 */
8311    val = GetItem( &(store->equinox), 0, 0, ' ', NULL, method, class, status );
8312    if( val != AST__BAD && val != 2000.0 ) return ret;
8313 
8314 /* Get the pixel sizes from the PC/CDELT keywords. They must be defined and
8315    not be zero.  */
8316    cdelt = GetItem( &(store->cdelt), 0, 0, ' ', NULL, method, class, status );
8317    if( cdelt == AST__BAD ) return ret;
8318    pc = GetItem( &(store->pc), 0, 0, ' ', NULL, method, class, status );
8319    if( pc == AST__BAD ) pc = 1.0;
8320    xpixelsz = cdelt*pc;
8321    cdelt = GetItem( &(store->cdelt), 1, 0, ' ', NULL, method, class, status );
8322    if( cdelt == AST__BAD ) return ret;
8323    pc = GetItem( &(store->pc), 1, 1, ' ', NULL, method, class, status );
8324    if( pc == AST__BAD ) pc = 1.0;
8325    ypixelsz = cdelt*pc;
8326    if( xpixelsz == 0.0 || ypixelsz == 0.0 ) return ret;
8327    xpixelsz *= -1000.0;
8328    ypixelsz *= 1000.0;
8329 
8330 /* Check the off-diagonal PC terms are zero. DSS does not allow any rotation. */
8331    val = GetItem( &(store->pc), 0, 1, ' ', NULL, method, class, status );
8332    if( val != AST__BAD && val != 0.0 ) return ret;
8333    val = GetItem( &(store->pc), 1, 0, ' ', NULL, method, class, status );
8334    if( val != AST__BAD && val != 0.0 ) return ret;
8335 
8336 /* Get the required projection parameter values from the store, supplying
8337    appropriate values if a simple TAN projection is being used. */
8338    for( m = 0; m < 22; m++ ){
8339       pvx[ m ] = GetItem( &(store->pv), 0, m, ' ', NULL, method, class, status );
8340       if( pvx[ m ] == AST__BAD || !gottpn ) pvx[ m ] = ( m == 1 ) ? 1.0 : 0.0;
8341       pvy[ m ] = GetItem( &(store->pv), 1, m, ' ', NULL, method, class, status );
8342       if( pvy[ m ] == AST__BAD || !gottpn ) pvy[ m ] = ( m == 1 ) ? 1.0 : 0.0;
8343    }
8344 
8345 /* Check that no other projection parameters have been set. */
8346    if( GetMaxJM( &(store->pv), ' ', status ) > 21 ) return ret;
8347 
8348 /* Check that specific parameters take their required zero value. */
8349    if( pvx[ 3 ] != 0.0 || pvy[ 3 ] != 0.0 ) return ret;
8350    for( m = 11; m < 17; m++ ){
8351       if( pvx[ m ] != 0.0 || pvy[ m ] != 0.0 ) return ret;
8352    }
8353    if( pvx[ 18 ] != 0.0 || pvy[ 18 ] != 0.0 ) return ret;
8354    if( pvx[ 20 ] != 0.0 || pvy[ 20 ] != 0.0 ) return ret;
8355 
8356 /* Check that other projection parameters are related correctly. */
8357    if( !EQUAL( 2*pvx[ 17 ], pvx[ 19 ] ) ) return ret;
8358    if( !EQUAL( pvx[ 17 ], pvx[ 21 ] ) ) return ret;
8359    if( !EQUAL( 2*pvy[ 17 ], pvy[ 19 ] ) ) return ret;
8360    if( !EQUAL( pvy[ 17 ], pvy[ 21 ] ) ) return ret;
8361 
8362 /* Initialise all polynomial co-efficients to zero. */
8363    for( m = 0; m < 20; m++ ){
8364       amdx[ m ] = 0.0;
8365       amdy[ m ] = 0.0;
8366    }
8367 
8368 /* Polynomial co-efficients. There is redundancy here too, so we
8369    arbitrarily choose to leave AMDX/Y7 and AMDX/Y12 set to zero.  */
8370    amdx[ 0 ] = 3600.0*pvx[ 1 ];
8371    amdx[ 1 ] = 3600.0*pvx[ 2 ];
8372    amdx[ 2 ] = 3600.0*pvx[ 0 ];
8373    amdx[ 3 ] = 3600.0*pvx[ 4 ];
8374    amdx[ 4 ] = 3600.0*pvx[ 5 ];
8375    amdx[ 5 ] = 3600.0*pvx[ 6 ];
8376    amdx[ 7 ] = 3600.0*pvx[ 7 ];
8377    amdx[ 8 ] = 3600.0*pvx[ 8 ];
8378    amdx[ 9 ] = 3600.0*pvx[ 9 ];
8379    amdx[ 10 ] = 3600.0*pvx[ 10 ];
8380    amdx[ 12 ] = 3600.0*pvx[ 17 ];
8381    amdy[ 0 ] = 3600.0*pvy[ 1 ];
8382    amdy[ 1 ] = 3600.0*pvy[ 2 ];
8383    amdy[ 2 ] = 3600.0*pvy[ 0 ];
8384    amdy[ 3 ] = 3600.0*pvy[ 4 ];
8385    amdy[ 4 ] = 3600.0*pvy[ 5 ];
8386    amdy[ 5 ] = 3600.0*pvy[ 6 ];
8387    amdy[ 7 ] = 3600.0*pvy[ 7 ];
8388    amdy[ 8 ] = 3600.0*pvy[ 8 ];
8389    amdy[ 9 ] = 3600.0*pvy[ 9 ];
8390    amdy[ 10 ] = 3600.0*pvy[ 10 ];
8391    amdy[ 12 ] = 3600.0*pvy[ 17 ];
8392 
8393 /* The plate scale is the mean of the first X and Y co-efficients. */
8394    pltscl = 0.5*( amdx[ 0 ] + amdy[ 0 ] );
8395 
8396 /* There is redundancy in the DSS encoding. We can choose an arbitrary
8397    pixel corner (CNPIX1, CNPIX2) so long as we use the corresponding origin
8398    for the cartesian co-ordinate system in which the plate centre is
8399    specified (PPO3, PPO6). Arbitrarily set CNPIX1 and CNPIX2 to one. */
8400    cnpix1 = 1.0;
8401    cnpix2 = 1.0;
8402 
8403 /* Find the corresponding plate centre PPO3 and PPO6 (other co-efficients
8404    are set to zero). */
8405    ppo1 = 0.0;
8406    ppo2 = 0.0;
8407    val = GetItem( &(store->crpix), 0, 0, ' ', NULL, method, class, status );
8408    if( val == AST__BAD ) return ret;
8409    ppo3 = xpixelsz*( val + cnpix1 - 0.5 );
8410    ppo4 = 0.0;
8411    ppo5 = 0.0;
8412    val = GetItem( &(store->crpix), 0, 1, ' ', NULL, method, class, status );
8413    if( val == AST__BAD ) return ret;
8414    ppo6 = ypixelsz*( val + cnpix2 - 0.5 );
8415 
8416 /* The reference RA. Get it in degrees. */
8417    val = GetItem( &(store->crval), 0, 0, ' ', NULL, method, class, status );
8418    if( val == AST__BAD ) return ret;
8419 
8420 /* Convert to hours and ensure it is in the range 0 to 24 */
8421    val /= 15.0;
8422    while( val < 0 ) val += 24.0;
8423    while( val >= 24.0 ) val -= 24.0;
8424 
8425 /* Split into hours, mins and seconds. */
8426    pltrah = (int) val;
8427    val = 60.0*( val - pltrah );
8428    pltram = (int) val;
8429    pltras = 60.0*( val - pltram );
8430 
8431 /* The reference DEC. Get it in degrees. */
8432    val = GetItem( &(store->crval), 1, 0, ' ', NULL, method, class, status );
8433    if( val == AST__BAD ) return ret;
8434 
8435 /* Ensure it is in the range -180 to +180 */
8436    while( val < -180.0 ) val += 360.0;
8437    while( val >= 180.0 ) val -= 360.0;
8438 
8439 /* Save the sign. */
8440    if( val > 0.0 ){
8441       pltdecsn = "+";
8442    } else {
8443       pltdecsn = "-";
8444       val = -val;
8445    }
8446 
8447 /* Split into degrees, mins and seconds. */
8448    pltdecd = (int) val;
8449    val = 60.0*( val - pltdecd );
8450    pltdecm = (int) val;
8451    pltdecs = 60.0*( val - pltdecm );
8452 
8453 /* Store the DSS keywords in the FitsChan. */
8454    SetValue( this, "CNPIX1", &cnpix1, AST__FLOAT, "X corner (pixels)", status );
8455    SetValue( this, "CNPIX2", &cnpix2, AST__FLOAT, "Y corner (pixels)", status );
8456    SetValue( this, "PPO1", &ppo1, AST__FLOAT, "Orientation co-efficients", status );
8457    SetValue( this, "PPO2", &ppo2, AST__FLOAT, "", status );
8458    SetValue( this, "PPO3", &ppo3, AST__FLOAT, "", status );
8459    SetValue( this, "PPO4", &ppo4, AST__FLOAT, "", status );
8460    SetValue( this, "PPO5", &ppo5, AST__FLOAT, "", status );
8461    SetValue( this, "PPO6", &ppo6, AST__FLOAT, "", status );
8462    SetValue( this, "XPIXELSZ", &xpixelsz, AST__FLOAT, "X pixel size (microns)", status );
8463    SetValue( this, "YPIXELSZ", &ypixelsz, AST__FLOAT, "Y pixel size (microns)", status );
8464    SetValue( this, "PLTRAH", &pltrah, AST__FLOAT, "RA at plate centre", status );
8465    SetValue( this, "PLTRAM", &pltram, AST__FLOAT, "", status );
8466    SetValue( this, "PLTRAS", &pltras, AST__FLOAT, "", status );
8467    SetValue( this, "PLTDECD", &pltdecd, AST__FLOAT, "DEC at plate centre", status );
8468    SetValue( this, "PLTDECM", &pltdecm, AST__FLOAT, "", status );
8469    SetValue( this, "PLTDECS", &pltdecs, AST__FLOAT, "", status );
8470    SetValue( this, "PLTDECSN", &pltdecsn, AST__STRING, "", status );
8471    SetValue( this, "PLTSCALE", &pltscl, AST__FLOAT, "Plate scale (arcsec/mm)", status );
8472    comm = "Plate solution x co-efficients";
8473    for( i = 0; i < 20; i++ ){
8474       SetValue( this, FormatKey( "AMDX", i + 1, -1, ' ', status ), amdx + i,
8475                 AST__FLOAT, comm, status );
8476       comm = NULL;
8477    }
8478    comm = "Plate solution y co-efficients";
8479    for( i = 0; i < 20; i++ ){
8480       SetValue( this, FormatKey( "AMDY", i + 1, -1, ' ', status ), amdy + i,
8481                 AST__FLOAT, comm, status );
8482       comm = NULL;
8483    }
8484 
8485 /* If no error has occurred, return one. */
8486    if( astOK ) ret = 1;
8487 
8488 /* Return the answer. */
8489    return ret;
8490 }
8491 
DSSToStore(AstFitsChan * this,FitsStore * store,const char * method,const char * class,int * status)8492 static void DSSToStore( AstFitsChan *this, FitsStore *store,
8493                         const char *method, const char *class, int *status ){
8494 
8495 /*
8496 *  Name:
8497 *     DSSToStore
8498 
8499 *  Purpose:
8500 *     Extract WCS information from the supplied FitsChan using a DSS
8501 *     encoding, and store it in the supplied FitsStore.
8502 
8503 *  Type:
8504 *     Private function.
8505 
8506 *  Synopsis:
8507 *     #include "fitschan.h"
8508 
8509 *     void DSSToStore( AstFitsChan *this, FitsStore *store,
8510                        const char *method, const char *class, int *status )
8511 
8512 *  Class Membership:
8513 *     FitsChan member function.
8514 
8515 *  Description:
8516 *     A FitsStore is a structure containing a generalised represention of
8517 *     a FITS WCS FrameSet. Functions exist to convert a FitsStore to and
8518 *     from a set of FITS header cards (using a specified encoding), or
8519 *     an AST FrameSet. In other words, a FitsStore is an encoding-
8520 *     independant intermediary staging post between a FITS header and
8521 *     an AST FrameSet.
8522 *
8523 *     This function extracts DSS keywords from the supplied FitsChan, and
8524 *     stores the corresponding WCS information in the supplied FitsStore.
8525 *     The conversion from DSS encoding to standard WCS encoding is
8526 *     described in an ear;y draft of the Calabretta & Greisen paper
8527 *     "Representations of celestial coordinates in FITS" (A&A, in prep.),
8528 *     and uses the now deprecated "TAN with polynomial corrections",
8529 *     which is still supported by the WcsMap class as type AST__TPN.
8530 *     Here we use "lambda=1" (i.e. plate co-ordinate are measured in mm,
8531 *     not degrees).
8532 *
8533 *     It is assumed that DSS images are 2 dimensional.
8534 
8535 *  Parameters:
8536 *     this
8537 *        Pointer to the FitsChan.
8538 *     store
8539 *        Pointer to the FitsStore structure.
8540 *     method
8541 *        Pointer to a string holding the name of the calling method.
8542 *        This is only for use in constructing error messages.
8543 *     class
8544 *        Pointer to a string holding the name of the supplied object class.
8545 *        This is only for use in constructing error messages.
8546 *     status
8547 *        Pointer to the inherited status variable.
8548 */
8549 
8550 /* Local Variables: */
8551    char *text;         /* Pointer to textual keyword value */
8552    char pltdecsn[11];  /* First 10 non-blank characters from PLTDECSN keyword */
8553    char keyname[10];   /* Buffer for keyword name */
8554    double amdx[20];    /* AMDXi keyword value */
8555    double amdy[20];    /* AMDYi keyword value */
8556    double cnpix1;      /* CNPIX1 keyword value */
8557    double cnpix2;      /* CNPIX2 keyword value */
8558    double crval2;      /* Equivalent CRVAL2 keyword value */
8559    double dummy;       /* Unused keyword value */
8560    double pltdecd;     /* PLTDECD keyword value */
8561    double pltdecm;     /* PLTDECM keyword value */
8562    double pltdecs;     /* PLTDECS keyword value */
8563    double pltrah;      /* PLTRAH keyword value */
8564    double pltram;      /* PLTRAM keyword value */
8565    double pltras;      /* PLTRAS keyword value */
8566    double ppo3;        /* PPO3 keyword value */
8567    double ppo6;        /* PPO6 keyword value */
8568    double pv;          /* Projection parameter value */
8569    double xpixelsz;    /* XPIXELSZ keyword value */
8570    double ypixelsz;    /* YPIXELSZ keyword value */
8571    int i;              /* Loop count */
8572 
8573 /* Check the inherited status. */
8574    if( !astOK ) return;
8575 
8576 /* Get the optional DSS keywords, supplying defaults for any missing keywords. */
8577    cnpix1 = 0.0;
8578    cnpix2 = 0.0;
8579    GetValue( this, "CNPIX1", AST__FLOAT, &cnpix1, 0, 1, method, class, status );
8580    GetValue( this, "CNPIX2", AST__FLOAT, &cnpix2, 0, 1, method, class, status );
8581 
8582 /* Get the required DSS keywords. Report an error if any are missing. */
8583    GetValue( this, "PPO3", AST__FLOAT, &ppo3, 1, 1, method, class, status );
8584    GetValue( this, "PPO6", AST__FLOAT, &ppo6, 1, 1, method, class, status );
8585    GetValue( this, "XPIXELSZ", AST__FLOAT, &xpixelsz, 1, 1, method, class, status );
8586    GetValue( this, "YPIXELSZ", AST__FLOAT, &ypixelsz, 1, 1, method, class, status );
8587    GetValue( this, "PLTRAH", AST__FLOAT, &pltrah, 1, 1, method, class, status );
8588    GetValue( this, "PLTRAM", AST__FLOAT, &pltram, 1, 1, method, class, status );
8589    GetValue( this, "PLTRAS", AST__FLOAT, &pltras, 1, 1, method, class, status );
8590    GetValue( this, "PLTDECD", AST__FLOAT, &pltdecd, 1, 1, method, class, status );
8591    GetValue( this, "PLTDECM", AST__FLOAT, &pltdecm, 1, 1, method, class, status );
8592    GetValue( this, "PLTDECS", AST__FLOAT, &pltdecs, 1, 1, method, class, status );
8593 
8594 /* Copy the first 10 non-blank characters from the PLTDECSN keyword. */
8595    GetValue( this, "PLTDECSN", AST__STRING, &text, 1, 1, method, class, status );
8596    if( astOK ) {
8597       text += strspn( text, " " );
8598       text[ strcspn( text, " " ) ] = 0;
8599       strncpy( pltdecsn, text, 10 );
8600    }
8601 
8602 /* Read other related keywords. We do not need these, but we read them
8603    so that they are not propagated to any output FITS file. */
8604    GetValue( this, "PLTSCALE", AST__FLOAT, &dummy, 0, 1, method, class, status );
8605    GetValue( this, "PPO1", AST__FLOAT, &dummy, 0, 1, method, class, status );
8606    GetValue( this, "PPO2", AST__FLOAT, &dummy, 0, 1, method, class, status );
8607    GetValue( this, "PPO4", AST__FLOAT, &dummy, 0, 1, method, class, status );
8608    GetValue( this, "PPO5", AST__FLOAT, &dummy, 0, 1, method, class, status );
8609 
8610 /* Get the polynomial co-efficients. These can be defaulted if they are
8611    missing, so do not report an error. */
8612    for( i = 0; i < 20; i++ ){
8613       (void) sprintf( keyname, "AMDX%d", i + 1 );
8614       amdx[i] = AST__BAD;
8615       GetValue( this, keyname, AST__FLOAT, amdx + i, 0, 1, method, class, status );
8616       (void) sprintf( keyname, "AMDY%d", i + 1 );
8617       amdy[i] = AST__BAD;
8618       GetValue( this, keyname, AST__FLOAT, amdy + i, 0, 1, method, class, status );
8619    }
8620 
8621 /* Check the above went OK. */
8622    if( astOK ) {
8623 
8624 /* Calculate and store the equivalent PV projection parameters. */
8625       if( amdx[2] != AST__BAD ) {
8626          pv = amdx[2]/3600.0;
8627          SetItem( &(store->pv), 0, 0, ' ', pv, status );
8628       }
8629       if( amdx[0] != AST__BAD ) {
8630          pv = amdx[0]/3600.0;
8631          SetItem( &(store->pv), 0, 1, ' ', pv, status );
8632       }
8633       if( amdx[1] != AST__BAD ) {
8634          pv = amdx[1]/3600.0;
8635          SetItem( &(store->pv), 0, 2, ' ', pv, status );
8636       }
8637       if( amdx[3] != AST__BAD && amdx[6] != AST__BAD ) {
8638          pv = ( amdx[3] + amdx[6] )/3600.0;
8639          SetItem( &(store->pv), 0, 4, ' ', pv, status );
8640       }
8641       if( amdx[4] != AST__BAD ) {
8642          pv = amdx[4]/3600.0;
8643          SetItem( &(store->pv), 0, 5, ' ', pv, status );
8644       }
8645       if( amdx[5] != AST__BAD && amdx[6] != AST__BAD ) {
8646          pv = ( amdx[5] + amdx[6] )/3600.0;
8647          SetItem( &(store->pv), 0, 6, ' ', pv, status );
8648       }
8649       if( amdx[7] != AST__BAD && amdx[11] != AST__BAD ) {
8650          pv = ( amdx[7] + amdx[11] )/3600.0;
8651          SetItem( &(store->pv), 0, 7, ' ', pv, status );
8652       }
8653       if( amdx[8] != AST__BAD ) {
8654          pv = amdx[8]/3600.0;
8655          SetItem( &(store->pv), 0, 8, ' ', pv, status );
8656       }
8657       if( amdx[9] != AST__BAD && amdx[11] != AST__BAD ) {
8658          pv = ( amdx[9] + amdx[11] )/3600.0;
8659          SetItem( &(store->pv), 0, 9, ' ', pv, status );
8660       }
8661       if( amdx[10] != AST__BAD ) {
8662          pv = amdx[10]/3600.0;
8663          SetItem( &(store->pv), 0, 10, ' ', pv, status );
8664       }
8665       if( amdx[12] != AST__BAD ) {
8666          pv = amdx[12]/3600.0;
8667          SetItem( &(store->pv), 0, 17, ' ', pv, status );
8668          SetItem( &(store->pv), 0, 19, ' ', 2*pv, status );
8669          SetItem( &(store->pv), 0, 21, ' ', pv, status );
8670       }
8671       if( amdy[2] != AST__BAD ) {
8672          pv = amdy[2]/3600.0;
8673          SetItem( &(store->pv), 1, 0, ' ', pv, status );
8674       }
8675       if( amdy[0] != AST__BAD ) {
8676          pv = amdy[0]/3600.0;
8677          SetItem( &(store->pv), 1, 1, ' ', pv, status );
8678       }
8679       if( amdy[1] != AST__BAD ) {
8680          pv = amdy[1]/3600.0;
8681          SetItem( &(store->pv), 1, 2, ' ', pv, status );
8682       }
8683       if( amdy[3] != AST__BAD && amdy[6] != AST__BAD ) {
8684          pv = ( amdy[3] + amdy[6] )/3600.0;
8685          SetItem( &(store->pv), 1, 4, ' ', pv, status );
8686       }
8687       if( amdy[4] != AST__BAD ) {
8688          pv = amdy[4]/3600.0;
8689          SetItem( &(store->pv), 1, 5, ' ', pv, status );
8690       }
8691       if( amdy[5] != AST__BAD && amdy[6] != AST__BAD ) {
8692          pv = ( amdy[5] + amdy[6] )/3600.0;
8693          SetItem( &(store->pv), 1, 6, ' ', pv, status );
8694       }
8695       if( amdy[7] != AST__BAD && amdy[11] != AST__BAD ) {
8696          pv = ( amdy[7] + amdy[11] )/3600.0;
8697          SetItem( &(store->pv), 1, 7, ' ', pv, status );
8698       }
8699       if( amdy[8] != AST__BAD ) {
8700          pv = amdy[8]/3600.0;
8701          SetItem( &(store->pv), 1, 8, ' ', pv, status );
8702       }
8703       if( amdy[9] != AST__BAD && amdy[11] != AST__BAD ) {
8704          pv = ( amdy[9] + amdy[11] )/3600.0;
8705          SetItem( &(store->pv), 1, 9, ' ', pv, status );
8706       }
8707       if( amdy[10] != AST__BAD ) {
8708          pv = amdy[10]/3600.0;
8709          SetItem( &(store->pv), 1, 10, ' ', pv, status );
8710       }
8711       if( amdy[12] != AST__BAD ) {
8712          pv = amdy[12]/3600.0;
8713          SetItem( &(store->pv), 1, 17, ' ', pv, status );
8714          SetItem( &(store->pv), 1, 19, ' ', 2*pv, status );
8715          SetItem( &(store->pv), 1, 21, ' ', pv, status );
8716       }
8717 
8718 /* Calculate and store the equivalent CRPIX values. */
8719       if( xpixelsz != 0.0 ) {
8720          SetItem( &(store->crpix), 0, 0, ' ',
8721                   ( ppo3/xpixelsz ) - cnpix1 + 0.5, status );
8722       } else if( astOK ){
8723          astError( AST__BDFTS, "%s(%s): FITS keyword XPIXELSZ has illegal "
8724                    "value 0.0", status, method, class );
8725       }
8726       if( ypixelsz != 0.0 ) {
8727          SetItem( &(store->crpix), 0, 1, ' ',
8728                   ( ppo6/ypixelsz ) - cnpix2 + 0.5, status );
8729       } else if( astOK ){
8730          astError( AST__BDFTS, "%s(%s): FITS keyword YPIXELSZ has illegal "
8731                    "value 0.0", status, method, class );
8732       }
8733 
8734 /* Calculate and store the equivalent CRVAL values. */
8735       SetItem( &(store->crval), 0, 0, ' ',
8736                15.0*( pltrah + pltram/60.0 + pltras/3600.0 ), status );
8737       crval2 = pltdecd + pltdecm/60.0 + pltdecs/3600.0;
8738       if( !strcmp( pltdecsn, "-") ) crval2 = -crval2;
8739       SetItem( &(store->crval), 1, 0, ' ', crval2, status );
8740 
8741 /* Calculate and store the equivalent PC matrix. */
8742       SetItem( &(store->pc), 0, 0, ' ', -0.001*xpixelsz, status );
8743       SetItem( &(store->pc), 1, 1, ' ', 0.001*ypixelsz, status );
8744 
8745 /* Set values of 1.0 for the CDELT values. */
8746       SetItem( &(store->cdelt), 0, 0, ' ', 1.0, status );
8747       SetItem( &(store->cdelt), 1, 0, ' ', 1.0, status );
8748 
8749 /* Store remaining constant items */
8750       SetItem( &(store->lonpole), 0, 0, ' ', 180.0, status );
8751       SetItem( &(store->equinox), 0, 0, ' ', 2000.0, status );
8752       SetItemC( &(store->radesys), 0, 0, ' ', "FK5", status );
8753       SetItem( &(store->wcsaxes), 0, 0, ' ', 2.0, status );
8754       store->naxis = 2;
8755       SetItemC( &(store->ctype), 0, 0, ' ', "RA---TPN", status );
8756       SetItemC( &(store->ctype), 1, 0, ' ', "DEC--TPN", status );
8757    }
8758 }
8759 
EmptyFits(AstFitsChan * this,int * status)8760 static void EmptyFits( AstFitsChan *this, int *status ){
8761 
8762 /*
8763 *++
8764 *  Name:
8765 c     astEmptyFits
8766 f     AST_EMPTYFITS
8767 
8768 *  Purpose:
8769 *     Delete all cards in a FitsChan.
8770 
8771 *  Type:
8772 *     Public virtual function.
8773 
8774 *  Synopsis:
8775 c     #include "fitschan.h"
8776 c     void astEmptyFits( AstFitsChan *this )
8777 f     CALL AST_EMPTYFITS( THIS, STATUS )
8778 
8779 *  Class Membership:
8780 *     FitsChan method.
8781 
8782 *  Description:
8783 c     This function
8784 f     This routine
8785 *     deletes all cards and associated information from a FitsChan.
8786 
8787 *  Parameters:
8788 c     this
8789 f     THIS = INTEGER (Given)
8790 *        Pointer to the FitsChan.
8791 f     STATUS = INTEGER (Given and Returned)
8792 f        The global status.
8793 
8794 *  Notes:
8795 *     - This method simply deletes the cards currently in the FitsChan.
8796 c     Unlike astWriteFits,
8797 f     Unlike AST_WRITEFITS,
8798 *     they are not first written out to the sink function or sink file.
8799 *     - Any Tables or warnings stored in the FitsChan are also deleted.
8800 *     - This method attempt to execute even if an error has occurred
8801 *     previously.
8802 *--
8803 */
8804 
8805 /* Local Variables: */
8806    astDECLARE_GLOBALS         /* Declare the thread specific global data */
8807    const char *class;         /* Pointer to string holding object class */
8808    const char *method;        /* Pointer to string holding calling method */
8809    int old_ignore_used;       /* Original setting of ignore_used variable */
8810 
8811 /* Check a FitsChan was supplied. */
8812    if( !this ) return;
8813 
8814 /* Get a pointer to the structure holding thread-specific global data. */
8815    astGET_GLOBALS(this);
8816 
8817 /* Store the method and class strings. */
8818    method = "astEmpty";
8819    class = astGetClass( this );
8820 
8821 /* Delete all cards from the circular linked list stored in the FitsChan,
8822    starting with the card at the head of the list. */
8823    old_ignore_used = ignore_used;
8824    ignore_used = 0;
8825    astClearCard( this );
8826    while( !astFitsEof( this ) ) DeleteCard( this, method, class, status );
8827    ignore_used = old_ignore_used;
8828 
8829 /* Delete the KeyMap which holds keywords and the latest sequence number
8830    used by each of them. */
8831    if( this->keyseq ) this->keyseq = astAnnul( this->keyseq );
8832 
8833 /* Delete the KeyMap holding the keyword names. */
8834    if( this->keywords ) this->keywords = astAnnul( this->keywords );
8835 
8836 /* Free any memory used to hold the Warnings attribute value. */
8837    this->warnings = astFree( this->warnings );
8838 
8839 /* Other objects in the FitsChan structure. */
8840    if( this->tables ) this->tables = astAnnul( this->tables );
8841 }
8842 
EncodeFloat(char * buf,int digits,int width,int maxwidth,double value,int * status)8843 static int EncodeFloat( char *buf, int digits, int width, int maxwidth,
8844                         double value, int *status ){
8845 /*
8846 *
8847 *  Name:
8848 *     EncodeFloat
8849 
8850 *  Purpose:
8851 *     Formats a floating point value.
8852 
8853 *  Type:
8854 *     Private function.
8855 
8856 *  Synopsis:
8857 *     #include "fitschan.h"
8858 *     int EncodeFloat( char *buf, int digits, int width, int maxwidth,
8859 *                      double value, int *status )
8860 
8861 *  Class Membership:
8862 *     FitsChan method.
8863 
8864 *  Description:
8865 *     This function formats the value using a G format specified in order
8866 *     to use the minimum field width (trailing zeros are not printed).
8867 *     However, the G specifier does not include a decimal point unless it
8868 *     is necessary. FITS requires that floating point values always include
8869 *     a decimal point, so this function inserts one, if necessary.
8870 
8871 *  Parameters:
8872 *     buf
8873 *        A character string into which the value is written.
8874 *     digits
8875 *        The number of digits after the decimal point. If the supplied value
8876 *        is negative, the number of digits actually used may be reduced if
8877 *        the string would otherwise extend beyond the number of columns
8878 *        allowed by the FITS standard. If the value is positive, the
8879 *        specified number of digits are always produced, even if it means
8880 *        breaking the FITS standard.
8881 *     width
8882 *        The minimum field width to use. The value is right justified in
8883 *        this field width.
8884 *     maxwidth
8885 *        The maximum field width to use. A value of zero is returned if
8886 *        the maximum field width is exceeded.
8887 *     value
8888 *        The value to format.
8889 *     status
8890 *        Pointer to the inherited status variable.
8891 
8892 *  Returned Value:
8893 *     The field width actually used, or zero if the value could not be
8894 *     formatted. This does not include the trailing null character.
8895 
8896 *  Notes:
8897 *     -  If there is room, a trailing zero is also added following the
8898 *     inserted decimal point.
8899 */
8900 
8901 /* Local Variables: */
8902    char *c;
8903    char *w, *r;
8904    int i;
8905    int ldigits;
8906    int n;
8907    int ret;
8908 
8909 /* Check the global error status. */
8910    if ( !astOK ) return 0;
8911 
8912 /* The supplied value of "digits" may be negative. Obtain the positive
8913    value giving the initial number of decimal digits to use. */
8914    ldigits = ( digits > 0 ) ? digits : -digits;
8915 
8916 /* Loop until a suitably encoded value has been obtained. */
8917    while( 1 ){
8918 
8919 /* Write the value into the buffer.  Most are formatted with a G specifier.
8920    This will result in values between  -0.001 and -0.0001 being formatted
8921    without an exponent, and thus occupying (ldigits+6) characters. With
8922    an exponent, these values would be formatted in (ldigits+5) characters
8923    thus saving one character. This is important because the default value
8924    of ldigits is 15, resulting in 21 characters being used by the G
8925    specifier. This is one more than the maximum allowed by the FITS
8926    standard. Using an exponent instead would result in 20 characters
8927    being used without any loss of precision, thus staying within the FITS
8928    limit. Note, the precision used with the E specifier is one less than
8929    with the G specifier because the digit to the left of the decimal place
8930    is significant with the E specifier, and so we only need (ldigits-1)
8931    significant digits to the right of the decimal point. */
8932       if( value > -0.001 && value < -0.0001 ) {
8933          (void) sprintf( buf, "%*.*E", width, ldigits - 1, value );
8934       } else {
8935          (void) sprintf( buf, "%*.*G", width, ldigits, value );
8936       }
8937 
8938 /* Check that the value zero is not encoded with a minus sign (e.g. "-0.").
8939    This also rounds out long sequences of zeros or nines.  */
8940       CheckZero( buf, value, width, status );
8941 
8942 /* If the formatted value includes an exponent, it will have 2 digits.
8943    If the exponent includes a leading zero, remove it. */
8944       if( ( w = strstr( buf, "E-0" ) ) ) {
8945          w += 2;
8946       } else if( ( w = strstr( buf, "E+0" ) ) ){
8947          w += 2;
8948       } else if( ( w = strstr( buf, "E0" ) ) ){
8949          w += 1;
8950       }
8951 
8952 /* If a leading zero was found, shuffle everything down from the start of
8953    the string by one character, over-writing the redundant zero, and insert
8954    a space at the start of the string. */
8955       if( w ) {
8956          r = w - 1 ;
8957          while( w != buf ) *(w--) = *(r--);
8958          *w = ' ';
8959       }
8960 
8961 /* If the used field width was too large, reduce it and try again, so
8962    long as we are allowed to change the number of digits being used. */
8963       ret = strlen( buf );
8964       if( ret > width && digits < 0 ){
8965          ldigits -= ( ret - width );
8966 
8967 /* Otherwise leave the loop. Return zero field width if the maximum field
8968    width was exceeded. */
8969       } else {
8970          if( ret > maxwidth ) ret = 0;
8971          break;
8972       }
8973    }
8974 
8975 /* If a formatted value was obtained, we need to ensure that the it includes
8976    a decimal point. */
8977    if( ret ){
8978 
8979 /* Get a pointer to the first digit in the buffer. */
8980       c = strpbrk( buf, "0123456789" );
8981 
8982 /* Something funny is going on if there are no digits in the buffer,
8983    so return a zero field width. */
8984       if( !c ){
8985          ret = 0;
8986 
8987 /* Otherwise... */
8988       } else {
8989 
8990 /* Find the number of digits following and including the first digit. */
8991          n = strspn( c, "0123456789" );
8992 
8993 /* If the first non-digit character is a decimal point, do nothing. */
8994          if( c[ n ] != '.' ){
8995 
8996 /* If there are two or more leading spaces, move the start of the string
8997    two character to the left, and insert ".0" in the gap created. This
8998    keeps the field right justified within the desired field width. */
8999             if( buf[ 0 ] == ' ' && buf[ 1 ] == ' ' ){
9000                for( i = 2; i < c - buf + n; i++ ) buf[ i - 2 ] = buf[ i ];
9001                c[ n - 2 ] = '.';
9002                c[ n - 1 ] = '0';
9003 
9004 /* If there is just one leading space, move the start of the string
9005    one character to the left, and insert "." in the gap created. This
9006    keeps the field right justified within the desired field width. */
9007             } else if( buf[ 0 ] == ' ' ){
9008                for( i = 0; i < n; i++ ) c[ i - 1 ] = c[ i ];
9009                c[ n - 1 ] = '.';
9010 
9011 /* If there are no leading spaces we need to move the end of the string
9012    to the right. This will result in the string no longer being right
9013    justified in the required field width. Return zero if there is
9014    insufficient room for an extra character. */
9015             } else {
9016                ret++;
9017                if( ret > maxwidth ){
9018                   ret = 0;
9019 
9020 /* Otherwise, more the end of the string one place to the right and insert
9021    the decimal point. */
9022                } else {
9023                   for( i = strlen( c ); i >= n; i-- ) c[ i + 1 ] = c[ i ];
9024                   c[ n ] = '.';
9025                }
9026             }
9027          }
9028       }
9029    }
9030 
9031 /* Return the field width. */
9032    return ret;
9033 }
9034 
EncodeValue(AstFitsChan * this,char * buf,int col,int digits,const char * method,int * status)9035 static int EncodeValue( AstFitsChan *this, char *buf, int col, int digits,
9036                         const char *method, int *status ){
9037 
9038 /*
9039 *  Name:
9040 *     EncodeValue
9041 
9042 *  Purpose:
9043 *     Encode the current card's keyword value into a string.
9044 
9045 *  Type:
9046 *     Private function.
9047 
9048 *  Synopsis:
9049 *     #include "fitschan.h"
9050 
9051 *     int EncodeValue( AstFitsChan *this, char *buf, int col, int digits,
9052 *                      const char *method, int *status )
9053 
9054 *  Class Membership:
9055 *     FitsChan member function.
9056 
9057 *  Description:
9058 *     This function encodes the keyword value defined in the current card
9059 *     of the supplied FitsChan and stores it at the start of the supplied
9060 *     buffer. The number of characters placed in the buffer is returned
9061 *     (not including a terminating null).
9062 
9063 *  Parameters:
9064 *     this
9065 *        Pointer to the FitsChan.
9066 *     buf
9067 *        The buffer to receive the formatted value. This should be at least
9068 *        70 characters long.
9069 *     col
9070 *        The column number within the FITS header card corresponding to the
9071 *        start of "buf".
9072 *     digits
9073 *        The number of digits to use when formatting floating point
9074 *        values. If the supplied value is negative, the number of digits
9075 *        actually used may be reduced if the string would otherwise extend
9076 *        beyond the number of columns allowed by the FITS standard. If the
9077 *        value is positive, the specified number of digits are always
9078 *        produced, even if it means breaking the FITS standard.
9079 *     method
9080 *        Pointer to a string holding the name of the calling method.
9081 *        This is only for use in constructing error messages.
9082 *     status
9083 *        Pointer to the inherited status variable.
9084 
9085 *  Returned Value:
9086 *     The number of columns used by the encoded value.
9087 
9088 *  Notes:
9089 *     -  The function returns 0 if an error has already occurred
9090 *     or if an error occurs for any reason within this function.
9091 */
9092 
9093 /* Local Variables: */
9094    char *c;         /* Pointer to next character */
9095    char *name;      /* Pointer to the keyword name */
9096    double dval;     /* Keyword value */
9097    void *data;      /* Pointer to keyword value */
9098    int i;           /* Loop count */
9099    int ilen;        /* Length of imaginary part */
9100    int len;         /* Returned length */
9101    int quote;       /* Quote character found? */
9102    int rlen;        /* Length of real part */
9103    int type;        /* Data type for keyword in current card */
9104 
9105 /* Check the global status. */
9106    if( !astOK ) return 0;
9107 
9108 /* Initialise returned length. */
9109    len = 0;
9110 
9111 /* Get the data type of the keyword. */
9112    type = CardType( this, status );
9113 
9114 /* Get a pointer to the data value in the current card. */
9115    data = CardData( this, NULL, status );
9116 
9117 /* Return if there is no defined value associated with the keyword in the
9118    current card. */
9119    if( type != AST__UNDEF ) {
9120 
9121 /* Get the name of the keyword. */
9122       name = CardName( this, status );
9123 
9124 /* Go through each supported data type (roughly in the order of
9125    decreasing usage)... */
9126 
9127 /* AST__FLOAT - stored internally in a variable of type "double".  Right
9128    justified to column 30 in the header card. */
9129       if( type == AST__FLOAT ){
9130          dval = *( (double *) data );
9131          len = EncodeFloat( buf, digits, FITSRLCOL - FITSNAMLEN - 2,
9132                             AST__FITSCHAN_FITSCARDLEN - col + 1, dval, status );
9133          if( len <= 0 && astOK ) {
9134             astError( AST__BDFTS, "%s(%s): Cannot encode floating point value "
9135                       "%g into a FITS header card for keyword '%s'.", status, method,
9136                       astGetClass( this ), dval, name );
9137          }
9138 
9139 /* AST__STRING & AST__CONTINUE - stored internally in a null terminated array of
9140    type "char".  The encoded string is enclosed in single quotes, starting
9141    at FITS column 11 and ending in at least column 20. Single quotes
9142    in the string are replaced by two adjacent single quotes. */
9143       } else if( type == AST__STRING || type == AST__CONTINUE ){
9144          c = (char *) data;
9145 
9146 /* Enter the opening quote. */
9147          len = 0;
9148          buf[ len++ ] = '\'';
9149 
9150 /* Inspect each character, looking for quotes. */
9151          for ( i = 0; c[ i ]; ) {
9152             quote = ( c[ i ] == '\'' );
9153 
9154 /* If it will not fit into the header card (allowing for doubled
9155    quotes), give up here. */
9156             if ( len + ( quote ? 2 : 1 ) > AST__FITSCHAN_FITSCARDLEN - col ) break;
9157 
9158 /* Otherwise, copy it into the output buffer and double any quotes. */
9159             buf[ len++ ] = c[ i ];
9160             if ( quote ) buf[ len++ ] = '\'';
9161 
9162 /* Look at the next character. */
9163             i++;
9164          }
9165 
9166 /* Pad the string out to the required minimum length with blanks and
9167    add the final quote. */
9168          while( len < FITSSTCOL - col ) buf[ len++ ] = ' ';
9169          buf[ len++ ] = '\'';
9170 
9171 /* Inspect any characters that weren't used. If any are non-blank,
9172    report an error. */
9173          for ( ; c[ i ]; i++ ) {
9174             if ( !isspace( c[ i ] ) ) {
9175                astError( AST__BDFTS,
9176                          "%s(%s): Cannot encode string '%s' into a FITS "
9177                          "header card for keyword '%s'.", status, method, astGetClass( this ),
9178                          (char *) data, name );
9179                break;
9180             }
9181          }
9182 
9183 /* INTEGER - stored internally in a variable of type "int". Right justified
9184    to column 30 in the header card. */
9185       } else if( type == AST__INT ){
9186          len = sprintf(  buf, "%*d", FITSRLCOL - col + 1,
9187                          *( (int *) data ) );
9188          if( len < 0 || len > AST__FITSCHAN_FITSCARDLEN - col ) {
9189             astError( AST__BDFTS, "%s(%s): Cannot encode integer value %d into a "
9190                       "FITS header card for keyword '%s'.", status, method, astGetClass( this ),
9191                       *( (int *) data ), name );
9192          }
9193 
9194 /* LOGICAL - stored internally in a variable of type "int". Represented by
9195    a "T" or "F" in column 30 of the FITS header card. */
9196       } else if( type == AST__LOGICAL ){
9197          for( i = 0; i < FITSRLCOL - col; i++ ) buf[ i ] = ' ';
9198          if( *( (int *) data ) ){
9199             buf[ FITSRLCOL - col ] = 'T';
9200          } else {
9201             buf[ FITSRLCOL - col ] = 'F';
9202          }
9203          len = FITSRLCOL - col + 1;
9204 
9205 /* AST__COMPLEXF - stored internally in an array of two "doubles". The real
9206    part is right justified to FITS column 30. The imaginary part is right
9207    justified to FITS column 50. */
9208       } else if( type == AST__COMPLEXF ){
9209          dval = ( (double *) data )[ 0 ];
9210          rlen = EncodeFloat( buf, digits, FITSRLCOL - FITSNAMLEN - 2,
9211                              AST__FITSCHAN_FITSCARDLEN - col + 1, dval, status );
9212          if( rlen <= 0 || rlen > AST__FITSCHAN_FITSCARDLEN - col ) {
9213             astError( AST__BDFTS, "%s(%s): Cannot encode real part of a complex "
9214                       "floating point value [%g,%g] into a FITS header card "
9215                       "for keyword '%s'.", status, method, astGetClass( this ), dval,
9216                       ( (double *) data )[ 1 ], name );
9217          } else {
9218             dval = ( (double *) data )[ 1 ];
9219             ilen = EncodeFloat( buf + rlen, digits,
9220                                 FITSIMCOL - FITSRLCOL,
9221                                 AST__FITSCHAN_FITSCARDLEN - col - rlen, dval, status );
9222             if( ilen <= 0 ) {
9223                astError( AST__BDFTS, "%s(%s): Cannot encode imaginary part of a "
9224                          "complex floating point value [%g,%g] into a FITS header "
9225                          "card for keyword '%s'.", status, method, astGetClass( this ),
9226                          ( (double *) data )[ 0 ], dval, name );
9227             } else {
9228                len = ilen + rlen;
9229             }
9230          }
9231 
9232 /* AST__COMPLEXI - stored internally in a an array of two "ints". */
9233       } else if( type == AST__COMPLEXI ){
9234          rlen = sprintf(  buf, "%*d", FITSRLCOL - col + 1,
9235                           ( (int *) data )[ 0 ] );
9236          if( rlen < 0 || rlen > AST__FITSCHAN_FITSCARDLEN - col ) {
9237             astError( AST__BDFTS, "%s(%s): Cannot encode real part of a complex "
9238                       "integer value [%d,%d] into a FITS header card "
9239                       "for keyword '%s'.", status, method, astGetClass( this ),
9240                       ( (int *) data )[ 0 ],
9241                       ( (int *) data )[ 1 ], name );
9242          } else {
9243             ilen = sprintf(  buf + rlen, "%*d",  FITSIMCOL - FITSRLCOL + 1,
9244                              ( (int *) data )[ 1 ] );
9245             if( ilen < 0 || ilen > AST__FITSCHAN_FITSCARDLEN - col - rlen ) {
9246                astError( AST__BDFTS, "%s(%s): Cannot encode imaginary part of a "
9247                          "complex integer value [%d,%d] into a FITS header card "
9248                          "for keyword '%s'.", status, method, astGetClass( this ),
9249                          ( (int *) data )[ 0 ],
9250                          ( (int *) data )[ 1 ], name );
9251             } else {
9252                len = ilen + rlen;
9253             }
9254          }
9255 
9256 /* Report an internal (ast) programming error if the keyword is of none of the
9257    above types. */
9258       } else if( astOK ){
9259          astError( AST__INTER, "EncodeValue: AST internal programming error - "
9260                    "FITS %s data-type not yet supported.", status,
9261                    type_names[ type ] );
9262       }
9263    }
9264 
9265 /* If an error has occurred, return zero length. */
9266    if( !astOK ) len = 0;
9267 
9268 /* Return the answer. */
9269    return len;
9270 }
9271 
ExtractGrismMap(AstMapping * map,int iax,AstMapping ** new_map,int * status)9272 static AstGrismMap *ExtractGrismMap( AstMapping *map, int iax,
9273                                      AstMapping **new_map, int *status ){
9274 /*
9275 *  Name:
9276 *     ExtractGrismMap
9277 
9278 *  Purpose:
9279 *     Extract a GrismMap from the end of the supplied Mapping.
9280 
9281 *  Type:
9282 *     Private function.
9283 
9284 *  Synopsis:
9285 *     #include "fitschan.h"
9286 *     AstGrismMap *ExtractGrismMap( AstMapping *map, int iax,
9287 *                                   AstMapping **new_map, int *status )
9288 
9289 *  Class Membership:
9290 *     FitsChan member function.
9291 
9292 *  Description:
9293 *     This function examines the supplied Mapping; if the specified output
9294 *     coordinate of the Mapping is created directly by an un-inverted GrismMap,
9295 *     then a pointer to the GrismMap is returned as the function value. A new
9296 *     Mapping is also returned via parameter "new_map" which is a copy of
9297 *     the supplied Mapping, except that the GrismMap is replaced with a
9298 *     UnitMap. If no GrismMap is found, NULL is returned for both Mappings.
9299 *     The condition that "the specified output coordinate of the Mapping is
9300 *     created directly by an un-inverted GrismMap" means that the output
9301 *     of the GrismMap is no subsequently modified by any further Mappings
9302 *     before being returned as the "iax"th output of the supplied Mapping.
9303 *     This means the GrismMap must be "at the end of" a CmpMap, not in
9304 *     the middle of the CmpMap.
9305 
9306 *  Parameters:
9307 *     map
9308 *        Pointer to the Mapping to check.
9309 *     iax
9310 *        The index for the output coordinate to be checked.
9311 *     new_map
9312 *        Pointer to a location at which to return a pointer to a new
9313 *        Mapping which is a copy of "map" except that the GrismMap is
9314 *        replaced by a UnitMap. NULL is returned if the specified output
9315 *        was not created by a GrismMap.
9316 *     status
9317 *        Pointer to the inherited status variable.
9318 
9319 *  Returned Value:
9320 *     The extracted GrismMap, or NULL if the specified output was not
9321 *     created by a GrismMap.
9322 */
9323 
9324 /* Local Variables: */
9325    AstMapping *mapa;     /* First component Mapping */
9326    AstMapping *mapb;     /* Second component Mapping */
9327    AstMapping *new_mapa; /* Replacement for first component Mapping */
9328    AstMapping *new_mapb; /* Replacement for second component Mapping */
9329    AstGrismMap *ret;     /* Returned GrismMap */
9330    int inva;             /* Invert attribute for mapa within the CmpMap */
9331    int invb;             /* Invert attribute for mapb within the CmpMap */
9332    int na;               /* Number of outputs for mapa */
9333    int old_inva;         /* Current Invert attribute for mapa */
9334    int old_invb;         /* Current Invert attribute for mapb */
9335    int series;           /* Are component Mappings applied in series? */
9336 
9337 /* Initialise */
9338    ret = NULL;
9339    *new_map = NULL;
9340 
9341 /* Check the inherited status. */
9342    if( !astOK ) return ret;
9343 
9344 /* If the supplied Mapping is a GrismMap which has not been inverted,
9345    return it as the function value and return a UnitMap as the new
9346    Mapping. */
9347    if( astIsAGrismMap( map ) ) {
9348       if( !astGetInvert( map ) ) {
9349          ret = astClone( map );
9350          *new_map = (AstMapping *) astUnitMap( 1, "", status );
9351       }
9352 
9353 /* If the supplied Mapping is a CmpMap, get its two component Mappings,
9354    see if they are applied in parallel or series, and get the Invert
9355    attribute values which the component Mappings had at the time the
9356    CmpMap was created. */
9357    } else if(  astIsACmpMap( map ) ) {
9358       astDecompose( map, &mapa, &mapb, &series, &inva, &invb );
9359 
9360 /* Temporaily reset the Invert attributes of the component Mappings back to
9361    the values they had when the CmpMap was created. */
9362       old_inva = astGetInvert( mapa );
9363       old_invb = astGetInvert( mapb );
9364       astSetInvert( mapa, inva );
9365       astSetInvert( mapb, invb );
9366 
9367 /* If the supplied Mapping is a series CmpMap, attempt to extract a
9368    GrismMap from the second component Mapping ("mapb"). The first
9369    component Mapping ("mapa") is unchanged. We do not need to consdier
9370    the first component since we are only interested in GrismMaps which are
9371    at the end of the CmpMap. */
9372       if( series ) {
9373          ret = ExtractGrismMap( mapb, iax, &new_mapb, status );
9374          if( ret ) new_mapa = astClone( mapa );
9375 
9376 /* If the supplied Mapping is a parallel CmpMap, attempt to extract a
9377    GrismMap from the component Mapping which produces output "iax". The
9378    other component Mapping is unchanged. */
9379       } else {
9380          na = astGetNout( mapa );
9381          if( iax < na ) {
9382             ret = ExtractGrismMap( mapa, iax, &new_mapa, status );
9383             if( ret ) new_mapb = astClone( mapb );
9384          } else {
9385             ret = ExtractGrismMap( mapb, iax - na, &new_mapb, status );
9386             if( ret ) new_mapa = astClone( mapa );
9387          }
9388       }
9389 
9390 /* If succesful, create a new CmpMap to return. */
9391       if( ret ) {
9392          *new_map = (AstMapping *) astCmpMap( new_mapa, new_mapb, series, "", status );
9393          new_mapa = astAnnul( new_mapa );
9394          new_mapb = astAnnul( new_mapb );
9395       }
9396 
9397 /* Re-instate the original Invert attributes of the component Mappings. */
9398       astSetInvert( mapa, old_inva );
9399       astSetInvert( mapb, old_invb );
9400 
9401 /* Annul the component Mapping pointers. */
9402       mapa = astAnnul( mapa );
9403       mapb = astAnnul( mapb );
9404    }
9405 
9406 /* Return the result. */
9407    return ret;
9408 }
9409 
MakeBasisVectors(AstMapping * map,int nin,int nout,double * g0,AstPointSet * psetg,AstPointSet * psetw,int * status)9410 static int MakeBasisVectors( AstMapping *map, int nin, int nout,
9411                              double *g0, AstPointSet *psetg,
9412                              AstPointSet *psetw, int *status ){
9413 /*
9414 *  Name:
9415 *     MakeBasisVectors
9416 
9417 *  Purpose:
9418 *     Create a set of basis vectors in grid coordinates
9419 
9420 *  Type:
9421 *     Private function.
9422 
9423 *  Synopsis:
9424 *     #include "fitschan.h"
9425 *     int MakeBasisVectors( AstMapping *map, int nin, int nout,
9426 *                           double *g0, AstPointSet *psetg,
9427 *                           AstPointSet *psetw, int *status )
9428 
9429 *  Class Membership:
9430 *     FitsChan member function.
9431 
9432 *  Description:
9433 *     This function returns a set of unit vectors in grid coordinates,
9434 *     one for each grid axis. Each unit vector is parallel to the
9435 *     corresponding grid axis, and rooted at a specified grid position
9436 *     ("g0"). The IWC coordinates corresponding to "g0" and to the end of
9437 *     each of the unit vectors are also returned, together with a flag
9438 *     indicating if all the IWC coordinate values are good.
9439 
9440 *  Parameters:
9441 *     map
9442 *        A pointer to a Mapping which transforms grid coordinates into
9443 *        intermediate world coordinates (IWC). The number of outputs must
9444 *        be greater than or equal to the number of inputs.
9445 *     nin
9446 *        The number of inputs for "map" (i.e. the number of grid axes).
9447 *     nout
9448 *        The number of outputs for "map" (i.e. the number of IWC axes).
9449 *     g0
9450 *        Pointer to an array of holding the grid coordinates at the
9451 *        "root" position.
9452 *     psetg
9453 *        A pointer to a PointSet which can be used to hold the required
9454 *        grid positions. This should have room for nin+1 positions. On
9455 *        return, the first position holds "g0", and the subsequent "nin"
9456 *        positions hold are offset from "g0" by unit vectors along the
9457 *        corresponding grid axis.
9458 *     psetw
9459 *        A pointer to a PointSet which can be used to hold the required
9460 *        IWC position. This should also have room for nin+1 positions. On
9461 *        return, the values are the IWC coordinates corresponding to the
9462 *        grid positions returned in "psetg".
9463 *     status
9464 *        Pointer to the inherited status variable.
9465 
9466 *  Returned Value:
9467 *     A value of 1 is returned if all the axis values in "psetw" are good.
9468 *     Zero is returned otherwise.
9469 
9470 *  Notes:
9471 *     -  Zero is returned if an error occurs.
9472 */
9473 
9474 /* Local Variables: */
9475    double **ptrg;
9476    double **ptrw;
9477    double *c;
9478    int i;
9479    int ii;
9480    int j;
9481    int ret;
9482 
9483 /* Initialise */
9484    ret = 0;
9485 
9486 /* Check the inherited status. */
9487    if( !astOK ) return ret;
9488 
9489 /* Get pointers to the data in the two supplied PointSets. */
9490    ptrg = astGetPoints( psetg );
9491    ptrw = astGetPoints( psetw );
9492 
9493 /* Check the pointers can be used safely. */
9494    if( astOK ) {
9495 
9496 /* Assume success. */
9497       ret = 1;
9498 
9499 /* Store the required grid positions in PointSet "pset1". The first
9500    position is the supplied root grid position, g0. The next "nin"
9501    positions are offset from the root position by a unit vector along
9502    each grid axis in turn. Store values for each grid axis in turn. */
9503       for( i = 0; i < nin; i++ ) {
9504 
9505 /* Get a pointer to the first axis value for this grid axis. */
9506          c = ptrg[ i ];
9507 
9508 /* Initially set all values for this axis to the supplied root grid value. */
9509          for( ii = 0; ii < nin + 1; ii++ ) c[ ii ] = g0[ i ];
9510 
9511 /* Modify the value corresponding to the vector along this grid axis. */
9512          c[ i + 1 ] += 1.0;
9513       }
9514 
9515 /* Transform these grid positions in IWC positions using the supplied
9516    Mapping. */
9517       (void) astTransform( map, psetg, 1, psetw );
9518 
9519 /* Check that all the transformed positions are good. */
9520       for( j = 0; j < nout; j++ ) {
9521          c = ptrw[ j ];
9522          for( ii = 0; ii < nin + 1; ii++, c++ ) {
9523             if( *c == AST__BAD ) {
9524                ret = 0;
9525                break;
9526             }
9527          }
9528       }
9529    }
9530 
9531 /* Return the result. */
9532    return ret;
9533 }
9534 
FindBasisVectors(AstMapping * map,int nin,int nout,double * dim,AstPointSet * psetg,AstPointSet * psetw,int * status)9535 static int FindBasisVectors( AstMapping *map, int nin, int nout,
9536                              double *dim, AstPointSet *psetg,
9537                              AstPointSet *psetw, int *status ){
9538 /*
9539 *  Name:
9540 *     FindBasisVectors
9541 
9542 *  Purpose:
9543 *     Find the a set of basis vectors in grid coordinates
9544 
9545 *  Type:
9546 *     Private function.
9547 
9548 *  Synopsis:
9549 *     #include "fitschan.h"
9550 *     int FindBasisVectors( AstMapping *map, int nin, int nout,
9551 *                           double *dim, AstPointSet *psetg,
9552 *                           AstPointSet *psetw, int *status )
9553 
9554 *  Class Membership:
9555 *     FitsChan member function.
9556 
9557 *  Description:
9558 *     This function returns a set of unit vectors in grid coordinates,
9559 *     one for each grid axis. Each unit vector is parallel to the
9560 *     corresponding grid axis, and rooted at a specified grid position
9561 *     ("g0"). The IWC coordinates corresponding to "g0" and to the end of
9562 *     each of the unit vectors are also returned, together with a flag
9563 *     indicating if all the IWC coordinate values are good.
9564 
9565 *  Parameters:
9566 *     map
9567 *        A pointer to a Mapping which transforms grid coordinates into
9568 *        intermediate world coordinates (IWC). The number of outputs must
9569 *        be greater than or equal to the number of inputs.
9570 *     nin
9571 *        The number of inputs for "map" (i.e. the number of grid axes).
9572 *     nout
9573 *        The number of outputs for "map" (i.e. the number of IWC axes).
9574 *     dim
9575 *        Array dimensions, in pixels, if known (otherwise supplied a NULL
9576 *        pointer to values of AST__BAD).
9577 *     psetg
9578 *        A pointer to a PointSet which can be used to hold the required
9579 *        grid position. This should have room for nin+1 positions. On
9580 *        return, the first position holds the "root" position and the
9581 *        subsequent "nin" positions hold are offset from root position
9582 *        by unit vectors along the corresponding grid axis.
9583 *     psetw
9584 *        A pointer to a PointSet which can be used to hold the required
9585 *        IWC position. This should also have room for nin+1 positions. On
9586 *        return, the values are the IWC coordinates corresponding to the
9587 *        grid positions returned in "psetg".
9588 *     status
9589 *        Pointer to the inherited status variable.
9590 
9591 *  Returned Value:
9592 *     A value of 1 is returned if a set of basis vectors was found
9593 *     succesfully. Zero is returned otherwise.
9594 
9595 *  Notes:
9596 *     -  Zero is returned if an error occurs.
9597 */
9598 
9599 /* Local Variables: */
9600    double *g0;
9601    double dd;
9602    double ddlim;
9603    int i;
9604    int ii;
9605    int ret;
9606 
9607 /* Initialise */
9608    ret = 0;
9609 
9610 /* Check the inherited status. */
9611    if( !astOK ) return ret;
9612 
9613 /* Allocate an array to store the candidate root position. */
9614    g0 = astMalloc( sizeof( double )*(size_t) nin );
9615    if( astOK ) {
9616 
9617 /* First try the grid centre, if known. */
9618       ddlim = 0;
9619       ret = 0;
9620       if( dim ) {
9621          ret = 1;
9622          for( i = 0; i < nin; i++ ) {
9623             if( dim[ i ] != AST__BAD ) {
9624                g0[ i ] = 0.5*( 1 + dim[ i ] );
9625                if( dim[ i ] > ddlim ) ddlim = dim[ i ];
9626             } else {
9627                ret = 0;
9628                break;
9629             }
9630          }
9631       }
9632       if( ret ) ret = MakeBasisVectors( map, nin, nout, g0, psetg, psetw, status );
9633 
9634 /* If this did not produce a set of good IWC positions, try grid position
9635    (1,1,1...). */
9636       if( !ret ) {
9637          for( i = 0; i < nin; i++ ) g0[ i ] = 1.0;
9638          ret = MakeBasisVectors( map, nin, nout, g0, psetg, psetw, status );
9639       }
9640 
9641 /* If this did not produce a set of good IWC positions, try a sequence of
9642    grid positions which move an increasing distance along each grid axis
9643    from (1,1,1,...). Stop when we get further than "ddlim" from the
9644    origin. */
9645       dd = 10.0;
9646       if( ddlim == 0.0 ) ddlim = 10240.0;
9647       while( !ret && dd <= ddlim ) {
9648 
9649 /* First try positions which extend across the middle of the data set.
9650    If the image dimensions are known, make the line go from the "bottom
9651    left corner" towards the "top right corner", taking the aspect ratio
9652    of the image into account. Otherise, just use a vector of (1,1,1,..) */
9653          for( i = 0; i < nin; i++ ) {
9654             if( dim && dim[ i ] != AST__BAD ) {
9655                g0[ i ] = dd*dim[ i ]/ddlim;
9656             } else {
9657                g0[ i ] = dd;
9658             }
9659          }
9660          ret = MakeBasisVectors( map, nin, nout, g0, psetg, psetw, status );
9661 
9662 /* If the above didn't produce good positions, try moving out along each
9663    grid axis in turn. */
9664          for( ii = 0; !ret && ii < nin; ii++ ) {
9665             for( i = 0; i < nin; i++ ) g0[ i ] = 1.0;
9666             g0[ ii ] = dd;
9667             ret = MakeBasisVectors( map, nin, nout, g0, psetg, psetw, status );
9668          }
9669 
9670 /* Go further out from the origin for the next set of tests (if any). */
9671          dd *= 2.0;
9672       }
9673    }
9674 
9675 /* Free resources. */
9676    g0 = astFree( g0 );
9677 
9678 /* Return the result. */
9679    return ret;
9680 }
9681 
FindLonLatSpecAxes(FitsStore * store,char s,int * axlon,int * axlat,int * axspec,const char * method,const char * class,int * status)9682 static int FindLonLatSpecAxes( FitsStore *store, char s, int *axlon, int *axlat,
9683                            int *axspec, const char *method, const char *class, int *status ) {
9684 /*
9685 *  Name:
9686 *     FindLonLatSpecAxes
9687 
9688 *  Purpose:
9689 *     Search the CTYPE values in a FitsStore for celestial and spectral axes.
9690 
9691 *  Type:
9692 *     Private function.
9693 
9694 *  Synopsis:
9695 *     int FindLonLatSpecAxes( FitsStore *store, char s, int *axlon, int *axlat,
9696 *                             int *axspec, const char *method, const char *class, int *status )
9697 
9698 *  Class Membership:
9699 *     FitsChan
9700 
9701 *  Description:
9702 *     The supplied FitsStore is searched for axes with a specified axis
9703 *     description character which describe celestial longitude or latitude
9704 *     or spectral position.
9705 
9706 *  Parameters:
9707 *     store
9708 *        A structure containing values for FITS keywords relating to
9709 *        the World Coordinate System.
9710 *     s
9711 *        A character identifying the co-ordinate version to use. A space
9712 *        means use primary axis descriptions. Otherwise, it must be an
9713 *        upper-case alphabetical characters ('A' to 'Z').
9714 *     axlon
9715 *        Address of a location at which to return the index of the
9716 *        longitude axis (if found). This is the value of "i" within the
9717 *        keyword name "CTYPEi". A value of -1 is returned if no longitude
9718 *        axis is found.
9719 *     axlat
9720 *        Address of a location at which to return the index of the
9721 *        latitude axis (if found). This is the value of "i" within the
9722 *        keyword name "CTYPEi". A value of -1 is returned if no latitude
9723 *        axis is found.
9724 *     axspec
9725 *        Address of a location at which to return the index of the
9726 *        spectral axis (if found). This is the value of "i" within the
9727 *        keyword name "CTYPEi". A value of -1 is returned if no spectral
9728 *        axis is found.
9729 *     method
9730 *        A pointer to a string holding the name of the calling method.
9731 *        This is used only in the construction of error messages.
9732 *     class
9733 *        A pointer to a string holding the class of the object being
9734 *        read. This is used only in the construction of error messages.
9735 *     status
9736 *        Pointer to the inherited status variable.
9737 
9738 *  Returned Value:
9739 *     One is returned if both celestial axes were found. Zero is returned if
9740 *     either axis was not found. The presence of a spectral axis does not
9741 *     affect the returned value.
9742 
9743 *  Notes:
9744 *     -  If an error occurs, zero is returned.
9745 */
9746 
9747 /* Local Variables: */
9748    char *assys;
9749    char *astype;
9750    char algcode[5];
9751    char stype[5];
9752    const char *ctype;
9753    double dval;
9754    int i;
9755    int wcsaxes;
9756 
9757 /* Initialise */
9758    *axlon = -1;
9759    *axlat = -1;
9760    *axspec = -1;
9761 
9762 /* Check the global status. */
9763    if ( !astOK ) return 0;
9764 
9765 /* Obtain the number of FITS WCS axes in the header. If the WCSAXES header
9766    was specified, use it. Otherwise assume it is the same as the number
9767    of pixel axes. */
9768    dval = GetItem( &(store->wcsaxes), 0, 0, s, NULL, method, class, status );
9769    if( dval != AST__BAD ) {
9770       wcsaxes = (int) dval + 0.5;
9771    } else {
9772       wcsaxes = store->naxis;
9773    }
9774 
9775 /* Loop round the FITS WCS axes, getting each CTYPE value. */
9776    for( i = 0; i < wcsaxes && astOK; i++ ){
9777       ctype = GetItemC( &(store->ctype), i, 0, s, NULL, method, class, status );
9778 
9779 /* Check a value was found. */
9780       if( ctype ) {
9781 
9782 /* First check for spectral axes, either FITS-WCS or AIPS-like. */
9783          if( IsSpectral( ctype, stype, algcode, status ) ||
9784              IsAIPSSpectral( ctype, &astype, &assys, status ) ) {
9785             *axspec = i;
9786 
9787 /* Otherwise look for celestial axes. Celestial axes must have a "-" as the
9788    fifth character in CTYPE. */
9789          } else if( ctype[4] == '-' ) {
9790 
9791 /* See if this is a longitude axis (e.g. if the first 4 characters of CTYPE
9792    are "RA--" or "xLON" or "yzLN" ). */
9793             if( !strncmp( ctype, "RA--", 4 ) ||
9794                 !strncmp( ctype, "AZ--", 4 ) ||
9795                 !strncmp( ctype + 1, "LON", 3 ) ||
9796                 !strncmp( ctype + 2, "LN", 2 ) ){
9797                *axlon = i;
9798 
9799 /* Otherwise see if it is a latitude axis. */
9800             } else if( !strncmp( ctype, "DEC-", 4 ) ||
9801                        !strncmp( ctype, "EL--", 4 ) ||
9802                        !strncmp( ctype + 1, "LAT", 3 ) ||
9803                        !strncmp( ctype + 2, "LT", 2 ) ){
9804                *axlat = i;
9805             }
9806          }
9807       }
9808    }
9809 
9810 /* Indicate failure if an error occurred. */
9811    if( !astOK ) {
9812       *axlon = -1;
9813       *axlat = -1;
9814       *axspec = -1;
9815    }
9816 
9817 /* Return the result. */
9818    return ( *axlat != -1 && *axlon != -1 );
9819 }
9820 
FindWcs(AstFitsChan * this,int last,int all,int rewind,const char * method,const char * class,int * status)9821 static void FindWcs( AstFitsChan *this, int last, int all, int rewind,
9822                      const char *method, const char *class, int *status ){
9823 
9824 /*
9825 *  Name:
9826 *     FindWcs
9827 
9828 *  Purpose:
9829 *     Find the first or last FITS WCS related keyword in a FitsChan.
9830 
9831 *  Type:
9832 *     Private function.
9833 
9834 *  Synopsis:
9835 *     #include "fitschan.h"
9836 
9837 *     void FindWcs( AstFitsChan *this, int last, int all, int rewind,
9838 *                   const char *method, const char *class, int *status )
9839 
9840 *  Class Membership:
9841 *     FitsChan member function.
9842 
9843 *  Description:
9844 *     A search is made through the FitsChan for the first or last card
9845 *     which relates to a FITS WCS keyword (any encoding). If "last" is
9846 *     non-zero, the next card becomes the current card. If "last" is
9847 *     zero, the WCS card is left as the current card. Cards marked as
9848 *     having been read are included or not, as specified by "all".
9849 
9850 *  Parameters:
9851 *     this
9852 *        Pointer to the FitsChan.
9853 *     last
9854 *        If non-zero, the last WCS card is searched for. Otherwise, the
9855 *        first WCS card is searched for.
9856 *     all
9857 *        If non-zero, then cards marked as having been read are included
9858 *        in the search. Otherwise such cards are ignored.
9859 *     rewind
9860 *        Only used if "last" is zero (i.e. the first card is being
9861 *        searched for). If "rewind" is non-zero, then the search starts
9862 *        from the first card in the FitsChan. If zero, the search starts
9863 *        from the current card.
9864 *     method
9865 *        Pointer to a string holding the name of the calling method.
9866 *        This is only for use in constructing error messages.
9867 *     class
9868 *        Pointer to a string holding the name of the supplied object class.
9869 *        This is only for use in constructing error messages.
9870 *     status
9871 *        Pointer to the inherited status variable.
9872 
9873 *  Notes:
9874 *     -  The FitsChan is left at end-of-file if no FITS-WCS keyword cards
9875 *     are found in the FitsChan.
9876 *-
9877 */
9878 
9879 /* Local Variables: */
9880    astDECLARE_GLOBALS       /* Declare the thread specific global data */
9881    const char *keyname;     /* Keyword name from current card */
9882    int nfld;                /* Number of fields in keyword template */
9883    int old_ignore_used;     /* Original value of variable ignore_used */
9884 
9885 /* Check the global status. Also check the FitsChan is not empty. */
9886    if( !astOK || !this->head ) return;
9887 
9888 /* Get a pointer to the structure holding thread-specific global data. */
9889    astGET_GLOBALS(this);
9890 
9891 /* Indicate that we should, or should not, skip over cards marked as having
9892    been read. */
9893    old_ignore_used = ignore_used;
9894    ignore_used = all ? 0 : 1;
9895 
9896 /* If required, set the FitsChan to start or end of file. */
9897    if( last ) {
9898       astSetCard( this, INT_MAX );
9899    } else if( rewind ) {
9900       astClearCard( this );
9901    }
9902 
9903 /* If the current card is marked as used, and we are skipping used cards,
9904    move on to the next unused card */
9905    if( CARDUSED( this->card ) ) MoveCard( this, last?-1:1, method, class, status );
9906 
9907 /* Check each card moving backwards from the end to the start, or
9908    forwards from the start to the end, until a WCS keyword is found,
9909    or the other end of the FitsChan is reached. */
9910    while( astOK ){
9911 
9912 /* Get the keyword name from the current card. */
9913       keyname = CardName( this, status );
9914 
9915 /* Save a pointer to the keyword if it is the first non-null, non-comment
9916    card. */
9917       if( keyname ) {
9918 
9919 /* If it matches any of the WCS keywords, move on one card
9920    and break out of the loop. */
9921          if( Match( keyname, "CRVAL%d%0c", 0, NULL, &nfld, method, class, status ) ||
9922              Match( keyname, "CRPIX%d%0c", 0, NULL, &nfld, method, class, status ) ||
9923              Match( keyname, "CDELT%d%0c", 0, NULL, &nfld, method, class, status ) ||
9924              Match( keyname, "CROTA%d", 0, NULL, &nfld, method, class, status ) ||
9925              Match( keyname, "CTYPE%d%0c", 0, NULL, &nfld, method, class, status ) ||
9926              Match( keyname, "CUNIT%d%0c", 0, NULL, &nfld, method, class, status ) ||
9927              Match( keyname, "PC%3d%3d%0c", 0, NULL, &nfld, method, class, status ) ||
9928              Match( keyname, "CD%3d%3d%0c", 0, NULL, &nfld, method, class, status ) ||
9929              Match( keyname, "CD%1d_%1d%0c", 0, NULL, &nfld, method, class, status ) ||
9930              Match( keyname, "PC%1d_%1d%0c", 0, NULL, &nfld, method, class, status ) ||
9931              Match( keyname, "LONGPOLE", 0, NULL, &nfld, method, class, status ) ||
9932              Match( keyname, "LONPOLE%0c", 0, NULL, &nfld, method, class, status ) ||
9933              Match( keyname, "LATPOLE%0c", 0, NULL, &nfld, method, class, status ) ||
9934              Match( keyname, "PROJP%d", 0, NULL, &nfld, method, class, status ) ||
9935              Match( keyname, "PV%d_%d%0c", 0, NULL, &nfld, method, class, status ) ||
9936              Match( keyname, "PS%d_%d%0c", 0, NULL, &nfld, method, class, status ) ||
9937              Match( keyname, "EPOCH", 0, NULL, &nfld, method, class, status ) ||
9938              Match( keyname, "EQUINOX%0c", 0, NULL, &nfld, method, class, status ) ||
9939              Match( keyname, "MJD-OBS",  0, NULL, &nfld, method, class, status ) ||
9940              Match( keyname, "DATE-OBS", 0, NULL, &nfld, method, class, status ) ||
9941              Match( keyname, "TIMESYS", 0, NULL, &nfld, method, class, status ) ||
9942              Match( keyname, "RADECSYS", 0, NULL, &nfld, method, class, status ) ||
9943              Match( keyname, "RADESYS%0c", 0, NULL, &nfld, method, class, status ) ||
9944              Match( keyname, "C%1dVAL%d", 0, NULL, &nfld, method, class, status ) ||
9945              Match( keyname, "C%1dPIX%d", 0, NULL, &nfld, method, class, status ) ||
9946              Match( keyname, "C%1dELT%d", 0, NULL, &nfld, method, class, status ) ||
9947              Match( keyname, "C%1dYPE%d", 0, NULL, &nfld, method, class, status ) ||
9948              Match( keyname, "C%1dNIT%d", 0, NULL, &nfld, method, class, status ) ||
9949              Match( keyname, "CNPIX1", 0, NULL, &nfld, method, class, status ) ||
9950              Match( keyname, "CNPIX2", 0, NULL, &nfld, method, class, status ) ||
9951              Match( keyname, "PPO%d", 0, NULL, &nfld, method, class, status ) ||
9952              Match( keyname, "AMDX%d", 0, NULL, &nfld, method, class, status ) ||
9953              Match( keyname, "AMDY%d", 0, NULL, &nfld, method, class, status ) ||
9954              Match( keyname, "XPIXELSZ", 0, NULL, &nfld, method, class, status ) ||
9955              Match( keyname, "YPIXELSZ", 0, NULL, &nfld, method, class, status ) ||
9956              Match( keyname, "PLTRAH", 0, NULL, &nfld, method, class, status ) ||
9957              Match( keyname, "PLTRAM", 0, NULL, &nfld, method, class, status ) ||
9958              Match( keyname, "PLTRAS", 0, NULL, &nfld, method, class, status ) ||
9959              Match( keyname, "PLTDECD", 0, NULL, &nfld, method, class, status ) ||
9960              Match( keyname, "PLTDECM", 0, NULL, &nfld, method, class, status ) ||
9961              Match( keyname, "PLTDECS", 0, NULL, &nfld, method, class, status ) ||
9962              Match( keyname, "PLTDECSN", 0, NULL, &nfld, method, class, status ) ||
9963              Match( keyname, "PLTSCALE", 0, NULL, &nfld, method, class, status ) ||
9964              Match( keyname, "PPO1", 0, NULL, &nfld, method, class, status ) ||
9965              Match( keyname, "PPO2", 0, NULL, &nfld, method, class, status ) ||
9966              Match( keyname, "PPO4", 0, NULL, &nfld, method, class, status ) ||
9967              Match( keyname, "PPO5", 0, NULL, &nfld, method, class, status ) ||
9968              Match( keyname, "WCSNAME%0c", 0, NULL, &nfld, method, class, status ) ||
9969              Match( keyname, "SPECSYS%0c", 0, NULL, &nfld, method, class, status ) ||
9970              Match( keyname, "SSYSSRC%0c", 0, NULL, &nfld, method, class, status ) ||
9971              Match( keyname, "ZSOURCE%0c", 0, NULL, &nfld, method, class, status ) ||
9972              Match( keyname, "VELOSYS%0c", 0, NULL, &nfld, method, class, status ) ||
9973              Match( keyname, "RESTFRQ%0c", 0, NULL, &nfld, method, class, status ) ||
9974              Match( keyname, "MJD_AVG%0c", 0, NULL, &nfld, method, class, status ) ||
9975              Match( keyname, "OBSGEO-X", 0, NULL, &nfld, method, class, status ) ||
9976              Match( keyname, "OBSGEO-Y", 0, NULL, &nfld, method, class, status ) ||
9977              Match( keyname, "OBSGEO-Z", 0, NULL, &nfld, method, class, status ) ) {
9978             if( last ) MoveCard( this, 1, method, class, status );
9979             break;
9980          }
9981       }
9982 
9983 /* Leave the FitsChan at end-of-file if no WCS cards were found. */
9984       if( (last && FitsSof( this, status ) ) ||
9985           (!last && astFitsEof( this ) ) ) {
9986          astSetCard( this, INT_MAX );
9987          break;
9988       } else {
9989          MoveCard( this, last?-1:1, method, class, status );
9990       }
9991    }
9992 
9993 /* Re-instate the original flag indicating if cards marked as having been
9994    read should be skipped over. */
9995    ignore_used = old_ignore_used;
9996 
9997 /* Return. */
9998    return;
9999 }
10000 
FindString(int n,const char * list[],const char * test,const char * text,const char * method,const char * class,int * status)10001 static int FindString( int n, const char *list[], const char *test,
10002                        const char *text, const char *method,
10003                        const char *class, int *status ){
10004 /*
10005 *  Name:
10006 *     FindString
10007 
10008 *  Purpose:
10009 *     Find a given string within an array of character strings.
10010 
10011 *  Type:
10012 *     Private function.
10013 
10014 *  Synopsis:
10015 *     #include "fitschan.h"
10016 *     int FindString( int n, const char *list[], const char *test,
10017 *                     const char *text, const char *method, const char *class, int *status )
10018 
10019 *  Class Membership:
10020 *     FitsChan method.
10021 
10022 *  Description:
10023 *     This function identifies a supplied string within a supplied
10024 *     array of valid strings, and returns the index of the string within
10025 *     the array. The test option may not be abbreviated, but case is
10026 *     insignificant.
10027 
10028 *  Parameters:
10029 *     n
10030 *        The number of strings in the array pointed to be "list".
10031 *     list
10032 *        A pointer to an array of legal character strings.
10033 *     test
10034 *        A candidate string.
10035 *     text
10036 *        A string giving a description of the object, parameter,
10037 *        attribute, etc, to which the test value refers.
10038 *        This is only for use in constructing error messages. It should
10039 *        start with a lower case letter.
10040 *     method
10041 *        Pointer to a string holding the name of the calling method.
10042 *        This is only for use in constructing error messages.
10043 *     class
10044 *        Pointer to a string holding the name of the supplied object class.
10045 *        This is only for use in constructing error messages.
10046 *     status
10047 *        Pointer to the inherited status variable.
10048 
10049 *  Returned Value:
10050 *     The index of the identified string within the supplied array, starting
10051 *     at zero.
10052 
10053 *  Notes:
10054 *     -  A value of -1 is returned if an error has already occurred, or
10055 *     if this function should fail for any reason (for instance if the
10056 *     supplied option is not specified in the supplied list).
10057 */
10058 
10059 /* Local Variables: */
10060    int ret;                /* The returned index */
10061 
10062 /* Check global status. */
10063    if( !astOK ) return -1;
10064 
10065 /* Compare the test string with each element of the supplied list. Leave
10066    the loop when a match is found. */
10067    for( ret = 0; ret < n; ret++ ) {
10068       if( !Ustrcmp( test, list[ ret ], status ) ) break;
10069    }
10070 
10071 /* Report an error if the supplied test string does not match any element
10072    in the supplied list. */
10073    if( ret >= n && astOK ) {
10074       astError( AST__RDERR, "%s(%s): Illegal value '%s' supplied for %s.", status,
10075                 method, class, test, text );
10076       ret = -1;
10077    }
10078 
10079 /* Return the answer. */
10080    return ret;
10081 }
10082 
FitOK(int n,double * act,double * est,double tol,int * status)10083 static int FitOK( int n, double *act, double *est, double tol, int *status ) {
10084 /*
10085 *  Name:
10086 *     FitOK
10087 
10088 *  Purpose:
10089 *     See if a fit is usable.
10090 
10091 *  Type:
10092 *     Private function.
10093 
10094 *  Synopsis:
10095 *     #include "fitschan.h"
10096 *     int FitOK( int n, double *act, double *est, double tol, int *status )
10097 
10098 *  Class Membership:
10099 *     FitsChan member function.
10100 
10101 *  Description:
10102 *     This function is supplied with a set of actual data values, and the
10103 *     corresponding values estimated by some fitting process. It tests
10104 *     that the RMS residual between them is no more than "tol".
10105 
10106 *  Parameters:
10107 *     n
10108 *        Number of data points.
10109 *     act
10110 *        Pointer to the start of the actual data values.
10111 *     est
10112 *        Pointer to the start of the estimated data values.
10113 *     tol
10114 *        The largest acceptable RMS error between "act" and "est".
10115 *     status
10116 *        Pointer to the inherited status variable.
10117 
10118 *  Returned Value:
10119 *     A value of 1 is returned if the two sets of values agree. Zero is
10120 *     returned otherwise.
10121 
10122 *  Notes:
10123 *     -  Zero is returned if an error occurs.
10124 */
10125 
10126 /* Local Variables: */
10127    int ret, i;
10128    double s1, s2;
10129    double *px, *py, diff, mserr;
10130 
10131 /* Initialise */
10132    ret = 0;
10133 
10134 /* Check the inherited status. */
10135    if( !astOK ) return ret;
10136 
10137 /* Initialise the sum of the squared residuals, and the number summed. */
10138    s1 = 0.0;
10139    s2 = 0.0;
10140 
10141 /* Initialise pointers to the next actual and estimated values to use. */
10142    px = act;
10143    py = est;
10144 
10145 /* Loop round all pairs of good actual and estimate value. */
10146    for( i = 0; i < n; i++, px++, py++ ){
10147       if( *px != AST__BAD && *py != AST__BAD ) {
10148 
10149 /* Increment the sums need to find the RMS residual between the actual
10150    and estimated values. */
10151          diff = *px - *py;
10152          s1 += diff*diff;
10153          s2 += 1.0;
10154       }
10155    }
10156 
10157 /* If the sums are usable... */
10158    if( s2 > 0.0 ) {
10159 
10160 /* Form the mean squared residual, and check if it is less than the
10161    squared error limit. */
10162       mserr = s1/s2;
10163       if( mserr < tol*tol ) ret = 1;
10164    }
10165 
10166 /* Return the result. */
10167    return ret;
10168 }
10169 
FitsAxisOrder(AstFitsChan * this,int nwcs,AstFrame * wcsfrm,int * perm,int * status)10170 static int FitsAxisOrder( AstFitsChan *this, int nwcs, AstFrame *wcsfrm,
10171                           int *perm, int *status ){
10172 /*
10173 *  Name:
10174 *     FitsAxisOrder
10175 
10176 *  Purpose:
10177 *     Return the order of WCS axes specified by attribute FitsAxisOrder.
10178 
10179 *  Type:
10180 *     Private function.
10181 
10182 *  Synopsis:
10183 *     #include "fitschan.h"
10184 *     int FitsAxisOrder( AstFitsChan *this, int nwcs, AstFrame *wcsfrm,
10185 *                        int *perm, int *status )
10186 
10187 *  Class Membership:
10188 *     FitsChan member function.
10189 
10190 *  Description:
10191 *     This function returns an array indicating the order of the WCS axes
10192 *     within the output FITS header, as specified by the FitsAxisOrder
10193 *     attribute.
10194 
10195 *  Parameters:
10196 *     this
10197 *        Pointer to the FitsChan.
10198 *     nwcs
10199 *        The number of axes in "wcsfrm".
10200 *     wcsfrm
10201 *        The Frame containing the output WCS axes.
10202 *     perm
10203 *        Pointer to an array of "nwcs" integers. On exit, element "k"
10204 *        of this array holds the zero-based index of the FITS-WCS axis
10205 *        (i.e. one less than the value of "i" in the keyword names
10206 *        "CTYPEi", "CRVALi", etc) that describes the k'th axis in "wcsfrm".
10207 *        In other words, "perm[ast_index] = fits_index". The order is
10208 *        determined by the FitsAxisOrder attribute. If this attribute is
10209 *        "<copy>" or "<auto>", then "perm[k]=k" for all k on exit (i.e.
10210 *        a unit mapping between axes in "wcsfrm" and the FITS header).
10211 *     status
10212 *        Pointer to the inherited status variable.
10213 
10214 *  Returned Value:
10215 *     Returns zero if the FitsAxisOrder attribute is "<auto">, and
10216 *     non-zero otherwise. This is a flag indicating if the returned
10217 *     values in "perm" can be used s they are.
10218 
10219 */
10220 
10221 /* Local Variables: */
10222    AstKeyMap *km;    /* KeyMap holding axis indices keyed by axis symbols */
10223    char **words;     /* Pointer to array of words from FitsAxisOrder */
10224    char attr_name[15];/* Attribute name */
10225    const char *attr; /* Pointer to a string holding the FitsAxisOrder value */
10226    int i;            /* Loop count */
10227    int j;            /* Zero-based axis index */
10228    int k;            /* Zero-based axis index */
10229    int nword;        /* Number of words in FitsAxisOrder */
10230    int result;       /* Retrned value */
10231 
10232 /* Check the inherited status. */
10233    if( !astOK ) return 0;
10234 
10235 /* Initialise the returned array to a unit mapping from Frame axis to
10236    FITS axis. */
10237    for( i = 0; i < nwcs; i++ ) perm[ i ] = i;
10238 
10239 /* Get the FitsAxisOrder attribute value, and set the returned value to
10240    indicate if it is "<auto>". */
10241    attr = astGetFitsAxisOrder( this );
10242    result = !astChrMatch( attr, "<auto>" );
10243 
10244 /* Return immediately if it is "<auto>" or "<copy>". */
10245    if( result && !astChrMatch( attr, "<copy>" ) ) {
10246 
10247 /* Create a KeyMap in which each key is the Symbol for an axis and the
10248    associated value is the zero based index of the axis within "wcsfrm". */
10249       km = astKeyMap( "KeyCase=0", status );
10250       for( i = 0; i < nwcs; i++ ){
10251          sprintf( attr_name, "Symbol(%d)", i + 1 );
10252          astMapPut0I( km, astGetC( wcsfrm, attr_name ), i, NULL );
10253       }
10254 
10255 /* Split the FitsAxisOrder value into a collection of space-separated words. */
10256       words = astChrSplit( attr, &nword );
10257 
10258 /* Loop round them all. */
10259       k = 0;
10260       for( i = 0; i < nword; i++ ) {
10261 
10262 /* Get the zero based index within "wcsfrm" of the axis that has a Symbol
10263    equal to the current word from FitsAxisOrder. */
10264          if( astMapGet0I( km, words[ i ], &j ) ) {
10265 
10266 /* If this "wcsfrm" axis has already been used, report an error. */
10267             if( j < 0 ) {
10268                if( astOK ) astError( AST__ATTIN, "astWrite(fitschan): "
10269                            "attribute FitsAxisOrder (%s) refers to axis "
10270                            "%s more than once.", status, attr, words[ i ] );
10271 
10272 /* Otherwise, set the corresponding element of the returned array, and
10273    ensure this axis cannot be used again by assigning it an index of -1
10274    in the KeyMap. */
10275             } else {
10276                perm[ j ] = k++;
10277                astMapPut0I( km, words[ i ], -1, NULL );
10278             }
10279          }
10280 
10281 /* Free the memory holding the copy of the word. */
10282          words[ i ] = astFree( words[ i ] );
10283       }
10284 
10285 /* Report an error if any wcsfrm axes were not included in FitsAxisOrder. */
10286       if( astOK ) {
10287          for( i = 0; i < nwcs; i++ ){
10288             sprintf( attr_name, "Symbol(%d)", i + 1 );
10289             if( astMapGet0I( km, astGetC( wcsfrm, attr_name ), &j ) ) {
10290                if( j >= 0 ) {
10291                   astError( AST__ATTIN, "astWrite(fitschan): attribute FitsAxisOrder "
10292                       "(%s) does not specify a position for WCS axis '%s'.",
10293                       status, attr, astGetC( wcsfrm, attr_name ) );
10294                   break;
10295                }
10296             }
10297          }
10298       }
10299 
10300 /* Free resources. */
10301       words = astFree( words );
10302       km = astAnnul( km );
10303    }
10304 
10305    return result;
10306 }
10307 
FitsFromStore(AstFitsChan * this,FitsStore * store,int encoding,double * dim,AstFrameSet * fs,const char * method,const char * class,int * status)10308 static int FitsFromStore( AstFitsChan *this, FitsStore *store, int encoding,
10309                           double *dim, AstFrameSet *fs, const char *method,
10310                           const char *class, int *status ){
10311 
10312 /*
10313 *  Name:
10314 *     FitsFromStore
10315 
10316 *  Purpose:
10317 *     Store WCS keywords in a FitsChan.
10318 
10319 *  Type:
10320 *     Private function.
10321 
10322 *  Synopsis:
10323 
10324 *     int FitsFromStore( AstFitsChan *this, FitsStore *store, int encoding,
10325 *                        double *dim, AstFrameSet *fs, const char *method,
10326 *                        const char *class, int *status )
10327 
10328 *  Class Membership:
10329 *     FitsChan
10330 
10331 *  Description:
10332 *     A FitsStore is a structure containing a generalised represention of
10333 *     a FITS WCS FrameSet. Functions exist to convert a FitsStore to and
10334 *     from a set of FITS header cards (using a specified encoding), or
10335 *     an AST FrameSet. In other words, a FitsStore is an encoding-
10336 *     independant intermediary staging post between a FITS header and
10337 *     an AST FrameSet.
10338 *
10339 *     This function copies the WCS information stored in the supplied
10340 *     FitsStore into the supplied FitsChan, using a specified encoding.
10341 
10342 *  Parameters:
10343 *     this
10344 *        Pointer to the FitsChan.
10345 *     store
10346 *        Pointer to the FitsStore.
10347 *     encoding
10348 *        The encoding to use.
10349 *     dim
10350 *        Pointer to an array holding the array dimensions (AST__BAD
10351 *        indicates that the dimenson is not known).
10352 *     method
10353 *        Pointer to a string holding the name of the calling method.
10354 *        This is only for use in constructing error messages.
10355 *     class
10356 *        Pointer to a string holding the name of the supplied object class.
10357 *        This is only for use in constructing error messages.
10358 *     status
10359 *        Pointer to the inherited status variable.
10360 
10361 *  Returned Value:
10362 *     A value of 1 is returned if succesfull, and zero is returned
10363 *     otherwise.
10364 */
10365 
10366 /* Local Variables: */
10367    int ret;
10368 
10369 /* Initialise */
10370    ret = 0;
10371 
10372 /* Check the inherited status. */
10373    if( !astOK ) return ret;
10374 
10375 /* Set the current card so that it points to the last WCS-related keyword
10376    in the FitsChan (whether previously read or not). Any new WCS related
10377    keywords either over-write pre-existing cards for the same keyword, or
10378    (if no pre-existing card exists) are inserted after the last WCS related
10379    keyword. */
10380    FindWcs( this, 1, 1, 0, method, class, status );
10381 
10382 /* Do each non-standard FITS encoding... */
10383    if( encoding == DSS_ENCODING ){
10384       ret = DSSFromStore( this, store, method, class, status );
10385    } else if( encoding == FITSPC_ENCODING ){
10386       ret = PCFromStore( this, store, method, class, status );
10387    } else if( encoding == FITSIRAF_ENCODING ){
10388       ret = IRAFFromStore( this, store, method, class, status );
10389    } else if( encoding == FITSAIPS_ENCODING ){
10390       ret = AIPSFromStore( this, store, method, class, status );
10391    } else if( encoding == FITSAIPSPP_ENCODING ){
10392       ret = AIPSPPFromStore( this, store, method, class, status );
10393    } else if( encoding == FITSCLASS_ENCODING ){
10394       ret = CLASSFromStore( this, store, fs, dim, method, class, status );
10395 
10396 /* Standard FITS-WCS encoding */
10397    } else {
10398       ret = WcsFromStore( this, store, method, class, status );
10399    }
10400 
10401 /* If there are any Tables in the FitsStore move the KeyMap that contains
10402    them from the FitsStore to the FitsChan, from where they can be
10403    retrieved using the public astGetTables method. */
10404    if( astMapSize( store->tables ) > 0 ) {
10405       if( !this->tables ) this->tables = astKeyMap( " ", status );
10406       astMapCopy( this->tables, store->tables );
10407       (void) astAnnul( store->tables );
10408       store->tables = astKeyMap( " ", status );
10409    }
10410 
10411 /* If an error has occurred, return zero. */
10412    if( !astOK ) ret = 0;
10413 
10414 /* Return the answer. */
10415    return ret;
10416 }
10417 
FitsToStore(AstFitsChan * this,int encoding,const char * method,const char * class,int * status)10418 static FitsStore *FitsToStore( AstFitsChan *this, int encoding,
10419                                const char *method, const char *class, int *status ){
10420 
10421 /*
10422 *  Name:
10423 *     FitsToStore
10424 
10425 *  Purpose:
10426 *     Return a pointer to a FitsStore structure containing WCS information
10427 *     read from the supplied FitsChan.
10428 
10429 *  Type:
10430 *     Private function.
10431 
10432 *  Synopsis:
10433 *     #include "fitschan.h"
10434 
10435 *     FitsStore *FitsToStore( AstFitsChan *this, int encoding,
10436 *                             const char *method, const char *class )
10437 
10438 *  Class Membership:
10439 *     FitsChan member function.
10440 
10441 *  Description:
10442 *     A FitsStore is a structure containing a generalised represention of
10443 *     a FITS WCS FrameSet. Functions exist to convert a FitsStore to and
10444 *     from a set of FITS header cards (using a specified encoding), or
10445 *     an AST FrameSet. In other words, a FitsStore is an encoding-
10446 *     independant intermediary staging post between a FITS header and
10447 *     an AST FrameSet.
10448 *
10449 *     This function creates a new FitsStore containing WCS information
10450 *     read from the supplied FitsChan using the specified encoding. An
10451 *     error is reported and a null pointer returned if the FitsChan does
10452 *     not contain usable WCS information with the specified encoding.
10453 
10454 *  Parameters:
10455 *     this
10456 *        Pointer to the FitsChan.
10457 *     encoding
10458 *        The encoding to use.
10459 *     method
10460 *        Pointer to a string holding the name of the calling method.
10461 *        This is only for use in constructing error messages.
10462 *     class
10463 *        Pointer to a string holding the name of the supplied object class.
10464 *        This is only for use in constructing error messages.
10465 
10466 *  Returned Value:
10467 *     A pointer to a new FitsStore, or NULL if an error has occurred. The
10468 *     FitsStore should be released using FreeStore function when it is no
10469 *     longer needed.
10470 */
10471 
10472 /* Local Variables: */
10473    AstFitsChan *trans;
10474    FitsStore *ret;
10475 
10476 /* Initialise */
10477    ret = NULL;
10478 
10479 /* Check the inherited status. */
10480    if( !astOK ) return ret;
10481 
10482 /* Allocate memory for the new FitsStore, and store NULL pointers in it. */
10483    ret = (FitsStore *) astMalloc( sizeof(FitsStore) );
10484    if( ret ) {
10485       ret->cname = NULL;
10486       ret->ctype = NULL;
10487       ret->ctype_com = NULL;
10488       ret->cunit = NULL;
10489       ret->ps = NULL;
10490       ret->radesys = NULL;
10491       ret->wcsname = NULL;
10492       ret->wcsaxes = NULL;
10493       ret->pc = NULL;
10494       ret->cdelt = NULL;
10495       ret->crpix = NULL;
10496       ret->crval = NULL;
10497       ret->equinox = NULL;
10498       ret->latpole = NULL;
10499       ret->lonpole = NULL;
10500       ret->mjdobs = NULL;
10501       ret->mjdavg = NULL;
10502       ret->dut1 = NULL;
10503       ret->pv = NULL;
10504       ret->specsys = NULL;
10505       ret->ssyssrc = NULL;
10506       ret->obsgeox = NULL;
10507       ret->obsgeoy = NULL;
10508       ret->obsgeoz = NULL;
10509       ret->restfrq = NULL;
10510       ret->restwav = NULL;
10511       ret->zsource = NULL;
10512       ret->velosys = NULL;
10513       ret->asip = NULL;
10514       ret->bsip = NULL;
10515       ret->apsip = NULL;
10516       ret->bpsip = NULL;
10517       ret->imagfreq = NULL;
10518       ret->axref = NULL;
10519       ret->naxis = 0;
10520       ret->timesys = NULL;
10521       ret->tables = astKeyMap( " ", status );
10522       ret->skyref = NULL;
10523       ret->skyrefp = NULL;
10524       ret->skyrefis = NULL;
10525    }
10526 
10527 /* Call the routine apropriate to the encoding. */
10528    if( encoding == DSS_ENCODING ){
10529       DSSToStore( this, ret, method, class, status );
10530 
10531 /* All other foreign encodings are treated as variants of FITS-WCS. */
10532    } else {
10533 
10534 /* Create a new FitsChan containing standard translations for any
10535    non-standard keywords in the supplied FitsChan. The non-standard
10536    keywords are marked as provisionally read in the supplied FitsChan. */
10537       trans = SpecTrans( this, encoding, method, class, status );
10538 
10539 /* Copy the required values to the FitsStore, using keywords in "trans"
10540    in preference to those in "this". */
10541       WcsToStore( this, trans, ret, method, class, status );
10542 
10543 /* Delete the temporary FitsChan holding translations of non-standard
10544    keywords. */
10545       if( trans ) trans = (AstFitsChan *) astDelete( trans );
10546 
10547 /* Store the number of pixel axes. This is taken as the highest index used
10548    in any primary CRPIX keyword. */
10549       ret->naxis = GetMaxJM( &(ret->crpix), ' ', status ) + 1;
10550    }
10551 
10552 /* If an error has occurred, free the returned FitsStore, and return a null
10553    pointer. */
10554    if( !astOK ) ret = FreeStore( ret, status );
10555 
10556 /* Return the answer. */
10557    return ret;
10558 }
10559 
FreeItem(double **** item,int * status)10560 static void FreeItem( double ****item, int *status ){
10561 /*
10562 *  Name:
10563 *     FreeItem
10564 
10565 *  Purpose:
10566 *     Frees all dynamically allocated memory associated with a specified
10567 *     item in a FitsStore.
10568 
10569 *  Type:
10570 *     Private function.
10571 
10572 *  Synopsis:
10573 *     #include "fitschan.h"
10574 *     void FreeItem( double ****item, int *status );
10575 
10576 *  Class Membership:
10577 *     FitsChan member function.
10578 
10579 *  Description:
10580 *     Frees all dynamically allocated memory associated with the specified
10581 *     item in a FitsStore. A NULL pointer is stored in the FitsStore.
10582 
10583 *  Parameters:
10584 *     item
10585 *        The address of the pointer within the FitsStore which locates the
10586 *        arrays of values for the required keyword (eg &(store->crval) ).
10587 *        The array located by the supplied pointer contains a vector of
10588 *        pointers. Each of these pointers is associated with a particular
10589 *        co-ordinate version (s), and locates an array of pointers for that
10590 *        co-ordinate version. Each such array of pointers has an element
10591 *        for each intermediate axis number (j), and the pointer locates an
10592 *        array of axis keyword values. These arrays of keyword values have
10593 *        one element for every pixel axis (i) or projection parameter (m).
10594 *     status
10595 *        Pointer to the inherited status variable.
10596 
10597 *  Notes:
10598 *    - This function attempt to execute even if an error has occurred.
10599 */
10600 
10601 /* Local Variables: */
10602    int si;               /* Integer co-ordinate version index */
10603    int j;                /* Intermediate co-ordinate axis index */
10604    int oldstatus;        /* Old error status value */
10605    int oldreport;        /* Old error reporting value */
10606 
10607 /* Other initialisation to avoid compiler warnings. */
10608    oldreport = 0;
10609 
10610 /* Check the supplied pointer */
10611    if( item && *item ){
10612 
10613 /* Start a new error reporting context. */
10614       oldstatus = astStatus;
10615       if( !astOK ) {
10616          oldreport = astReporting( 0 );
10617          astClearStatus;
10618       }
10619 
10620 /* Loop round each coordinate version. */
10621       for( si = 0; si < astSizeOf( (void *) *item )/sizeof(double **);
10622            si++ ){
10623 
10624 /* Check the pointer stored for this co-ordinate version is not null. */
10625          if( (*item)[si] ) {
10626 
10627 /* Loop round the intermediate axes. */
10628             for( j = 0; j < astSizeOf( (void *) (*item)[si] )/sizeof(double *);
10629                  j++ ){
10630 
10631 /* Free the pixel axis/parameter index pointer. */
10632                (*item)[si][j] = (double *) astFree( (void *) (*item)[si][j] );
10633             }
10634 
10635 /* Free the intermediate axes pointer */
10636             (*item)[si] = (double **) astFree( (void *) (*item)[si] );
10637          }
10638       }
10639 
10640 /* Free the co-ordinate versions pointer */
10641       *item = (double ***) astFree( (void *) *item );
10642 
10643 /* If there was an error status on entry to this function, re-instate it.
10644    Otherwise, allow any new error status to remain. */
10645       if( oldstatus ){
10646          if( !astOK ) astClearStatus;
10647          astSetStatus( oldstatus );
10648          astReporting( oldreport );
10649       }
10650    }
10651 }
10652 
FreeItemC(char ***** item,int * status)10653 static void FreeItemC( char *****item, int *status ){
10654 /*
10655 *  Name:
10656 *     FreeItemC
10657 
10658 *  Purpose:
10659 *     Frees all dynamically allocated memory associated with a specified
10660 *     string item in a FitsStore.
10661 
10662 *  Type:
10663 *     Private function.
10664 
10665 *  Synopsis:
10666 *     #include "fitschan.h"
10667 *     void FreeItemC( char *****item, int *status )
10668 
10669 *  Class Membership:
10670 *     FitsChan member function.
10671 
10672 *  Description:
10673 *     Frees all dynamically allocated memory associated with the specified
10674 *     string item in a FitsStore. A NULL pointer is stored in the FitsStore.
10675 
10676 *  Parameters:
10677 *     item
10678 *        The address of the pointer within the FitsStore which locates the
10679 *        arrays of values for the required keyword (eg &(store->ctype) ).
10680 *        The array located by the supplied pointer contains a vector of
10681 *        pointers. Each of these pointers is associated with a particular
10682 *        co-ordinate version (s), and locates an array of pointers for that
10683 *        co-ordinate version. Each such array of pointers has an element
10684 *        for each intermediate axis number (j), and the pointer locates an
10685 *        array of axis keyword values. These arrays of keyword values have
10686 *        one element (a char pointyer) for every pixel axis (i) or
10687 *        projection parameter (m).
10688 *     status
10689 *        Pointer to the inherited status variable.
10690 
10691 *  Notes:
10692 *    - This function attempts to execute even if an error has occurred.
10693 */
10694 
10695 /* Local Variables: */
10696    int si;               /* Integer co-ordinate version index */
10697    int i;                /* Intermediate co-ordinate axis index */
10698    int jm;               /* Pixel co-ordinate axis or parameter index */
10699    int oldstatus;        /* Old error status value */
10700    int oldreport;        /* Old error reporting value */
10701 
10702 /* Other initialisation to avoid compiler warnings. */
10703    oldreport = 0;
10704 
10705 /* Check the supplied pointer */
10706    if( item && *item ){
10707 
10708 /* Start a new error reporting context. */
10709       oldstatus = astStatus;
10710       if( !astOK ) {
10711          oldreport = astReporting( 0 );
10712          astClearStatus;
10713       }
10714 
10715 /* Loop round each coordinate version. */
10716       for( si = 0; si < astSizeOf( (void *) *item )/sizeof(char ***);
10717            si++ ){
10718 
10719 /* Check the pointer stored for this co-ordinate version is not null. */
10720          if( (*item)[si] ) {
10721 
10722 /* Loop round the intermediate axes. */
10723             for( i = 0; i < astSizeOf( (void *) (*item)[si] )/sizeof(char **);
10724                  i++ ){
10725 
10726 /* Check the pointer stored for this intermediate axis is not null. */
10727                if( (*item)[si][i] ) {
10728 
10729 /* Loop round the pixel axes or parameter values. */
10730                   for( jm = 0; jm < astSizeOf( (void *) (*item)[si][i] )/sizeof(char *);
10731                        jm++ ){
10732 
10733 /* Free the string. */
10734                      (*item)[si][i][jm] = (char *) astFree( (void *) (*item)[si][i][jm] );
10735                   }
10736 
10737 /* Free the pixel axes/parameter pointer */
10738                   (*item)[si][i] = (char **) astFree( (void *) (*item)[si][i] );
10739                }
10740             }
10741 
10742 /* Free the intermediate axes pointer */
10743             (*item)[si] = (char ***) astFree( (void *) (*item)[si] );
10744          }
10745       }
10746 
10747 /* Free the co-ordinate versions pointer */
10748       *item = (char ****) astFree( (void *) *item );
10749 
10750 /* If there was an error status on entry to this function, re-instate it.
10751    Otherwise, allow any new error status to remain. */
10752       if( oldstatus ){
10753          if( !astOK ) astClearStatus;
10754          astSetStatus( oldstatus );
10755          astReporting( oldreport );
10756       }
10757    }
10758 }
10759 
FreeStore(FitsStore * store,int * status)10760 static FitsStore *FreeStore( FitsStore *store, int *status ){
10761 /*
10762 *  Name:
10763 *     FreeStore
10764 
10765 *  Purpose:
10766 *     Free dynamic arrays stored in a FitsStore structure.
10767 
10768 *  Type:
10769 *     Private function.
10770 
10771 *  Synopsis:
10772 *     FitsStore *FreeStore( FitsStore *store, int *status )
10773 
10774 *  Class Membership:
10775 *     FitsChan
10776 
10777 *  Description:
10778 *     This function frees all dynamically allocated arrays stored in the
10779 *     supplied FitsStore structure, and returns a NULL pointer.
10780 
10781 *  Parameters:
10782 *     store
10783 *        Pointer to the structure to clean.
10784 *     status
10785 *        Pointer to the inherited status variable.
10786 
10787 *  Notes:
10788 *     - This function attempts to execute even if an error exists on entry.
10789 */
10790 
10791 /* Return if no FitsStore was supplied. */
10792    if( !store ) return NULL;
10793 
10794 /* Free each of the dynamic arrays stored in the FitsStore. */
10795    FreeItemC( &(store->cname), status );
10796    FreeItemC( &(store->ctype), status );
10797    FreeItemC( &(store->ctype_com), status );
10798    FreeItemC( &(store->cunit), status );
10799    FreeItemC( &(store->radesys), status );
10800    FreeItemC( &(store->wcsname), status );
10801    FreeItemC( &(store->specsys), status );
10802    FreeItemC( &(store->ssyssrc), status );
10803    FreeItemC( &(store->ps), status );
10804    FreeItemC( &(store->timesys), status );
10805    FreeItem( &(store->pc), status );
10806    FreeItem( &(store->cdelt), status );
10807    FreeItem( &(store->crpix), status );
10808    FreeItem( &(store->crval), status );
10809    FreeItem( &(store->equinox), status );
10810    FreeItem( &(store->latpole), status );
10811    FreeItem( &(store->lonpole), status );
10812    FreeItem( &(store->mjdobs), status );
10813    FreeItem( &(store->dut1), status );
10814    FreeItem( &(store->mjdavg), status );
10815    FreeItem( &(store->pv), status );
10816    FreeItem( &(store->wcsaxes), status );
10817    FreeItem( &(store->obsgeox), status );
10818    FreeItem( &(store->obsgeoy), status );
10819    FreeItem( &(store->obsgeoz), status );
10820    FreeItem( &(store->restfrq), status );
10821    FreeItem( &(store->restwav), status );
10822    FreeItem( &(store->zsource), status );
10823    FreeItem( &(store->velosys), status );
10824    FreeItem( &(store->asip), status );
10825    FreeItem( &(store->bsip), status );
10826    FreeItem( &(store->apsip), status );
10827    FreeItem( &(store->bpsip), status );
10828    FreeItem( &(store->imagfreq), status );
10829    FreeItem( &(store->axref), status );
10830    store->tables = astAnnul( store->tables );
10831    FreeItem( &(store->skyref), status );
10832    FreeItem( &(store->skyrefp), status );
10833    FreeItemC( &(store->skyrefis), status );
10834    return (FitsStore *) astFree( (void *) store );
10835 }
10836 
FormatKey(const char * key,int c1,int c2,char s,int * status)10837 static char *FormatKey( const char *key, int c1, int c2, char s, int *status ){
10838 /*
10839 *  Name:
10840 *     FormatKey
10841 
10842 *  Purpose:
10843 *     Format a keyword name with indices and co-ordinate version character.
10844 
10845 *  Type:
10846 *     Private function.
10847 
10848 *  Synopsis:
10849 *     char *FormatKey( const char *key, int c1, int c2, char s, int *status )
10850 
10851 *  Class Membership:
10852 *     FitsChan
10853 
10854 *  Description:
10855 *     This function formats a keyword name by including the supplied
10856 *     axis/parameter indices and co-ordinate version character.
10857 
10858 *  Parameters:
10859 *     key
10860 *        The base name of the keyword (e.g. "CD", "CRVAL", etc).
10861 *     c1
10862 *        An integer value to append to the end of the keyword. Ignored if
10863 *        less than zero.
10864 *     c2
10865 *        A second integer value to append to the end of the keyword. Ignored if
10866 *        less than zero. This second integer is preceded by an underscore.
10867 *     s
10868 *        The co-ordinate version character to append to the end of the
10869 *        final string. Ignored if blank.
10870 *     status
10871 *        Pointer to the inherited status variable.
10872 *  Returned Value;
10873 *     A pointer to a static character buffer containing the final string.
10874 *     NULL if an error occurs.
10875 */
10876 
10877 /* Local Variables: */
10878    astDECLARE_GLOBALS
10879    char *ret;
10880    int len;
10881    int nc;
10882 
10883 /* Initialise */
10884    ret = NULL;
10885 
10886 /* Check inherited status */
10887    if( !astOK ) return ret;
10888 
10889 /* Get a pointer to the structure holding thread-specific global data. */
10890    astGET_GLOBALS(NULL);
10891 
10892 /* No characters stored yet. A value of -1 is used to indicate that an
10893    error has occurred. */
10894    len = 0;
10895 
10896 /* Store the supplied keyword base name. */
10897    if( len >= 0 && ( nc = sprintf( formatkey_buff + len, "%s", key ) ) >= 0 ){
10898       len += nc;
10899    } else {
10900       len = -1;
10901    }
10902 
10903 /* If index c1 has been supplied, append it to the end of the string. */
10904    if( c1 >= 0 ) {
10905       if( len >= 0 && ( nc = sprintf( formatkey_buff + len, "%d", c1 ) ) >= 0 ){
10906          len += nc;
10907       } else {
10908          len = -1;
10909       }
10910 
10911 /* If index c2 has been supplied, append it to the end of the string,
10912    preceded by an underscore. */
10913       if( c2 >= 0 ) {
10914          if( len >= 0 && ( nc = sprintf( formatkey_buff + len, "_%d", c2 ) ) >= 0 ){
10915             len += nc;
10916          } else {
10917             len = -1;
10918          }
10919       }
10920    }
10921 
10922 /* If a co-ordinate version character has been supplied, append it to the end
10923    of the string. */
10924    if( s != ' ' ) {
10925       if( len >= 0 && ( nc = sprintf( formatkey_buff + len, "%c", s ) ) >= 0 ){
10926          len += nc;
10927       } else {
10928          len = -1;
10929       }
10930    }
10931 
10932 /* Report an error if necessary */
10933    if( len < 0 && astOK ) {
10934       astError( AST__INTER, "FormatKey(fitschan): AST internal error; failed "
10935                 "to format the keyword %s with indices %d and %d, and "
10936                 "co-ordinate version %c.", status, key, c1, c2, s );
10937       ret = NULL;
10938    } else {
10939       ret = formatkey_buff;
10940    }
10941    return formatkey_buff;
10942 }
10943 
FsetFromStore(AstFitsChan * this,FitsStore * store,const char * method,const char * class,int * status)10944 static AstObject *FsetFromStore( AstFitsChan *this, FitsStore *store,
10945                                  const char *method, const char *class, int *status ){
10946 /*
10947 *  Name:
10948 *     FsetFromStore
10949 
10950 *  Purpose:
10951 *     Create a FrameSet using the the information previously stored in
10952 *     the suppllied FitsStore structure.
10953 
10954 *  Type:
10955 *     Private function.
10956 
10957 *  Synopsis:
10958 *     AstObject *FsetFromStore( AstFitsChan *this, FitsStore *store,
10959 *                               const char *method, const char *class, int *status )
10960 
10961 *  Class Membership:
10962 *     FitsChan
10963 
10964 *  Description:
10965 *     A FitsStore is a structure containing a generalised represention of
10966 *     a FITS WCS FrameSet. Functions exist to convert a FitsStore to and
10967 *     from a set of FITS header cards (using a specified encoding), or
10968 *     an AST FrameSet. In other words, a FitsStore is an encoding-
10969 *     independant intermediary staging post between a FITS header and
10970 *     an AST FrameSet.
10971 *
10972 *     This function creates a new FrameSet containing WCS information
10973 *     stored in the supplied FitsStore. A null pointer is returned and no
10974 *     error is reported if this is not possible.
10975 
10976 *  Parameters:
10977 *     this
10978 *        The FitsChan from which the keywords were read. Warning messages
10979 *        are added to this FitsChan if the celestial co-ordinate system is
10980 *        not recognized.
10981 *     store
10982 *        Pointer to the FitsStore.
10983 *     method
10984 *        Pointer to a string holding the name of the calling method.
10985 *        This is only for use in constructing error messages.
10986 *     class
10987 *        Pointer to a string holding the name of the supplied object class.
10988 *        This is only for use in constructing error messages.
10989 *     status
10990 *        Pointer to the inherited status variable.
10991 
10992 *  Returned Value:
10993 *     A pointer to the new FrameSet, or a null pointer if no FrameSet
10994 *     could be constructed.
10995 
10996 *  Notes:
10997 *     -  The pixel Frame is given a title of "Pixel Coordinates", and
10998 *     each axis in the pixel Frame is given a label of the form "Pixel
10999 *     axis <n>", where <n> is the axis index (starting at one).
11000 *     -  The FITS CTYPE keyword values are used to set the labels for any
11001 *     non-celestial axes in the physical coordinate Frames, and the FITS
11002 *     CUNIT keywords are used to set the corresponding units strings.
11003 *     -  On exit, the pixel Frame is the base Frame, and the physical
11004 *     Frame derived from the primary axis descriptions is the current Frame.
11005 *     - Extra Frames are added to hold any secondary axis descriptions. All
11006 *     axes within such a Frame refer to the same coordinate version ('A',
11007 *     'B', etc).
11008 */
11009 
11010 /* Local Variables: */
11011    AstFrame *frame;   /* Pointer to pixel Frame */
11012    AstFrameSet *ret;  /* Pointer to returned FrameSet */
11013    char buff[ 20 ];   /* Buffer for axis label */
11014    char s;            /* Co-ordinate version character */
11015    int i;             /* Pixel axis index */
11016    int physical;      /* Index of primary physical co-ordinate Frame */
11017    int pixel;         /* Index of pixel Frame in returned FrameSet */
11018    int use;           /* Has this co-ordinate version been used? */
11019 
11020 /* Initialise */
11021    ret = NULL;
11022 
11023 /* Check the inherited status. */
11024    if( !astOK ) return (AstObject *) ret;
11025 
11026 /* Only proceed if there are some axes. */
11027    if( store->naxis ) {
11028 
11029 /* Create a Frame describing the pixel coordinate system. Give it the Domain
11030    GRID. */
11031       frame = astFrame( store->naxis, "Title=Pixel Coordinates,Domain=GRID", status );
11032 
11033 /* Store labels for each pixel axis. */
11034       if( astOK ){
11035          for( i = 0; i < store->naxis; i++ ){
11036             sprintf( buff, "Pixel axis %d", i + 1 );
11037             astSetLabel( frame, i, buff );
11038          }
11039       }
11040 
11041 /* Create the FrameSet initially holding just the pixel coordinate frame
11042    (this becomes the base Frame). */
11043       ret = astFrameSet( frame, "", status );
11044 
11045 /* Annul the pointer to the pixel coordinate Frame. */
11046       frame = astAnnul( frame );
11047 
11048 /* Get the index of the pixel Frame in the FrameSet. */
11049       pixel = astGetCurrent( ret );
11050 
11051 /* Produce the Frame describing the primary axis descriptions, and add it
11052    into the FrameSet. */
11053       AddFrame( this, ret, pixel, store->naxis, store, ' ', method, class, status );
11054 
11055 /* Get the index of the primary physical co-ordinate Frame in the FrameSet. */
11056       physical = astGetCurrent( ret );
11057 
11058 /* Loop, producing secondary axis Frames for each of the co-ordinate
11059    versions stored in the FitsStore. */
11060       for( s = 'A'; s <= GetMaxS( &(store->crval), status ) && astOK; s++ ){
11061 
11062 /* Only use this co-ordinate version character if any of the required
11063    keywords (for any axis) are stored in the FitsStore. */
11064          use = 0;
11065          for( i = 0; i < store->naxis; i++ ){
11066             if( GetItem( &(store->crval), i, 0, s, NULL, method, class, status ) != AST__BAD ||
11067                 GetItem( &(store->crpix), 0, i, s, NULL, method, class, status ) != AST__BAD ||
11068                 GetItemC( &(store->ctype), i, 0, s, NULL, method, class, status ) != NULL ){
11069                use = 1;
11070                break;
11071             }
11072          }
11073 
11074 /* If this co-ordinate version has been used, add a Frame to the returned
11075    FrameSet holding this co-ordinate version. */
11076          if( use ) AddFrame( this, ret, pixel, store->naxis, store, s, method, class, status );
11077       }
11078 
11079 /* Ensure the pixel Frame is the Base Frame and the primary physical
11080    Frame is the Current Frame. */
11081       astSetBase( ret, pixel );
11082       astSetCurrent( ret, physical );
11083 
11084 /* Remove any unneeded Frames that hold a FITS representation of offset
11085    coordinates. */
11086       TidyOffsets( ret, status );
11087 
11088 /* If an error has occurred, free the returned FrameSet and return a null
11089    pointer. */
11090       if( !astOK ) ret = astAnnul( ret );
11091    }
11092 
11093 /* Return the answer. */
11094    return (AstObject *) ret;
11095 }
11096 
FsetToStore(AstFitsChan * this,AstFrameSet * fset,int naxis,double * dim,int encoding,const char * class,const char * method,int * status)11097 static FitsStore *FsetToStore( AstFitsChan *this, AstFrameSet *fset, int naxis,
11098                                double *dim, int encoding, const char *class,
11099                                const char *method, int *status ){
11100 
11101 /*
11102 *  Name:
11103 *     FsetToStore
11104 
11105 *  Purpose:
11106 *     Fill a FitsStore structure with a description of the supplied
11107 *     FrameSet.
11108 
11109 *  Type:
11110 *     Private function.
11111 
11112 *  Synopsis:
11113 
11114 *     FitsStore *FsetToStore( AstFitsChan *this, AstFrameSet *fset, int naxis,
11115 *                             double *dim, int encoding, const char *class,
11116 *                             const char *method, int *status )
11117 
11118 *  Class Membership:
11119 *     FitsChan
11120 
11121 *  Description:
11122 *     A FitsStore is a structure containing a generalised represention of
11123 *     a FITS WCS FrameSet. Functions exist to convert a FitsStore to and
11124 *     from a set of FITS header cards (using a specified encoding), or
11125 *     an AST FrameSet. In other words, a FitsStore is an encoding-
11126 *     independant intermediary staging post between a FITS header and
11127 *     an AST FrameSet.
11128 *
11129 *     This function creates a new FitsStore containing WCS information
11130 *     read from the supplied FitsChan using the specified encoding. An
11131 *     error is reported and a null pointer returned if the FitsChan does
11132 *     not contain usable WCS information with the specified encoding.
11133 
11134 *  Parameters:
11135 *     this
11136 *        Pointer to the FitsChan.
11137 *     fset
11138 *        Pointer to the FrameSet.
11139 *     naxis
11140 *        The number of axes in the Base Frame of the supplied FrameSet.
11141 *     dim
11142 *        Pointer to an array of pixel axis dimensions. Individual elements
11143 *        will be AST__BAD if dimensions are not known.
11144 *     encoding
11145 *        The encoding being used.
11146 *     method
11147 *        Pointer to a string holding the name of the calling method.
11148 *        This is only for use in constructing error messages.
11149 *     class
11150 *        Pointer to a string holding the name of the supplied object class.
11151 *        This is only for use in constructing error messages.
11152 *     status
11153 *        Pointer to the inherited status variable.
11154 
11155 *  Returned Value:
11156 *     A pointer to a new FitsStore, or NULL if an error has occurred. The
11157 *     FitsStore should be released using FreeStore function when it is no
11158 *     longer needed.
11159 
11160 *  Notes:
11161 *     - A NULL pointer will be returned if this function is invoked
11162 *     with the AST error status set, or if it should fail for any
11163 *     reason.
11164 *     - The Base Frame in the FrameSet is used as the pixel Frame, and
11165 *     the Current Frame is used to create the primary axis descriptions.
11166 *     Attempts are made to create secondary axis descriptions for any
11167 *     other Frames in the FrameSet (up to a total of 26).
11168 */
11169 
11170 /* Local Variables: */
11171    AstFrame *frame;     /* A Frame */
11172    const char *id;      /* Frame Ident string */
11173    int nfrm;            /* Number of Frames in FrameSet */
11174    char *sid;           /* Pointer to array of version letters */
11175    int frms[ 'Z' + 1 ]; /* Array of Frame indices */
11176    FitsStore *ret;      /* Returned FitsStore */
11177    char s;              /* Next available co-ordinate version character */
11178    char s0;             /* Co-ordinate version character */
11179    int ibase;           /* Base Frame index */
11180    int icurr;           /* Current Frame index */
11181    int ifrm;            /* Next Frame index */
11182    int isoff;           /* Is the Frame an offset SkyFrame? */
11183    int primok;          /* Primary Frame stored succesfully? */
11184 
11185 /* Initialise */
11186    ret = NULL;
11187 
11188 /* Check the inherited status. */
11189    if( !astOK ) return ret;
11190 
11191 /* Allocate memory for the new FitsStore, and store NULL pointers in it. */
11192    ret = (FitsStore *) astMalloc( sizeof(FitsStore) );
11193    if( astOK ) {
11194       ret->cname = NULL;
11195       ret->ctype = NULL;
11196       ret->ctype_com = NULL;
11197       ret->cunit = NULL;
11198       ret->ps = NULL;
11199       ret->radesys = NULL;
11200       ret->wcsname = NULL;
11201       ret->wcsaxes = NULL;
11202       ret->pc = NULL;
11203       ret->cdelt = NULL;
11204       ret->crpix = NULL;
11205       ret->crval = NULL;
11206       ret->equinox = NULL;
11207       ret->latpole = NULL;
11208       ret->lonpole = NULL;
11209       ret->dut1 = NULL;
11210       ret->mjdobs = NULL;
11211       ret->mjdavg = NULL;
11212       ret->pv = NULL;
11213       ret->specsys = NULL;
11214       ret->ssyssrc = NULL;
11215       ret->obsgeox = NULL;
11216       ret->obsgeoy = NULL;
11217       ret->obsgeoz = NULL;
11218       ret->restfrq = NULL;
11219       ret->restwav = NULL;
11220       ret->zsource = NULL;
11221       ret->velosys = NULL;
11222       ret->asip = NULL;
11223       ret->bsip = NULL;
11224       ret->apsip = NULL;
11225       ret->bpsip = NULL;
11226       ret->imagfreq = NULL;
11227       ret->axref = NULL;
11228       ret->naxis = naxis;
11229       ret->timesys = NULL;
11230       ret->tables = astKeyMap( " ", status );
11231       ret->skyref = NULL;
11232       ret->skyrefp = NULL;
11233       ret->skyrefis = NULL;
11234 
11235 /* Obtain the index of the Base Frame (i.e. the pixel frame ). */
11236       ibase = astGetBase( fset );
11237 
11238 /* Obtain the index of the Current Frame (i.e. the Frame to use as the
11239    primary physical coordinate frame). */
11240       icurr = astGetCurrent( fset );
11241 
11242 /* Does the current Frame contain a SkyFrame that describes offset
11243    coordinates? */
11244       isoff = IsSkyOff( fset, icurr, status );
11245 
11246 /* Add a description of the primary axes to the FitsStore, based on the
11247    Current Frame in the FrameSet. */
11248       primok = AddVersion( this, fset, ibase, icurr, ret, dim, ' ',
11249                            encoding, isoff, method, class, status );
11250 
11251 /* Do not add any alternate axis descriptions if the primary axis
11252    descriptions could not be produced. */
11253       if( primok && astOK ) {
11254 
11255 /* Get the number of Frames in the FrameSet. */
11256          nfrm = astGetNframe( fset );
11257 
11258 /* We now need to allocate a version letter to each Frame. Allocate
11259    memory to hold the version letter assigned to each Frame. */
11260          sid = (char *) astMalloc( ( nfrm + 1 )*sizeof( char ) );
11261 
11262 /* The frms array has an entry for each of the 26 possible version
11263    letters (starting at A and ending at Z). Each entry holds the index of
11264    the Frame which has been assigned that version character. Initialise
11265    this array to indicate that no version letters have yet been assigned. */
11266          for( s = 'A'; s <= 'Z'; s++ ) {
11267             frms[ (int) s ] = 0;
11268          }
11269 
11270 /* Loop round all frames (excluding the current and base and IWC Frames which
11271    do not need version letters). If the Frame has an Ident attribute consisting
11272    of a single upper case letter, use it as its version letter unless that
11273    letter has already been given to an earlier frame. IWC Frames are not
11274    written out - identify them by giving them a "sid" value of 1 (an
11275    illegal FITS axis description character). */
11276          for( ifrm = 1; ifrm <= nfrm; ifrm++ ){
11277             sid[ ifrm ] = 0;
11278             if( ifrm != icurr && ifrm != ibase ) {
11279                frame = astGetFrame( fset, ifrm );
11280                if( astChrMatchN( astGetDomain( frame ), "IWC", 3 ) ) {
11281                   sid[ ifrm ] = 1;
11282                } else {
11283                   id = astGetIdent( frame );
11284                   if( strlen( id ) == 1 && isupper( id[ 0 ] ) ) {
11285                      if( frms[ (int) id[ 0 ] ] == 0 ) {
11286                         frms[ (int) id[ 0 ] ] = ifrm;
11287                         sid[ ifrm ] = id[ 0 ];
11288                      }
11289                   }
11290                }
11291                (void) astAnnul( frame );
11292             }
11293          }
11294 
11295 /* Now go round all the Frames again, looking for Frames which did not
11296    get a version letter assigned to it on the previous loop. Assign them
11297    letters now, selected them from the letters not already assigned
11298    (lowest to highest). */
11299          s = 'A' - 1;
11300          for( ifrm = 1; ifrm <= nfrm; ifrm++ ){
11301             if( ifrm != icurr && ifrm != ibase && sid[ ifrm ] != 1 ) {
11302                if( sid[ ifrm ] == 0 ){
11303                   while( frms[ (int) ++s ] != 0 );
11304                   if( s <= 'Z' ) {
11305                      sid[ ifrm ] = s;
11306                      frms[ (int) s ] = ifrm;
11307                   }
11308                }
11309             }
11310          }
11311 
11312 /* If the primary headers describe offset coordinates, create an alternate
11313    axis description for the correspondsing absolute coordinate system. */
11314          if( isoff && ++s <= 'Z' ) {
11315             (void) AddVersion( this, fset, ibase, icurr, ret, dim,
11316                                s, encoding, -1, method, class, status );
11317          }
11318 
11319 /* Now go through all the other Frames in the FrameSet, attempting to
11320    create alternate axis descriptions for each one. */
11321          for( ifrm = 1; ifrm <= nfrm; ifrm++ ){
11322             s0 = sid[ ifrm ];
11323             if( s0 != 0 && s0 != 1 ) {
11324 
11325 /* Does it contain an offset sky frame? */
11326                isoff = IsSkyOff( fset, ifrm, status );
11327 
11328 /* Write out the Frame - offset if it is offset, absolute otherwise. */
11329                (void) AddVersion( this, fset, ibase, ifrm, ret, dim,
11330                                   s0, encoding, isoff, method, class, status );
11331 
11332 /* If the Frame is offset, create an extra alternate axis description for
11333    the correspondsing absolute coordinate system. */
11334                if( isoff && ++s <= 'Z' ) {
11335                   (void) AddVersion( this, fset, ibase, ifrm, ret, dim,
11336                                      s, encoding, -1, method, class, status );
11337                }
11338             }
11339          }
11340 
11341 /* Free memory holding version letters */
11342          sid = (char *) astFree( (void *) sid );
11343       }
11344 
11345 /* If an error has occurred, or if the primary Frame could not be cerated,
11346    free the returned FitsStore, and return a null pointer. */
11347       if( !astOK || !primok ) ret = FreeStore( ret, status );
11348    }
11349 
11350 /* Return the answer. */
11351    return ret;
11352 }
11353 
GetClean(AstFitsChan * this,int * status)11354 static int GetClean( AstFitsChan *this, int *status ) {
11355 
11356 /*
11357 *  Name:
11358 *     GetClean
11359 
11360 *  Purpose:
11361 *     Return the value of the Clean attribute.
11362 
11363 *  Type:
11364 *     Private function.
11365 
11366 *  Synopsis:
11367 *     #include "fitschan.h"
11368 
11369 *     int GetClean( AstFitsChan *this, int *status )
11370 
11371 *  Class Membership:
11372 *     FitsChan member function.
11373 
11374 *  Description:
11375 *     This function returns the value of the Clean attribute. Since this
11376 *     attribute controls the behaviour of the FitsChan in the event of an
11377 *     error condition, it is is necessary to ignore any inherited error
11378 *     condition when getting the attribute value. This is why the
11379 *     astMAKE_GET macro is not used.
11380 
11381 *  Parameters:
11382 *     this
11383 *        Pointer to the FitsChan.
11384 *     status
11385 *        Pointer to the inherited status variable.
11386 
11387 *  Returned Value:
11388 *     The Clean value to use.
11389 */
11390 
11391 /* Return if no FitsChan pointer was supplied. */
11392    if ( !this ) return 0;
11393 
11394 /* Return the attribute value, supplying a default value of 0 (false). */
11395    return ( this->clean == -1 ) ? 0 : (this->clean ? 1 : 0 );
11396 }
11397 
GetObjSize(AstObject * this_object,int * status)11398 static int GetObjSize( AstObject *this_object, int *status ) {
11399 /*
11400 *  Name:
11401 *     GetObjSize
11402 
11403 *  Purpose:
11404 *     Return the in-memory size of an Object.
11405 
11406 *  Type:
11407 *     Private function.
11408 
11409 *  Synopsis:
11410 *     #include "fitschan.h"
11411 *     int GetObjSize( AstObject *this, int *status )
11412 
11413 *  Class Membership:
11414 *     FitsChan member function (over-rides the astGetObjSize protected
11415 *     method inherited from the parent class).
11416 
11417 *  Description:
11418 *     This function returns the in-memory size of the supplied FitsChan,
11419 *     in bytes.
11420 
11421 *  Parameters:
11422 *     this
11423 *        Pointer to the FitsChan.
11424 *     status
11425 *        Pointer to the inherited status variable.
11426 
11427 *  Returned Value:
11428 *     The Object size, in bytes.
11429 
11430 *  Notes:
11431 *     - A value of zero will be returned if this function is invoked
11432 *     with the global status set, or if it should fail for any reason.
11433 */
11434 
11435 /* Local Variables: */
11436    AstFitsChan *this;         /* Pointer to FitsChan structure */
11437    FitsCard *card;            /* Pointer to next FitsCard */
11438    int result;                /* Result value to return */
11439 
11440 /* Initialise. */
11441    result = 0;
11442 
11443 /* Check the global error status. */
11444    if ( !astOK ) return result;
11445 
11446 /* Obtain a pointers to the FitsChan structure. */
11447    this = (AstFitsChan *) this_object;
11448 
11449 /* Ensure the source function has been called */
11450    ReadFromSource( this, status );
11451 
11452 /* Invoke the GetObjSize method inherited from the parent class, and then
11453    add on any components of the class structure defined by thsi class
11454    which are stored in dynamically allocated memory. */
11455    result = (*parent_getobjsize)( this_object, status );
11456    result += astTSizeOf( this->warnings );
11457    result += astGetObjSize( this->keyseq );
11458    result += astGetObjSize( this->keywords );
11459    result += astGetObjSize( this->tables );
11460    card = (FitsCard *) ( this->head );
11461    while( card ) {
11462       result += astTSizeOf( card );
11463       result += card->size;
11464       result += astTSizeOf( card->comment );
11465       card = GetLink( card, NEXT, "astGetObjSize", "FitsChan", status );
11466       if( (void *) card == this->head ) break;
11467    }
11468 
11469 /* If an error occurred, clear the result value. */
11470    if ( !astOK ) result = 0;
11471 
11472 /* Return the result, */
11473    return result;
11474 }
11475 
GetCDMatrix(AstFitsChan * this,int * status)11476 static int GetCDMatrix( AstFitsChan *this, int *status ){
11477 
11478 /*
11479 *  Name:
11480 *     GetCDMatrix
11481 
11482 *  Purpose:
11483 *     Get the value of the CDMatrix attribute.
11484 
11485 *  Type:
11486 *     Private function.
11487 
11488 *  Synopsis:
11489 *     #include "fitschan.h"
11490 
11491 *     int GetCDMatrix( AstFitsChan *this, int *status )
11492 
11493 *  Class Membership:
11494 *     FitsChan member function.
11495 
11496 *  Description:
11497 *     If the CDMatrix attribute has been set, then its value is returned.
11498 *     Otherwise, the supplied FitsChan is searched for keywords of the
11499 *     form CDi_j. If any are found a non-zero value is returned. Otherwise
11500 *     a zero value is returned.
11501 
11502 *  Parameters:
11503 *     this
11504 *        Pointer to the FitsChan.
11505 *     status
11506 *        Pointer to the inherited status variable.
11507 
11508 *  Returned Value:
11509 *     The attribute value to use.
11510 
11511 *  Notes:
11512 *     -  A value of zero is returned if an error has already occurred
11513 *     or if an error occurs for any reason within this function.
11514 */
11515 
11516 /* Local Variables... */
11517    int ret;            /* Returned value */
11518    int icard;          /* Index of current card on entry */
11519 
11520 /* Check the global status. */
11521    if( !astOK ) return 0;
11522 
11523 /* If a value has been supplied for the CDMatrix attribute, use it. */
11524    if( astTestCDMatrix( this ) ) {
11525       ret = this->cdmatrix;
11526 
11527 /* Otherwise, check for the existence of CDi_j keywords... */
11528    } else {
11529 
11530 /* Save the current card index, and rewind the FitsChan. */
11531       icard = astGetCard( this );
11532       astClearCard( this );
11533 
11534 /* If the FitsChan contains any keywords with the format "CDi_j" then return
11535    1. Otherwise return zero. */
11536       ret = astKeyFields( this, "CD%1d_%1d", 0, NULL, NULL ) ? 1 : 0;
11537 
11538 /* Reinstate the original current card index. */
11539       astSetCard( this, icard );
11540    }
11541 
11542 /* Return  the result. */
11543    return astOK ? ret : 0;
11544 }
11545 
GetEncoding(AstFitsChan * this,int * status)11546 static int GetEncoding( AstFitsChan *this, int *status ){
11547 
11548 /*
11549 *  Name:
11550 *     GetEncoding
11551 
11552 *  Purpose:
11553 *     Get the value of the Encoding attribute.
11554 
11555 *  Type:
11556 *     Private function.
11557 
11558 *  Synopsis:
11559 *     #include "fitschan.h"
11560 *     int GetEncoding( AstFitsChan *this, int *status )
11561 
11562 *  Class Membership:
11563 *     FitsChan member function.
11564 
11565 *  Description:
11566 *     If the Encoding attribute has been set, then its value is returned.
11567 *     Otherwise, an attempt is made to determine the encoding scheme by
11568 *     looking for selected keywords within the FitsChan. Checks are made
11569 *     for the following keywords in the order specified, and the
11570 *     corresponding encoding is adopted when the first one is found ( where
11571 
11572 *     i, j and m are integers and s is a single upper case character):
11573 *
11574 *     1) Any keywords starting with "BEGAST" = Native encoding
11575 *     2) DELTAV and VELO-xxx (or VLSR) keywords = FITS-CLASS.
11576 *     3) Any AIPS spectral CTYPE values:
11577 
11578 *         Any of CDi_j, PROJP, LONPOLE, LATPOLE = FITS-AIPS++ encoding:
11579 *         None of the above = FITS-AIPS encoding.
11580 *     4) Any keywords matching PCiiijjj = FITS-PC encoding
11581 *     5) Any keywords matching CDiiijjj = FITS-IRAF encoding
11582 *     6) Any keywords matching CDi_j, AND at least one of RADECSYS, PROJPi
11583 *        or CmVALi = FITS-IRAF encoding
11584 *     7) Any keywords RADECSYS, PROJPi or CmVALi, and no CDi_j or PCi_j
11585 *        keywords, = FITS-PC encoding
11586 *     8) Any keywords matching CROTAi = FITS-AIPS encoding
11587 *     9) Keywords matching CRVALi = FITS-WCS encoding
11588 *     10) The PLTRAH keyword = DSS encoding
11589 *     11) If none of the above keywords are found, Native encoding is assumed.
11590 *
11591 *     For cases 2) to 9), a check is also made that the header contains
11592 *     at least one of each keyword CTYPE, CRPIX and CRVAL. If not, then
11593 *     the checking process continues to the next case. This goes some way
11594 *     towards ensuring that the critical keywords used to determine the
11595 *     encoding are part of a genuine WCS description and have not just been
11596 *     left in the header by accident.
11597 
11598 *  Parameters:
11599 *     this
11600 *        Pointer to the FitsChan.
11601 *     status
11602 *        Pointer to the inherited status variable.
11603 
11604 *  Returned Value:
11605 *     The encoding scheme identifier.
11606 
11607 *  Notes:
11608 *     -  The function returns UNKNOWN_ENCODING if an error has already occurred
11609 *     or if an error occurs for any reason within this function.
11610 */
11611 
11612 /* Local Variables... */
11613    int hascd;          /* Any CDi_j keywords found? */
11614    int haspc;          /* Any PCi_j keywords found? */
11615    int haswcs;         /* Any CRVAL, CTYPE and CRPIX found? */
11616    int icard;          /* Index of current card on entry */
11617    int ret;            /* Returned value */
11618 
11619 /* Check the global status. */
11620    if( !astOK ) return UNKNOWN_ENCODING;
11621 
11622 /* If a value has been supplied for the Encoding attribute, use it. */
11623    if( astTestEncoding( this ) ) {
11624       ret = this->encoding;
11625 
11626 /* Otherwise, check for the existence of certain critcal keywords... */
11627    } else {
11628 
11629 /* See if the header contains some CTYPE, CRPIX and CRVAL keywords. */
11630       haswcs = astKeyFields( this, "CTYPE%d", 0, NULL, NULL ) &&
11631                astKeyFields( this, "CRPIX%d", 0, NULL, NULL ) &&
11632                astKeyFields( this, "CRVAL%d", 0, NULL, NULL );
11633 
11634 /* See if there are any CDi_j keywords. */
11635       hascd = astKeyFields( this, "CD%1d_%1d", 0, NULL, NULL );
11636 
11637 /* See if there are any PCi_j keywords. */
11638       haspc = astKeyFields( this, "PC%1d_%1d", 0, NULL, NULL );
11639 
11640 /* Save the current card index, and rewind the FitsChan. */
11641       icard = astGetCard( this );
11642       astClearCard( this );
11643 
11644 /* If the FitsChan contains any keywords starting with "BEGAST", then return
11645    "Native" encoding. */
11646       if( astKeyFields( this, "BEGAST%2f", 0, NULL, NULL ) ){
11647          ret = NATIVE_ENCODING;
11648 
11649 /* Otherwise, look for a FITS-CLASS signature... */
11650       } else if( haswcs && LooksLikeClass( this, "astGetEncoding", "AstFitsChan", status ) ){
11651          ret = FITSCLASS_ENCODING;
11652 
11653 /* Otherwise, if the FitsChan contains any CTYPE keywords which have the
11654    peculiar form used by AIPS, then use "FITS-AIPS" or "FITS-AIPS++" encoding. */
11655       } else if( haswcs && HasAIPSSpecAxis( this, "astGetEncoding", "AstFitsChan", status ) ){
11656          if( hascd ||
11657              astKeyFields( this, "PROJP%d", 0, NULL, NULL ) ||
11658              astKeyFields( this, "LONPOLE", 0, NULL, NULL ) ||
11659              astKeyFields( this, "LATPOLE", 0, NULL, NULL ) ) {
11660             ret = FITSAIPSPP_ENCODING;
11661          } else {
11662             ret = FITSAIPS_ENCODING;
11663          }
11664 
11665 /* Otherwise, if the FitsChan contains any keywords with the format
11666    "PCiiijjj" then return "FITS-PC" encoding. */
11667       } else if( haswcs && astKeyFields( this, "PC%3d%3d", 0, NULL, NULL ) ){
11668          ret = FITSPC_ENCODING;
11669 
11670 /* Otherwise, if the FitsChan contains any keywords with the format
11671    "CDiiijjj" then return "FITS-IRAF" encoding. */
11672       } else if( haswcs && astKeyFields( this, "CD%3d%3d", 0, NULL, NULL ) ){
11673          ret = FITSIRAF_ENCODING;
11674 
11675 /* Otherwise, if the FitsChan contains any keywords with the format
11676    "CDi_j"  AND there is a RADECSYS. PROJPi or CmVALi keyword, then return
11677    "FITS-IRAF" encoding. If "CDi_j" is present but none of the others
11678    are, return "FITS-WCS" encoding. */
11679       } else if( haswcs && hascd ) {
11680          if( (  astKeyFields( this, "RADECSYS", 0, NULL, NULL ) &&
11681                !astKeyFields( this, "RADESYS", 0, NULL, NULL ) ) ||
11682              ( astKeyFields( this, "PROJP%d", 0, NULL, NULL ) &&
11683               !astKeyFields( this, "PV%d_%d", 0, NULL, NULL ) ) ||
11684              ( astKeyFields( this, "C%1dVAL%d", 0, NULL, NULL )) ){
11685             ret = FITSIRAF_ENCODING;
11686          } else {
11687             ret = FITSWCS_ENCODING;
11688          }
11689 
11690 /* Otherwise, if the FitsChan contains any keywords with the format
11691    RADECSYS. PROJPi or CmVALi keyword, then return "FITS-PC" encoding,
11692    so long as there are no FITS-WCS equivalent keywords. */
11693       } else if( haswcs && !haspc && !hascd && (
11694                    ( astKeyFields( this, "RADECSYS", 0, NULL, NULL ) &&
11695                    !astKeyFields( this, "RADESYS", 0, NULL, NULL ) ) ||
11696                  ( astKeyFields( this, "PROJP%d", 0, NULL, NULL ) &&
11697                    !astKeyFields( this, "PV%d_%d", 0, NULL, NULL ) ) ||
11698                  astKeyFields( this, "C%1dVAL%d", 0, NULL, NULL ) ) ) {
11699          ret = FITSPC_ENCODING;
11700 
11701 /* Otherwise, if the FitsChan contains any keywords with the format
11702    "CROTAi" then return "FITS-AIPS" encoding. */
11703       } else if( haswcs && astKeyFields( this, "CROTA%d", 0, NULL, NULL ) ){
11704          ret = FITSAIPS_ENCODING;
11705 
11706 /* Otherwise, if the FitsChan contains any keywords with the format
11707    "CRVALi" then return "FITS-WCS" encoding. */
11708       } else if( haswcs && astKeyFields( this, "CRVAL%d", 0, NULL, NULL ) ){
11709          ret = FITSWCS_ENCODING;
11710 
11711 /* Otherwise, if the FitsChan contains the "PLTRAH" keywords, use "DSS"
11712    encoding. */
11713       } else if( astKeyFields( this, "PLTRAH", 0, NULL, NULL ) ){
11714          ret = DSS_ENCODING;
11715 
11716 /* If none of these conditions is met, assume Native encoding. */
11717       } else {
11718          ret = NATIVE_ENCODING;
11719       }
11720 
11721 /* Reinstate the original current card index. */
11722       astSetCard( this, icard );
11723    }
11724 
11725 /* Return  the encoding scheme. */
11726    return astOK ? ret : UNKNOWN_ENCODING;
11727 }
11728 
GetFiducialNSC(AstWcsMap * map,double * phi,double * theta,int * status)11729 static void GetFiducialNSC( AstWcsMap *map, double *phi, double *theta, int *status ){
11730 /*
11731 *  Name:
11732 *     GetFiducialNSC
11733 
11734 *  Purpose:
11735 *     Return the Native Spherical Coordinates at the fiducial point of a
11736 *     WcsMap projection.
11737 
11738 *  Type:
11739 *     Private function.
11740 
11741 *  Synopsis:
11742 *     #include "fitschan.h"
11743 *     void GetFiducialNSC( AstWcsMap *map, double *phi, double *theta, int *status )
11744 
11745 *  Class Membership:
11746 *     FitsChan member function.
11747 
11748 *  Description:
11749 *     This function returns the native spherical coords corresponding at
11750 *     the fiducial point of a WcsMap.
11751 *
11752 *     The values of parameters 1 and 2 on the longitude axis of the WcsMap
11753 *     are usually used as the native spherical coordinates of the
11754 *     fiducial point. The default values for these parameters are equal
11755 *     to the native spherical coordinates of the projection reference point.
11756 *     The exception is that a TPN projection always uses the default
11757 *     values, since the projection parameters are used to store polynomial
11758 *     coefficients.
11759 
11760 *  Parameters:
11761 *     map
11762 *        Pointer to the WcsMap.
11763 *     phi
11764 *        Address of a location at which to return the native spherical
11765 *        longitude at the fiducial point (radians).
11766 *     theta
11767 *        Address of a location at which to return the native spherical
11768 *        latitude at the fiducial point (radians).
11769 *     status
11770 *        Pointer to the inherited status variable.
11771 */
11772 
11773 /* Local Variables: */
11774    int axlon;                /* Index of longitude axis */
11775 
11776 /* Initialise */
11777    *phi = AST__BAD;
11778    *theta = AST__BAD;
11779 
11780 /* Check the inherited status. */
11781    if( !astOK ) return;
11782 
11783 /* If this is not a TPN projection get he value of the required
11784    projection parameters (the default values for these are equal to the
11785    fixed native shperical coordinates at the projection reference point). */
11786    if( astGetWcsType( map ) != AST__TPN ) {
11787       axlon = astGetWcsAxis( map, 0 );
11788       if( astGetPV( map, axlon, 0 ) != 0.0 ) {
11789          *phi = AST__DD2R*astGetPV( map, axlon, 1 );
11790          *theta = AST__DD2R*astGetPV( map, axlon, 2 );
11791       } else {
11792          *phi = astGetNatLon( map );
11793          *theta = astGetNatLat( map );
11794       }
11795 
11796 /* If this is a TPN projection, the returned values are always the fixed
11797    native shperical coordinates at the projection reference point). */
11798    } else {
11799       *phi = astGetNatLon( map );
11800       *theta = astGetNatLat( map );
11801    }
11802 }
11803 
GetFiducialPPC(AstWcsMap * map,double * x0,double * y0,int * status)11804 static void GetFiducialPPC( AstWcsMap *map, double *x0, double *y0, int *status ){
11805 /*
11806 *  Name:
11807 *     GetFiducialPPC
11808 
11809 *  Purpose:
11810 *     Return the IWC at the fiducial point of a WcsMap projection.
11811 
11812 *  Type:
11813 *     Private function.
11814 
11815 *  Synopsis:
11816 *     #include "fitschan.h"
11817 *     void GetFiducialPPC( AstWcsMap *map, double *x0, double *y0, int *status )
11818 
11819 *  Class Membership:
11820 *     FitsChan member function.
11821 
11822 *  Description:
11823 *     This function returns the projection plane coords corresponding to
11824 *     the native spherical coords of the fiducial point of a FITS-WCS
11825 *     header. Note, projection plane coordinates (PPC) are equal to
11826 *     Intermediate World Coordinates (IWC) except for cases where the
11827 *     fiducial point does not correspond to the projection reference point.
11828 *     In these cases, IWC and PPC will be connected by a translation
11829 *     which ensures that the fiducial point corresponds to the origin of
11830 *     IWC.
11831 *
11832 *     The values of parameters 1 and 2 on the longitude axis of
11833 *     the WcsMap are used as the native spherical coordinates of the
11834 *     fiducial point. The default values for these parameters are equal
11835 *     to the native spherical coordinates of the projection reference point.
11836 
11837 *  Parameters:
11838 *     map
11839 *        Pointer to the WcsMap.
11840 *     x0
11841 *        Address of a location at which to return the PPC X axis value at
11842 *        the fiducial point (radians).
11843 *     y0
11844 *        Address of a location at which to return the PPC Y axis value at
11845 *        the fiducial point (radians).
11846 *     status
11847 *        Pointer to the inherited status variable.
11848 */
11849 
11850 /* Local Variables: */
11851    AstPointSet *pset1;       /* Pointer to the native spherical PointSet */
11852    AstPointSet *pset2;       /* Pointer to the intermediate world PointSet */
11853    double **ptr1;            /* Pointer to pset1 data */
11854    double **ptr2;            /* Pointer to pset2 data */
11855    int axlat;                /* Index of latitude axis */
11856    int axlon;                /* Index of longitude axis */
11857    int i;                    /* Loop count */
11858    int naxes;                /* Number of axes */
11859 
11860 /* Initialise */
11861    *x0 = AST__BAD;
11862    *y0 = AST__BAD;
11863 
11864 /* Check the inherited status. */
11865    if( !astOK ) return;
11866 
11867 /* Save number of axes in the WcsMap. */
11868    naxes = astGetNin( map );
11869 
11870 /* Allocate resources. */
11871    pset1 = astPointSet( 1, naxes, "", status );
11872    ptr1 = astGetPoints( pset1 );
11873    pset2 = astPointSet( 1, naxes, "", status );
11874    ptr2 = astGetPoints( pset2 );
11875 
11876 /* Check pointers can be used safely. */
11877    if( astOK ) {
11878 
11879 /* Get the indices of the longitude and latitude axes in WcsMap. */
11880       axlon = astGetWcsAxis( map, 0 );
11881       axlat = astGetWcsAxis( map, 1 );
11882 
11883 /* Use zero on all non-celestial axes. */
11884       for( i = 0; i < naxes; i++ ) ptr1[ i ][ 0 ] = 0.0;
11885 
11886 /* Get the native spherical coords at the fiducial point. */
11887       GetFiducialNSC( map, ptr1[ axlon ], ptr1[ axlat ], status );
11888 
11889 /* Use the inverse WcsMap to convert the native longitude and latitude of
11890    the fiducial point into PPC (x,y). */
11891       (void) astTransform( map, pset1, 0, pset2 );
11892 
11893 /* Return the calculated PPC coords. */
11894       *x0 = ptr2[ axlon ][ 0 ];
11895       *y0 = ptr2[ axlat ][ 0 ];
11896    }
11897 
11898 /* Free resources. */
11899    pset1 = astAnnul( pset1 );
11900    pset2 = astAnnul( pset2 );
11901 }
11902 
GetFiducialWCS(AstWcsMap * wcsmap,AstMapping * map2,int colon,int colat,double * fidlon,double * fidlat,int * status)11903 static int GetFiducialWCS( AstWcsMap *wcsmap, AstMapping *map2, int colon,
11904                            int colat, double *fidlon, double *fidlat, int *status ){
11905 /*
11906 *  Name:
11907 *     GetFiducialWCS
11908 
11909 *  Purpose:
11910 *     Decide on the celestial coordinates of the fiducial point.
11911 
11912 *  Type:
11913 *     Private function.
11914 
11915 *  Synopsis:
11916 *     #include "fitschan.h"
11917 *     int GetFiducialWCS( AstWcsMap wcsmap, AstMapping map2, int colon,
11918 *                         int colat, double *fidlon, double *fidlat, int *status )
11919 
11920 *  Class Membership:
11921 *     FitsChan member function.
11922 
11923 *  Description:
11924 *     This function returns the celestial longitude and latitude values
11925 *     to use for the fiducial point. These are the values stored in FITS
11926 *     keywords CRVALi.
11927 
11928 *  Parameters:
11929 *     wcsmap
11930 *        The WcsMap which converts Projection Plane Coordinates into
11931 *        native spherical coordinates. The number of outputs from this
11932 *        Mapping should match the number of inputs to "map2".
11933 *     map2
11934 *        The Mapping which converts native spherical coordinates into WCS
11935 *        coordinates.
11936 *     colon
11937 *        The index of the celestial longitude output from "map2".
11938 *     colat
11939 *        The index of the celestial latitude output from "map2".
11940 *     fidlon
11941 *        Pointer to a location at which to return the celestial longitude
11942 *        value at the fiducial point. The value is returned in radians.
11943 *     fidlat
11944 *        Pointer to a location at which to return the celestial latitude
11945 *        value at the fiducial point. The value is returned in radians.
11946 *     status
11947 *        Pointer to the inherited status variable.
11948 
11949 *  Returned Value:
11950 *     Zero if the fiducial point longitude or latitude could not be
11951 *     determined. One otherwise.
11952 */
11953 
11954 /* Local Variables: */
11955    AstPointSet *pset1;       /* Pointer to the native spherical PointSet */
11956    AstPointSet *pset2;       /* Pointer to the WCS PointSet */
11957    double **ptr1;            /* Pointer to pset1 data */
11958    double **ptr2;            /* Pointer to pset2 data */
11959    int axlat;                /* Index of latitude axis */
11960    int axlon;                /* Index of longitude axis */
11961    int iax;                  /* Axis index */
11962    int naxin;                /* Number of IWC axes */
11963    int naxout;               /* Number of WCS axes */
11964    int ret;                  /* The returned FrameSet */
11965 
11966 /* Initialise */
11967    ret = 0;
11968 
11969 /* Check the inherited status. */
11970    if( !astOK ) return ret;
11971 
11972 /* Allocate resources. */
11973    naxin = astGetNin( map2 );
11974    naxout = astGetNout( map2 );
11975    pset1 = astPointSet( 1, naxin, "", status );
11976    ptr1 = astGetPoints( pset1 );
11977    pset2 = astPointSet( 1, naxout, "", status );
11978    ptr2 = astGetPoints( pset2 );
11979    if( astOK ) {
11980 
11981 /* Get the indices of the latitude and longitude outputs in the WcsMap.
11982    These are not necessarily the same as "colat" and "colon" because "map2"
11983    may contain a PermMap. */
11984       axlon = astGetWcsAxis( wcsmap, 0 );
11985       axlat = astGetWcsAxis( wcsmap, 1 );
11986 
11987 /* Use zero on all non-celestial axes. */
11988       for( iax = 0; iax < naxin; iax++ ) ptr1[ iax ][ 0 ] = 0.0;
11989 
11990 /* Get the native spherical coords at the fiducial point. */
11991       GetFiducialNSC( wcsmap, ptr1[ axlon ], ptr1[ axlat ], status );
11992 
11993 /* The fiducial point in the celestial coordinate system is found by
11994    transforming the fiducial point in native spherical co-ordinates
11995    into absolute physical coordinates using map2. */
11996       (void) astTransform( map2, pset1, 1, pset2 );
11997 
11998 /* Store the returned WCS values. */
11999       *fidlon = ptr2[ colon ][ 0 ];
12000       *fidlat = ptr2[ colat ][ 0 ];
12001 
12002 /* Indicate if we have been succesfull. */
12003       if( astOK && *fidlon != AST__BAD && *fidlat != AST__BAD ) ret = 1;
12004    }
12005 
12006 /* Free resources. */
12007    pset1 = astAnnul( pset1 );
12008    pset2 = astAnnul( pset2 );
12009 
12010 /* Return the result. */
12011    return ret;
12012 }
12013 
GetFitsSor(const char * string,int * status)12014 static const char *GetFitsSor( const char *string, int *status ) {
12015 /*
12016 *  Name:
12017 *     GetFitsSor
12018 
12019 *  Purpose:
12020 *     Get the string used to represent an AST spectral standard of rest.
12021 
12022 *  Type:
12023 *     Private function.
12024 
12025 *  Synopsis:
12026 *     #include "fitschan.h"
12027 *     const char *GetFitsSor( const char *string, int *status )
12028 
12029 *  Class Membership:
12030 *     FitsChan member function.
12031 
12032 *  Description:
12033 *     This function returns a pointer to a static string which is the
12034 *     FITS equivalent to a given SpecFrame StdOfRest value.
12035 
12036 *  Parameters:
12037 *     string
12038 *        Pointer to a constant null-terminated string containing the
12039 *        SpecFrame StdOfRest value.
12040 *     status
12041 *        Pointer to the inherited status variable.
12042 
12043 *  Returned Value:
12044 *     Pointer to a static null-terminated string containing the FITS
12045 *     equivalent to the supplied string. NULL is returned if the supplied
12046 *     string has no FITS equivalent.
12047 
12048 *  Notes:
12049 *     - A NULL pointer value will be returned if this function is
12050 *     invoked wth the global error status set, or if it should fail
12051 *     for any reason.
12052 */
12053 
12054 /* Local Variables: */
12055    const char *result;           /* Pointer value to return */
12056 
12057 /* Check the global error status. */
12058    if ( !astOK ) return NULL;
12059 
12060 /* Compare the supplied string with SpecFrame value for which there is a
12061    known FITS equivalent. */
12062    if( !strcmp( string, "Topocentric" ) ){
12063       result = "TOPOCENT";
12064    } else if( !strcmp( string, "Geocentric" )){
12065       result = "GEOCENTR";
12066    } else if( !strcmp( string, "Barycentric" )){
12067       result = "BARYCENT";
12068    } else if( !strcmp( string, "Heliocentric" )){
12069       result = "HELIOCEN";
12070    } else if( !strcmp( string, "LSRK" )){
12071       result = "LSRK";
12072    } else if( !strcmp( string, "LSRD" )){
12073       result = "LSRD";
12074    } else if( !strcmp( string, "Galactic" )){
12075       result = "GALACTOC";
12076    } else if( !strcmp( string, "Local_group" )){
12077       result = "LOCALGRP";
12078    } else if( !strcmp( string, "Source" )){
12079       result = "SOURCE";
12080    } else {
12081       result = NULL;
12082    }
12083 
12084 /* Return the answer. */
12085    return result;
12086 }
12087 
GetItem(double **** item,int i,int jm,char s,char * name,const char * method,const char * class,int * status)12088 static double GetItem( double ****item, int i, int jm, char s, char *name,
12089                        const char *method, const char *class, int *status ){
12090 /*
12091 *  Name:
12092 *     GetItem
12093 
12094 *  Purpose:
12095 *     Retrieve a value for a axis keyword value from a FitStore structure.
12096 
12097 *  Type:
12098 *     Private function.
12099 
12100 *  Synopsis:
12101 *     #include "fitschan.h"
12102 *     double GetItem( double ****item, int i, int jm, char s, char *name,
12103 *                     const char *method, const char *class, int *status )
12104 
12105 *  Class Membership:
12106 *     FitsChan member function.
12107 
12108 *  Description:
12109 *     The requested keyword value is retrieved from the specified array,
12110 *     at a position indicated by the axis and co-ordinate version.
12111 *     AST__BAD is returned if the array does not contain the requested
12112 *     value.
12113 
12114 *  Parameters:
12115 *     item
12116 *        The address of the pointer within the FitsStore which locates the
12117 *        arrays of values for the required keyword (eg &(store->crval) ).
12118 *        The array located by the supplied pointer contains a vector of
12119 *        pointers. Each of these pointers is associated with a particular
12120 *        co-ordinate version (s), and locates an array of pointers for that
12121 *        co-ordinate version. Each such array of pointers has an element
12122 *        for each intermediate axis number (i), and the pointer locates an
12123 *        array of axis keyword values. These arrays of keyword values have
12124 *        one element for every pixel axis (j) or projection parameter (m).
12125 *     i
12126 *        The zero based intermediate axis index in the range 0 to 98. Set
12127 *        this to zero for keywords (e.g. CRPIX) which are not indexed by
12128 *        intermediate axis number.
12129 *     jm
12130 *        The zero based pixel axis index (in the range 0 to 98) or parameter
12131 *        index (in the range 0 to WCSLIB_MXPAR-1). Set this to zero for
12132 *        keywords (e.g. CRVAL) which are not indexed by either pixel axis or
12133 *        parameter number.
12134 *     s
12135 *        The co-ordinate version character (A to Z, or space), case
12136 *        insensitive
12137 *     name
12138 *        A string holding a name for the item of information. A NULL
12139 *        pointer may be supplied, in which case it is ignored. If a
12140 *        non-NULL pointer is supplied, an error is reported if the item
12141 *        of information has not been stored, and the supplied name is
12142 *        used to identify the information within the error message.
12143 *     method
12144 *        Pointer to a string holding the name of the calling method.
12145 *        This is only for use in constructing error messages.
12146 *     class
12147 *        Pointer to a string holding the name of the supplied object class.
12148 *        This is only for use in constructing error messages.
12149 *     status
12150 *        Pointer to the inherited status variable.
12151 
12152 *  Returned Value:
12153 *     The required keyword value, or AST__BAD if no value has previously
12154 *     been stored for the keyword (or if an error has occurred).
12155 */
12156 
12157 /* Local Variables: */
12158    double ret;           /* Returned keyword value */
12159    int si;               /* Integer co-ordinate version index */
12160 
12161 /* Initialise */
12162    ret = AST__BAD;
12163 
12164 /* Check the inherited status. */
12165    if( !astOK ) return ret;
12166 
12167 /* Convert the character co-ordinate version into an integer index, and
12168    check it is within range. The primary axis description (s=' ') is
12169    given index zero. 'A' is 1, 'B' is 2, etc. */
12170    if( s == ' ' ) {
12171       si = 0;
12172    } else if( islower(s) ){
12173       si = (int) ( s - 'a' ) + 1;
12174    } else {
12175       si = (int) ( s - 'A' ) + 1;
12176    }
12177    if( si < 0 || si > 26 ) {
12178       astError( AST__INTER, "GetItem(fitschan): AST internal error; "
12179                 "co-ordinate version '%c' ( char(%d) ) is invalid.", status, s, s );
12180 
12181 /* Check the intermediate axis index is within range. */
12182    } else if( i < 0 || i > 98 ) {
12183       astError( AST__INTER, "GetItem(fitschan): AST internal error; "
12184                 "intermediate axis index %d is invalid.", status, i );
12185 
12186 /* Check the pixel axis or parameter index is within range. */
12187    } else if( jm < 0 || jm > 99 ) {
12188       astError( AST__INTER, "GetItem(fitschan): AST internal error; "
12189                 "pixel axis or parameter index %d is invalid.", status, jm );
12190 
12191 /* Otherwise, if the array holding the required keyword is not null,
12192    proceed... */
12193    } else if( *item ){
12194 
12195 /* Find the number of coordinate versions in the supplied array.
12196    Only proceed if it encompasses the requested co-ordinate
12197    version. */
12198       if( astSizeOf( (void *) *item )/sizeof(double **) > si ){
12199 
12200 /* Find the number of intermediate axes in the supplied array.
12201    Only proceed if it encompasses the requested intermediate axis. */
12202          if( astSizeOf( (void *) (*item)[si] )/sizeof(double *) > i ){
12203 
12204 /* Find the number of pixel axes or parameters in the supplied array.
12205    Only proceed if it encompasses the requested index. */
12206             if( astSizeOf( (void *) (*item)[si][i] )/sizeof(double) > jm ){
12207 
12208 /* Return the required keyword value. */
12209                ret = (*item)[si][i][jm];
12210             }
12211          }
12212       }
12213    }
12214 
12215 /* If required, report an error if the requested item of information has
12216    not been stored. */
12217    if( ret == AST__BAD && name && astOK ){
12218       astError( AST__NOFTS, "%s(%s): No value can be found for %s.", status,
12219                 method, class, name );
12220    }
12221    return ret;
12222 }
12223 
GetMaxJM(double **** item,char s,int * status)12224 static int GetMaxJM( double ****item, char s, int *status ){
12225 /*
12226 *  Name:
12227 *     GetMaxJM
12228 
12229 *  Purpose:
12230 *     Return the largest pixel axis or parameter index stored for an
12231 *     numerical axis keyword value in a FitStore structure.
12232 
12233 *  Type:
12234 *     Private function.
12235 
12236 *  Synopsis:
12237 *     #include "fitschan.h"
12238 *     int GetMaxJM( double ****item, char s, int *status)
12239 
12240 *  Class Membership:
12241 *     FitsChan member function.
12242 
12243 *  Description:
12244 *     The number of pixel axis numbers or projection parameters stored for
12245 *     a specified axis keyword is found and returned.
12246 
12247 *  Parameters:
12248 *     item
12249 *        The address of the pointer within the FitsStore which locates the
12250 *        arrays of values for the required keyword (eg &(store->crpix) ).
12251 *        The array located by the supplied pointer contains a vector of
12252 *        pointers. Each of these pointers is associated with a particular
12253 *        co-ordinate version (s), and locates an array of pointers for that
12254 *        co-ordinate version. Each such array of pointers has an element
12255 *        for each intermediate axis number (i), and the pointer locates an
12256 *        array of axis keyword values. These arrays of keyword values have
12257 *        one element for every pixel axis (j) or projection parameter (m).
12258 *     s
12259 *        The co-ordinate version character (A to Z, or space), case
12260 *        insensitive
12261 *     status
12262 *        Pointer to the inherited status variable.
12263 
12264 *  Returned Value:
12265 *     The maximum pixel axis number or projection parameter index (zero
12266 *     based).
12267 */
12268 
12269 /* Local Variables: */
12270    int jm;               /* Number of parameters/pixel axes */
12271    int i;                /* Intermediate axis index */
12272    int ret;              /* Returned axis index */
12273    int si;               /* Integer co-ordinate version index */
12274 
12275 /* Initialise */
12276    ret = -1;
12277 
12278 /* Check the inherited status. */
12279    if( !astOK ) return ret;
12280 
12281 /* If the array holding the required keyword is not null, proceed... */
12282    if( *item ){
12283 
12284 /* Convert the character co-ordinate version into an integer index, and
12285    check it is within range. The primary axis description (s=' ') is
12286    given index zero. 'A' is 1, 'B' is 2, etc. */
12287       if( s == ' ' ) {
12288          si = 0;
12289       } else if( islower(s) ){
12290          si = (int) ( s - 'a' ) + 1;
12291       } else {
12292          si = (int) ( s - 'A' ) + 1;
12293       }
12294       if( si < 0 || si > 26 ) {
12295          astError( AST__INTER, "GetMaxJM(fitschan): AST internal error; "
12296                    "co-ordinate version '%c' ( char(%d) ) is invalid.", status, s, s );
12297          return ret;
12298       }
12299 
12300 /* Find the number of coordinate versions in the supplied array.
12301    Only proceed if it encompasses the requested co-ordinate
12302    version. */
12303       if( astSizeOf( (void *) *item )/sizeof(double **) > si ){
12304 
12305 /* Check that the pointer to the array of intermediate axis values is not null. */
12306          if( (*item)[si] ){
12307 
12308 /* Loop round each used element in this array. */
12309             for( i = 0; i < astSizeOf( (void *) (*item)[si] )/sizeof(double *);
12310                  i++ ){
12311                if( (*item)[si][i] ){
12312 
12313 /* Get the size of the pixel axis/projection parameter array for the
12314    current intermediate axis, and subtract 1 to get the largest index. */
12315                   jm = astSizeOf( (void *) (*item)[si][i] )/sizeof(double) - 1;
12316 
12317 /* Ignore any trailing unused (AST__BAD) values. */
12318                   while( jm >= 0 && (*item)[si][i][jm] == AST__BAD ) jm--;
12319 
12320 /* Update the returned value if the current value is larger. */
12321                   if( jm > ret ) ret = jm;
12322                }
12323             }
12324          }
12325       }
12326    }
12327    return ret;
12328 }
12329 
GetMaxJMC(char ***** item,char s,int * status)12330 static int GetMaxJMC( char *****item, char s, int *status ){
12331 /*
12332 *  Name:
12333 *     GetMaxJMC
12334 
12335 *  Purpose:
12336 *     Return the largest pixel axis or parameter index stored for an
12337 *     character-valued axis keyword value in a FitStore structure.
12338 
12339 *  Type:
12340 *     Private function.
12341 
12342 *  Synopsis:
12343 *     #include "fitschan.h"
12344 *     int GetMaxJMC( char *****item, char s, int *status)
12345 
12346 *  Class Membership:
12347 *     FitsChan member function.
12348 
12349 *  Description:
12350 *     The number of pixel axis numbers or projection parameters stored for
12351 *     a specified axis keyword is found and returned.
12352 
12353 *  Parameters:
12354 *     item
12355 *        The address of the pointer within the FitsStore which locates the
12356 *        arrays of values for the required keyword (eg &(store->ctype) ).
12357 *        The array located by the supplied pointer contains a vector of
12358 *        pointers. Each of these pointers is associated with a particular
12359 *        co-ordinate version (s), and locates an array of pointers for that
12360 *        co-ordinate version. Each such array of pointers has an element
12361 *        for each intermediate axis number (i), and the pointer locates an
12362 *        array of axis keyword string pointers. These arrays of keyword
12363 *        string pointers have one element for every pixel axis (j) or
12364 *        projection parameter (m).
12365 *     s
12366 *        The co-ordinate version character (A to Z, or space), case
12367 *        insensitive
12368 *     status
12369 *        Pointer to the inherited status variable.
12370 
12371 *  Returned Value:
12372 *     The maximum pixel axis number or projection parameter index (zero
12373 *     based).
12374 */
12375 
12376 /* Local Variables: */
12377    int jm;               /* Number of parameters/pixel axes */
12378    int i;                /* Intermediate axis index */
12379    int ret;              /* Returned axis index */
12380    int si;               /* Integer co-ordinate version index */
12381 
12382 /* Initialise */
12383    ret = -1;
12384 
12385 /* Check the inherited status. */
12386    if( !astOK ) return ret;
12387 
12388 /* If the array holding the required keyword is not null, proceed... */
12389    if( *item ){
12390 
12391 /* Convert the character co-ordinate version into an integer index, and
12392    check it is within range. The primary axis description (s=' ') is
12393    given index zero. 'A' is 1, 'B' is 2, etc. */
12394       if( s == ' ' ) {
12395          si = 0;
12396       } else if( islower(s) ){
12397          si = (int) ( s - 'a' ) + 1;
12398       } else {
12399          si = (int) ( s - 'A' ) + 1;
12400       }
12401       if( si < 0 || si > 26 ) {
12402          astError( AST__INTER, "GetMaxJMC(fitschan): AST internal error; "
12403                    "co-ordinate version '%c' ( char(%d) ) is invalid.", status, s, s );
12404          return ret;
12405       }
12406 
12407 /* Find the number of coordinate versions in the supplied array.
12408    Only proceed if it encompasses the requested co-ordinate
12409    version. */
12410       if( astSizeOf( (void *) *item )/sizeof(char ***) > si ){
12411 
12412 /* Check that the pointer to the array of intermediate axis values is not null. */
12413          if( (*item)[si] ){
12414 
12415 /* Loop round each used element in this array. */
12416             for( i = 0; i < astSizeOf( (void *) (*item)[si] )/sizeof(char **);
12417                  i++ ){
12418                if( (*item)[si][i] ){
12419 
12420 /* Get the size of the pixel axis/projection parameter array for the
12421    current intermediate axis, and subtract 1 to get the largest index. */
12422                   jm = astSizeOf( (void *) (*item)[si][i] )/sizeof(char *) - 1;
12423 
12424 /* Ignore any trailing unused (NULL) values. */
12425                   while( jm >= 0 && (*item)[si][i][jm] == NULL ) jm--;
12426 
12427 /* Update the returned value if the current value is larger. */
12428                   if( jm > ret ) ret = jm;
12429                }
12430             }
12431          }
12432       }
12433    }
12434    return ret;
12435 }
12436 
GetMaxI(double **** item,char s,int * status)12437 static int GetMaxI( double ****item, char s, int *status ){
12438 /*
12439 *  Name:
12440 *     GetMaxI
12441 
12442 *  Purpose:
12443 *     Return the largest WCS axis index stored for an axis keyword value in
12444 *     a FitStore structure.
12445 
12446 *  Type:
12447 *     Private function.
12448 
12449 *  Synopsis:
12450 *     #include "fitschan.h"
12451 *     int GetMaxJM( double ****item, char s)
12452 
12453 *  Class Membership:
12454 *     FitsChan member function.
12455 
12456 *  Description:
12457 *     The number of Wcs axis numbers stored for a specified axis keyword is
12458 *     found and returned.
12459 
12460 *  Parameters:
12461 *     item
12462 *        The address of the pointer within the FitsStore which locates the
12463 *        arrays of values for the required keyword (eg &(store->crval) ).
12464 *        The array located by the supplied pointer contains a vector of
12465 *        pointers. Each of these pointers is associated with a particular
12466 *        co-ordinate version (s), and locates an array of pointers for that
12467 *        co-ordinate version. Each such array of pointers has an element
12468 *        for each intermediate axis number (i), and the pointer locates an
12469 *        array of axis keyword values. These arrays of keyword values have
12470 *        one element for every pixel axis (j) or projection parameter (m).
12471 *     s
12472 *        The co-ordinate version character (A to Z, or space), case
12473 *        insensitive
12474 
12475 *  Returned Value:
12476 *     The maximum WCS axis index (zero based).
12477 */
12478 
12479 /* Local Variables: */
12480    int ret;              /* Returned axis index */
12481    int si;               /* Integer co-ordinate version index */
12482 
12483 /* Initialise */
12484    ret = -1;
12485 
12486 /* Check the inherited status. */
12487    if( !astOK ) return ret;
12488 
12489 /* If the array holding the required keyword is not null, proceed... */
12490    if( *item ){
12491 
12492 /* Convert the character co-ordinate version into an integer index, and
12493    check it is within range. The primary axis description (s=' ') is
12494    given index zero. 'A' is 1, 'B' is 2, etc. */
12495       if( s == ' ' ) {
12496          si = 0;
12497       } else if( islower(s) ){
12498          si = (int) ( s - 'a' ) + 1;
12499       } else {
12500          si = (int) ( s - 'A' ) + 1;
12501       }
12502       if( si < 0 || si > 26 ) {
12503          astError( AST__INTER, "GetMaxI(fitschan): AST internal error; "
12504                    "co-ordinate version '%c' ( char(%d) ) is invalid.", status, s, s );
12505          return ret;
12506       }
12507 
12508 /* Find the number of coordinate versions in the supplied array.
12509    Only proceed if it encompasses the requested co-ordinate
12510    version. */
12511       if( astSizeOf( (void *) *item )/sizeof(double **) > si ){
12512 
12513 /* Check that the pointer to the array of intermediate axis values is not null. */
12514          if( (*item)[si] ){
12515 
12516 /* Get the size of the intermediate axis array and subtract 1 to get the largest
12517    index. */
12518             ret = astSizeOf( (void *) (*item)[si] )/sizeof(double *) - 1;
12519 
12520 /* Ignore any trailing unused (NULL) values. */
12521             while( ret >= 0 && (*item)[si][ret] == NULL ) ret--;
12522          }
12523       }
12524    }
12525    return ret;
12526 }
12527 
GetMaxS(double **** item,int * status)12528 static char GetMaxS( double ****item, int *status ){
12529 /*
12530 *  Name:
12531 *     GetMaxS
12532 
12533 *  Purpose:
12534 *     Return the largest (i.e. closest to Z) coordinate version character
12535 *     stored for a axis keyword value in a FitStore structure.
12536 
12537 *  Type:
12538 *     Private function.
12539 
12540 *  Synopsis:
12541 *     #include "fitschan.h"
12542 *     char GetMaxS( double ****item, int *status)
12543 
12544 *  Class Membership:
12545 *     FitsChan member function.
12546 
12547 *  Description:
12548 *     The largest (i.e. closest to Z) coordinate version character
12549 *     stored for a axis keyword value in a FitStore structure is found
12550 *     and returned.
12551 
12552 *  Parameters:
12553 *     item
12554 *        The address of the pointer within the FitsStore which locates the
12555 *        arrays of values for the required keyword (eg &(store->crval) ).
12556 *        The array located by the supplied pointer contains a vector of
12557 *        pointers. Each of these pointers is associated with a particular
12558 *        co-ordinate version (s), and locates an array of pointers for that
12559 *        co-ordinate version. Each such array of pointers has an element
12560 *        for each intermediate axis number (i), and the pointer locates an
12561 *        array of axis keyword values. These arrays of keyword values have
12562 *        one element for every pixel axis (j) or projection parameter (m).
12563 *     status
12564 *        Pointer to the inherited status variable.
12565 
12566 *  Returned Value:
12567 *     The highest coordinate version character.
12568 */
12569 
12570 /* Local Variables: */
12571    char ret;              /* Returned axis index */
12572    int si;                /* Integer index into alphabet */
12573 
12574 /* Initialise */
12575    ret = ' ';
12576 
12577 /* Check the inherited status. */
12578    if( !astOK ) return ret;
12579 
12580 /* If the array holding the required keyword is not null, proceed... */
12581    if( *item ){
12582 
12583 /* Find the length of this array, and subtract 1 to get the largest index
12584    in the array. */
12585       si = astSizeOf( (void *) *item )/sizeof(double **) - 1;
12586 
12587 /* Ignore any trailing null (i.e. unused) values. */
12588       while( si >= 0 && !(*item)[si] ) si--;
12589 
12590 /* Store the corresponding character */
12591       if( si == 0 ) {
12592          ret = ' ';
12593       } else {
12594          ret = 'A' + si - 1;
12595       }
12596    }
12597    return ret;
12598 }
12599 
GetItemC(char ***** item,int i,int jm,char s,char * name,const char * method,const char * class,int * status)12600 static char *GetItemC( char *****item, int i, int jm, char s, char *name,
12601                        const char *method, const char *class, int *status ){
12602 /*
12603 *  Name:
12604 *     GetItemC
12605 
12606 *  Purpose:
12607 *     Retrieve a string value for a axis keyword value from a FitStore
12608 *     structure.
12609 
12610 *  Type:
12611 *     Private function.
12612 
12613 *  Synopsis:
12614 *     #include "fitschan.h"
12615 *     char *GetItemC( char *****item, int i, int jm, char s, char *name,
12616 *                     const char *method, const char *class, int *status  )
12617 
12618 *  Class Membership:
12619 *     FitsChan member function.
12620 
12621 *  Description:
12622 *     The requested keyword string value is retrieved from the specified
12623 *     array, at a position indicated by the axis and co-ordinate version.
12624 *     NULL is returned if the array does not contain the requested
12625 *     value.
12626 
12627 *  Parameters:
12628 *     item
12629 *        The address of the pointer within the FitsStore which locates the
12630 *        arrays of values for the required keyword (eg &(store->ctype) ).
12631 *        The array located by the supplied pointer contains a vector of
12632 *        pointers. Each of these pointers is associated with a particular
12633 *        co-ordinate version (s), and locates an array of pointers for that
12634 *        co-ordinate version. Each such array of pointers has an element
12635 *        for each intermediate axis number (i), and the pointer locates an
12636 *        array of axis keyword string pointers. These arrays of keyword
12637 *        string pointers have one element for every pixel axis (j) or
12638 *        projection parameter (m).
12639 *     i
12640 *        The zero based intermediate axis index in the range 0 to 98. Set
12641 *        this to zero for keywords (e.g. CRPIX) which are not indexed by
12642 *        intermediate axis number.
12643 *     jm
12644 *        The zero based pixel axis index (in the range 0 to 98) or parameter
12645 *        index (in the range 0 to WCSLIB__MXPAR-1). Set this to zero for
12646 *        keywords (e.g. CTYPE) which are not indexed by either pixel axis or
12647 *        parameter number.
12648 *     s
12649 *        The co-ordinate version character (A to Z, or space), case
12650 *        insensitive
12651 *     name
12652 *        A string holding a name for the item of information. A NULL
12653 *        pointer may be supplied, in which case it is ignored. If a
12654 *        non-NULL pointer is supplied, an error is reported if the item
12655 *        of information has not been stored, and the supplied name is
12656 *        used to identify the information within the error message.
12657 *     method
12658 *        Pointer to a string holding the name of the calling method.
12659 *        This is only for use in constructing error messages.
12660 *     class
12661 *        Pointer to a string holding the name of the supplied object class.
12662 *        This is only for use in constructing error messages.
12663 *     status
12664 *        Pointer to the inherited status variable.
12665 
12666 *  Returned Value:
12667 *     A pointer to the required keyword string value, or NULL if no value
12668 *     has previously been stored for the keyword (or if an error has
12669 *     occurred).
12670 */
12671 
12672 /* Local Variables: */
12673    char *ret;            /* Returned keyword value */
12674    int si;               /* Integer co-ordinate version index */
12675 
12676 /* Initialise */
12677    ret = NULL;
12678 
12679 /* Check the inherited status. */
12680    if( !astOK ) return ret;
12681 
12682 /* Convert the character co-ordinate version into an integer index, and
12683    check it is within range. The primary axis description (s=' ') is
12684    given index zero. 'A' is 1, 'B' is 2, etc. */
12685    if( s == ' ' ) {
12686       si = 0;
12687    } else if( islower(s) ){
12688       si = (int) ( s - 'a' ) + 1;
12689    } else {
12690       si = (int) ( s - 'A' ) + 1;
12691    }
12692    if( si < 0 || si > 26 ) {
12693       astError( AST__INTER, "GetItemC(fitschan): AST internal error; "
12694                 "co-ordinate version '%c' ( char(%d) ) is invalid.", status, s, s );
12695 
12696 /* Check the intermediate axis index is within range. */
12697    } else if( i < 0 || i > 98 ) {
12698       astError( AST__INTER, "GetItemC(fitschan): AST internal error; "
12699                 "intermediate axis index %d is invalid.", status, i );
12700 
12701 /* Check the pixel axis or parameter index is within range. */
12702    } else if( jm < 0 || jm > 99 ) {
12703       astError( AST__INTER, "GetItem(fitschan): AST internal error; "
12704                 "pixel axis or parameter index %d is invalid.", status, jm );
12705 
12706 /* Otherwise, if the array holding the required keyword is not null,
12707    proceed... */
12708    } else if( *item ){
12709 
12710 /* Find the number of coordinate versions in the supplied array.
12711    Only proceed if it encompasses the requested co-ordinate
12712    version. */
12713       if( astSizeOf( (void *) *item )/sizeof(char ***) > si ){
12714 
12715 /* Find the number of intermediate axes in the supplied array.
12716    Only proceed if it encompasses the requested intermediate axis. */
12717          if( astSizeOf( (void *) (*item)[si] )/sizeof(char **) > i ){
12718 
12719 /* Find the number of pixel axes or parameters in the supplied array.
12720    Only proceed if it encompasses the requested index. */
12721             if( astSizeOf( (void *) (*item)[si][i] )/sizeof(char *) > jm ){
12722 
12723 /* Return the required keyword value. */
12724                ret = (*item)[si][i][jm];
12725             }
12726          }
12727       }
12728    }
12729 
12730 /* If required, report an error if the requested item of information has
12731    not been stored. */
12732    if( !ret && name && astOK ){
12733       astError( AST__NOFTS, "%s(%s): No value can be found for %s.", status,
12734                 method, class, name );
12735    }
12736    return ret;
12737 }
12738 
GetNamedTable(AstFitsChan * this,const char * extname,int extver,int extlevel,int report,const char * method,int * status)12739 static AstFitsTable *GetNamedTable( AstFitsChan *this, const char *extname,
12740                                     int extver, int extlevel, int report,
12741                                     const char *method, int *status ){
12742 
12743 /*
12744 *  Name:
12745 *     GetNamedTable
12746 
12747 *  Purpose:
12748 *     Return a FitsTable holding the contents of a named FITS binary table.
12749 
12750 *  Type:
12751 *     Private function.
12752 
12753 *  Synopsis:
12754 *     #include "fitschan.h"
12755 
12756 *     AstFitsTable *GetNamedTable( AstFitsChan *this, const char *extname,
12757 *                                  int extver, int extlevel, int report,
12758 *                                  const char *method, int *status )
12759 
12760 *  Class Membership:
12761 *     FitsChan member function.
12762 
12763 *  Description:
12764 *     If a table source function has been registered with FitsChan (using
12765 *     astTableSource), invoke it to read the required table from the external
12766 *     FITS file. If the extension is available in the FITS file, this will
12767 *     put a FitsTable into the "tables" KeyMap in the FitsChan structure,
12768 *     using the FITS extension name as the key - this will replace any
12769 *     FitsTable already present in the KeyMap with the same key. Finally,
12770 *     return a pointer to the FitsTable stored in the KeyMap - if any.
12771 *
12772 *     This strategy allows the astPutTables or astPutTable method to be used
12773 *     as an alternative to registering a table source function with the
12774 *     FitsChan.  Note, any table read using the source function is used
12775 *     in preference to any table stored in the FitsChan by an earlier call
12776 *     to astPutTables/astPutTable.
12777 
12778 *  Parameters:
12779 *     this
12780 *        Pointer to the FitsChan.
12781 *     extname
12782 *        The key associated with the required table - should be the name
12783 *        of the FITS extension containing the binary table.
12784 *     extver
12785 *        The FITS "EXTVER" value for the required table.
12786 *     extlevel
12787 *        The FITS "EXTLEVEL" value for the required table.
12788 *     report
12789 *        If non-zero, report an error if the named table is not available.
12790 *     method
12791 *        Pointer to a string holding the name of the calling method.
12792 *        This is only for use in constructing error messages.
12793 *     status
12794 *        Pointer to the inherited status variable.
12795 
12796 *  Returned Value:
12797 *     Pointer to the FitsTable, or NULL if the table is not avalable.
12798 */
12799 
12800 /* Local Variables: */
12801    AstFitsTable *ret;
12802 
12803 /* Initialise */
12804    ret = NULL;
12805 
12806 /* Check the inherited status. */
12807    if( !astOK ) return ret;
12808 
12809 /* Fitrst attempt to read the required table from the external FITS file.
12810    Only proceed if table source function and wrapper have been supplied
12811    using astTableSource. */
12812    if( this->tabsource && this->tabsource_wrap ){
12813 
12814 /* Invoke the table source function asking it to place the required FITS
12815    table in the FitsChan. This is an externally supplied function which may
12816    not be thread-safe, so lock a mutex first. Note, a cloned FitsChan pointer
12817    is sent to the table source function since the table source function will
12818    annul the supplied FitsChan pointer. Also store the channel data
12819    pointer in a global variable so that it can be accessed in the source
12820    function using macro astChannelData. */
12821       astStoreChannelData( this );
12822       LOCK_MUTEX2;
12823       ( *this->tabsource_wrap )( this->tabsource, astClone( this ), extname,
12824                                  extver, extlevel, status );
12825       UNLOCK_MUTEX2;
12826    }
12827 
12828 /* Now get a pointer to the required FitsTable, stored as an entry in the
12829    "tables" KeyMap. Report an error if required. */
12830    if( ! (this->tables) || !astMapGet0A( this->tables, extname, &ret ) ){
12831       if( report && astOK ) {
12832          astError( AST__NOTAB, "%s(%s): Failed to read FITS binary table "
12833                    "from extension '%s' (extver=%d, extlevel=%d).", status,
12834                    method, astGetClass( this ), extname, extver, extlevel );
12835       }
12836    }
12837 
12838 /* Return the result. */
12839    return ret;
12840 }
12841 
GetTables(AstFitsChan * this,int * status)12842 static AstKeyMap *GetTables( AstFitsChan *this, int *status ) {
12843 
12844 /*
12845 *++
12846 *  Name:
12847 c     astGetTables
12848 f     AST_GETTABLES
12849 
12850 *  Purpose:
12851 *     Retrieve any FitsTables currently in a FitsChan.
12852 
12853 *  Type:
12854 *     Public virtual function.
12855 
12856 *  Synopsis:
12857 c     #include "fitschan.h"
12858 c     AstKeyMap *astGetTables( AstFitsChan *this )
12859 f     RESULT = AST_GETTABLES( THIS, STATUS )
12860 
12861 *  Class Membership:
12862 *     FitsChan method.
12863 
12864 *  Description:
12865 *     If the supplied FitsChan currently contains any tables, then this
12866 *     function returns a pointer to a KeyMap. Each entry in the KeyMap
12867 *     is a pointer to a FitsTable holding the data for a FITS binary
12868 *     table. The key used to access each entry is the FITS extension
12869 *     name in which the table should be stored.
12870 *
12871 *     Tables can be present in a FitsChan as a result either of using the
12872 c     astPutTable (or astPutTables)
12873 f     AST_PUTTABLE (or AST_PUTTABLES)
12874 *     method to store existing tables in the FitsChan, or of using the
12875 c     astWrite
12876 f     AST_WRITE
12877 *     method to write a FrameSet to the FitsChan. For the later case, if
12878 *     the FitsChan "TabOK" attribute is positive and the FrameSet requires
12879 *     a look-up table to describe one or more axes, then the "-TAB"
12880 *     algorithm code described in FITS-WCS paper III is used and the table
12881 *     values are stored in the FitsChan in the form of a FitsTable object
12882 *     (see the documentation for the "TabOK" attribute).
12883 
12884 *  Parameters:
12885 c     this
12886 f     THIS = INTEGER (Given)
12887 *        Pointer to the FitsChan.
12888 f     STATUS = INTEGER (Given and Returned)
12889 f        The global status.
12890 
12891 *  Returned Value:
12892 c     astGetTables()
12893 f     AST_GETTABLES = INTEGER
12894 *        A pointer to a deep copy of the KeyMap holding the tables currently
12895 *        in the FitsChan, or
12896 c        NULL
12897 f        AST__NULL
12898 *        if the FitsChan does not contain any tables. The returned
12899 *        pointer should be annulled using
12900 c        astAnnul
12901 f        AST_ANNUL
12902 *        when no longer needed.
12903 
12904 *  Notes:
12905 *     - A null Object pointer (AST__NULL) will be returned if this
12906 c     function is invoked with the AST error status set, or if it
12907 f     function is invoked with STATUS set to an error value, or if it
12908 *     should fail for any reason.
12909 *--
12910 */
12911 
12912 /* Local Variables: */
12913    AstKeyMap *result;      /* Pointer value to return */
12914 
12915 /* Initialise. */
12916    result = NULL;
12917 
12918 /* Check the global error status. */
12919    if ( !astOK ) return result;
12920 
12921 /* If the FitsChan contains any tables, return a pointer to a copy of
12922    the KeyMap containing them. Otherwise, return a NULL pointer. */
12923    if( this->tables && astMapSize( this->tables ) > 0 ) {
12924       result = astCopy( this->tables );
12925    }
12926 
12927 /* Return the result. */
12928    return result;
12929 }
12930 
GetUsedPolyTan(AstFitsChan * this,AstFitsChan * out,int latax,int lonax,char s,const char * method,const char * class,int * status)12931 static int GetUsedPolyTan( AstFitsChan *this, AstFitsChan *out, int latax,
12932                            int lonax, char s, const char *method,
12933                            const char *class, int *status ){
12934 /*
12935 *  Name:
12936 *     GetUsedPolyTan
12937 
12938 *  Purpose:
12939 *     Get the value to use for the PolyTan attribute.
12940 
12941 *  Type:
12942 *     Private function.
12943 
12944 *  Synopsis:
12945 *     #include "fitschan.h"
12946 *     int GetUsedPolyTan( AstFitsChan *this, AstFitsChan *out, int latax,
12947 *                         int lonax, char s, const char *method,
12948 *                         const char *class, int *status )
12949 
12950 *  Class Membership:
12951 *     FitsChan member function.
12952 
12953 *  Description:
12954 *     If the PolyTan attribute is zero or positive, then its value is
12955 *     returned. If it is negative, the supplied FitsChan is searched for
12956 *     keywords of the form PVi_m. If any are found on the latitude axis,
12957 *     or if any are found on the longitude axis with "m" > 4, +1 is
12958 *     returned (meaning "use the distorted TAN conventio"). Otherwise 0
12959 *     is returned (meaning "use the standard TAN convention").
12960 *
12961 *     If all the PVi_m values for m > 0 on either axis are zero, a warning is
12962 *     issued and zero is returned.
12963 
12964 *  Parameters:
12965 *     this
12966 *        Pointer to the FitsChan.
12967 *     out
12968 *        Pointer to a secondary FitsChan. If the PV values in "this" are
12969 *        found to be unusable, they will be marked as used in both "this"
12970 *        and "out".
12971 *     latax
12972 *        The one-based index of the latitude axis within the FITS header.
12973 *     lonax
12974 *        The one-based index of the longitude axis within the FITS header.
12975 *     s
12976 *        A character identifying the co-ordinate version to use. A space
12977 *        means use primary axis descriptions. Otherwise, it must be an
12978 *        upper-case alphabetical characters ('A' to 'Z').
12979 *     method
12980 *        A pointer to a string holding the name of the calling method.
12981 *        This is used only in the construction of error messages.
12982 *     class
12983 *        A pointer to a string holding the class of the object being
12984 *        read. This is used only in the construction of error messages.
12985 *     status
12986 *        Pointer to the inherited status variable.
12987 
12988 *  Returned Value:
12989 *     The attribute value to use.
12990 
12991 *  Notes:
12992 *     -  A value of zero is returned if an error has already occurred
12993 *     or if an error occurs for any reason within this function.
12994 */
12995 
12996 /* Local Variables... */
12997    char template[ 20 ];
12998    double pval;
12999    int lbnd_lat;
13000    int lbnd_lon;
13001    int m;
13002    int nfound1;
13003    int nfound2;
13004    int ok;
13005    int ret;
13006    int ubnd_lat;
13007    int ubnd_lon;
13008 
13009 /* Check the global status. */
13010    if( !astOK ) return 0;
13011 
13012 /* Get the value of the PolyTan attribute. */
13013    ret = astGetPolyTan( this );
13014 
13015 /* If it is negative, we examine the FitsChan to see which convention to
13016    use. */
13017    if( ret < 0 ) {
13018       ret = 0;
13019 
13020 /* Search the FitsChan for latitude PV cards. */
13021       if( s != ' ' ) {
13022          sprintf( template, "PV%d_%%d%c", latax, s );
13023       } else {
13024          sprintf( template, "PV%d_%%d", latax );
13025       }
13026       nfound1 = astKeyFields( this, template, 1, &ubnd_lat, &lbnd_lat );
13027 
13028 /* Search the FitsChan for longitude PV cards. */
13029       if( s != ' ' ) {
13030          sprintf( template, "PV%d_%%d%c", lonax, s );
13031       } else {
13032          sprintf( template, "PV%d_%%d", lonax );
13033       }
13034       nfound2 = astKeyFields( this, template, 1, &ubnd_lon, &lbnd_lon );
13035 
13036 /* If any were found with "m" value greater than 4, assume the distorted
13037    TAN convention is in use. Otherwise assume the stdanrd TAN convention is
13038    in use. */
13039       if( nfound1 || ( nfound2 && ubnd_lon > 4 ) ) ret = 1;
13040 
13041 /* If the distorted TAN convention is to be used, check that at least one
13042    of the PVi_m values is non-zero on each axis. We ignore the PVi_0
13043    (constant) terms in this check. */
13044       if( ret > 0 ) {
13045 
13046 /* Do the latitude axis first, skipping the first (constant) term. Assume
13047    that all latitude pV values are zero until we find one that is not. */
13048          ok = 0;
13049          for( m = 1; m <= ubnd_lat && !ok; m++ ) {
13050 
13051 /* Form the PVi_m keyword name. */
13052             if( s != ' ' ) {
13053                sprintf( template, "PV%d_%d%c", latax, m, s );
13054             } else {
13055                sprintf( template, "PV%d_%d", latax, m );
13056             }
13057 
13058 /* Get it's value. */
13059             if( ! GetValue( this, template, AST__FLOAT, &pval, 0, 0,
13060                             method,  class, status ) ) {
13061 
13062 /* If the PVi_m header is not present in the FitsChan, use a default value. */
13063                pval = ( m == 1 ) ? 1.0 : 0.0;
13064             }
13065 
13066 /* If the PVi_m header has a non-zero value, we can leave the loop. */
13067             if( pval != 0.0 ) ok = 1;
13068          }
13069 
13070 /* If all the latitude PVi_m values are zero, issue a warning and return
13071    zero, indicating that a simple undistorted TAN projection should be used. */
13072          if( !ok ) {
13073             Warn( this, "badpv", "This FITS header describes a distorted TAN "
13074                   "projection, but all the distortion coefficients (the "
13075                   "PVi_m headers) on the latitude axis are zero.", method,
13076                   class, status );
13077             ret = 0;
13078 
13079 
13080 /* Also, delete the PV keywords so that no attempt is made to use them. */
13081             for( m = 1; m <= ubnd_lat; m++ ) {
13082                if( s != ' ' ) {
13083                   sprintf( template, "PV%d_%d%c", latax, m, s );
13084                } else {
13085                   sprintf( template, "PV%d_%d", latax, m );
13086                }
13087                astClearCard( this );
13088                if( FindKeyCard( this, template, method, class, status ) ) {
13089                   DeleteCard( this, method, class, status );
13090                }
13091             }
13092 
13093 /* Otherwise, do the same check for the longitude axis. */
13094          } else {
13095             ok = 0;
13096             for( m = 1; m <= ubnd_lon && !ok; m++ ) {
13097 
13098                if( s != ' ' ) {
13099                   sprintf( template, "PV%d_%d%c", lonax, m, s );
13100                } else {
13101                   sprintf( template, "PV%d_%d", lonax, m );
13102                }
13103 
13104                if( ! GetValue( this, template, AST__FLOAT, &pval, 0, 0,
13105                                method, class, status ) ) {
13106 
13107                   pval = ( m == 1 ) ? 1.0 : 0.0;
13108                }
13109 
13110                if( pval != 0.0 ) ok = 1;
13111             }
13112 
13113             if( !ok ) {
13114                Warn( this, "badpv", "This FITS header describes a distorted TAN "
13115                      "projection, but all the distortion coefficients (the "
13116                      "PVi_m headers) on the longitude axis are zero.", method,
13117                      class, status );
13118                ret = 0;
13119 
13120                for( m = 1; m <= ubnd_lon; m++ ) {
13121                   if( s != ' ' ) {
13122                      sprintf( template, "PV%d_%d%c", lonax, m, s );
13123                   } else {
13124                      sprintf( template, "PV%d_%d", lonax, m );
13125                   }
13126                   astClearCard( this );
13127                   if( FindKeyCard( this, template, method, class, status ) ) {
13128                      DeleteCard( this, method, class, status );
13129                   }
13130                }
13131             }
13132          }
13133       }
13134    }
13135 
13136 /* Return  the result. */
13137    return astOK ? ret : 0;
13138 }
13139 
GoodWarns(const char * value,int * status)13140 static int GoodWarns( const char *value, int *status ){
13141 /*
13142 *  Name:
13143 *     GoodWarns
13144 
13145 *  Purpose:
13146 *     Checks a string to ensure it is a legal list of warning conditions.
13147 
13148 *  Type:
13149 *     Private function.
13150 
13151 *  Synopsis:
13152 *     #include "fitschan.h"
13153 *     int GoodWarns( const char *value, int *status )
13154 
13155 *  Class Membership:
13156 *     FitsChan member function.
13157 
13158 *  Description:
13159 *     This function checks the supplied string to ensure it contains a space
13160 *     separated list of zero or more recognised warning conditions. An
13161 *     error is reported if it does not.
13162 
13163 *  Parameters:
13164 *     value
13165 *        The string to check.
13166 *     status
13167 *        Pointer to the inherited status variable.
13168 
13169 *  Returned Value:
13170 *     Zero is returned if the supplied string is not a legal list of
13171 *     conditions, or if an error has already occurred. One is returned
13172 *     otherwise.
13173 */
13174 
13175 /* Local Variables: */
13176    char *b;              /* Pointer to next buffer element */
13177    const char *c  ;      /* Pointer to next character */
13178    char buf[100];        /* Buffer for condition name */
13179    int inword;           /* Are we in a word? */
13180    int n;                /* Number of conditions supplied */
13181    int ret;              /* Returned value */
13182 
13183 /* Initialise */
13184    ret = 0;
13185 
13186 /* Check the inherited status. */
13187    if( !astOK ) return ret;
13188 
13189 /* Report an error and return if the pointer is null. */
13190    if( !value ){
13191       astError( AST__ATTIN, "astSetWarnings(fitschan): Null pointer "
13192                 "supplied for the Warnings attribute." , status);
13193       return ret;
13194    }
13195 
13196 /* Initialise things */
13197    inword = 0;
13198    buf[ 0 ] = ' ';
13199    b = buf + 1;
13200    n = 0;
13201    ret = 1;
13202 
13203 /* Loop round each character in the supplied string. */
13204    for( c = value ; c < value + strlen( value ) + 1; c++ ){
13205 
13206 /* Have we found the first space or null following a word? */
13207       if( ( !(*c) || isspace( *c ) ) && inword ){
13208 
13209 /* Add a space to the end of the buffer and terminate it. */
13210          *(b++) = ' ';
13211          *b = 0;
13212 
13213 /* Check the word is legal by searching for it in the string of all
13214    conditions, which should be lower case and have spaces at start and end.
13215    The word in the buffer is delimited by spaces and so it will not match
13216    a substring within a condition. If it is legal increment the number of
13217    conditions found. */
13218          if( strstr( ALLWARNINGS, buf ) ){
13219             n++;
13220 
13221 /* Otherwise, report an error and break. */
13222          } else {
13223             ret = 0;
13224             *(--b) = 0;
13225             astError( AST__ATTIN, "astSetWarnings(fitschan): Unknown "
13226                       "condition '%s' specified when setting the Warnings "
13227                       "attribute.", status, buf + 1 );
13228             break;
13229          }
13230 
13231 /* Reset the pointer to the next character in the buffer, retaining the
13232    initial space in the buffer. */
13233          b = buf + 1;
13234 
13235 /* Indicate we are no longer in a word. */
13236          inword = 0;
13237 
13238 /* Have we found the first non-space, non-null character following a space? */
13239       } else if( *c && !isspace( *c ) && !inword ){
13240 
13241 /* Note we are now in a word. */
13242          inword = 1;
13243       }
13244 
13245 /* If we are in a word, copy the lowercase character to the buffer. */
13246       if( inword ) *(b++) = tolower( *c );
13247    }
13248    return ret;
13249 }
13250 
GrismSpecWcs(char * algcode,FitsStore * store,int i,char s,AstSpecFrame * specfrm,const char * method,const char * class,int * status)13251 static AstMapping *GrismSpecWcs( char *algcode, FitsStore *store, int i,
13252                                  char s, AstSpecFrame *specfrm,
13253                                  const char *method, const char *class, int *status ) {
13254 /*
13255 *  Name:
13256 *     GrismSpecWcs
13257 
13258 *  Purpose:
13259 *     Create a Mapping describing a FITS-WCS grism-dispersion algorithm
13260 
13261 *  Type:
13262 *     Private function.
13263 
13264 *  Synopsis:
13265 *     #include "fitschan.h"
13266 *     AstMapping *GrismSpecWcs( char *algcode, FitsStore *store, int i, char s,
13267 *                               AstSpecFrame *specfrm, const char *method,
13268 *                               const char *class, int *status )
13269 
13270 *  Class Membership:
13271 *     FitsChan member function.
13272 
13273 *  Description:
13274 *     This function uses the contents of the supplied FitsStore to create
13275 *     a Mapping which goes from Intermediate World Coordinate (known as "w"
13276 *     in the context of FITS-WCS paper III) to the spectral system
13277 *     described by the supplied SpecFrame.
13278 *
13279 *     The returned Mapping implements the grism "GRA" and "GRI" algorithms
13280 *     described in FITS-WCS paper III.
13281 
13282 *  Parameters:
13283 *     algcode
13284 *        Pointer to a string holding the code for the required algorithm
13285 *        ("-GRA" or "-GRI").
13286 *     store
13287 *        Pointer to the FitsStore structure holding the values to use for
13288 *        the WCS keywords.
13289 *     i
13290 *        The zero-based index of the spectral axis within the FITS header
13291 *     s
13292 *        A character identifying the co-ordinate version to use. A space
13293 *        means use primary axis descriptions. Otherwise, it must be an
13294 *        upper-case alphabetical characters ('A' to 'Z').
13295 *     specfrm
13296 *        Pointer to the SpecFrame. This specifies the "S" system - the
13297 *        system in which the CRVAL kewyords (etc) are specified.
13298 *     method
13299 *        A pointer to a string holding the name of the calling method.
13300 *        This is used only in the construction of error messages.
13301 *     class
13302 *        A pointer to a string holding the class of the object being
13303 *        read. This is used only in the construction of error messages.
13304 *     status
13305 *        Pointer to the inherited status variable.
13306 
13307 *  Returned Value:
13308 *     A pointer to a Mapping, or NULL if an error occurs.
13309 */
13310 
13311 /* Local Variables: */
13312    AstFrameSet *fs;
13313    AstMapping *gmap;
13314    AstMapping *map1;
13315    AstMapping *map2;
13316    AstMapping *map2a;
13317    AstMapping *map2b;
13318    AstMapping *ret;
13319    AstMapping *smap;
13320    AstSpecFrame *wfrm;
13321    double crv;
13322    double dg;
13323    double gcrv;
13324    double pv;
13325    double wcrv;
13326 
13327 /* Check the global status. */
13328    ret = NULL;
13329    if( !astOK ) return ret;
13330 
13331 /* The returned Mapping will be a CmpMap including a GrismMap. This
13332    GrismMap will produced wavelength as output. We also need the Mapping
13333    from wavelength to the system represented by the supplied SpecFrame.
13334    To get this, we first create a copy of the supplied SpecFrame (in order
13335    to inherit the standard of rest, epoch, etc), and set its System to
13336    wavlength in vacuum (for "-GRI") or air (for "-GRA"), and then use
13337    astConvert to get the Mapping from the SpecFrame system to relevant
13338    form of wavelength. */
13339    wfrm = astCopy( specfrm );
13340    astSetSystem( wfrm, strcmp( algcode, "-GRI" )?AST__AIRWAVE:AST__WAVELEN );
13341    astSetUnit( wfrm, 0, "m" );
13342    fs = astConvert( specfrm, wfrm, "" );
13343    if( fs ) {
13344       smap = astGetMapping( fs, AST__BASE, AST__CURRENT );
13345       fs = astAnnul( fs );
13346 
13347 /* Get the CRVAL value for the spectral axis (this will be in the S system). */
13348       crv = GetItem( &(store->crval), i, 0, s, NULL, method, class, status );
13349       if( crv == AST__BAD ) crv = 0.0;
13350 
13351 /* Convert it to the wavelength system (vacuum or air) in metres. */
13352       astTran1( smap, 1, &crv, 1, &wcrv );
13353 
13354 /* Create a GrismMap, and then use the projection parameters stored in
13355    the FitsStore to set its attributes (convert degrees values to radians
13356    and supply the defaults specified in FITS-WCS paper III). The FITS
13357    paper specifies units in which these parameters should be stored in a
13358    FITS header - distances are in metres and angles in degrees. */
13359       gmap = (AstMapping *) astGrismMap( "", status );
13360       pv = GetItem( &(store->pv), i, 0, s, NULL, method, class, status );
13361       astSetGrismG( gmap, ( pv != AST__BAD )?pv:0.0 );
13362       pv = GetItem( &(store->pv), i, 1, s, NULL, method, class, status );
13363       astSetGrismM( gmap, ( pv != AST__BAD )?(int) ( pv + 0.5 ):0);
13364       pv = GetItem( &(store->pv), i, 2, s, NULL, method, class, status );
13365       astSetGrismAlpha( gmap, ( pv != AST__BAD )?pv*AST__DD2R:0.0 );
13366       pv = GetItem( &(store->pv), i, 3, s, NULL, method, class, status );
13367       astSetGrismNR( gmap, ( pv != AST__BAD )?pv:1.0 );
13368       pv = GetItem( &(store->pv), i, 4, s, NULL, method, class, status );
13369       astSetGrismNRP( gmap, ( pv != AST__BAD )?pv:0.0 );
13370       pv = GetItem( &(store->pv), i, 5, s, NULL, method, class, status );
13371       astSetGrismEps( gmap, ( pv != AST__BAD )?pv*AST__DD2R:0.0 );
13372       pv = GetItem( &(store->pv), i, 6, s, NULL, method, class, status );
13373       astSetGrismTheta( gmap, ( pv != AST__BAD )?pv*AST__DD2R:0.0 );
13374 
13375 /* Store the reference wavelength found above as an attribute of the
13376    GrismMap. */
13377       astSetGrismWaveR( gmap, wcrv );
13378 
13379 /* Invert the GrismMap to get the (Wavelength -> grism parameter) Mapping, and
13380    then combine it with the (S -> Wavelength) Mapping to get the (S -> grism
13381    parameter) Mapping. */
13382       astInvert( gmap );
13383       map1 = (AstMapping *) astCmpMap( smap, gmap, 1, "", status );
13384 
13385 /* Convert the reference point value from wavelength to grism parameter. */
13386       astTran1( gmap, 1, &wcrv, 1, &gcrv );
13387 
13388 /* Find the rate of change of grism parameter with respect to the S
13389    system at the reference point, dg/dS. */
13390       dg = astRate( map1, &crv, 0, 0 );
13391       if( dg != AST__BAD && dg != 0.0 ) {
13392 
13393 /* FITS-WCS paper II requires headers to be constructed so that dS/dw = 1.0
13394    at the reference point. Therefore dg/dw = dg/dS. Create a WinMap which
13395    scales and shifts the "w" value to get the grism parameter value. */
13396          map2a = (AstMapping *) astZoomMap( 1, dg, "", status );
13397          map2b = (AstMapping *) astShiftMap( 1, &gcrv, "", status );
13398          map2 = (AstMapping *) astCmpMap( map2a, map2b, 1, "", status );
13399          map2a = astAnnul( map2a );
13400          map2b = astAnnul( map2b );
13401 
13402 /* The Mapping to be returned is the concatenation of the above Mapping
13403    (from w to g) with the Mapping from g to S. */
13404          astInvert( map1 );
13405          ret = (AstMapping *) astCmpMap( map2, map1, 1, "", status );
13406          map2 = astAnnul( map2 );
13407       }
13408       map1 = astAnnul( map1 );
13409       smap = astAnnul( smap );
13410       gmap = astAnnul( gmap );
13411    }
13412    wfrm = astAnnul( wfrm );
13413 
13414 /* Return the result */
13415    return ret;
13416 }
13417 
KeyFields(AstFitsChan * this,const char * filter,int maxfld,int * ubnd,int * lbnd,int * status)13418 static int KeyFields( AstFitsChan *this, const char *filter, int maxfld,
13419                     int *ubnd, int *lbnd, int *status ){
13420 
13421 /*
13422 *+
13423 *  Name:
13424 *     astKeyFields
13425 
13426 *  Purpose:
13427 *     Find the ranges taken by integer fields within the keyword names
13428 *     in a FitsChan.
13429 
13430 *  Type:
13431 *     Protected virtual function.
13432 
13433 *  Synopsis:
13434 *     #include "fitschan.h"
13435 *     int astKeyFields( AstFitsChan *this, const char *filter, int maxfld,
13436 *                       int *ubnd, int *lbnd )
13437 
13438 *  Class Membership:
13439 *     FitsChan method.
13440 
13441 *  Description:
13442 *     This function returns the number of cards within a FitsChan which
13443 *     refer to keywords which match the supplied filter template. If the
13444 *     filter contains any integer field specifiers (e.g. "%d", "%3d", etc),
13445 *     it also returns the upper and lower bounds found for the integer
13446 *     fields.
13447 
13448 *  Parameters:
13449 *     this
13450 *        Pointer to the FitsChan.
13451 *     filter
13452 *        The filter string.
13453 *     maxfld
13454 *        The size of the "ubnd" and "lbnd" arrays.
13455 *     ubnd
13456 *        A pointer to an integer array in which to return the
13457 *        upper bound found for each integer field in the filter.
13458 *        They are stored in the order in which they occur in the filter.
13459 *        If the filter contains too many fields to fit in the supplied
13460 *        array, the excess trailing fields are ignored.
13461 *     lbnd
13462 *        A pointer to an integer array in which to return the
13463 *        lower bound found for each integer field in the filter.
13464 
13465 *  Returned Value:
13466 *     astKeyFields()
13467 *        The total number of cards matching the supplied filter in the
13468 *        FitsChan.
13469 
13470 *  Filter Syntax:
13471 *     -  The criteria for a keyword name to match a filter template are
13472 *     as follows:
13473 *     -  All characters in the template other than "%" (and the field width
13474 *     and type specifiers which follow a "%") must be matched by an
13475 *     identical character in the test string.
13476       -  If a "%" occurs in the template, then the next character in the
13477 *     template should be a single digit specifying a field width. If it is
13478 *     zero, then the test string may contain zero or more matching characters.
13479 *     Otherwise, the test string must contain exactly the specified number
13480 *     of matching characters (i.e. 1 to 9). The field width digit may be
13481 *     omitted, in which case the test string must contain one or more matching
13482 *     characters. The next character in the template specifies the type of
13483 *     matching characters and must be one of "d", "c" or "f". Decimal digits
13484 *     are matched by "d", all upper (but not lower) case alphabetical
13485 *     characters are matched by "c", and all characters which may legally be
13486 *     found within a FITS keyword name are matched by "f".
13487 
13488 *  Examples:
13489 *     -  The filter "CRVAL1" accepts the single keyword CRVAL1.
13490 *     -  The filter "CRVAL%1d" accepts the single keyword CRVAL0, CRVAL1,
13491 *     CRVAL2, up to CRVAL9.
13492 *     -  The filter "CRVAL%d" accepts any keyword consisting of the string
13493 *     "CRVAL" followed by any integer value.
13494 *     -  The filter "CR%0s1" accepts any keyword starting with the string "CR"
13495 *     and ending with the character "1" (including CR1).
13496 
13497 *  Notes:
13498 *     -  The entire FitsChan is searched, irrespective of the setting of
13499 *     the Card attribute.
13500 *     -  If "maxfld" is supplied as zero, "ubnd" and "lbnd" are ignored,
13501 *     but the number of matching cards is still returned as the function value.
13502 *     -  If no matching cards are found in the FitsChan, or if there are no
13503 *     integer fields in the filter, then the lower and upper bounds are
13504 *     returned as zero and -1 (i.e. reversed).
13505 *     -  If an error has already occured, or if this function should fail
13506 *     for any reason, a value of zero is returned for the function value,
13507 *     and the lower and upper bounds are set to zero and -1.
13508 *-
13509 */
13510 
13511 /* Local Variables: */
13512    const char *class;     /* Object class */
13513    const char *method;    /* Method name */
13514    int *fields;           /* Pointer to array of field values */
13515    int i;                 /* Field index */
13516    int icard;             /* Index of current card on entry */
13517    int nmatch;            /* No. of matching cards */
13518    int nf;                /* No. of integer fields in the filter */
13519    int nfld;              /* No. of integer fields in current keyword name */
13520 
13521 /* Initialise the returned values. */
13522    nmatch = 0;
13523    for( i = 0; i < maxfld; i++ ){
13524       lbnd[ i ] = 0;
13525       ubnd[ i ] = -1;
13526    }
13527    nf = 0;
13528 
13529 /* Check the global error status. */
13530    if ( !astOK || !filter ) return nf;
13531 
13532 /* Ensure the source function has been called */
13533    ReadFromSource( this, status );
13534 
13535 /* Store the method name and object class for use in error messages. */
13536    method = "astKeyFields";
13537    class = astGetClass( this );
13538 
13539 /* Count the number of integer fields in the filter string. */
13540    nf = CountFields( filter, 'd', method, class, status );
13541 
13542 /* If this is larger than the supplied arrays, use the size of the arrays
13543    instead. */
13544    if( nf > maxfld ) nf = maxfld;
13545 
13546 /* Allocate memory to hold the integer field values extracted from
13547    each matching keyword. */
13548    fields = (int *) astMalloc( sizeof( int )*(size_t) nf );
13549 
13550 /* Save the current card index, and rewind the FitsChan. */
13551    icard = astGetCard( this );
13552    astClearCard( this );
13553 
13554 /* Check that the FitsChan is not empty and the pointer can be used. */
13555    if( !astFitsEof( this ) && astOK ){
13556 
13557 /* Initialise the returned bounds. Any excess elements in the array are left
13558    at the previously initialised values. */
13559       for( i = 0; i < nf; i++ ){
13560          lbnd[ i ] = INT_MAX;
13561          ubnd[ i ] = -INT_MAX;
13562       }
13563 
13564 /* Initialise the number of matching keywords. */
13565       nmatch = 0;
13566 
13567 /* Loop round all the cards in the FitsChan. */
13568       while( !astFitsEof( this ) && astOK ){
13569 
13570 /* If the current keyword name matches the filter, update the returned
13571    bounds and increment the number of matches. */
13572          if( Match( CardName( this, status ), filter, nf, fields, &nfld,
13573                     method, class, status ) ){
13574             for( i = 0; i < nf; i++ ){
13575                if( fields[ i ] > ubnd[ i ] ) ubnd[ i ] = fields[ i ];
13576                if( fields[ i ] < lbnd[ i ] ) lbnd[ i ] = fields[ i ];
13577             }
13578             nmatch++;
13579          }
13580 
13581 /* Move on to the next card. */
13582          MoveCard( this, 1, method, class, status );
13583       }
13584 
13585 /* If bounds were not found, returned 0 and -1. */
13586       for( i = 0; i < nf; i++ ){
13587          if( lbnd[ i ] == INT_MAX ){
13588             lbnd[ i ] = 0;
13589             ubnd[ i ] = -1;
13590          }
13591       }
13592    }
13593 
13594 /* Reinstate the original current card index. */
13595    astSetCard( this, icard );
13596 
13597 /* Free the memory used to hold the integer field values extracted from
13598    each matching keyword. */
13599    fields = (int *) astFree( (void *) fields );
13600 
13601 /* If an error has occurred, returned no matches and reversed bounds. */
13602    if( !astOK ){
13603       nmatch = 0;
13604       for( i = 0; i < maxfld; i++ ){
13605          lbnd[ i ] = 0;
13606          ubnd[ i ] = -1;
13607       }
13608    }
13609 
13610 /* Returned the answer. */
13611    return nmatch;
13612 }
13613 
FindFits(AstFitsChan * this,const char * name,char card[AST__FITSCHAN_FITSCARDLEN+1],int inc,int * status)13614 static int FindFits( AstFitsChan *this, const char *name,
13615                      char card[ AST__FITSCHAN_FITSCARDLEN + 1 ], int inc, int *status ){
13616 
13617 /*
13618 *++
13619 *  Name:
13620 c     astFindFits
13621 f     AST_FINDFITS
13622 
13623 *  Purpose:
13624 *     Find a FITS card in a FitsChan by keyword.
13625 
13626 *  Type:
13627 *     Public virtual function.
13628 
13629 *  Synopsis:
13630 c     #include "fitschan.h"
13631 
13632 c     int astFindFits( AstFitsChan *this, const char *name, char card[ 81 ],
13633 c                      int inc )
13634 f     RESULT = AST_FINDFITS( THIS, NAME, CARD, INC, STATUS )
13635 
13636 *  Class Membership:
13637 *     FitsChan member function.
13638 
13639 *  Description:
13640 *     This function searches for a card in a FitsChan by keyword. The
13641 *     search commences at the current card (identified by the Card
13642 *     attribute) and ends when a card is found whose FITS keyword
13643 *     matches the template supplied, or when the last card in the
13644 *     FitsChan has been searched.
13645 *
13646 *     If the search is successful (i.e. a card is found which matches
13647 c     the template), the contents of the card are (optionally)
13648 f     the template), the contents of the card are
13649 *     returned and the Card attribute is adjusted to identify the card
13650 *     found or, if required, the one following it. If the search is
13651 c     not successful, the function returns zero and the Card attribute
13652 f     not successful, the function returns .FALSE. and the Card attribute
13653 *     is set to the "end-of-file".
13654 
13655 *  Parameters:
13656 c     this
13657 f     THIS = INTEGER (Given)
13658 *        Pointer to the FitsChan.
13659 c     name
13660 f     NAME = CHARACTER * ( * ) (Given)
13661 c        Pointer to a null-terminated character string containing a
13662 f        A character string containing a
13663 *        template for the keyword to be found. In the simplest case,
13664 *        this should simply be the keyword name (the search is case
13665 *        insensitive and trailing spaces are ignored). However, this
13666 *        template may also contain "field specifiers" which are
13667 *        capable of matching a range of characters (see the "Keyword
13668 *        Templates" section for details). In this case, the first card
13669 *        with a keyword which matches the template will be found. To
13670 *        find the next FITS card regardless of its keyword, you should
13671 *        use the template "%f".
13672 c     card
13673 f     CARD = CHARACTER * ( 80 ) (Returned)
13674 c        An array of at least 81 characters (to allow room for a
13675 c        terminating null)
13676 f        A character variable with at least 80 characters
13677 *        in which the FITS card which is found will be returned.  If
13678 c        the search is not successful (or a NULL pointer is given), a
13679 f        the search is not successful, a
13680 *        card will not be returned.
13681 c     inc
13682 f     INC = LOGICAL (Given)
13683 c        If this value is zero (and the search is successful), the
13684 f        If this value is .FALSE. (and the search is successful), the
13685 *        FitsChan's Card attribute will be set to the index of the card
13686 c        that was found. If it is non-zero, however, the Card
13687 f        that was found. If it is .TRUE., however, the Card
13688 *        attribute will be incremented to identify the card which
13689 *        follows the one found.
13690 f     STATUS = INTEGER (Given and Returned)
13691 f        The global status.
13692 
13693 *  Returned Value:
13694 c     astFindFits()
13695 f     AST_FINDFITS = LOGICAL
13696 c        One if the search was successful, otherwise zero.
13697 f        .TRUE. if the search was successful, otherwise .FALSE..
13698 
13699 *  Notes:
13700 *     - The search always starts with the current card, as identified
13701 *     by the Card attribute. To ensure you search the entire contents
13702 *     of a FitsChan, you should first clear the Card attribute (using
13703 c     astClear). This effectively "rewinds" the FitsChan.
13704 f     AST_CLEAR). This effectively "rewinds" the FitsChan.
13705 *     - If a search is unsuccessful, the Card attribute is set to the
13706 *     "end-of-file" (i.e. to one more than the number of cards in the
13707 *     FitsChan). No error occurs.
13708 c     - A value of zero will be returned if this function is invoked
13709 f     - A value of .FALSE. will be returned if this function is invoked
13710 *     with the AST error status set, or if it should fail for any
13711 *     reason.
13712 
13713 *  Examples:
13714 c     result = astFindFits( fitschan, "%f", card, 1 );
13715 f     RESULT = AST_FINDFITS( FITSCHAN, '%f', CARD, .TRUE., STATUS )
13716 *        Returns the current card in a FitsChan and advances the Card
13717 *        attribute to identify the card that follows (the "%f"
13718 *        template matches any keyword).
13719 c     result = astFindFits( fitschan, "BITPIX", card, 1 );
13720 f     RESULT = AST_FINDFITS( FITSCHAN, 'BITPIX', CARD, .TRUE., STATUS )
13721 *        Searches a FitsChan for a FITS card with the "BITPIX" keyword
13722 *        and returns that card. The Card attribute is then incremented
13723 *        to identify the card that follows it.
13724 c     result = astFindFits( fitschan, "COMMENT", NULL, 0 );
13725 f     RESULT = AST_FINDFITS( FITSCHAN, 'COMMENT', CARD, .FALSE., STATUS )
13726 *        Sets the Card attribute of a FitsChan to identify the next
13727 c        COMMENT card (if any). The card itself is not returned.
13728 f        COMMENT card (if any) and returns that card.
13729 c     result = astFindFits( fitschan, "CRVAL%1d", card, 1 );
13730 f     RESULT = AST_FINDFITS( FITSCHAN, 'CRVAL%1d', CARD, .TRUE., STATUS )
13731 *        Searches a FitsChan for the next card with a keyword of the
13732 *        form "CRVALi" (for example, any of the keywords "CRVAL1",
13733 *        "CRVAL2" or "CRVAL3" would be matched). The card found (if
13734 *        any) is returned, and the Card attribute is then incremented
13735 *        to identify the following card (ready to search for another
13736 *        keyword with the same form, perhaps).
13737 
13738 *  Keyword Templates:
13739 *     The templates used to match FITS keywords are normally composed
13740 *     of literal characters, which must match the keyword exactly
13741 *     (apart from case). However, a template may also contain "field
13742 *     specifiers" which can match a range of possible characters. This
13743 *     allows you to search for keywords that contain (for example)
13744 *     numbers, where the digits comprising the number are not known in
13745 *     advance.
13746 *
13747 *     A field specifier starts with a "%" character. This is followed
13748 *     by an optional single digit (0 to 9) specifying a field
13749 *     width. Finally, there is a single character which specifies the
13750 
13751 *     type of character to be matched, as follows:
13752 *
13753 *     - "c": matches all upper case letters,
13754 *     - "d": matches all decimal digits,
13755 *     - "f": matches all characters which are permitted within a FITS
13756 *     keyword (upper case letters, digits, underscores and hyphens).
13757 *
13758 *     If the field width is omitted, the field specifier matches one
13759 *     or more characters. If the field width is zero, it matches zero
13760 *     or more characters. Otherwise, it matches exactly the number of
13761 
13762 *     characters specified. In addition to this:
13763 *
13764 *     - The template "%f" will match a blank FITS keyword consisting
13765 *     of 8 spaces (as well as matching all other keywords).
13766 *     - A template consisting of 8 spaces will match a blank keyword
13767 *     (only).
13768 *
13769 
13770 *     For example:
13771 *
13772 *     - The template "BitPix" will match the keyword "BITPIX" only.
13773 *     - The template "crpix%1d" will match keywords consisting of
13774 *     "CRPIX" followed by one decimal digit.
13775 *     - The template "P%c" will match any keyword starting with "P"
13776 *     and followed by one or more letters.
13777 *     - The template "E%0f" will match any keyword beginning with "E".
13778 *     - The template "%f" will match any keyword at all (including a
13779 *     blank one).
13780 *--
13781 */
13782 
13783 /* Local Variables: */
13784    char *c;               /* Pointer to next character to check */
13785    char *lname;           /* Pointer to copy of name without trailing spaces */
13786    const char *class;     /* Object class */
13787    const char *method;    /* Calling method */
13788    int ret;               /* Was a card found? */
13789 
13790 /* Check the global status, and supplied keyword name. */
13791    if( !astOK ) return 0;
13792 
13793 /* Ensure the source function has been called */
13794    ReadFromSource( this, status );
13795 
13796 /* Store the calling method and object class. */
13797    method = "astFindFits";
13798    class = astGetClass( this );
13799 
13800 /* Get a local copy of the keyword template. */
13801    lname = (char *) astStore( NULL, (void *) name, strlen(name) + 1 );
13802 
13803 /* Terminate it to exclude trailing spaces. */
13804    c = lname + strlen(lname) - 1;
13805    while( *c == ' ' && c >= lname ) *(c--) = 0;
13806 
13807 /* Use the private FindKeyCard function to find the card and make it the
13808    current card. Always use the supplied current card (if any) if the
13809    template is "%f" or "%0f". */
13810    if ( !strcmp( lname, "%f" ) || !strcmp( lname, "%0f" ) ){
13811       ret = astFitsEof( this ) ? 0 : 1;
13812    } else {
13813       ret = FindKeyCard( this, lname, method, class, status );
13814    }
13815 
13816 /* Only proceed if the card was found. */
13817    if( ret && astOK ){
13818 
13819 /* Format the current card if a destination string was supplied. */
13820       if( card ) FormatCard( this, card, method, status );
13821 
13822 /* Increment the current card pointer if required. */
13823       if( inc ) MoveCard( this, 1, method, class, status );
13824 
13825 /* Indicate that a card has been formatted. */
13826       ret = 1;
13827    }
13828 
13829 /* Free the memory holding the local copy of the keyword template. */
13830    lname = (char *) astFree( (void *) lname );
13831 
13832 /* If an errror has occurred, return zero. */
13833    if( !astOK ) ret = 0;
13834 
13835 /* Return the answer. */
13836    return ret;
13837 }
13838 
FindKeyCard(AstFitsChan * this,const char * name,const char * method,const char * class,int * status)13839 static int FindKeyCard( AstFitsChan *this, const char *name,
13840                         const char *method, const char *class, int *status ){
13841 /*
13842 *  Name:
13843 *     FindKeyCard
13844 
13845 *  Purpose:
13846 *     Find the next card refering to given keyword.
13847 
13848 *  Type:
13849 *     Private function.
13850 
13851 *  Synopsis:
13852 *     #include "fitschan.h"
13853 *     int FindKeyCard( AstFitsChan *this, const char *name,
13854 *                      const char *method, const char *class, int *status )
13855 
13856 *  Class Membership:
13857 *     FitsChan member function.
13858 
13859 *  Description:
13860 *     Finds the next card which refers to the supplied keyword and makes
13861 *     it the current card. The search starts with the current card and ends
13862 *     when it reaches the last card.
13863 
13864 *  Parameters:
13865 *     this
13866 *        Pointer to the FitsChan.
13867 *     name
13868 *        Pointer to a string holding the keyword template (using the
13869 *        syntax expected by the Match function).
13870 *     method
13871 *        Pointer to string holding name of calling method.
13872 *     status
13873 *        Pointer to the inherited status variable.
13874 
13875 *  Returned Value:
13876 *     A value of 1 is returned if a card was found refering to the given
13877 *     keyword. Otherwise zero is returned.
13878 
13879 *  Notes:
13880 *     -  If a NULL pointer is supplied for "name" then the current card
13881 *     is left unchanged.
13882 *     -  The current card is set to NULL (end-of-file) if no card can be
13883 *     found for the supplied keyword.
13884 */
13885 
13886 /* Local Variables: */
13887    int nfld;             /* Number of fields in keyword template */
13888    int ret;              /* Was a card found? */
13889 
13890 /* Check the global status, and supplied keyword name. */
13891    if( !astOK || !name ) return 0;
13892 
13893 /* Indicate that no card has been found yet. */
13894    ret = 0;
13895 
13896 /* Search forward through the list until all cards have been checked. */
13897    while( !astFitsEof( this ) && astOK ){
13898 
13899 /* Break out of the loop if the keyword name from the current card matches
13900    the supplied keyword name. */
13901       if( Match( CardName( this, status ), name, 0, NULL, &nfld, method, class, status ) ){
13902          ret = 1;
13903          break;
13904 
13905 /* Otherwise, move the current card on to the next card. */
13906       } else {
13907          MoveCard( this, 1, method, class, status );
13908       }
13909    }
13910 
13911 /* Return. */
13912    return ret;
13913 }
13914 
FitLine(AstMapping * map,double * g,double * g0,double * w0,double dim,double * tol,int * status)13915 static double *FitLine( AstMapping *map, double *g, double *g0, double *w0,
13916                         double dim, double *tol, int *status ){
13917 /*
13918 *  Name:
13919 *     FitLine
13920 
13921 *  Purpose:
13922 *     Check a Mapping for linearity.
13923 
13924 *  Type:
13925 *     Private function.
13926 
13927 *  Synopsis:
13928 *     #include "fitschan.h"
13929 *     double *FitLine( AstMapping *map, double *g, double *g0, double *w0,
13930 *                      double dim, double *tol, int *status )
13931 
13932 *  Class Membership:
13933 *     FitsChan member function.
13934 
13935 *  Description:
13936 *     This function applies the supplied Mapping to a set of points along
13937 *     a straight line in the input space. It checks to see if the transformed
13938 *     positions also lie on a straight line (in the output space). If so,
13939 *     it returns the vector along this line in the output space which
13940 *     corresponds to a unit vector along the line in the input space. If
13941 *     not, a NULL pointer is returned.
13942 *
13943 *     The returned vector is found by doing a least squares fit.
13944 
13945 *  Parameters:
13946 *     map
13947 *        A pointer to the Mapping to test. The number of outputs must be
13948 *        greater than or equal to the number of inputs.
13949 *     g
13950 *        A pointer to an array holding a unit vector within the input space
13951 *        defining the straight line to be checked. The number of elements
13952 *        within this array should equal the number of inputs for "map".
13953 *     g0
13954 *        A pointer to an array holding a position within the input space
13955 *        giving the central position of the vector "g". The number of elements
13956 *        within this array should equal the number of inputs for "map".
13957 *     w0
13958 *        A pointer to an array holding a vector within the output space
13959 *        which corresponds to "g0". The number of elements within this array
13960 *        should equal the number of outputs for "map".
13961 *     dim
13962 *        The length of the pixel axis, or AST__BAD if unknown.
13963 *     tol
13964 *        Pointer to an array holding the tolerance for equality on each
13965 *        output axis.
13966 *     status
13967 *        Pointer to the inherited status variable.
13968 
13969 *  Returned Value:
13970 *     A pointer to dynamically allocated memory holding the required vector
13971 *     in the output space. The number of elements in this vector will equal
13972 *     the number of outputs for "map". The memory should be freed using
13973 *     astFree when no longer needed. If the Mapping is not linear, NULL
13974 *     is returned.
13975 
13976 *  Notes:
13977 *     -  NULL is returned if an error occurs.
13978 */
13979 
13980 /* Local Constants: */
13981 #define NPO2 50
13982 #define NP (2*NPO2+1)
13983 
13984 /* Local Variables: */
13985    AstPointSet *pset1;
13986    AstPointSet *pset2;
13987    double **ptr1;
13988    double **ptr2;
13989    double *offset;
13990    double *pax;
13991    double *ret;
13992    double *voffset;
13993    double dax;
13994    double denom;
13995    double gap;
13996    double sd2;
13997    double sd;
13998    double sdw;
13999    double sw;
14000    double wmax;
14001    double wmin;
14002    int i;
14003    int j;
14004    int n;
14005    int nin;
14006    int nout;
14007    int ok;
14008 
14009 /* Initialise */
14010    ret = NULL;
14011 
14012 /* Check the inherited status and supplied axis size. */
14013    if( !astOK || dim == 0.0 ) return ret;
14014 
14015 /* Get the number of inputs and outputs for the Mapping. Return if the
14016    number of outputs is smaller than the number of inputs. */
14017    nin = astGetNin( map );
14018    nout = astGetNout( map );
14019    if( nout < nin ) return ret;
14020 
14021 /* Check the supplied position is good on all axes. */
14022    for( j = 0; j < nout; j++ ) {
14023       if( w0[ j ] == AST__BAD ) return ret;
14024    }
14025 
14026 /* We use NP points in the fit. If a value for "dim" has been supplied,
14027    we use points evenly distributed over one tenth of this size, If
14028    not, we use a gap of 1.0 (corresponds to an axis length of 100 pixels).
14029    Choose the gap. */
14030    gap = ( dim != AST__BAD ) ? 0.1*dim/NP : 1.0;
14031 
14032 /* Create PointSets to hold the input and output positions. */
14033    pset1 = astPointSet( NP, nin, "", status );
14034    ptr1 = astGetPoints( pset1 );
14035    pset2 = astPointSet( NP, nout, "", status );
14036    ptr2 = astGetPoints( pset2 );
14037 
14038 /* Allocate the returned array. */
14039    ret = astMalloc( sizeof( double )*(size_t) nout );
14040 
14041 /* Allocate workspace to hold the constant offsets of the fit. */
14042    offset = astMalloc( sizeof( double )*(size_t) nout );
14043    voffset = astMalloc( sizeof( double )*(size_t) nout );
14044 
14045 /* Indicate we have not yet got a usable returned vector. */
14046    ok = 0;
14047 
14048 /* Check we can use the pointers safely. */
14049    if( astOK ) {
14050 
14051 /* Set up the input positions: NP evenly spaced points along a line with
14052    unit direction vector given by "g", centred at position given by "g0". */
14053       for( j = 0; j < nin; j++ ) {
14054          pax = ptr1[ j ];
14055          dax = g[ j ]*gap;
14056          for( i = -NPO2; i <= NPO2; i++ ) *(pax++) = g0[ j ] + dax*i;
14057       }
14058 
14059 /* Transform these positions into the output space. */
14060       (void) astTransform( map, pset1, 1, pset2 );
14061 
14062 /* Loop over all output axes, finding the component of the returned vector. */
14063       ok = 1;
14064       for( j = 0; j < nout; j++ ) {
14065          pax = ptr2[ j ];
14066 
14067 /* Now loop over all the transformed points to form the other required
14068    sums. We also form the sums needed to estimate the variance in the
14069    calculated offset. */
14070          sdw = 0.0;
14071          sw = 0.0;
14072          sd = 0.0;
14073          sd2 = 0.0;
14074          n = 0;
14075          wmax = -DBL_MAX;
14076          wmin = DBL_MAX;
14077          for( i = -NPO2; i <= NPO2; i++, pax++ ) {
14078             if( *pax != AST__BAD ) {
14079 
14080 /* Increment the required sums. */
14081                sdw += i*(*pax);
14082                sw += (*pax);
14083                sd += i;
14084                sd2 += i*i;
14085                n++;
14086                if( *pax > wmax ) wmax = *pax;
14087                if( *pax < wmin ) wmin = *pax;
14088             }
14089          }
14090 
14091 /* If a reasonable number of good points were found, find the component of
14092    the returned vector (excluding a scale factor of 1/gap). */
14093          denom = sd2*n - sd*sd;
14094          if( n > NP/4 && denom != 0.0 ) {
14095 
14096 /* Find the constant scale factor to return for this axis. If the axis
14097    value is constant, return zero. */
14098             if( wmax > wmin ) {
14099                ret[ j ] = (sdw*n - sw*sd)/denom;
14100             } else {
14101                ret[ j ] = 0.0;
14102             }
14103 
14104 /* Now find the constant offset for this axis. */
14105             offset[ j ] = (sw*sd2 - sdw*sd)/denom;
14106          } else {
14107             ok = 0;
14108             break;
14109          }
14110       }
14111 
14112 /* Now check that the fit is good enough. Each axis is checked separately.
14113    All axes must be good. */
14114       if( ok ) {
14115          for( j = 0; j < nout; j++ ) {
14116 
14117 /* Store the axis values implied by the linear fit in the now un-needed ptr1[0]
14118    array. */
14119             pax = ptr1[ 0 ];
14120             for( i = -NPO2; i <= NPO2; i++, pax++ ) {
14121                *pax = i*ret[ j ] + offset[ j ];
14122             }
14123 
14124 /* Test the fit to see if we beleive that the mapping is linear. If
14125    it is, scale the returned value from units of "per gap" to units of
14126    "per pixel". Otherwise,indicate that he returned vector is unusable. */
14127             if( FitOK( NP, ptr2[ j ], ptr1[ 0 ], tol[ j ], status ) ) {
14128                ret[ j ] /= gap;
14129             } else {
14130                ok = 0;
14131                break;
14132             }
14133          }
14134       }
14135    }
14136 
14137 /* Annul the PointSets. */
14138    pset1 = astAnnul( pset1 );
14139    pset2 = astAnnul( pset2 );
14140 
14141 /* Free memory. */
14142    offset = astFree( offset );
14143    voffset = astFree( voffset );
14144 
14145 /* If an error has occurred, or if the returned vector is unusable,
14146    free any returned memory */
14147    if( !astOK || !ok ) ret = astFree( ret );
14148 
14149 /* Return the answer. */
14150    return ret;
14151 
14152 /* Undefine local constants: */
14153 #undef NP
14154 #undef NPO2
14155 }
14156 
FitsEof(AstFitsChan * this,int * status)14157 static int FitsEof( AstFitsChan *this, int *status ){
14158 
14159 /*
14160 *+
14161 *  Name:
14162 *     astFitsEof
14163 
14164 *  Purpose:
14165 *     See if the FitsChan is at "end-of-file".
14166 
14167 *  Type:
14168 *     Protected virtual function.
14169 
14170 *  Synopsis:
14171 *     #include "fitschan.h"
14172 *     int astFitsEof( AstFitsChan *this )
14173 
14174 *  Class Membership:
14175 *     FitsChan method.
14176 
14177 *  Description:
14178 *     A value of zero is returned if any more cards remain to be read from the
14179 *     FitsChan. Otherwise a value of 1 is returned. Thus, it is
14180 *     equivalent to testing the FitsChan for an "end-of-file" condition.
14181 
14182 *  Parameters:
14183 *     this
14184 *        Pointer to the FitsChan.
14185 
14186 *  Returned Value:
14187 *     One if no more cards remain to be read, otherwise zero.
14188 
14189 *  Notes:
14190 *     - This function attempts to execute even if an error has already
14191 *     occurred.
14192 *-
14193 */
14194 
14195 /* Check the supplied object. */
14196    if( !this ) return 1;
14197 
14198 /* Ensure the source function has been called */
14199    ReadFromSource( this, status );
14200 
14201 /* If no more cards remain to be read, the current card pointer in the
14202    FitsChan will be NULL. Return an appropriate integer value. */
14203    return  this->card ? 0 : 1;
14204 }
14205 
FitsSof(AstFitsChan * this,int * status)14206 static int FitsSof( AstFitsChan *this, int *status ){
14207 
14208 /*
14209 *+
14210 *  Name:
14211 *     FitsSof
14212 
14213 *  Purpose:
14214 *     See if the FitsChan is at "start-of-file".
14215 
14216 *  Type:
14217 *     Private function.
14218 
14219 *  Synopsis:
14220 *     #include "fitschan.h"
14221 
14222 *     int FitsSof( AstFitsChan *this, int *status )
14223 
14224 *  Class Membership:
14225 *     FitsChan member function.
14226 
14227 *  Description:
14228 *     A value of 1 is returned if the current card is the first card in
14229 *     the FitsChan. Otherwise a value of zero is returned.  This function
14230 *     is much more efficient than "astGetCard(this) <= 1" .
14231 
14232 *  Parameters:
14233 *     this
14234 *        Pointer to the FitsChan.
14235 *     status
14236 *        Pointer to the inherited status variable.
14237 
14238 *  Returned Value:
14239 *     Zero if the current card is the first card.
14240 
14241 *  Notes:
14242 *     - This function attempts to execute even if an error has already
14243 *     occurred.
14244 *     - A non-zero value is returned if the FitsChan is empty.
14245 *-
14246 */
14247 
14248 /* Return if no FitsChan was supplied, or if the FitsChan is empty. */
14249    if ( !this || !this->head ) return 1;
14250 
14251 /* Ensure the source function has been called */
14252    ReadFromSource( this, status );
14253 
14254 /* If the current card is at the head of the linked list, it is the first
14255    card. */
14256    return  this->card == this->head;
14257 }
14258 
14259 /*
14260 *++
14261 *  Name:
14262 c     astGetFits<X>
14263 f     AST_GETFITS<X>
14264 
14265 *  Purpose:
14266 *     Get a named keyword value from a FitsChan.
14267 
14268 *  Type:
14269 *     Public virtual function.
14270 
14271 *  Synopsis:
14272 c     #include "fitschan.h"
14273 
14274 c     int astGetFits<X>( AstFitsChan *this, const char *name, <X>type *value )
14275 f     RESULT = AST_GETFITS<X>( THIS, NAME, VALUE, STATUS )
14276 
14277 *  Class Membership:
14278 *     FitsChan method.
14279 
14280 *  Description:
14281 *     This is a family of functions which gets a value for a named keyword,
14282 *     or the value of the current card, from a FitsChan using one of several
14283 *     different data types. The data type of the returned value is selected
14284 *     by replacing <X> in the function name by one of the following strings
14285 *     representing the recognised FITS data types:
14286 *
14287 *     - CF - Complex floating point values.
14288 *     - CI - Complex integer values.
14289 *     - F  - Floating point values.
14290 *     - I  - Integer values.
14291 *     - L  - Logical (i.e. boolean) values.
14292 *     - S  - String values.
14293 *     - CN - A "CONTINUE" value, these are treated like string values, but
14294 *            are encoded without an equals sign.
14295 *
14296 *     The data type of the "value"
14297 c     parameter
14298 f     argument
14299 
14300 *     depends on <X> as follows:
14301 *
14302 c     - CF - "double *" (a pointer to a 2 element array to hold the real and
14303 c            imaginary parts of the complex value).
14304 c     - CI - "int *" (a pointer to a 2 element array to hold the real and
14305 c            imaginary parts of the complex value).
14306 c     - F  - "double *".
14307 c     - I  - "int *".
14308 c     - L  - "int *".
14309 c     - S  - "char **" (a pointer to a static "char" array is returned at the
14310 c            location given by the "value" parameter, Note, the stored string
14311 c            may change on subsequent invocations of astGetFitsS so a
14312 c            permanent copy should be taken of the string if necessary).
14313 c     - CN - Like"S".
14314 f     - CF - DOUBLE PRECISION(2) (a 2 element array to hold the real and
14315 f            imaginary parts of the complex value).
14316 f     - CI - INTEGER(2) (a 2 element array to hold the real and imaginary
14317 f            parts of the complex value).
14318 f     - F  - DOUBLE PRECISION.
14319 f     - I  - INTEGER
14320 f     - L  - LOGICAL
14321 f     - S  - CHARACTER
14322 f     - CN - CHARACTER
14323 
14324 *  Parameters:
14325 c     this
14326 f     THIS = INTEGER (Given)
14327 *        Pointer to the FitsChan.
14328 c     name
14329 f     NAME = CHARACTER * ( * ) (Given)
14330 c        Pointer to a null-terminated character string
14331 f        A character string
14332 *        containing the FITS keyword name. This may be a complete FITS
14333 *        header card, in which case the keyword to use is extracted from
14334 *        it. No more than 80 characters are read from this string. If
14335 c        NULL
14336 f        a single dot '.'
14337 *        is supplied, the value of the current card is returned.
14338 c     value
14339 f     VALUE = <X>type (Returned)
14340 c        A pointer to a
14341 f        A
14342 *        buffer to receive the keyword value. The data type depends on <X>
14343 *        as described above. The conents of the buffer on entry are left
14344 *        unchanged if the keyword is not found.
14345 f     STATUS = INTEGER (Given and Returned)
14346 f        The global status.
14347 
14348 *  Returned Value:
14349 c     astGetFits<X><X>()
14350 f     AST_GETFITS<X> = LOGICAL
14351 c        A value of zero
14352 f        .FALSE.
14353 *        is returned if the keyword was not found in the FitsChan (no error
14354 *        is reported). Otherwise, a value of
14355 c        one
14356 f        .TRUE.
14357 *        is returned.
14358 
14359 *  Notes:
14360 *     -  If a name is supplied, the card following the current card is
14361 *     checked first. If this is not the required card, then the rest of the
14362 *     FitsChan is searched, starting with the first card added to the
14363 *     FitsChan. Therefore cards should be accessed in the order they are
14364 *     stored in the FitsChan (if possible) as this will minimise the time
14365 *     spent searching for cards.
14366 *     -  If the requested card is found, it becomes the current card,
14367 *     otherwise the current card is left pointing at the "end-of-file".
14368 *     -  If the stored keyword value is not of the requested type, it is
14369 *     converted into the requested type.
14370 *     -  If the keyword is found in the FitsChan, but has no associated
14371 *     value, an error is reported. If necessary, the
14372 c     astTestFits
14373 f     AST_TESTFITS
14374 *     function can be used to determine if the keyword has a defined
14375 *     value in the FitsChan prior to calling this function.
14376 *     -  An error will be reported if the keyword name does not conform
14377 *     to FITS requirements.
14378 c     -  Zero
14379 *     -  .FALSE.
14380 *     is returned as the function value if an error has already occurred,
14381 *     or if this function should fail for any reason.
14382 *     - The FITS standard says that string keyword values should be
14383 *     padded with trailing spaces if they are shorter than 8 characters.
14384 *     For this reason, trailing spaces are removed from the string
14385 *     returned by
14386 c     astGetFitsS
14387 f     AST_GETFITSS
14388 *     if the original string (including any trailing spaces) contains 8
14389 *     or fewer characters. Trailing spaces are not removed from longer
14390 *     strings.
14391 *--
14392 */
14393 
14394 /* Define a macro which expands to the implementation of the astGetFits<X>
14395    routine for a given data type. */
14396 #define MAKE_FGET(code,ctype,ftype) \
14397 static int GetFits##code( AstFitsChan *this, const char *name, ctype value, int *status ){ \
14398 \
14399 /* Local Variables: */ \
14400    const char *class;     /* Object class */ \
14401    const char *method;    /* Calling method */ \
14402    char *lcom;            /* Supplied keyword comment */ \
14403    char *lname;           /* Supplied keyword name */ \
14404    char *lvalue;          /* Supplied keyword value */ \
14405    char *string;          /* Pointer to returned string value */ \
14406    char *c;               /* Pointer to next character */ \
14407    int cl;                /* Length of string value */ \
14408    int ret;               /* The returned value */ \
14409 \
14410 /* Check the global error status. */ \
14411    if ( !astOK ) return 0; \
14412 \
14413 /* Ensure the source function has been called */ \
14414    ReadFromSource( this, status ); \
14415 \
14416 /* Store the calling method and object class. */ \
14417    method = "astGetFits"#code; \
14418    class = astGetClass( this ); \
14419 \
14420 /* Initialise the returned value. */ \
14421    ret = 0; \
14422 \
14423 /* Extract the keyword name from the supplied string. */ \
14424    if( name ) { \
14425       (void) Split( name, &lname, &lvalue, &lcom, method, class, status ); \
14426    } else { \
14427       lname = NULL; \
14428       lvalue = NULL; \
14429       lcom = NULL; \
14430    } \
14431 \
14432 /* Attempt to find a card in the FitsChan refering to this keyword, \
14433    and make it the current card. Only proceed if a card was found. No \
14434    need to do the search if the value of the current card is required. */ \
14435    if( !lname || SearchCard( this, lname, method, class, status ) ){ \
14436 \
14437 /* Convert the stored data value to the requested type, and store it in \
14438    the supplied buffer. */ \
14439       if( !CnvValue( this, ftype, 0, value, method, status ) && astOK ) { \
14440          astError( AST__FTCNV, "%s(%s): Cannot convert FITS keyword " \
14441                    "'%s' to %s.", status, method, class, \
14442                    CardName( this, status ), type_names[ ftype ] ); \
14443       } \
14444 \
14445 /* If the returned value is a string containing 8 or fewer characters, \
14446    replace trailing spaces with null characters. */ \
14447       if( astOK ) { \
14448          if( ftype == AST__STRING ) { \
14449             string = *( (char **) value ); \
14450             if( string ) { \
14451                cl =strlen( string ); \
14452                if( cl <= 8 ) { \
14453                   c = string + cl - 1; \
14454                   while( *c == ' ' && c > string ) { \
14455                      *c = 0; \
14456                      c--; \
14457                   } \
14458                } \
14459             } \
14460          } \
14461 \
14462 /* Indicate that a value is available. */ \
14463          ret = 1; \
14464       } \
14465 \
14466    } \
14467 \
14468 /* Context error message. */ \
14469    if( !astOK && lname && *lname ) { \
14470       astError( astStatus, "%s(%s): Cannot get value for FITS keyword " \
14471                    "'%s'.", status, method, class, lname ); \
14472    } \
14473 \
14474 /* Release the memory used to hold keyword name, value and comment strings. */ \
14475    lname = (char *) astFree( (void *) lname ); \
14476    lvalue = (char *) astFree( (void *) lvalue ); \
14477    lcom = (char *) astFree( (void *) lcom ); \
14478 \
14479 /* Return the answer. */ \
14480    return ret; \
14481 \
14482 }
14483 
14484 /* Use the above macro to give defintions for the astGetFits<X> method
14485    for each FITS data type. */
MAKE_FGET(CF,double *,AST__COMPLEXF)14486 MAKE_FGET(CF,double *,AST__COMPLEXF)
14487 MAKE_FGET(CI,int *,AST__COMPLEXI)
14488 MAKE_FGET(F,double *,AST__FLOAT)
14489 MAKE_FGET(I,int *,AST__INT)
14490 MAKE_FGET(L,int *,AST__LOGICAL)
14491 MAKE_FGET(S,char **,AST__STRING)
14492 MAKE_FGET(CN,char **,AST__CONTINUE)
14493 #undef MAKE_FGET
14494 
14495 static int FitsGetCom( AstFitsChan *this, const char *name,
14496                        char **comment, int *status ){
14497 
14498 /*
14499 *+
14500 *  Name:
14501 *     astFitsGetCom
14502 
14503 *  Purpose:
14504 *     Get a keyword comment from a FitsChan.
14505 
14506 *  Type:
14507 *     Protected virtual function.
14508 
14509 *  Synopsis:
14510 *     #include "fitschan.h"
14511 
14512 *     int astFitsGetCom( AstFitsChan *this, const char *name,
14513 *                        char **comment )
14514 
14515 *  Class Membership:
14516 *     FitsChan method.
14517 
14518 *  Description:
14519 *     This function gets the comment associated with the next occurrence of
14520 *     a named keyword in a FitsChan.
14521 
14522 *  Parameters:
14523 *     this
14524 *        Pointer to the FitsChan.
14525 *     name
14526 *        A pointer to a
14527 *        string holding the keyword name. This may be a complete FITS
14528 *        header card, in which case the keyword to use is extracted from
14529 *        it. No more than 80 characters are read from this string.
14530 *     comment
14531 *        A pointer to a location at which to return a pointer to a string
14532 *        holding the keyword comment. Note, the stored string will change on
14533 *        subsequent invocations of astFitsGetCom so a permanent copy
14534 *        should be taken of the string if necessary.
14535 
14536 *  Returned Value:
14537 *     astFitsGetCom()
14538 *        A value of zero is returned if the keyword was not found before
14539 *        the end of the FitsChan was reached (no error is reported).
14540 *        Otherwise, a value of one is returned.
14541 
14542 *  Notes:
14543 *     -  If a NULL pointer is supplied for "name" then the comment from
14544 *     the current card is returned.
14545 *     -  The returned value is obtained from the next card refering to
14546 *     the required keyword, starting the search with the current card.
14547 *     Any cards occuring before the current card are not seached. If
14548 *     the entire contents of the FitsChan must be searched, then ensure
14549 *     the current card is the first card in the FitsChan by clearing the Card
14550 *     attribute. This effectively "rewinds" the FitsChan.
14551 *     -  The current card is updated to become the card following the one
14552 *     read by this function. If the card read by this function is the
14553 *     last one in the FitsChan, then the current card is left pointing at the
14554 *     "end-of-file".
14555 *     -  An error will be reported if the keyword name does not conform
14556 *     to FITS requirements.
14557 *     -  A NULL pointer is returned for the comment string if the keyword
14558 *     has no comment.
14559 *     -  Zero is returned as the function value if an error has already
14560 *     occurred, or if this function should fail for any reason.
14561 *-
14562 */
14563 
14564 /* Local Variables: */
14565    astDECLARE_GLOBALS     /* Declare the thread specific global data */
14566    const char *method;    /* Calling method */
14567    const char *class;     /* Object class */
14568    char *lcom;            /* Supplied keyword comment */
14569    char *lname;           /* Supplied keyword name */
14570    char *lvalue;          /* Supplied keyword value */
14571    int ret;               /* The returned value */
14572 
14573 /* Check the global error status. */
14574    if ( !astOK ) return 0;
14575 
14576 /* Get a pointer to the structure holding thread-specific global data. */
14577    astGET_GLOBALS(this);
14578 
14579 /* Ensure the source function has been called */
14580    ReadFromSource( this, status );
14581 
14582 /* Initialise the returned value. */
14583    ret = 0;
14584 
14585 /* Store the method name and object class. */
14586    method = "astFitsGetCom";
14587    class = astGetClass( this );
14588 
14589 /* Extract the keyword name from the supplied string (if supplied). */
14590    if( name ){
14591       (void) Split( name, &lname, &lvalue, &lcom, method, class, status );
14592    } else {
14593       lname = NULL;
14594       lcom = NULL;
14595       lvalue = NULL;
14596    }
14597 
14598 /* Find the next card in the FitsChan refering to this keyword. This will
14599    be the current card if no keyword name was supplied. The matching card
14600    is made the current card. Only proceed if a card was found. */
14601    if( FindKeyCard( this, lname, method, class, status ) ){
14602 
14603 /* Copy the comment into a static buffer, and return a pointer to it. */
14604       if( CardComm( this, status ) ){
14605          (void) strncpy( fitsgetcom_sval, CardComm( this, status ), AST__FITSCHAN_FITSCARDLEN );
14606          fitsgetcom_sval[ AST__FITSCHAN_FITSCARDLEN ] = 0;
14607          if( comment ) *comment = fitsgetcom_sval;
14608       } else {
14609          if( comment ) *comment = NULL;
14610       }
14611 
14612 /* Move on to the next card. */
14613       MoveCard( this, 1, method, class, status );
14614 
14615 /* Indicate that a value is available. */
14616       if( astOK ) ret = 1;
14617    }
14618 
14619 /* Release the memory used to hold keyword name, value and comment strings. */
14620    lname = (char *) astFree( (void *) lname );
14621    lvalue = (char *) astFree( (void *) lvalue );
14622    lcom = (char *) astFree( (void *) lcom );
14623 
14624 /* Return the answer. */
14625    return ret;
14626 }
14627 
SetFits(AstFitsChan * this,const char * keyname,void * value,int type,const char * comment,int overwrite,int * status)14628 static int SetFits( AstFitsChan *this, const char *keyname, void *value,
14629                     int type, const char *comment, int overwrite, int *status ){
14630 
14631 /*
14632 *  Name:
14633 *     SetFits
14634 
14635 *  Purpose:
14636 *     Store a keyword value of any type in a FitsChan.
14637 
14638 *  Type:
14639 *     Private function.
14640 
14641 *  Synopsis:
14642 *     #include "fitschan.h"
14643 
14644 *     int SetFits( AstFitsChan *this, const char *keyname, void *value,
14645 *                  int type, const char *comment, int overwrite, int *status )
14646 
14647 *  Class Membership:
14648 *     FitsChan member function.
14649 
14650 *  Description:
14651 *     This function stores the supplied value for the supplied keyword
14652 *     in the supplied FitsChan, assuming it is of the supplied data type.
14653 
14654 *  Parameters:
14655 *     this
14656 *        Pointer to the FitsChan.
14657 *     name
14658 *        A pointer to a string holding the keyword name.
14659 *     value
14660 *        A pointer to a buffer holding the keyword value. For strings,
14661 *        the buffer should hold the address of a pointer to the character
14662 *        string.
14663 *     type
14664 *        The keyword type.
14665 *     comment
14666 *        A pointer to a string holding a comment to associated with the
14667 *        keyword. If a NULL pointer or a blank string is supplied, then
14668 *        any comment included in the string supplied for the "name" parameter
14669 *        is used instead. If "name" contains no comment, then any existing
14670 *        comment in the card being over-written is retained, or a NULL
14671 *        pointer is stored if a new card is being inserted. If the data
14672 *        value being stored for the card is the same as the card being
14673 *        over-written, then any existing comment is retained.
14674 *     overwrite
14675 *        If non-zero, the new card formed from the supplied keyword name,
14676 *        value and comment string over-writes the current card, and the
14677 *        current card is incremented to refer to the next card. If zero, the
14678 *        new card is inserted in front of the current card and the current
14679 *        card is left unchanged. In either case, if the current card on
14680 *        entry points to the "end-of-file", the new card is appended to the
14681 *        end of the list.
14682 *     status
14683 *        Pointer to the inherited status variable.
14684 
14685 *  Returned Value:
14686 *     A value of 0 is returned if the value could not be stored for any
14687 *     reason. A value of 1 is returned otherwise.
14688 
14689 *  Notes:
14690 *     -  Nothing is stored in the FitsChan and a value of zero is returned
14691 *     (but no error is reported) if an AST__FLOAT value is supplied equal
14692 *     to AST__BAD.
14693 */
14694 
14695 /* Local Variables: */
14696    const char *cval;
14697    const char *ecval;
14698    double dval;
14699    double ecdval[ 2 ];
14700    double edval;
14701    int ecival[ 2 ];
14702    int eival;
14703    int ival;
14704    int ret;
14705 
14706 /* Check the global status, and the supplied pointer. */
14707    if( !astOK || !value ) return 0;
14708 
14709 /* Initialise the returned value to indicate that the supplied name was
14710    stored. */
14711    ret = 1;
14712 
14713 /* Check each data type in turn. */
14714    if( type == AST__FLOAT ){
14715       dval = *( (double *) value );
14716       if( dval != AST__BAD ) {
14717 
14718 /* If the data value has not changed, and the card has a coment,
14719    set the comment pointer NULL so that the existing comment will be
14720    retained. */
14721          if( overwrite && CnvValue( this, type, 0, &edval, "SetFits",
14722                                     status ) &&
14723              CardComm( this, status ) ) {
14724             if( EQUAL( edval, dval ) ) comment = NULL;
14725          }
14726          astSetFitsF( this, keyname, dval, comment, overwrite );
14727       } else {
14728          ret = 0;
14729       }
14730    } else if( type == AST__STRING ){
14731       cval = *( (char **) value);
14732       if( cval ){
14733 
14734 /* If the data value has not changed, retain the original comment. */
14735          if( overwrite && CnvValue( this, type, 0, &ecval, "SetFits",
14736                                     status ) &&
14737              CardComm( this, status ) ) {
14738             if( Similar( ecval, cval, status ) ) comment = NULL;
14739          }
14740 
14741 /* Ignore comments if they are identical to the keyword value. */
14742          if( comment && !strcmp( cval, comment ) ) comment = NULL;
14743          astSetFitsS( this, keyname, cval, comment, overwrite );
14744       } else {
14745          ret = 0;
14746       }
14747    } else if( type == AST__CONTINUE ){
14748       cval = *( (char **) value);
14749       if( cval ){
14750          astSetFitsCN( this, keyname, cval, comment, overwrite );
14751       } else {
14752          ret = 0;
14753       }
14754    } else if( type == AST__COMMENT ){
14755       astSetFitsCom( this, keyname, comment, overwrite );
14756    } else if( type == AST__INT ){
14757       ival = *( (int *) value );
14758 
14759 /* If the data value has not changed, retain the original comment. */
14760       if( overwrite && CnvValue( this, type, 0, &eival, "SetFits",
14761                                  status ) &&
14762          CardComm( this, status ) ) {
14763          if( eival == ival ) comment = NULL;
14764       }
14765       astSetFitsI( this, keyname, ival, comment, overwrite );
14766    } else if( type == AST__COMPLEXF ){
14767       if( ( (double *) value )[0] != AST__BAD &&
14768           ( (double *) value )[1] != AST__BAD ) {
14769 
14770 /* If the data value has not changed, retain the original comment. */
14771          if( overwrite && CnvValue( this, type, 0, ecdval, "SetFits",
14772                                     status ) &&
14773              CardComm( this, status ) ) {
14774             if( EQUAL( ecdval[ 0 ], ( (double *) value )[ 0 ] ) &&
14775                 EQUAL( ecdval[ 1 ], ( (double *) value )[ 1 ] ) ) comment = NULL;
14776          }
14777          astSetFitsCF( this, keyname, (double *) value, comment, overwrite );
14778       } else {
14779          ret = 0;
14780       }
14781    } else if( type == AST__COMPLEXI ){
14782 
14783 /* If the data value has not changed, retain the original comment. */
14784       if( overwrite && CnvValue( this, type, 0, ecival, "SetFits",
14785                                  status ) &&
14786           CardComm( this, status ) ) {
14787          if( ecival[ 0 ] == ( (int *) value )[ 0 ] &&
14788              ecival[ 1 ] == ( (int *) value )[ 1 ] ) comment = NULL;
14789       }
14790       astSetFitsCI( this, keyname, (int *) value, comment, overwrite );
14791    } else if( type == AST__LOGICAL ){
14792       ival = ( *( (int *) value ) != 0 );
14793 
14794 /* If the data value has not changed, retain the original comment. */
14795       if( overwrite && CnvValue( this, type, 0, &eival, "SetFits",
14796                                  status ) &&
14797           CardComm( this, status ) ) {
14798          if( eival == ival ) comment = NULL;
14799       }
14800       astSetFitsL( this, keyname, ival, comment, overwrite );
14801    } else if( type == AST__UNDEF ){
14802       if( overwrite && CardType( this, status ) == AST__UNDEF && CardComm( this, status ) ) {
14803          comment = NULL;
14804       }
14805       astSetFitsU( this, keyname, comment, overwrite );
14806    }
14807    return ret;
14808 }
14809 
14810 /*
14811 *++
14812 *  Name:
14813 c     astSetFits<X>
14814 f     AST_SETFITS<X>
14815 
14816 *  Purpose:
14817 *     Store a keyword value in a FitsChan.
14818 
14819 *  Type:
14820 *     Public virtual function.
14821 
14822 *  Synopsis:
14823 c     #include "fitschan.h"
14824 
14825 c     void astSetFits<X>( AstFitsChan *this, const char *name, <X>type value,
14826 c                         const char *comment, int overwrite )
14827 f     CALL AST_SETFITS<X>( THIS, NAME, VALUE, COMMENT, OVERWRITE, STATUS )
14828 
14829 *  Class Membership:
14830 *     FitsChan method.
14831 
14832 *  Description:
14833 c     This is a family of functions which store values for named keywords
14834 f     This is a family of routines which store values for named keywords
14835 *     within a FitsChan at the current card position. The supplied keyword
14836 *     value can either over-write an existing keyword value, or can be
14837 *     inserted as a new header card into the FitsChan.
14838 *
14839 c     The keyword data type is selected by replacing <X> in the function name
14840 f     The keyword data type is selected by replacing <X> in the routine name
14841 *     by one of the following strings representing the recognised FITS data
14842 
14843 *     types:
14844 *
14845 *     - CF - Complex floating point values.
14846 *     - CI - Complex integer values.
14847 *     - F  - Floating point values.
14848 *     - I  - Integer values.
14849 *     - L  - Logical (i.e. boolean) values.
14850 *     - S  - String values.
14851 *     - CN - A "CONTINUE" value, these are treated like string values, but
14852 *            are encoded without an equals sign.
14853 *
14854 
14855 *     The data type of the "value" parameter depends on <X> as follows:
14856 *
14857 c     - CF - "double *" (a pointer to a 2 element array holding the real and
14858 c            imaginary parts of the complex value).
14859 c     - CI - "int *" (a pointer to a 2 element array holding the real and
14860 c            imaginary parts of the complex value).
14861 c     - F  - "double".
14862 c     - I  - "int".
14863 c     - L  - "int".
14864 c     - S  - "const char *".
14865 c     - CN - "const char *".
14866 *
14867 f     - CF - DOUBLE PRECISION(2) (a 2 element array holding the real and
14868 f            imaginary parts of the complex value).
14869 f     - CI - INTEGER(2) (a 2 element array holding the real and imaginary
14870 f            parts of the complex value).
14871 f     - F  - DOUBLE PRECISION.
14872 f     - I  - INTEGER
14873 f     - L  - LOGICAL
14874 f     - S  - CHARACTER
14875 f     - CN - CHARACTER
14876 
14877 *  Parameters:
14878 c     this
14879 f     THIS = INTEGER (Given)
14880 *        Pointer to the FitsChan.
14881 c     name
14882 f     NAME = CHARACTER * ( * ) (Given)
14883 c        Pointer to a null-terminated character string
14884 f        A character string
14885 *        containing the FITS keyword name. This may be a complete FITS
14886 *        header card, in which case the keyword to use is extracted from
14887 *        it. No more than 80 characters are read from this string.
14888 c     value
14889 f     VALUE = <X>type (Given)
14890 *        The keyword value to store with the named keyword. The data type
14891 *        of this parameter depends on <X> as described above.
14892 c     comment
14893 f     COMMENT = CHARACTER * ( * ) (Given)
14894 c        A pointer to a null terminated string
14895 f        A string
14896 *        holding a comment to associated with the keyword.
14897 c        If a NULL pointer or
14898 f        If
14899 *        a blank string is supplied, then any comment included in the string
14900 *        supplied for the
14901 c        "name" parameter is used instead. If "name"
14902 f        NAME parameter is used instead. If NAME
14903 *        contains no comment, then any existing comment in the card being
14904 *        over-written is retained. Otherwise, no comment is stored with
14905 *        the card.
14906 c     overwrite
14907 f     OVERWRITE = LOGICAL (Given)
14908 c        If non-zero,
14909 f        If .TRUE.,
14910 *        the new card formed from the supplied keyword name, value and comment
14911 *        string over-writes the current card, and the current card is
14912 *        incremented to refer to the next card (see the "Card" attribute). If
14913 c        zero,
14914 f        .FALSE.,
14915 *        the new card is inserted in front of the current card and the current
14916 *        card is left unchanged. In either case, if the current card on entry
14917 *        points to the "end-of-file", the new card is appended to the end of
14918 *        the list.
14919 f     STATUS = INTEGER (Given and Returned)
14920 f        The global status.
14921 
14922 *  Notes:
14923 *     - The
14924 c     function astSetFitsU
14925 f     routine AST_SETFITSU
14926 *     can be used to indicate that no value is associated with a keyword.
14927 *     - The
14928 c     function astSetFitsCM
14929 f     routine AST_SETFITSCM
14930 *     can be used to store a pure comment card (i.e. a card with a blank
14931 *     keyword).
14932 *     -  To assign a new value for an existing keyword within a FitsChan,
14933 c     first find the card describing the keyword using astFindFits, and
14934 c     then use one of the astSetFits<X> family to over-write the old value.
14935 f     first find the card describing the keyword using AST_FINDFITS, and
14936 f     then use one of the AST_SETFITS<X> family to over-write the old value.
14937 *     -  If, on exit, there are no cards following the card written by
14938 c     this function, then the current card is left pointing at the
14939 f     this routine, then the current card is left pointing at the
14940 *     "end-of-file".
14941 *     -  An error will be reported if the keyword name does not conform
14942 *     to FITS requirements.
14943 *--
14944 */
14945 
14946 /* Define a macro which expands to the implementation of the astSetFits<X>
14947    routine for a given data type. */
14948 #define MAKE_FSET(code,ctype,ftype,valexp) \
14949 static void SetFits##code( AstFitsChan *this, const char *name, ctype value, const char *comment, int overwrite, int *status ) { \
14950 \
14951 /* Local variables: */ \
14952    const char *class;     /* Object class */ \
14953    const char *method;    /* Calling method */ \
14954    const char *com;       /* Comment to use */ \
14955    char *lcom;            /* Supplied keyword comment */ \
14956    char *lname;           /* Supplied keyword name */ \
14957    char *lvalue;          /* Supplied keyword value */ \
14958    int free_com;          /* Should com be freed before returned? */ \
14959 \
14960 /* Check the global error status. */ \
14961    if ( !astOK ) return; \
14962 \
14963 /* Ensure the source function has been called */ \
14964    ReadFromSource( this, status ); \
14965 \
14966 /* Store the object clas and calling method. */ \
14967    class = astGetClass( this ); \
14968    method = "astSetFits"#code; \
14969 \
14970 /* Extract the keyword name from the supplied string. */ \
14971    (void) Split( name, &lname, &lvalue, &lcom, method, class, status ); \
14972 \
14973 /* Initialise a pointer to the comment to be stored. If the supplied \
14974    comment is blank, use the comment given with "name". */ \
14975    com = ChrLen( comment, status ) ? comment : lcom; \
14976 \
14977 /* If the comment is still blank, use the existing comment if we are \
14978    over-writing, or a NULL pointer otherwise. */ \
14979    free_com = 0; \
14980    if( !ChrLen( com, status ) ) { \
14981       com = NULL; \
14982       if( overwrite ) { \
14983          if( CardComm( this, status ) ){ \
14984             com = (const char *) astStore( NULL, (void *) CardComm( this, status ), \
14985                                            strlen( CardComm( this, status ) ) + 1 ); \
14986             free_com = 1; \
14987          } \
14988       } \
14989    } \
14990 \
14991 /* Insert the new card. */ \
14992    InsCard( this, overwrite, lname, ftype, valexp, com, method, class, status ); \
14993 \
14994 /* Release the memory used to hold keyword name, value and comment strings. */ \
14995    lname = (char *) astFree( (void *) lname ); \
14996    lvalue = (char *) astFree( (void *) lvalue ); \
14997    lcom = (char *) astFree( (void *) lcom ); \
14998 \
14999 /* Release the memory holding the stored comment string, so long as it was \
15000    allocated within this function. */ \
15001    if( free_com ) com = (const char *) astFree( (void *) com ); \
15002 \
15003 }
15004 
15005 /* Use the above macro to give defintions for the astSetFits<X> method
15006    for each FITS data type. */
15007 MAKE_FSET(I,int,AST__INT,(void *)&value)
15008 MAKE_FSET(F,double,AST__FLOAT,(void *)&value)
15009 MAKE_FSET(S,const char *,AST__STRING,(void *)value)
15010 MAKE_FSET(CN,const char *,AST__CONTINUE,(void *)value)
15011 MAKE_FSET(CF,double *,AST__COMPLEXF,(void *)value)
15012 MAKE_FSET(CI,int *,AST__COMPLEXI,(void *)value)
15013 MAKE_FSET(L,int,AST__LOGICAL,(void *)&value)
15014 #undef MAKE_FSET
15015 
SetFitsU(AstFitsChan * this,const char * name,const char * comment,int overwrite,int * status)15016 static void SetFitsU( AstFitsChan *this, const char *name, const char *comment,
15017                       int overwrite, int *status ) {
15018 
15019 /*
15020 *++
15021 *  Name:
15022 c     astSetFitsU
15023 f     AST_SETFITSU
15024 
15025 *  Purpose:
15026 *     Store an undefined keyword value in a FitsChan.
15027 
15028 *  Type:
15029 *     Public virtual function.
15030 
15031 *  Synopsis:
15032 c     #include "fitschan.h"
15033 
15034 c     void astSetFitsU( AstFitsChan *this, const char *name,
15035 c                       const char *comment, int overwrite )
15036 f     CALL AST_SETFITSU( THIS, NAME, COMMENT, OVERWRITE, STATUS )
15037 
15038 *  Description:
15039 *     This
15040 c     function
15041 f     routine
15042 *     stores an undefined value for a named keyword within
15043 *     a FitsChan at the current card position. The new undefined value
15044 *     can either over-write an existing keyword value, or can be inserted
15045 *     as a new header card into the FitsChan.
15046 
15047 *  Parameters:
15048 c     this
15049 f     THIS = INTEGER (Given)
15050 *        Pointer to the FitsChan.
15051 c     name
15052 f     NAME = CHARACTER * ( * ) (Given)
15053 c        Pointer to a null-terminated character string
15054 f        A character string
15055 *        containing the FITS keyword name. This may be a complete FITS
15056 *        header card, in which case the keyword to use is extracted from
15057 *        it. No more than 80 characters are read from this string.
15058 c     comment
15059 f     COMMENT = CHARACTER * ( * ) (Given)
15060 c        A pointer to a null terminated string
15061 f        A string
15062 *        holding a comment to associated with the keyword.
15063 c        If a NULL pointer or
15064 f        If
15065 *        a blank string is supplied, then any comment included in the string
15066 *        supplied for the
15067 c        "name" parameter is used instead. If "name"
15068 f        NAME parameter is used instead. If NAME
15069 *        contains no comment, then any existing comment in the card being
15070 *        over-written is retained. Otherwise, no comment is stored with
15071 *        the card.
15072 c     overwrite
15073 f     OVERWRITE = LOGICAL (Given)
15074 c        If non-zero,
15075 f        If .TRUE.,
15076 *        the new card formed from the supplied keyword name and comment
15077 *        string over-writes the current card, and the current card is
15078 *        incremented to refer to the next card (see the "Card" attribute). If
15079 c        zero,
15080 f        .FALSE.,
15081 *        the new card is inserted in front of the current card and the current
15082 *        card is left unchanged. In either case, if the current card on entry
15083 *        points to the "end-of-file", the new card is appended to the end of
15084 *        the list.
15085 f     STATUS = INTEGER (Given and Returned)
15086 f        The global status.
15087 
15088 *  Notes:
15089 *     -  If, on exit, there are no cards following the card written by
15090 *     this function, then the current card is left pointing at the
15091 *     "end-of-file".
15092 *     -  An error will be reported if the keyword name does not conform
15093 *     to FITS requirements.
15094 *--
15095 */
15096 
15097 /* Local variables: */
15098    const char *class;     /* Object class */
15099    const char *method;    /* Calling method */
15100    const char *com;       /* Comment to use */
15101    char *lcom;            /* Supplied keyword comment */
15102    char *lname;           /* Supplied keyword name */
15103    char *lvalue;          /* Supplied keyword value */
15104    int free_com;          /* Should com be freed before returned? */
15105 
15106 /* Check the global error status. */
15107    if ( !astOK ) return;
15108 
15109 /* Ensure the source function has been called */
15110    ReadFromSource( this, status );
15111 
15112 /* Store the object clas and calling method. */
15113    class = astGetClass( this );
15114    method = "astSetFitsU";
15115 
15116 /* Extract the keyword name from the supplied string. */
15117    (void) Split( name, &lname, &lvalue, &lcom, method, class, status );
15118 
15119 /* Initialise a pointer to the comment to be stored. If the supplied
15120    comment is blank, use the comment given with "name". */
15121    com = ChrLen( comment, status ) ? comment : lcom;
15122 
15123 /* If the comment is still blank, use the existing comment if we are
15124    over-writing, or a NULL pointer otherwise. */
15125    free_com = 0;
15126    if( !ChrLen( com, status ) ) {
15127       com = NULL;
15128       if( overwrite ) {
15129          if( CardComm( this, status ) ){
15130             com = (const char *) astStore( NULL, (void *) CardComm( this, status ),
15131                                            strlen( CardComm( this, status ) ) + 1 );
15132             free_com = 1;
15133          }
15134       }
15135    }
15136 
15137 /* Insert the new card. */
15138    InsCard( this, overwrite, lname, AST__UNDEF, NULL, com, method, class,
15139             status );
15140 
15141 /* Release the memory used to hold keyword name, value and comment strings. */
15142    lname = (char *) astFree( (void *) lname );
15143    lvalue = (char *) astFree( (void *) lvalue );
15144    lcom = (char *) astFree( (void *) lcom );
15145 
15146 /* Release the memory holding the stored comment string, so long as it was
15147    allocated within this function. */
15148    if( free_com ) com = (const char *) astFree( (void *) com );
15149 }
15150 
SetFitsCM(AstFitsChan * this,const char * comment,int overwrite,int * status)15151 static void SetFitsCM( AstFitsChan *this, const char *comment,
15152                        int overwrite, int *status ) {
15153 
15154 /*
15155 *++
15156 *  Name:
15157 c     astSetFitsCM
15158 f     AST_SETFITSCM
15159 
15160 *  Purpose:
15161 *     Store a comment card in a FitsChan.
15162 
15163 *  Type:
15164 *     Public virtual function.
15165 
15166 *  Synopsis:
15167 c     #include "fitschan.h"
15168 
15169 c     void astSetFitsCM( AstFitsChan *this, const char *comment,
15170 c                        int overwrite )
15171 f     CALL AST_SETFITSCM( THIS, COMMENT, OVERWRITE, STATUS )
15172 
15173 *  Description:
15174 *     This
15175 c     function
15176 f     routine
15177 *     stores a comment card ( i.e. a card with no keyword name or equals
15178 *     sign) within a FitsChan at the current card position. The new card
15179 *     can either over-write an existing card, or can be inserted as a new
15180 *     card into the FitsChan.
15181 
15182 *  Parameters:
15183 c     this
15184 f     THIS = INTEGER (Given)
15185 *        Pointer to the FitsChan.
15186 c     comment
15187 f     COMMENT = CHARACTER * ( * ) (Given)
15188 c        A pointer to a null terminated string
15189 f        A string
15190 *        holding the text of the comment card.
15191 c        If a NULL pointer or
15192 f        If
15193 *        a blank string is supplied, then a totally blank card is produced.
15194 c     overwrite
15195 f     OVERWRITE = LOGICAL (Given)
15196 c        If non-zero,
15197 f        If .TRUE.,
15198 *        the new card over-writes the current card, and the current card is
15199 *        incremented to refer to the next card (see the "Card" attribute). If
15200 c        zero,
15201 f        .FALSE.,
15202 *        the new card is inserted in front of the current card and the current
15203 *        card is left unchanged. In either case, if the current card on entry
15204 *        points to the "end-of-file", the new card is appended to the end of
15205 *        the list.
15206 f     STATUS = INTEGER (Given and Returned)
15207 f        The global status.
15208 
15209 *  Notes:
15210 *     -  If, on exit, there are no cards following the card written by
15211 *     this function, then the current card is left pointing at the
15212 *     "end-of-file".
15213 *--
15214 */
15215 
15216 /* Just call astSetFitsCom with a blank keyword name. */
15217    astSetFitsCom( this, "", comment, overwrite );
15218 }
15219 
SetFitsCom(AstFitsChan * this,const char * name,const char * comment,int overwrite,int * status)15220 static void SetFitsCom( AstFitsChan *this, const char *name,
15221                         const char *comment, int overwrite, int *status ){
15222 
15223 /*
15224 *+
15225 *  Name:
15226 *     astSetFitsCom
15227 
15228 *  Purpose:
15229 *     Store a comment for a keyword in a FitsChan.
15230 
15231 *  Type:
15232 *     Protected virtual function.
15233 
15234 *  Synopsis:
15235 *     #include "fitschan.h"
15236 
15237 *     void astSetFitsCom( AstFitsChan *this, const char *name,
15238 *                         const char *comment, int overwrite )
15239 
15240 *  Class Membership:
15241 *     FitsChan method.
15242 
15243 *  Description:
15244 *     This function replaces the comment within an existing card, or
15245 *     stores a new comment card within a FitsChan.
15246 
15247 *  Parameters:
15248 *     this
15249 *        Pointer to the FitsChan.
15250 *     name
15251 *        A pointer to a
15252 *        string holding the keyword name. This may be a complete FITS
15253 *        header card, in which case the keyword to use is extracted from
15254 *        it. No more than 80 characters are read from this string.
15255 *     comment
15256 *        A pointer to a
15257 *        string holding a comment to associated with the keyword.
15258 *        If a NULL or
15259 *        blank string is supplied, any existing comment associated with
15260 *        the keyword is removed.
15261 *     overwrite
15262 *        If non-zero, the new comment replaces the comment in the current
15263 *        card, and the current card is then incremented to refer to the next
15264 *        card. If zero, a new comment card is inserted in front of the current
15265 *        card and the current card is left unchanged. In either case, if the
15266 *        current card on entry points to the "end-of-file", the new card is
15267 *        appended to the end of the list.
15268 
15269 *  Notes:
15270 *     -  When replacing an existing comment, any existing keyword value is
15271 *     retained only if the supplied keyword name is the same as the keyword
15272 *     name in the current card. If the keyword names are different, then
15273 *     the new name replaces the old name, and any existing keyword data value
15274 *     is deleted. The card thus becomes a comment card with the supplied
15275 *     keyword name and comment, but no data value.
15276 *     -  If, on exit, there are no cards following the card written by
15277 *     this function, then the current card is left pointing at the
15278 *     "end-of-file".
15279 *     -  The current card can be set explicitly before calling this function
15280 *     either by assigning a value to the Card attribute (if the index of the
15281 *     required card is already known), or using astFindFits (if only the
15282 *     keyword name is known).
15283 *     -  An error will be reported if the keyword name does not conform
15284 *     to FITS requirements.
15285 *-
15286 */
15287 
15288 /* Local variables: */
15289    const char *class;     /* Pointer to object class string */
15290    const char *method;    /* Pointer to calling method string */
15291    const char *cname;     /* The existing keyword name */
15292    const char *com;       /* The comment to use */
15293    char *lcom;            /* Supplied keyword comment */
15294    char *lname;           /* Supplied keyword name */
15295    char *lvalue;          /* Supplied keyword value */
15296    void *old_data;        /* Pointer to the old data value */
15297    void *data;            /* Pointer to data value to be stored */
15298    size_t size;           /* The size of the data value */
15299 
15300 /* Check the global error status. */
15301    if ( !astOK ) return;
15302 
15303 /* Initialisation */
15304    size = 0;
15305 
15306 /* Ensure the source function has been called */
15307    ReadFromSource( this, status );
15308 
15309 /* Store the calling method and object class. */
15310    method = "astSetFitsCom";
15311    class = astGetClass( this );
15312 
15313 /* Extract the keyword name, etc, from the supplied string. */
15314    (void) Split( name, &lname, &lvalue, &lcom, method, class, status );
15315 
15316 /* If a blank comment has been supplied, use NULL instead. */
15317    com = ChrLen( comment, status )? comment : NULL;
15318 
15319 /* If we are inserting a new card, or over-writing an old card with a
15320    different name, create and store a comment card with the given keyword
15321    name and comment, but no data value. */
15322    cname = CardName( this, status );
15323    if( !overwrite || !cname || strcmp( lname, cname ) ){
15324       InsCard( this, overwrite, lname, AST__COMMENT, NULL, com, method, class, status );
15325 
15326 /* If we are overwriting an existing keyword comment, use the data type
15327    and value from the existing current card. Note, we have to take a copy
15328    of the old data value because InsCard over-writes by deleting the old
15329    card and then inserting a new one. */
15330    } else {
15331       old_data = CardData( this, &size, status );
15332       data = astStore( NULL, old_data, size );
15333       InsCard( this, 1, lname, CardType( this, status ), data, com, method, class, status );
15334       data = astFree( data );
15335    }
15336 
15337 /* Release the memory used to hold keyword name, value and comment strings. */
15338    lname = (char *) astFree( (void *) lname );
15339    lvalue = (char *) astFree( (void *) lvalue );
15340    lcom = (char *) astFree( (void *) lcom );
15341 }
15342 
FixNew(AstFitsChan * this,int flag,int remove,const char * method,const char * class,int * status)15343 static void FixNew( AstFitsChan *this, int flag, int remove,
15344                     const char *method, const char *class, int *status ){
15345 
15346 /*
15347 *
15348 *  Name:
15349 *     FixNew
15350 
15351 *  Purpose:
15352 *     Remove "new" flags from the whole FitsChan, and optionally remove
15353 *     "new" cards.
15354 
15355 *  Type:
15356 *     Private function.
15357 
15358 *  Synopsis:
15359 *     #include "fitschan.h"
15360 
15361 *     void FixNew( AstFitsChan *this, int flag, int remove,
15362 *                  const char *method, const char *class, int *status )
15363 
15364 *  Class Membership:
15365 *     FitsChan method.
15366 
15367 *  Description:
15368 *     This function searches the entire FitsChan for cards which are
15369 *     marked as new using the supplied flag (NEW1 or NEW2). If "remove"
15370 *     is non-zero, these cards are completely removed from the FitsChan
15371 *     (not just marked as used). If "remove" is zero, they are retained
15372 *     and the specified flag is cleared.
15373 
15374 *  Parameters:
15375 *     this
15376 *        Pointer to the FitsChan.
15377 *     flag
15378 *        The flag to use; NEW1 or NEW2.
15379 *     remove
15380 *        Remove flagged cards from the FitsChan?
15381 *     method
15382 *        Pointer to a string holding the name of the calling method.
15383 *        This is only for use in constructing error messages.
15384 *     class
15385 *        Pointer to a string holding the name of the supplied object class.
15386 *        This is only for use in constructing error messages.
15387 *     status
15388 *        Pointer to the inherited status variable.
15389 
15390 *  Notes:
15391 *     - This function attempts to execute even if an error has occurred.
15392 *     - If any cards are removed, the current Card is left at "end-of-file"
15393 *       on exit. If no cards are removed, the original current card is
15394 *       retained.
15395 *-
15396 */
15397 
15398 /* Local Variables: */
15399    int *flags;             /* Pointer to flags mask for the current card */
15400    int icard;              /* Index of current card on entry */
15401    int ndeleted;           /* Number of cards deleted by this call */
15402 
15403 /* Return if no FitsChan was supplied, or if the FitsChan is empty. */
15404    if ( !this || !this->head ) return;
15405 
15406 /* Save the current card index, and rewind the FitsChan. */
15407    icard = astGetCard( this );
15408    astClearCard( this );
15409 
15410 /* Indicate no cards have yet been deleted. */
15411    ndeleted = 0;
15412 
15413 /* Loop through the list of FitsCards in the FitsChan until the final
15414    card is reached. */
15415    while( astOK && this->card ){
15416 
15417 /* Get a pointer to the flags mask for this card. */
15418       flags = CardFlags( this, status );
15419 
15420 /* See if the Card has been marked with the requeste new flag. */
15421       if( flags && ( (*flags) & flag ) ) {
15422 
15423 /* If requested, remove the card. This will automatically move the
15424    current card on to the next card. */
15425          if( remove ){
15426             DeleteCard( this, method, class, status );
15427             ndeleted++;
15428 
15429 /* Otherwise, clear the flag. */
15430          } else {
15431             *flags = (*flags) & ~flag;
15432 
15433 /* Increment the card count and move on to the next card. */
15434             MoveCard( this, 1, method, class, status );
15435          }
15436 
15437 /* Move on to the next card if this card is not marked with the requested
15438    new flag. */
15439       } else {
15440          MoveCard( this, 1, method, class, status );
15441       }
15442    }
15443 
15444 /* If no cards were removed, we can safely re-instate the original
15445    current card. Otherwise, the current card is left at "end-of-file". */
15446    if( ndeleted == 0 ) astSetCard( this, icard );
15447 
15448 /* Return */
15449    return;
15450 }
15451 
FixUsed(AstFitsChan * this,int reset,int used,int remove,const char * method,const char * class,int * status)15452 static void FixUsed( AstFitsChan *this, int reset, int used, int remove,
15453                      const char *method, const char *class, int *status ){
15454 
15455 /*
15456 *
15457 *  Name:
15458 *     FixUsed
15459 
15460 *  Purpose:
15461 *     Remove "provisionally used" flags from the whole FitsChan.
15462 
15463 *  Type:
15464 *     Private function.
15465 
15466 *  Synopsis:
15467 *     #include "fitschan.h"
15468 *     void FixUsed( AstFitsChan *this, int reset, int used, int remove,
15469 *                   const char *method, const char *class, int *status )
15470 
15471 *  Class Membership:
15472 *     FitsChan method.
15473 
15474 *  Description:
15475 *     This function searches the entire FitsChan for cards which are
15476 *     marked as "provisionally used". The "provisionally used" flag is
15477 *     cleared for each such card. In addition, if "used" is non-zero then
15478 *     each such card is flagged as having been "definitely used". If
15479 *     "remove" is non-zero, then all "provisionally used" cards are deleted
15480 *     from the FitsChan.
15481 
15482 *  Parameters:
15483 *     this
15484 *        Pointer to the FitsChan.
15485 *     reset
15486 *        Set all cards so that they are neither provisionally used or
15487 *        definitely used. In this case neither the "used" nor the
15488 *        "remove" parameter are accssed.
15489 *     used
15490 *        Have the provisionally used cards definitely been used?
15491 *     remove
15492 *        Should provisionally used cards be deleted?
15493 *     method
15494 *        Pointer to a string holding the name of the calling method.
15495 *        This is only for use in constructing error messages.
15496 *     class
15497 *        Pointer to a string holding the name of the supplied object class.
15498 *        This is only for use in constructing error messages.
15499 *     status
15500 *        Pointer to the inherited status variable.
15501 
15502 *  Notes:
15503 *     - This function attempts to execute even if an error has occurred.
15504 *-
15505 */
15506 
15507 /* Local Variables: */
15508    astDECLARE_GLOBALS      /* Declare the thread specific global data */
15509    FitsCard *card0;        /* Pointer to current FitsCard */
15510    int *flags;             /* Pointer to flags mask for the current card */
15511    int old_ignore_used;    /* Original value of variable ignore_used */
15512    int old_status;         /* Original inherited status value */
15513    int rep;                /* Original error reporting flag */
15514 
15515 /* Return if no FitsChan was supplied, or if the FitsChan is empty. */
15516    if ( !this || !this->head ) return;
15517 
15518 /* Get a pointer to the structure holding thread-specific global data. */
15519    astGET_GLOBALS(this);
15520 
15521 /* Temporarily clear any bad status value and supress error reporting in
15522    this function. */
15523    old_status = astStatus;
15524    astClearStatus;
15525    rep = astReporting( 0 );
15526 
15527 /* Indicate that we should not skip over cards marked as having been
15528    read. */
15529    old_ignore_used = ignore_used;
15530    ignore_used = 0;
15531 
15532 /* Save a pointer to the current card, and the reset the current card to
15533    be the first card. */
15534    card0 = this->card;
15535    astClearCard( this );
15536 
15537 /* Loop through the list of FitsCards in the FitsChan until the final
15538    card is reached. */
15539    while( this->card ){
15540 
15541 /* Get a pointer to the flags mask for this card. */
15542       flags = CardFlags( this, status );
15543 
15544 /* Reset both used flags if required. */
15545       if( reset ) {
15546          *flags = (*flags) & ~PROVISIONALLY_USED;
15547          *flags = (*flags) & ~USED;
15548          MoveCard( this, 1, method, class, status );
15549 
15550 /* Otherwise perform the actions indicated by parameters "used" and
15551    "remove". */
15552       } else {
15553 
15554 /* See if the Card has been provisionally used. */
15555          if( flags && ( (*flags) & PROVISIONALLY_USED ) ) {
15556 
15557 /* Clear the provisionally used flag. */
15558             *flags = (*flags) & ~PROVISIONALLY_USED;
15559 
15560 /* If required, set the definitely used flag. */
15561             if( used ) *flags = (*flags) | USED;
15562 
15563 /* If required, delete the card. The next card is made current. If we are
15564    about to delete the original current card, we need to update the
15565    pointer to the card to be made current at the end of this function.
15566    If we end up back at the head of the chain, indicate that we have
15567    reached the end of file by setting card0 NULL.  */
15568             if( remove ) {
15569                if( card0 == this->card && card0 ) {
15570                   card0 = ( (FitsCard *) this->card )->next;
15571                   if( (void *) card0 == this->head ) card0 = NULL;
15572                }
15573                DeleteCard( this, method, class, status );
15574 
15575 /* Otherwise, just move on to the next card. */
15576             } else {
15577                MoveCard( this, 1, method, class, status );
15578             }
15579 
15580 /* If this card has not bee provisionally used, move on to the next card. */
15581          } else {
15582             MoveCard( this, 1, method, class, status );
15583          }
15584       }
15585    }
15586 
15587 /* Re-instate the original current card. */
15588    this->card = card0;
15589 
15590 /* If this card is now flagged as definitely used, move forward to the
15591    next un-used card. */
15592    flags = CardFlags( this, status );
15593    if( flags && (*flags & USED ) ) {
15594       ignore_used = 1;
15595       MoveCard( this, 1, method, class, status );
15596    }
15597 
15598 /* Re-instate the original flag indicating if cards marked as having been
15599    read should be skipped over. */
15600    ignore_used = old_ignore_used;
15601 
15602 /* Re-instate the original status value and error reporting condition. */
15603    astReporting( rep );
15604    astSetStatus( old_status );
15605 }
15606 
FormatCard(AstFitsChan * this,char * buf,const char * method,int * status)15607 static void FormatCard( AstFitsChan *this, char *buf, const char *method, int *status ){
15608 
15609 /*
15610 *
15611 *  Name:
15612 *     FormatCard
15613 
15614 *  Purpose:
15615 *     Formats the current card.
15616 
15617 *  Type:
15618 *     Private function.
15619 
15620 *  Synopsis:
15621 *     #include "fitschan.h"
15622 
15623 *     void FormatCard( AstFitsChan *this, char *buf, const char *method, int *status )
15624 
15625 *  Class Membership:
15626 *     FitsChan method.
15627 
15628 *  Description:
15629 *     This function write the current card into the supplied character
15630 *     buffer as a complete FITS header card.
15631 
15632 *  Parameters:
15633 *     this
15634 *        Pointer to the FitsChan.
15635 *     buf
15636 *        A character string into which the header card is written. This
15637 *        should be at least 81 characters long. The returned string is
15638 *        padded with spaces upto column 80. A terminating null character
15639 *        is added.
15640 *     method
15641 *        Pointer to a string holding the name of the calling method.
15642 *        This is only for use in constructing error messages.
15643 *     status
15644 *        Pointer to the inherited status variable.
15645 
15646 *  Notes:
15647 *     -  An error is reported if the requested header card does not conform to
15648 *     FITS standards.
15649 *
15650 */
15651 
15652 /* Local Variables: */
15653    const char *com;            /* Pointer to comment string to use */
15654    int comlen;                 /* Length of comment string */
15655    int comstart;               /* Column in which to start comment */
15656    int i;                      /* Loop counter for characters */
15657    int len;                    /* Output string length */
15658    int digits;                 /* No. of digits to use when formatting floating point values */
15659    int type;                   /* Card data type */
15660 
15661 /* Check the global error status, and check the current card is defined. */
15662    if ( !astOK || astFitsEof( this ) ) return;
15663 
15664 /* Get a pointer to the comment to use and determine its length. */
15665    com = CardComm( this, status );
15666    comlen = ChrLen( com, status );
15667 
15668 /* Copy the keyword name to the start of the output buffer, and store
15669    its length. */
15670    len = (int) strlen( strcpy( buf, CardName( this, status ) ) );
15671 
15672 /* Pad the name with spaces up to column 8. */
15673    while ( len < FITSNAMLEN ) buf[ len++ ] = ' ';
15674 
15675 /* If the card contains a keyword value... */
15676    type = CardType( this, status );
15677    if( type != AST__COMMENT ){
15678 
15679 /* Get the number of digits to use when formatting floating point values. */
15680       digits = astGetFitsDigits( this );
15681 
15682 /* Put an equals sign in column 9 (or a space if the keyword is a CONTINUE
15683    card), followed by a space in column 10. */
15684       buf[ len++ ] = ( type == AST__CONTINUE ) ? ' ' : '=';
15685       buf[ len++ ] = ' ';
15686 
15687 /* Format and store the keyword value, starting at column 11 and update the
15688    output string length. */
15689       len += EncodeValue( this, buf + len, FITSNAMLEN + 3, digits,
15690                           method, status );
15691 
15692 /* If there is a comment, determine which column it should start in so that
15693    it ends in column 80. */
15694       if( com ){
15695          comstart = AST__FITSCHAN_FITSCARDLEN - ( comlen - 2 ) + 1;
15696 
15697 /* Adjust the starting column to 32 if possible, avoiding over-writing
15698    the value, or running off the end of the card unless this is
15699    unavoidable. */
15700          if ( comstart > FITSCOMCOL ) comstart = FITSCOMCOL;
15701          if ( comstart < len + 2 ) comstart = len + 2;
15702 
15703 /* Pad the output buffer with spaces up to the start of the comment. */
15704          while ( len < comstart - 1 ) buf[ len++ ] = ' ';
15705 
15706 /* Then append "/ " to introduce the comment, truncating if the card
15707    length forces this. */
15708          for ( i = 0; ( i < 2 ) && ( len < AST__FITSCHAN_FITSCARDLEN ); i++ ) {
15709             buf[ len++ ] = "/ "[ i ];
15710          }
15711       }
15712    }
15713 
15714 /* Append any comment, truncating it if the card length forces
15715    this. */
15716    if ( com ) {
15717       for ( i = 0; com[ i ] && ( len < AST__FITSCHAN_FITSCARDLEN ); i++ ) {
15718          buf[ len++ ] = com[ i ];
15719       }
15720    }
15721 
15722 /* Pad with spaces up to the end of the card. */
15723    while ( len < AST__FITSCHAN_FITSCARDLEN ) buf[ len++ ] = ' ';
15724 
15725 /* Terminate it. */
15726    buf[ AST__FITSCHAN_FITSCARDLEN ] = 0;
15727 }
15728 
FullForm(const char * list,const char * test,int abbrev,int * status)15729 static int FullForm( const char *list, const char *test, int abbrev, int *status ){
15730 /*
15731 *  Name:
15732 *     FullForm
15733 
15734 *  Purpose:
15735 *     Identify the full form of an option string.
15736 
15737 *  Type:
15738 *     Private function.
15739 
15740 *  Synopsis:
15741 *     #include "fitschan.h"
15742 *     int FullForm( const char *list, const char *test, int abbrev, int *status )
15743 
15744 *  Class Membership:
15745 *     FitsChan method.
15746 
15747 *  Description:
15748 *     This function identifies a supplied test option within a supplied
15749 *     list of valid options, and returns the index of the option within
15750 *     the list. The test option may be abbreviated, and case is
15751 *     insignificant.
15752 
15753 *  Parameters:
15754 *     list
15755 *        A list of space separated option strings.
15756 *     test
15757 *        A candidate option string.
15758 *     abbrev
15759 *        1 if abbreviations are to be accepted. Zero otherwise.
15760 *     status
15761 *        Pointer to the inherited status variable.
15762 
15763 *  Returned Value:
15764 *     The index of the identified option within the supplied list, starting
15765 *     at zero. -1 is returned if the option is not recognised, and (if
15766 *     abbrev is 1 ) -2 if the option is ambiguous (no errors are reported
15767 *     in these cases). If abbrev is zero, the returned index will be the
15768 *     index of the first matching string.
15769 *
15770 
15771 *  Notes:
15772 *     -  A value of -1 is returned if an error has already occurred, or
15773 *     if this function should fail for any reason.
15774 */
15775 
15776 /* Local Variables: */
15777    char *context;          /* Context used by strtok_r */
15778    char *llist;            /* Pointer to a local copy of the options list */
15779    char *option;           /* Pointer to the start of the next option */
15780    int i;                  /* Current option index */
15781    int len;                /* Length of supplied option */
15782    int nmatch;             /* Number of matching options */
15783    int ret;                /* The returned index */
15784 
15785 /* Initialise the answer to indicate that the option has not been
15786    identified. */
15787    ret = -1;
15788 
15789 /* Avoid compiler warnings. */
15790    context = NULL;
15791 
15792 /* Check global status. */
15793    if( !astOK ) return ret;
15794 
15795 /* Take a local copy of the supplied options list. This is necessary since
15796    "strtok" modified the string by inserting null characters. */
15797    llist = (char *) astStore( NULL, (void *) list, strlen(list) + 1 );
15798    if( astOK ){
15799 
15800 /* Save the number of characters in the supplied test option (excluding
15801    trailing spaces). */
15802       len = ChrLen( test, status );
15803 
15804 /* Compare the supplied test option against each of the known options in
15805    turn. Count the number of matches. */
15806       nmatch = 0;
15807 #if HAVE_STRTOK_R
15808       option = strtok_r( llist, " ", &context );
15809 #else
15810       option = strtok( llist, " " );
15811 #endif
15812       i = 0;
15813       while( option ){
15814 
15815 /* If every character in the supplied label matches the corresponding
15816    character in the current test label we have a match. Increment the
15817    number of matches and save the current item index. If abbreviation is
15818    not allowed ensure that the lengths of the strings are equal. */
15819          if( !Ustrncmp( test, option, len, status ) && ( abbrev ||
15820              len == ChrLen( option, status ) ) ) {
15821             nmatch++;
15822             ret = i;
15823             if( !abbrev ) break;
15824          }
15825 
15826 /* Get a pointer to the next option. */
15827 #if HAVE_STRTOK_R
15828          option = strtok_r( NULL, " ", &context );
15829 #else
15830          option = strtok( NULL, " " );
15831 #endif
15832          i++;
15833       }
15834 
15835 /* Return -1 if no match was found. */
15836       if( !nmatch ){
15837          ret = -1;
15838 
15839 /* Return -2 if the option was ambiguous. */
15840       } else if( abbrev && nmatch > 1 ){
15841          ret = -2;
15842       }
15843 
15844 /* Free the local copy of the options list. */
15845       llist = (char *) astFree( (void *) llist );
15846    }
15847 
15848 /* Return the answer. */
15849    return ret;
15850 }
15851 
GetAllWarnings(AstFitsChan * this,int * status)15852 static const char *GetAllWarnings( AstFitsChan *this, int *status ){
15853 
15854 /*
15855 *+
15856 *  Name:
15857 *     astGetAllWarnings
15858 
15859 *  Purpose:
15860 *     Return a list of all condition names.
15861 
15862 *  Type:
15863 *     Protected virtual function.
15864 
15865 *  Synopsis:
15866 *     #include "fitschan.h"
15867 *     const char *GetAllWarnings( AstFitsChan *this )
15868 
15869 *  Class Membership:
15870 *     FitsChan method.
15871 
15872 *  Description:
15873 *     This function returns a space separated lits of the condition names
15874 *     currently recognized by the Warnings attribute.
15875 
15876 *  Parameters:
15877 *     this
15878 *        Pointer to the FitsChan.
15879 
15880 *  Returned Value:
15881 *     A pointer to a static string holding the condition names.
15882 
15883 *  Notes:
15884 *     - This routine does not check the inherited status.
15885 *-
15886 */
15887 
15888 /* Return the result. */
15889    return ALLWARNINGS;
15890 }
GetAttrib(AstObject * this_object,const char * attrib,int * status)15891 const char *GetAttrib( AstObject *this_object, const char *attrib, int *status ) {
15892 
15893 /*
15894 *  Name:
15895 *     GetAttrib
15896 
15897 *  Purpose:
15898 *     Get the value of a specified attribute for a FitsChan.
15899 
15900 *  Type:
15901 *     Private function.
15902 
15903 *  Synopsis:
15904 *     #include "fitschan.h"
15905 *     const char *GetAttrib( AstObject *this, const char *attrib, int *status )
15906 
15907 *  Class Membership:
15908 *     FitsChan member function (over-rides the protected astGetAttrib
15909 *     method inherited from the Channel class).
15910 
15911 *  Description:
15912 *     This function returns a pointer to the value of a specified
15913 *     attribute for a FitsChan, formatted as a character string.
15914 
15915 *  Parameters:
15916 *     this
15917 *        Pointer to the FitsChan.
15918 *     attrib
15919 *        Pointer to a null-terminated string containing the name of
15920 *        the attribute whose value is required. This name should be in
15921 *        lower case, with all white space removed.
15922 *     status
15923 *        Pointer to the inherited status variable.
15924 
15925 *  Returned Value:
15926 *     - Pointer to a null-terminated string containing the attribute
15927 *     value.
15928 
15929 *  Notes:
15930 *     - The returned string pointer may point at memory allocated
15931 *     within the FitsChan, or at static memory. The contents of the
15932 *     string may be over-written or the pointer may become invalid
15933 *     following a further invocation of the same function or any
15934 *     modification of the FitsChan. A copy of the string should
15935 *     therefore be made if necessary.
15936 *     - A NULL pointer will be returned if this function is invoked
15937 *     with the global error status set, or if it should fail for any
15938 *     reason.
15939 */
15940 
15941 /* Local Variables: */
15942    astDECLARE_GLOBALS            /* Declare the thread specific global data */
15943    AstFitsChan *this;            /* Pointer to the FitsChan structure */
15944    const char *result;           /* Pointer value to return */
15945    int ival;                     /* Integer attribute value */
15946 
15947 /* Initialise. */
15948    result = NULL;
15949 
15950 /* Check the global error status. */
15951    if ( !astOK ) return result;
15952 
15953 /* Get a pointer to the structure holding thread-specific global data. */
15954    astGET_GLOBALS(this_object);
15955 
15956 /* Obtain a pointer to the FitsChan structure. */
15957    this = (AstFitsChan *) this_object;
15958 
15959 /* Card. */
15960 /* ----- */
15961    if ( !strcmp( attrib, "card" ) ) {
15962       ival = astGetCard( this );
15963       if ( astOK ) {
15964          (void) sprintf( getattrib_buff, "%d", ival );
15965          result = getattrib_buff;
15966       }
15967 
15968 /* CardComm. */
15969 /* --------- */
15970    } else if ( !strcmp( attrib, "cardcomm" ) ) {
15971       result = astGetCardComm( this );
15972 
15973 /* CardName. */
15974 /* --------- */
15975    } else if ( !strcmp( attrib, "cardname" ) ) {
15976       result = astGetCardName( this );
15977 
15978 /* CardType. */
15979 /* --------- */
15980    } else if ( !strcmp( attrib, "cardtype" ) ) {
15981       ival = astGetCardType( this );
15982       if ( astOK ) {
15983          (void) sprintf( getattrib_buff, "%d", ival );
15984          result = getattrib_buff;
15985       }
15986 
15987 /* Encoding. */
15988 /* --------- */
15989    } else if ( !strcmp( attrib, "encoding" ) ) {
15990       ival = astGetEncoding( this );
15991       if ( astOK ) {
15992          if( ival == NATIVE_ENCODING ){
15993             result = NATIVE_STRING;
15994          } else if( ival == FITSPC_ENCODING ){
15995             result = FITSPC_STRING;
15996          } else if( ival == FITSIRAF_ENCODING ){
15997             result = FITSIRAF_STRING;
15998          } else if( ival == FITSAIPS_ENCODING ){
15999             result = FITSAIPS_STRING;
16000          } else if( ival == FITSAIPSPP_ENCODING ){
16001             result = FITSAIPSPP_STRING;
16002          } else if( ival == FITSCLASS_ENCODING ){
16003             result = FITSCLASS_STRING;
16004          } else if( ival == FITSWCS_ENCODING ){
16005             result = FITSWCS_STRING;
16006          } else if( ival == DSS_ENCODING ){
16007             result = DSS_STRING;
16008          } else {
16009             result = UNKNOWN_STRING;
16010          }
16011       }
16012 
16013 /* CDMatrix */
16014 /* -------- */
16015    } else if ( !strcmp( attrib, "cdmatrix" ) ) {
16016       ival = astGetCDMatrix( this );
16017       if ( astOK ) {
16018          (void) sprintf( getattrib_buff, "%d", ival );
16019          result = getattrib_buff;
16020       }
16021 
16022 /* DefB1950 */
16023 /* -------- */
16024    } else if ( !strcmp( attrib, "defb1950" ) ) {
16025       ival = astGetDefB1950( this );
16026       if ( astOK ) {
16027          (void) sprintf( getattrib_buff, "%d", ival );
16028          result = getattrib_buff;
16029       }
16030 
16031 /* TabOK */
16032 /* ----- */
16033    } else if ( !strcmp( attrib, "tabok" ) ) {
16034       ival = astGetTabOK( this );
16035       if ( astOK ) {
16036          (void) sprintf( getattrib_buff, "%d", ival );
16037          result = getattrib_buff;
16038       }
16039 
16040 /* CarLin */
16041 /* ------ */
16042    } else if ( !strcmp( attrib, "carlin" ) ) {
16043       ival = astGetCarLin( this );
16044       if ( astOK ) {
16045          (void) sprintf( getattrib_buff, "%d", ival );
16046          result = getattrib_buff;
16047       }
16048 
16049 /* PolyTan */
16050 /* ------- */
16051    } else if ( !strcmp( attrib, "polytan" ) ) {
16052       ival = astGetPolyTan( this );
16053       if ( astOK ) {
16054          (void) sprintf( getattrib_buff, "%d", ival );
16055          result = getattrib_buff;
16056       }
16057 
16058 /* Iwc */
16059 /* --- */
16060    } else if ( !strcmp( attrib, "iwc" ) ) {
16061       ival = astGetIwc( this );
16062       if ( astOK ) {
16063          (void) sprintf( getattrib_buff, "%d", ival );
16064          result = getattrib_buff;
16065       }
16066 
16067 /* Clean */
16068 /* ----- */
16069    } else if ( !strcmp( attrib, "clean" ) ) {
16070       ival = astGetClean( this );
16071       if ( astOK ) {
16072          (void) sprintf( getattrib_buff, "%d", ival );
16073          result = getattrib_buff;
16074       }
16075 
16076 /* FitsAxisOrder. */
16077 /* -------------- */
16078    } else if ( !strcmp( attrib, "fitsaxisorder" ) ) {
16079       result = astGetFitsAxisOrder( this );
16080 
16081 /* FitsDigits. */
16082 /* ----------- */
16083    } else if ( !strcmp( attrib, "fitsdigits" ) ) {
16084       ival = astGetFitsDigits( this );
16085       if ( astOK ) {
16086          (void) sprintf( getattrib_buff, "%d", ival );
16087          result = getattrib_buff;
16088       }
16089 
16090 /* Ncard. */
16091 /* ------ */
16092    } else if ( !strcmp( attrib, "ncard" ) ) {
16093       ival = astGetNcard( this );
16094       if ( astOK ) {
16095          (void) sprintf( getattrib_buff, "%d", ival );
16096          result = getattrib_buff;
16097       }
16098 
16099 /* Nkey. */
16100 /* ----- */
16101    } else if ( !strcmp( attrib, "nkey" ) ) {
16102       ival = astGetNkey( this );
16103       if ( astOK ) {
16104          (void) sprintf( getattrib_buff, "%d", ival );
16105          result = getattrib_buff;
16106       }
16107 
16108 /* AllWarnings */
16109 /* ----------- */
16110    } else if ( !strcmp( attrib, "allwarnings" ) ) {
16111       result = astGetAllWarnings( this );
16112 
16113 /* Warnings. */
16114 /* -------- */
16115    } else if ( !strcmp( attrib, "warnings" ) ) {
16116       result = astGetWarnings( this );
16117 
16118 /* If the attribute name was not recognised, pass it on to the parent
16119    method for further interpretation. */
16120    } else {
16121       result = (*parent_getattrib)( this_object, attrib, status );
16122    }
16123 
16124 /* Return the result. */
16125    return result;
16126 }
16127 
GetCard(AstFitsChan * this,int * status)16128 static int GetCard( AstFitsChan *this, int *status ){
16129 
16130 /*
16131 *+
16132 *  Name:
16133 *     astGetCard
16134 
16135 *  Purpose:
16136 *     Get the value of the Card attribute.
16137 
16138 *  Type:
16139 *     Protected virtual function.
16140 
16141 *  Synopsis:
16142 *     #include "fitschan.h"
16143 *     int astGetCard( AstFitsChan *this )
16144 
16145 *  Class Membership:
16146 *     FitsChan method.
16147 
16148 *  Description:
16149 *     This function returns the value of the Card attribute for the supplied
16150 *     FitsChan. This is the index of the next card to be read from the
16151 *     FitsChan. The index of the first card is 1. If there are no more
16152 *     cards to be read, a value one greater than the number of cards in the
16153 *     FitsChan is returned.
16154 
16155 *  Parameters:
16156 *     this
16157 *        Pointer to the FitsChan.
16158 
16159 *  Returned Value:
16160 *     The index of the next card to be read.
16161 
16162 *  Notes:
16163 *     - A value of zero will be returned if the current card is not defined.
16164 *     - This function attempts to execute even if an error has occurred.
16165 *-
16166 */
16167 
16168 /* Local Variables: */
16169    const char *class;      /* Pointer to class string */
16170    const char *method;     /* Pointer to method string */
16171    FitsCard *card0;        /* Pointer to current FitsCard */
16172    int index;              /* Index of next FitsCard */
16173 
16174 /* Ensure the source function has been called */
16175    ReadFromSource( this, status );
16176 
16177 /* Return if no FitsChan was supplied, or if the FitsChan is empty. */
16178    if ( !this || !this->head ) return 0;
16179 
16180 /* Store the method and object class. */
16181    method = "astGetCard";
16182    class = astGetClass( this );
16183 
16184 /* Save a pointer to the current card, and the reset the current card to
16185    be the first card. */
16186    card0 = this->card;
16187    astClearCard( this );
16188 
16189 /* Count through the list of FitsCards in the FitsChan until the original
16190    current card is reached. If the current card is not found (for instance
16191    if it has been marked as deleted and we are currently skipping such cards),
16192    this->card will be left null (end-of-file). */
16193    index = 1;
16194    while( this->card != card0 && astOK && this->card ){
16195 
16196 /* Increment the card count and move on to the next card. */
16197       index++;
16198       MoveCard( this, 1, method, class, status );
16199    }
16200 
16201 /* Return the card index. */
16202    return index;
16203 }
16204 
GetCardComm(AstFitsChan * this,int * status)16205 static const char *GetCardComm( AstFitsChan *this, int *status ){
16206 /*
16207 *+
16208 *  Name:
16209 *     GetCardComm
16210 
16211 *  Purpose:
16212 *     Get the value of the CardComm attribute.
16213 
16214 *  Type:
16215 *     Protected virtual function.
16216 
16217 *  Synopsis:
16218 *     #include "fitschan.h"
16219 *     const char *astGetCardComm( AstFitsChan *this)
16220 
16221 *  Class Membership:
16222 *     FitsChan method.
16223 
16224 *  Description:
16225 *     This function returns the value of the CardComm attribute for the
16226 *     supplied FitsChan. This is the comment for the current card.
16227 
16228 *  Parameters:
16229 *     this
16230 *        Pointer to the FitsChan.
16231 
16232 *  Returned Value:
16233 *     A pointer to a static string holding the comment. A zero-length
16234 *     string is returned if the card has no comment.
16235 
16236 *  Notes:
16237 *     - A value of NULL will be returned if an error has already
16238 *     occurred, or if this function should fail for any reason.
16239 *-
16240 */
16241 
16242 /* Local Variables */
16243    const char *result = NULL;
16244 
16245 /* Check inherited status */
16246    if( !astOK ) return result;
16247 
16248 /* Ensure the source function has been called */
16249    ReadFromSource( this, status );
16250 
16251 /* Get the comment for the current card. */
16252    result = CardComm( this, status );
16253 
16254 /* Return a zero-length string if the card has no comment. */
16255    if( astOK && !result ) result = "";
16256 
16257 /* Return the comment. */
16258    return result;
16259 }
16260 
GetCardName(AstFitsChan * this,int * status)16261 static const char *GetCardName( AstFitsChan *this, int *status ){
16262 /*
16263 *+
16264 *  Name:
16265 *     GetCardName
16266 
16267 *  Purpose:
16268 *     Get the value of the CardName attribute.
16269 
16270 *  Type:
16271 *     Protected virtual function.
16272 
16273 *  Synopsis:
16274 *     #include "fitschan.h"
16275 *     const char *astGetCardName( AstFitsChan *this)
16276 
16277 *  Class Membership:
16278 *     FitsChan method.
16279 
16280 *  Description:
16281 *     This function returns the value of the CardName attribute for the
16282 *     supplied FitsChan. This is the keyword name for the current card.
16283 
16284 *  Parameters:
16285 *     this
16286 *        Pointer to the FitsChan.
16287 
16288 *  Returned Value:
16289 *     A pointer to a static string holding the keyword name.
16290 
16291 *  Notes:
16292 *     - A value of NULL will be returned if an error has already
16293 *     occurred, or if this function should fail for any reason.
16294 *-
16295 */
16296 
16297 /* Ensure the source function has been called */
16298    ReadFromSource( this, status );
16299 
16300 /* Return the keyword name of the current card. */
16301    return CardName( this, status );
16302 }
16303 
GetCardType(AstFitsChan * this,int * status)16304 static int GetCardType( AstFitsChan *this, int *status ){
16305 /*
16306 *+
16307 *  Name:
16308 *     GetCardType
16309 
16310 *  Purpose:
16311 *     Get the value of the CardType attribute.
16312 
16313 *  Type:
16314 *     Protected virtual function.
16315 
16316 *  Synopsis:
16317 *     #include "fitschan.h"
16318 *     int astGetCardType( AstFitsChan *this )
16319 
16320 *  Class Membership:
16321 *     FitsChan method.
16322 
16323 *  Description:
16324 *     This function returns the value of teh CardType attribute for the supplied
16325 *     FitsChan. This is the data type of the keyword value for the current card.
16326 
16327 *  Parameters:
16328 *     this
16329 *        Pointer to the FitsChan.
16330 
16331 *  Returned Value:
16332 *     An integer representing the data type of the current card.
16333 
16334 *  Notes:
16335 *     - A value of AST__NOTYPE will be returned if an error has already
16336 *     occurred, or if this function should fail for any reason.
16337 *-
16338 */
16339 
16340 /* Ensure the source function has been called */
16341    ReadFromSource( this, status );
16342 
16343 /* Return the data type of the current card. */
16344    return CardType( this, status );
16345 }
16346 
GetFull(AstChannel * this_channel,int * status)16347 static int GetFull( AstChannel *this_channel, int *status ) {
16348 /*
16349 *  Name:
16350 *     GetFull
16351 
16352 *  Purpose:
16353 *     Obtain the value of the Full attribute for a FitsChan.
16354 
16355 *  Type:
16356 *     Private function.
16357 
16358 *  Synopsis:
16359 *     #include "fitschan.h"
16360 *     int GetFull( AstChannel *this, int *status )
16361 
16362 *  Class Membership:
16363 *     FitsChan member function (over-rides the protected astGetFull
16364 *     method inherited from the Channel class).
16365 
16366 *  Description:
16367 *     This function return the integer value of the Full attribute for
16368 *     a FitsChan.
16369 
16370 *  Parameters:
16371 *     this
16372 *        Pointer to the FitsChan.
16373 *     status
16374 *        Pointer to the inherited status variable.
16375 
16376 *  Returned Value:
16377 *     The Full attribute value.
16378 
16379 *  Notes:
16380 *     - This function modifies the default Full value from 0 to -1 for
16381 *     the benefit of the FitsChan class. This prevents non-essential
16382 *     information being written by the astWrite method unless it is
16383 *     requested by explicitlt setting a Full value.
16384 *     - A value of zero will be returned if this function is invoked
16385 *     with the global error status set, or if it should fail for any
16386 *     reason.
16387 */
16388 
16389 /* Local Variables: */
16390    AstFitsChan *this;            /* Pointer to the FitsChan structure */
16391    int result;                   /* Result value to return */
16392 
16393 /* Check the global error status. */
16394    if ( !astOK ) return 0;
16395 
16396 /* Obtain a pointer to the FitsChan structure. */
16397    this = (AstFitsChan *) this_channel;
16398 
16399 /* If the Full attribute us set, obtain its value using the parent class
16400    method. */
16401    if ( astTestFull( this ) ) {
16402       result = (* parent_getfull)( this_channel, status );
16403 
16404 /* Otherwise, supply a default value of -1. */
16405    } else {
16406       result = -1;
16407    }
16408 
16409 /* Return the result. */
16410    return result;
16411 }
16412 
GetLink(FitsCard * card,int next,const char * method,const char * class,int * status)16413 static FitsCard *GetLink( FitsCard *card, int next, const char *method,
16414                           const char *class, int *status ){
16415 /*
16416 *  Name:
16417 *     GetLink
16418 
16419 *  Purpose:
16420 *     Get a pointer to the next or previous card in the list.
16421 
16422 *  Type:
16423 *     Private function.
16424 
16425 *  Synopsis:
16426 *     #include "fitschan.h"
16427 *     FitsCard *GetLink( FitsCard *card, int next, const char *method,
16428 *                        const char *class, int *status )
16429 
16430 *  Class Membership:
16431 *     FitsChan member function.
16432 
16433 *  Description:
16434 *     Returns the a pointer to either the next or previous FitsCard
16435 *     structure in the circular linked list of such structures stored in a
16436 *     FitsChan. A check is performed to ensure that the forward and
16437 *     backward links from the supplied card are consistent and an error
16438 *     is reported if they are not (so long as no previous error has been
16439 *     reported). Memory corruption can result in inconsistent links
16440 *     which can result in infinite loops if an attempt is made to scan the
16441 *     list.
16442 
16443 *  Parameters:
16444 *     card
16445 *        The current card.
16446 *     next
16447 *        If non-zero, a pointer to the "next" card is returned. Otherwise
16448 *        a pointer to the "previous" card is returned.
16449 *     method
16450 *        Pointer to string holding the name of the calling method.
16451 *     class
16452 *        Pointer to string holding the object class.
16453 *     status
16454 *        Pointer to the inherited status variable.
16455 
16456 *  Returned Value:
16457 *     A pointer to the required card, or NULL if an error occurs.
16458 
16459 *  Notes:
16460 *     -  This function attempts to execute even if an error has occurred.
16461 */
16462 
16463 /* Local Variables: */
16464    FitsCard *ret;               /* Pointer to the returned card */
16465 
16466 /* Check that the "next" link from the previous card points back to
16467    the current card, and that the "prev" link from the next card points
16468    back to the current card. */
16469    if( card && ( card->prev->next != card ||
16470                  card->next->prev != card ) ){
16471 
16472 /* Report an error so long as no previous error has been reported, and
16473    return a NULL pointer. */
16474       if( astOK ){
16475          astError( AST__FCRPT, "%s(%s): A corrupted %s object has been "
16476                    "supplied.", status, method, class, class );
16477       }
16478       ret = NULL;
16479 
16480 /* If the links are good, return a pointer to the required card. */
16481    } else {
16482       ret = next ? card->next : card->prev;
16483    }
16484 
16485 /* Return the result. */
16486    return ret;
16487 }
16488 
GetNcard(AstFitsChan * this,int * status)16489 static int GetNcard( AstFitsChan *this, int *status ){
16490 
16491 /*
16492 *+
16493 *  Name:
16494 *     astGetNcard
16495 
16496 *  Purpose:
16497 *     Get the value of the Ncard attribute.
16498 
16499 *  Type:
16500 *     Protected virtual function.
16501 
16502 *  Synopsis:
16503 *     #include "fitschan.h"
16504 *     int astGetNcard( AstFitsChan *this )
16505 
16506 *  Class Membership:
16507 *     FitsChan method.
16508 
16509 *  Description:
16510 *     This function returns the value of the Ncard attribute for the supplied
16511 *     FitsChan. This is the number of cards currently in the FitsChan.
16512 
16513 *  Parameters:
16514 *     this
16515 *        Pointer to the FitsChan.
16516 
16517 *  Returned Value:
16518 *     The number of cards currently in the FitsChan.
16519 
16520 *  Notes:
16521 *     - A value of zero will be returned if an error has already
16522 *     occurred, or if this function should fail for any reason.
16523 *-
16524 */
16525 
16526 /* Local Variables: */
16527    const char *class;      /* Pointer to class string */
16528    const char *method;     /* Pointer to method string */
16529    FitsCard *card0;        /* Pointer to current card on entry */
16530    int ncard;              /* Number of cards so far */
16531 
16532 /* Ensure the source function has been called */
16533    ReadFromSource( this, status );
16534 
16535 /* Return zero if an error has already occurred, or no FitsChan was supplied,
16536    or the FitsChan is empty. */
16537    if ( !astOK || !this || !this->head ) return 0;
16538 
16539 /* Store the method and object class. */
16540    method = "astGetNcard";
16541    class = astGetClass( this );
16542 
16543 /* Save a pointer to the current card, and then reset the current card to
16544    be the first card. */
16545    card0 = this->card;
16546    astClearCard( this );
16547 
16548 /* Count through the cards in the FitsChan until the end of file is reached. */
16549    ncard = 0;
16550    while( astOK && this->card ){
16551 
16552 /* Increment the card count and move on to the next card. */
16553       ncard++;
16554       MoveCard( this, 1, method, class, status );
16555    }
16556 
16557 /* Reset the current card to be the original current card. */
16558    this->card = card0;
16559 
16560 /* Return the result. */
16561    return astOK ? ncard : 0;
16562 }
16563 
GetNkey(AstFitsChan * this,int * status)16564 static int GetNkey( AstFitsChan *this, int *status ){
16565 
16566 /*
16567 *+
16568 *  Name:
16569 *     astGetNkey
16570 
16571 *  Purpose:
16572 *     Get the value of the Nkey attribute.
16573 
16574 *  Type:
16575 *     Protected virtual function.
16576 
16577 *  Synopsis:
16578 *     #include "fitschan.h"
16579 *     int astGetNkey( AstFitsChan *this )
16580 
16581 *  Class Membership:
16582 *     FitsChan method.
16583 
16584 *  Description:
16585 *     This function returns the value of the Nkey attribute for the supplied
16586 *     FitsChan. This is the number of unique keywords currently in the
16587 *     FitsChan.
16588 
16589 *  Parameters:
16590 *     this
16591 *        Pointer to the FitsChan.
16592 
16593 *  Returned Value:
16594 *     The number of  unique keywords currently in the FitsChan.
16595 
16596 *  Notes:
16597 *     - A value of zero will be returned if an error has already
16598 *     occurred, or if this function should fail for any reason.
16599 *-
16600 */
16601 
16602 /* Local Variables: */
16603    AstKeyMap *km;          /* KeyMap holding unique keyword names */
16604    FitsCard *card0;        /* Pointer to current card on entry */
16605    const char *class;      /* Pointer to class string */
16606    const char *method;     /* Pointer to method string */
16607    int nkey;               /* Returned Nkey value */
16608 
16609 /* Ensure the source function has been called */
16610    ReadFromSource( this, status );
16611 
16612 /* Return zero if an error has already occurred, or no FitsChan was supplied,
16613    or the FitsChan is empty. */
16614    if ( !astOK || !this || !this->head ) return 0;
16615 
16616 /* Store the method and object class. */
16617    method = "astGetNkey";
16618    class = astGetClass( this );
16619 
16620 /* Create an empty KeyMap to hold the unused keyword names */
16621    km = astKeyMap( " ", status );
16622 
16623 /* Save a pointer to the current card, and then reset the current card to
16624    be the first card. */
16625    card0 = this->card;
16626    astClearCard( this );
16627 
16628 /* Loop through the cards in the FitsChan until the end of file is reached. */
16629    while( astOK && this->card ){
16630 
16631 /* Get the keyword name for the current card and add it to the keymap. */
16632       astMapPut0I( km, CardName( this, status ), 0, NULL );
16633 
16634 /* Move on to the next unused card. */
16635       MoveCard( this, 1, method, class, status );
16636    }
16637 
16638 /* Reset the current card to be the original current card. */
16639    this->card = card0;
16640 
16641 /* Get the number of keywords. */
16642    nkey = astMapSize( km );
16643 
16644 /* Annull the KeyMap . */
16645    km = astAnnul( km );
16646 
16647 /* Return the result. */
16648    return astOK ? nkey : 0;
16649 }
16650 
GetNextData(AstChannel * this_channel,int skip,char ** name,char ** val,int * status)16651 static void GetNextData( AstChannel *this_channel, int skip, char **name,
16652                          char **val, int *status ) {
16653 /*
16654 *  Name:
16655 *     GetNextData
16656 
16657 *  Purpose:
16658 *     Read the next item of data from a data source.
16659 
16660 *  Type:
16661 *     Private function.
16662 
16663 *  Synopsis:
16664 *     #include "fitschan.h"
16665 *     void GetNextData( AstChannel *this, int skip, char **name, char **val )
16666 
16667 *  Class Membership:
16668 *     FitsChan member function (over-rides the protected
16669 *     astGetNextData method inherited from the Channel class).
16670 
16671 *  Description:
16672 *     This function reads the next item of input data from a data
16673 *     source associated with a FitsChan and returns the result.  It
16674 *     decodes the data item and returns name/value pairs ready for
16675 *     use.
16676 
16677 *  Parameters:
16678 *     this
16679 *        Pointer to the FitsChan.
16680 *     skip
16681 *        A non-zero value indicates that a new Object is to be read,
16682 *        and that all input data up to the next "Begin" item are to be
16683 *        skipped in order to locate it. This is useful if the data
16684 *        source contains AST objects interspersed with other data (but
16685 *        note that these other data cannot appear inside AST Objects,
16686 *        only between them).
16687 *
16688 *        A zero value indicates that all input data are significant
16689 *        and the next item will therefore be read and an attempt made
16690 *        to interpret it whatever it contains. Any other data
16691 *        inter-mixed with AST Objects will then result in an error.
16692 *     name
16693 *        An address at which to store a pointer to a null-terminated
16694 *        dynamically allocated string containing the name of the next
16695 *        item in the input data stream. This name will be in lower
16696 *        case with no surrounding white space.  It is the callers
16697 *        responsibilty to free the memory holding this string (using
16698 *        astFree) when it is no longer required.
16699 *
16700 *        A NULL pointer value will be returned (without error) to
16701 *        indicate when there are no further input data items to be
16702 *        read.
16703 *     val
16704 *        An address at which to store a pointer to a null-terminated
16705 *        dynamically allocated string containing the value associated
16706 *        with the next item in the input data stream. No case
16707 *        conversion is performed on this string and all white space is
16708 *        potentially significant.  It is the callers responsibilty to
16709 *        free the memory holding this string (using astFree) when it
16710 *        is no longer required.
16711 *
16712 *        The returned pointer will be NULL if an Object data item is
16713 *        read (see the "Data Representation" section).
16714 
16715 *  Data Representation:
16716 
16717 *     The returned data items fall into the following categories:
16718 *
16719 *     - Begin: Identified by the name string "begin", this indicates
16720 *     the start of an Object definition. The associated value string
16721 *     gives the class name of the Object being defined.
16722 *
16723 *     - IsA: Identified by the name string "isa", this indicates the
16724 *     end of the data associated with a particular class structure
16725 *     within the definiton of a larger Object. The associated value
16726 *     string gives the name of the class whose data have just been
16727 *     read.
16728 *
16729 *     - End: Identified by the name string "end", this indicates the
16730 *     end of the data associated with a complete Object
16731 *     definition. The associated value string gives the class name of
16732 *     the Object whose definition is being ended.
16733 *
16734 *     - Non-Object: Identified by any other name string plus a
16735 *     non-NULL "val" pointer, this gives the value of a non-Object
16736 *     structure component (instance variable). The name identifies
16737 *     which instance variable it is (within the context of the class
16738 *     whose data are being read) and the value is encoded as a string.
16739 *
16740 *     - Object: Identified by any other name string plus a NULL "val"
16741 *     pointer, this identifies the value of an Object structure
16742 *     component (instance variable).  The name identifies which
16743 *     instance variable it is (within the context of the class whose
16744 *     data are being read) and the value is given by subsequent data
16745 *     items (so the next item should be a "Begin" item).
16746 
16747 *  Notes:
16748 *     - NULL pointer values will be returned if this function is
16749 *     invoked with the global error status set, or if it should fail
16750 *     for any reason.
16751 */
16752 
16753 /* Local Constants: */
16754 #define BUFF_LEN 100             /* Length of formatting buffer */
16755 
16756 /* Local Variables: */
16757    AstFitsChan *this;            /* Pointer to the FitsChan structure */
16758    char *keyword;                /* Pointer to current keyword string */
16759    char *newdata;                /* Pointer to stripped string value */
16760    char *upq;                    /* Pointer to unprequoted string */
16761    char buff[ BUFF_LEN + 1 ];    /* Buffer for formatting values */
16762    const char *class;            /* Pointer to object class */
16763    const char *method;           /* Pointer to method name */
16764    int cont;                     /* String ends with an ampersand? */
16765    int done;                     /* Data item found? */
16766    int freedata;                 /* Should the data pointer be freed? */
16767    int i;                        /* Loop counter for keyword characters */
16768    int len;                      /* Length of current keyword */
16769    int nc;                       /* Number of characters read by "astSscanf" */
16770    int nn;                       /* No. of characters after UnPreQuoting */
16771    int type;                     /* Data type code */
16772    void *data;                   /* Pointer to current data value */
16773 
16774 /* Initialise the returned pointer values. */
16775    *name = NULL;
16776    *val = NULL;
16777 
16778 /* Check the global error status. */
16779    if ( !astOK ) return;
16780 
16781 /* Obtain a pointer to the FitsChan structure. */
16782    this = (AstFitsChan *) this_channel;
16783 
16784 /* Store the method name and object class. */
16785    method = "astRead";
16786    class = astGetClass( this );
16787 
16788 /* Loop to consider successive cards stored in the FitsChan (starting
16789    at the "current" card) until a valid data item is read or "end of
16790    file" is reached. Also quit the loop if an error occurs. */
16791    done = 0;
16792    newdata = NULL;
16793    while ( !done && !astFitsEof( this ) && astOK ){
16794 
16795 /* Obtain the keyword string, data type code and data value pointer
16796    from the current card. */
16797       keyword = CardName( this, status );
16798       type = CardType( this, status );
16799       data = CardData( this, NULL, status );
16800 
16801 /* Mark all cards as having been used unless we are skipping over cards which
16802    may not be related to AST. */
16803       if( !skip ) MarkCard( this, status );
16804 
16805 /* Ignore comment cards. */
16806       if ( type != AST__COMMENT ) {
16807 
16808 /* Native encoding requires trailing white space to be removed from
16809    string values (so that null strings can be distinguished from blank
16810    strings). Do this now. */
16811          freedata = 0;
16812          if ( ( type == AST__STRING || type == AST__CONTINUE ) && data ){
16813             newdata = (char *) astStore( NULL, data, strlen( (char *) data ) + 1 );
16814             if( newdata ){
16815                newdata[ ChrLen( data, status ) ] = 0;
16816                data = (void *) newdata;
16817                freedata = 1;
16818             }
16819          }
16820 
16821 /* Obtain the keyword length and test the card to identify the type of
16822    AST data item (if any) that it represents. */
16823          len = (int) strlen( keyword );
16824 
16825 /* "Begin" item. */
16826 /* ------------- */
16827 
16828 /* This is identified by a string value and a keyword of the form
16829    "BEGASTxx", where "xx" are characters encoding a sequence
16830    number. */
16831          if ( ( type == AST__STRING ) &&
16832               ( nc = 0,
16833                 ( 0 == astSscanf( keyword, "BEGAST"
16834                                         "%*1[" SEQ_CHARS "]"
16835                                         "%*1[" SEQ_CHARS "]%n", &nc ) )
16836                 && ( nc >= len ) ) ) {
16837 
16838 /* Note we have found a data item. */
16839             done = 1;
16840 
16841 /* Set the returned name to "begin" and extract the associated class
16842    name from the string value. Store both of these in dynamically
16843    allocated strings. */
16844             *name = astString( "begin", 5 );
16845             *val = UnPreQuote( (const char *) data, status );
16846 
16847 /* Indicate that the current card has been used. */
16848             MarkCard( this, status );
16849 
16850 /* The "begin" item will be preceded by a header of COMMENT cards. Mark
16851    them as having been used. */
16852             ComBlock( this, -1, method, class, status );
16853 
16854 /* "IsA" item. */
16855 /* ----------- */
16856 
16857 /* This is identified by a string value and a keyword of the form
16858    "ISAxx", where "xx" are characters encoding a sequence
16859    number. Don't accept the item if we are skipping over cards looking
16860    for a "Begin" item. */
16861          } else if ( !skip &&
16862                      ( type == AST__STRING ) &&
16863                      ( nc = 0,
16864                        ( 0 == astSscanf( keyword,
16865                                       "ISA"
16866                                       "%*1[" SEQ_CHARS "]"
16867                                       "%*1[" SEQ_CHARS "]%n", &nc ) )
16868                        && ( nc >= len ) ) ) {
16869 
16870 /* Note we have found a data item. */
16871             done = 1;
16872 
16873 /* Set the returned name to "isa" and extract the associated class
16874    name from the string value. Store both of these in dynamically
16875    allocated strings. */
16876             *name = astString( "isa", 3 );
16877             *val = UnPreQuote( (const char *) data, status );
16878 
16879 /* "End" item. */
16880 /* ----------- */
16881 
16882 /* This is identified by a string value and a keyword of the form
16883    "ENDASTxx", where "xx" are characters encoding a sequence
16884    number. Don't accept the item if we are skipping over cards looking
16885    for a "Begin" item. */
16886          } else if ( !skip &&
16887                      ( type == AST__STRING ) &&
16888                      ( nc = 0,
16889                        ( 0 == astSscanf( keyword,
16890                                       "ENDAST"
16891                                       "%*1[" SEQ_CHARS "]"
16892                                       "%*1[" SEQ_CHARS "]%n", &nc ) )
16893                        && ( nc >= len ) ) ) {
16894 
16895 /* Note we have found a data item. */
16896             done = 1;
16897 
16898 /* Set the returned name to "end" and extract the associated class
16899    name from the string value. Store both of these in dynamically
16900    allocated strings. */
16901             *name = astString( "end", 3 );
16902             *val = UnPreQuote( (const char *) data, status );
16903 
16904 /* The "end" item eill be followed by a footer of COMMENT cards. Mark
16905    these cards as having been used. */
16906             ComBlock( this, 1, method, class, status );
16907 
16908 /* Object or data item. */
16909 /* -------------------- */
16910 
16911 /* These are identified by a string, int, or double value, and a
16912    keyword ending in two characters encoding a sequence number. Don't
16913    accept the item if we are skipping over cards looking for a "Begin"
16914    item. */
16915          } else if ( !skip &&
16916                      ( ( type == AST__STRING ) ||
16917                        ( type == AST__INT ) ||
16918                        ( type == AST__FLOAT ) ) &&
16919                      ( len > 2 ) &&
16920                      strchr( SEQ_CHARS, keyword[ len - 1 ] ) &&
16921                      strchr( SEQ_CHARS, keyword[ len - 2 ] ) ) {
16922 
16923 /* Note we have found a data item. */
16924             done = 1;
16925 
16926 /* Set the returned name by removing the last two characters from the
16927    keyword and converting to lower case. Store this in a dynamically
16928    allocated string. */
16929             *name = astString( keyword, len - 2 );
16930             for ( i = 0; ( *name )[ i ]; i++ ) {
16931                ( *name )[ i ] = tolower( ( *name )[ i ] );
16932             }
16933 
16934 /* Classify the data type. */
16935             switch ( type ) {
16936 
16937 /* If the value is a string, test if it is zero-length. If so, this
16938    "null" value indicates an Object data item (whose definition
16939    follows), so leave the returned value pointer as NULL. Otherwise,
16940    we have a string data item, so extract its value and store it in a
16941    dynamically allocated string. */
16942             case AST__STRING:
16943                if ( *( (char *) data ) ) {
16944 
16945 /* A long string value may be continued on subsequent CONTINUE cards. See
16946    if the current string may be continued. This is the case if the final
16947    non-blank character (before UnPreQuoting) is an ampersand. */
16948                   cont = ( ((char *) data)[ ChrLen( data, status ) - 1 ] == '&' );
16949 
16950 /* If the string does not end with an ampersand, just UnPreQUote it and
16951    return a copy. */
16952                   if( !cont ) {
16953                      *val = UnPreQuote( (const char *) data, status );
16954 
16955 /* Otherwise, initialise the returned string to hold a copy of the keyword
16956    value. */
16957                   } else {
16958                      nc = strlen( (const char *) data );
16959                      *val = astStore( NULL, (const char *) data, nc + 1 );
16960 
16961 /* Loop round reading any subsequent CONTINUE cards. Leave the loop when
16962    the end-of-file is hit, or an error occurs. */
16963                      while( cont && MoveCard( this, 1, method, class, status ) &&
16964                             astOK ){
16965 
16966 /* See if this is a CONTINUE card. If so, get its data pointer. */
16967                         if( CardType( this, status ) == AST__CONTINUE ){
16968                            data = CardData( this, NULL, status );
16969 
16970 /* See if the CONTINUE card ends with an ampersand (i.e. if there is
16971    a possibility of there being any remaining CONTINUE cards). */
16972                            cont = ( ( (char *) data)[ ChrLen( data, status ) - 1 ] == '&' );
16973 
16974 /* UnPreQUote it. */
16975                            upq = UnPreQuote( (const char *) data, status );
16976                            if( !astOK ) break;
16977 
16978 /* Expand the memory for the returned string to hold the new string. */
16979                            nn = strlen( upq );
16980                            *val = astRealloc( *val, nc + nn );
16981                            if( !astOK ) break;
16982 
16983 /* Copy the new string into the expanded memory, so that the first
16984    character of the new string over-writes the trailing ampersand
16985    currently in the buffer. */
16986                            strcpy( *val + nc - 1, upq );
16987 
16988 /* Release the memory holding the UnPreQUoted string . */
16989                            upq = astFree( upq );
16990 
16991 /* Update the current length of the returned string. */
16992                            nc += nn - 1;
16993 
16994 /* Mark the current card as having been read. */
16995                            MarkCard( this, status );
16996 
16997 /* Report an error if this is not a CONTINUE card. */
16998                         } else {
16999                            astError( AST__BADIN, "%s(%s): One or more "
17000                                      "FITS \"CONTINUE\" cards are missing "
17001                                      "after the card for keyword \"%s\".", status,
17002                                      method, class, keyword );
17003                         }
17004                      }
17005                   }
17006                }
17007                break;
17008 
17009 /* If the value is an int, format it and store the result in a
17010    dynamically allocated string. */
17011             case AST__INT:
17012                (void) sprintf( buff, "%d", *( (int *) data ) );
17013                *val = astString( buff, (int) strlen( buff ) );
17014                break;
17015 
17016 /* If the value is a double, format it and store the result in a
17017    dynamically allocated string. */
17018             case AST__FLOAT:
17019                (void) sprintf( buff, "%.*g", DBL_DIG, *( (double *) data ) );
17020                CheckZero( buff,  *( (double *) data ), 0, status );
17021                *val = astString( buff, (int) strlen( buff ) );
17022                break;
17023             }
17024 
17025 /* Anything else. */
17026 /* -------------- */
17027 
17028 /* If the input line didn't match any of the above and the "skip" flag
17029    is not set, then report an error.. */
17030          } else if ( !skip ) {
17031             astError( AST__BADIN,
17032                       "%s(%s): Cannot interpret the input data given by "
17033                       "FITS keyword \"%s\".", status, method, class, keyword );
17034          }
17035 
17036 /* Free any memory used to hold stripped string data. */
17037          if( freedata ) newdata = (char *) astFree( (void *) newdata );
17038       }
17039 
17040 /* Increment the current card. */
17041       MoveCard( this, 1, method, class, status );
17042    }
17043 
17044 /* If an error occurred, ensure that any allocated memory is freed and
17045    that NULL pointer values are returned. */
17046    if ( !astOK ) {
17047       *name = astFree( *name );
17048       *val = astFree( *val );
17049    }
17050 
17051 /* Undefine macros local to this function. */
17052 #undef BUFF_LEN
17053 }
17054 
GetSkip(AstChannel * this_channel,int * status)17055 static int GetSkip( AstChannel *this_channel, int *status ) {
17056 /*
17057 *  Name:
17058 *     GetSkip
17059 
17060 *  Purpose:
17061 *     Obtain the value of the Skip attribute for a FitsChan.
17062 
17063 *  Type:
17064 *     Private function.
17065 
17066 *  Synopsis:
17067 *     #include "fitschan.h"
17068 *     int GetSkip( AstChannel *this, int *status )
17069 
17070 *  Class Membership:
17071 *     FitsChan member function (over-rides the protected astGetSkip
17072 *     method inherited from the Channel class).
17073 
17074 *  Description:
17075 *     This function return the (boolean) integer value of the Skip
17076 *     attribute for a FitsChan.
17077 
17078 *  Parameters:
17079 *     this
17080 *        Pointer to the FitsChan.
17081 *     status
17082 *        Pointer to the inherited status variable.
17083 
17084 *  Returned Value:
17085 *     The Skip attribute value.
17086 
17087 *  Notes:
17088 *     - This function modifies the default Skip value from 0 to 1 for
17089 *     the benefit of the FitsChan class. This default value allows the
17090 *     astRead method to skip over unrelated FITS keywords when
17091 *     searching for the next Object to read.
17092 *     - A value of zero will be returned if this function is invoked
17093 *     with the global error status set, or if it should fail for any
17094 *     reason.
17095 */
17096 
17097 /* Local Variables: */
17098    AstFitsChan *this;            /* Pointer to the FitsChan structure */
17099    int result;                   /* Result value to return */
17100 
17101 /* Check the global error status. */
17102    if ( !astOK ) return 0;
17103 
17104 /* Obtain a pointer to the FitsChan structure. */
17105    this = (AstFitsChan *) this_channel;
17106 
17107 /* If the Skip attribute us set, obtain its value using the parent class
17108    method. */
17109    if ( astTestSkip( this ) ) {
17110       result = (* parent_getskip)( this_channel, status );
17111 
17112 /* Otherwise, supply a default value of 1. */
17113    } else {
17114       result = 1;
17115    }
17116 
17117 /* Return the result. */
17118    return result;
17119 }
17120 
GetValue(AstFitsChan * this,const char * keyname,int type,void * value,int report,int mark,const char * method,const char * class,int * status)17121 static int GetValue( AstFitsChan *this, const char *keyname, int type,
17122                      void *value, int report, int mark, const char *method,
17123                      const char *class, int *status ){
17124 /*
17125 *  Name:
17126 *     GetValue
17127 
17128 *  Purpose:
17129 *     Obtain a FITS keyword value.
17130 
17131 *  Type:
17132 *     Private function.
17133 
17134 *  Synopsis:
17135 *     int GetValue( AstFitsChan *this, const char *keyname, int type, void *value,
17136 *                   int report, int mark, const char *method, const char *class, int *status )
17137 
17138 *  Class Membership:
17139 *     FitsChan
17140 
17141 *  Description:
17142 *     This function gets a value for the specified keyword from the
17143 *     supplied FitsChan, and stores it in the supplied buffer. Optionally,
17144 *     the keyword is marked as having been read into an AST object so that
17145 *     it is not written out when the FitsChan is deleted.
17146 
17147 *  Parameters:
17148 *     this
17149 *        A pointer to the FitsChan containing the keyword values to be
17150 *        read.
17151 *     keyname
17152 *        A pointer to a string holding the keyword name.
17153 *     type
17154 *        The FITS data type in which to return the keyword value. If the
17155 *        stored value is not of the requested type, it is converted if
17156 *        possible.
17157 *     value
17158 *        A pointer to a buffer of suitable size to receive the keyword
17159 *        value. The supplied value is left unchanged if the keyword is
17160 *        not found.
17161 *     report
17162 *        Should an error be reported if the keyword cannot be found, or
17163 *        cannot be converted to the requested type?
17164 *     mark
17165 *        Should the card be marked as having been used?
17166 *     method
17167 *        A string holding the name of the calling method.
17168 *     class
17169 *        A string holding the object class.
17170 *     status
17171 *        Pointer to the inherited status variable.
17172 
17173 *  Returned Value:
17174 *     Zero if the keyword does not exist in "this", or cannot be
17175 *     converted to the requested type. One is returned otherwise.
17176 
17177 *  Notes:
17178 *     - An error is reported if the keyword value is undefined.
17179 *     - A value of zero is returned if an error has already occurred,
17180 *     or if an error occurs within this function.
17181 */
17182 
17183 /* Local Variables: */
17184    int icard;                         /* Current card index */
17185    int ret;                           /* Returned value */
17186 
17187 /* Check the status */
17188    if( !astOK ) return 0;
17189 
17190 /* Save the current card index. */
17191    icard = astGetCard( this );
17192 
17193 /* Attempt to find the supplied keyword. */
17194    ret = SearchCard( this, keyname, method, class, status );
17195 
17196 /* If the keyword was found, convert the current card's data value and copy
17197    it to the supplied buffer. */
17198    if( ret ){
17199       if( CnvValue( this, type, 0, value, method, status ) ) {
17200 
17201 /* If required, mark it as having been read into an AST object. */
17202          if( mark ) MarkCard( this, status );
17203 
17204 /* If the value is undefined, report an error if "report" is non-zero. */
17205          if( type == AST__UNDEF && report && astOK ) {
17206             ret = 0;
17207             astError( AST__FUNDEF, "%s(%s): FITS keyword \"%s\" has no value.",
17208                       status, method, class, keyname );
17209          }
17210 
17211 /* If the value could not be converted to the requested data, type report
17212    an error if reporting is enabled. */
17213       } else {
17214          ret = 0;
17215          if( report && astOK ){
17216             astError( AST__FTCNV, "%s(%s): Cannot convert FITS keyword '%s' to %s.",
17217                       status, method, class, keyname, type_names[ type ] );
17218          }
17219       }
17220 
17221 /* If the keyword was not found, report an error if "report" is non-zero. */
17222    } else if( report && astOK ){
17223       astError( AST__BDFTS, "%s(%s): Unable to find a value for FITS "
17224                 "keyword \"%s\".", status, method, class, keyname );
17225    }
17226 
17227 /* Reinstate the original current card index. */
17228    astSetCard( this, icard );
17229 
17230 /* If an error has occurred, return 0. */
17231    if( !astOK ) ret = 0;
17232 
17233 /* Return the result. */
17234    return ret;
17235 }
17236 
GetValue2(AstFitsChan * this1,AstFitsChan * this2,const char * keyname,int type,void * value,int report,const char * method,const char * class,int * status)17237 static int GetValue2( AstFitsChan *this1, AstFitsChan *this2, const char *keyname,
17238                       int type, void *value, int report, const char *method,
17239                       const char *class, int *status ){
17240 /*
17241 *  Name:
17242 *     GetValue2
17243 
17244 *  Purpose:
17245 *     Obtain a FITS keyword value from one of two FitsChans.
17246 
17247 *  Type:
17248 *     Private function.
17249 
17250 *  Synopsis:
17251 *     int GetValue2( AstFitsChan *this1, AstFitsChan *this2, const char *keyname,
17252 *                    int type, void *value, int report, const char *method,
17253 *                    const char *class, int *status )
17254 
17255 *  Class Membership:
17256 *     FitsChan
17257 
17258 *  Description:
17259 *     This function attempts to get a value for the specified keyword from
17260 *     the first supplied FitsChan. If this fails (due to the FitsChan not
17261 *     containing a value for the ketword) then an attempt is made to get
17262 *     a value for the keyword from the second supplied FitsChan.
17263 
17264 *  Parameters:
17265 *     this1
17266 *        A pointer to the first FitsChan to be used.
17267 *     this2
17268 *        A pointer to the second FitsChan to be used.
17269 *     keyname
17270 *        A pointer to a string holding the keyword name.
17271 *     type
17272 *        The FITS data type in which to return the keyword value. If the
17273 *        stored value is not of the requested type, it is converted if
17274 *        possible.
17275 *     value
17276 *        A pointer to a buffer of suitable size to receive the keyword
17277 *        value. The supplied value is left unchanged if the keyword is
17278 *        not found.
17279 *     report
17280 *        Should an error be reported if the keyword cannot be found, or
17281 *        cannot be converted to the requested type?
17282 *     method
17283 *        A string holding the name of the calling method.
17284 *     class
17285 *        A string holding the object class.
17286 *     status
17287 *        Pointer to the inherited status variable.
17288 
17289 *  Returned Value:
17290 *     Zero if the keyword does not exist in either FitsChan, or cannot be
17291 *     converted to the requested type. One is returned otherwise.
17292 
17293 *  Notes:
17294 *     -  A value of zero is returned if an error has already occurred,
17295 *     or if an error occurs within this function.
17296 *     -  If the card is found in the first FitsChan, it is not marked as
17297 *     having been used. If the card is found in the second FitsChan, it is
17298 *     marked as having been used.
17299 */
17300 
17301 /* Local Variables: */
17302    int ret;                           /* Returned value */
17303 
17304 /* Check the status */
17305    if( !astOK ) return 0;
17306 
17307 /* Try the first FitsChan. If this fails try the second. Do not report
17308    an error if the keyword is not found in the first FitsChan (this will
17309    be done, if required, once the second FitsChan has been searched). */
17310    ret = GetValue( this1, keyname, type, value, 0, 0, method, class, status );
17311    if( ! ret ) {
17312       ret = GetValue( this2, keyname, type, value, report, 1, method, class, status );
17313    }
17314 
17315 /* If an error has occurred, return 0. */
17316    if( !astOK ) ret = 0;
17317 
17318 /* Return the result. */
17319    return ret;
17320 }
17321 
HasAIPSSpecAxis(AstFitsChan * this,const char * method,const char * class,int * status)17322 static int HasAIPSSpecAxis( AstFitsChan *this, const char *method,
17323                             const char *class, int *status ){
17324 
17325 /*
17326 *  Name:
17327 *     HasAIPSSpecAxis
17328 
17329 *  Purpose:
17330 *     Does the FitsChan contain an AIPS spectral CTYPE keyword?
17331 
17332 *  Type:
17333 *     Private function.
17334 
17335 *  Synopsis:
17336 
17337 *     int HasAIPSSpecAxis( AstFitsChan *this, const char *method,
17338 *                          const char *class, int *status  )
17339 
17340 *  Class Membership:
17341 *     FitsChan
17342 
17343 *  Description:
17344 *     This function returns a non-zero value if the FitsCHan contains a
17345 *     CTYPE value which conforms to the non-standard system used by AIPS.
17346 
17347 *  Parameters:
17348 *     this
17349 *        A pointer to the FitsChan to be used.
17350 *     method
17351 *        Pointer to a string holding the name of the calling method.
17352 *        This is only for use in constructing error messages.
17353 *     class
17354 *        Pointer to a string holding the name of the supplied object class.
17355 *        This is only for use in constructing error messages.
17356 *     status
17357 *        Pointer to the inherited status variable.
17358 
17359 *  Returned Value:
17360 *     Non-zero if an AIPS spectral CTYPE keyword was found.
17361 */
17362 
17363 /* Local Variables: */
17364    char *assys;                   /* AIPS standard of rest type */
17365    char *astype;                  /* AIPS spectral type */
17366    char *cval;                    /* Pointer to character string */
17367    int j;                         /* Current axis index */
17368    int jhi;                       /* Highest axis index with a CTYPE */
17369    int jlo;                       /* Lowest axis index with a CTYPE */
17370    int ret;                       /* Returned value */
17371 
17372 /* Initialise */
17373    ret = 0;
17374 
17375 /* Check the status */
17376    if( !astOK ) return ret;
17377 
17378 /* If the FitsChan contains any CTYPE values, convert the bounds from
17379    one-based to zero-based, and loop round them all. */
17380    if( astKeyFields( this, "CTYPE%1d", 1, &jhi, &jlo ) ) {
17381       jlo--;
17382       jhi--;
17383       for( j = jlo; j <= jhi; j++ ) {
17384 
17385 /* Get the next CTYPE value. If found, see if it is an AIPS spectral
17386    CTYPE value. */
17387          if( GetValue( this, FormatKey( "CTYPE", j + 1, -1, ' ', status ),
17388                        AST__STRING, (void *) &cval, 0, 0, method,
17389                        class, status ) ){
17390             if( IsAIPSSpectral( cval, &astype, &assys, status ) ) {
17391                ret = 1;
17392                break;
17393             }
17394          }
17395       }
17396    }
17397 
17398 /* If an error has occurred, return 0. */
17399    if( !astOK ) ret = 0;
17400 
17401 /* Return the result. */
17402    return ret;
17403 }
17404 
HasCard(AstFitsChan * this,const char * name,const char * method,const char * class,int * status)17405 static int HasCard( AstFitsChan *this, const char *name,
17406                     const char *method, const char *class, int *status ){
17407 
17408 /*
17409 *  Name:
17410 *     HasCard
17411 
17412 *  Purpose:
17413 *     Check if the FitsChan contains a specified keyword.
17414 
17415 *  Type:
17416 *     Private function.
17417 
17418 *  Synopsis:
17419 *     #include "fitschan.h"
17420 
17421 *     int HasCard( AstFitsChan *this, const char *name,
17422 *                  const char *method, const char *class, int *status )
17423 
17424 *  Class Membership:
17425 *     FitsChan member function.
17426 
17427 *  Description:
17428 *     Returns a non-zero value if the FitsChan contains the given keyword,
17429 *     and zero otherwise. The current card is unchanged.
17430 
17431 *  Parameters:
17432 *     this
17433 *        Pointer to the FitsChan.
17434 *     name
17435 *        Pointer to a string holding the keyword name.
17436 *     method
17437 *        Pointer to string holding name of calling method.
17438 *     status
17439 *        Pointer to the inherited status variable.
17440 
17441 *  Returned Value:
17442 *     A value of 1 is returned if a card was found refering to the given
17443 *     keyword. Otherwise zero is returned.
17444 */
17445 
17446 /* Check the supplied pointers (we can rely on astMapHasKey to check the
17447    inherited status). */
17448    if( !name || !this || !this->keywords ) return 0;
17449 
17450 /* Search the KeyMap holding the keywords currently in the FitsChan,
17451    returning non-zero if the keyword was found. A KeyMap is used because
17452    it uses a hashing algorithm to find the entries and is therefore a lot
17453    quicker than searching through the list of linked FitsCards. */
17454    return astMapHasKey( this->keywords, name );
17455 }
astInitFitsChanVtab_(AstFitsChanVtab * vtab,const char * name,int * status)17456 void astInitFitsChanVtab_(  AstFitsChanVtab *vtab, const char *name, int *status ) {
17457 
17458 /*
17459 *+
17460 *  Name:
17461 *     astInitFitsChanVtab
17462 
17463 *  Purpose:
17464 *     Initialise a virtual function table for a FitsChan.
17465 
17466 *  Type:
17467 *     Protected function.
17468 
17469 *  Synopsis:
17470 *     #include "fitschan.h"
17471 *     void astInitFitsChanVtab( AstFitsChanVtab *vtab, const char *name )
17472 
17473 *  Class Membership:
17474 *     FitsChan vtab initialiser.
17475 
17476 *  Description:
17477 *     This function initialises the component of a virtual function
17478 *     table which is used by the FitsChan class.
17479 
17480 *  Parameters:
17481 *     vtab
17482 *        Pointer to the virtual function table. The components used by
17483 *        all ancestral classes will be initialised if they have not already
17484 *        been initialised.
17485 *     name
17486 *        Pointer to a constant null-terminated character string which contains
17487 *        the name of the class to which the virtual function table belongs (it
17488 *        is this pointer value that will subsequently be returned by the Object
17489 *        astClass function).
17490 *-
17491 */
17492 
17493 /* Local Variables: */
17494    astDECLARE_GLOBALS            /* Pointer to thread-specific global data */
17495    AstObjectVtab *object;        /* Pointer to Object component of Vtab */
17496    AstChannelVtab *channel;      /* Pointer to Channel component of Vtab */
17497    char buf[ 100 ];              /* Buffer large enough to store formatted INT_MAX */
17498 
17499 /* Check the local error status. */
17500    if ( !astOK ) return;
17501 
17502 /* Get a pointer to the thread specific global data structure. */
17503    astGET_GLOBALS(NULL);
17504 
17505 /* Initialize the component of the virtual function table used by the
17506    parent class. */
17507    astInitChannelVtab( (AstChannelVtab *) vtab, name );
17508 
17509 /* Store a unique "magic" value in the virtual function table. This
17510    will be used (by astIsAFitsChan) to determine if an object belongs
17511    to this class.  We can conveniently use the address of the (static)
17512    class_check variable to generate this unique value. */
17513    vtab->id.check = &class_check;
17514    vtab->id.parent = &(((AstChannelVtab *) vtab)->id);
17515 
17516 /* Initialise member function pointers. */
17517 /* ------------------------------------ */
17518 
17519 /* Store pointers to the member functions (implemented here) that provide
17520    virtual methods for this class. */
17521    vtab->PutCards = PutCards;
17522    vtab->PutFits = PutFits;
17523    vtab->DelFits = DelFits;
17524    vtab->GetTables = GetTables;
17525    vtab->PutTables = PutTables;
17526    vtab->PutTable = PutTable;
17527    vtab->TableSource = TableSource;
17528    vtab->SetTableSource = SetTableSource;
17529    vtab->RemoveTables = RemoveTables;
17530    vtab->PurgeWCS = PurgeWCS;
17531    vtab->RetainFits = RetainFits;
17532    vtab->FindFits = FindFits;
17533    vtab->KeyFields = KeyFields;
17534    vtab->ReadFits = ReadFits;
17535    vtab->ShowFits = ShowFits;
17536    vtab->WriteFits = WriteFits;
17537    vtab->EmptyFits = EmptyFits;
17538    vtab->FitsEof = FitsEof;
17539    vtab->GetFitsCF = GetFitsCF;
17540    vtab->GetFitsCI = GetFitsCI;
17541    vtab->GetFitsF = GetFitsF;
17542    vtab->GetFitsI = GetFitsI;
17543    vtab->GetFitsL = GetFitsL;
17544    vtab->TestFits = TestFits;
17545    vtab->GetFitsS = GetFitsS;
17546    vtab->GetFitsCN = GetFitsCN;
17547    vtab->FitsGetCom = FitsGetCom;
17548    vtab->SetFitsCom = SetFitsCom;
17549    vtab->SetFitsCF = SetFitsCF;
17550    vtab->SetFitsCI = SetFitsCI;
17551    vtab->SetFitsF = SetFitsF;
17552    vtab->SetFitsI = SetFitsI;
17553    vtab->SetFitsL = SetFitsL;
17554    vtab->SetFitsU = SetFitsU;
17555    vtab->SetFitsS = SetFitsS;
17556    vtab->SetFitsCN = SetFitsCN;
17557    vtab->SetFitsCM = SetFitsCM;
17558    vtab->ClearCard = ClearCard;
17559    vtab->TestCard = TestCard;
17560    vtab->SetCard = SetCard;
17561    vtab->GetCard = GetCard;
17562    vtab->ClearFitsDigits = ClearFitsDigits;
17563    vtab->TestFitsDigits = TestFitsDigits;
17564    vtab->SetFitsDigits = SetFitsDigits;
17565    vtab->GetFitsDigits = GetFitsDigits;
17566    vtab->ClearFitsAxisOrder = ClearFitsAxisOrder;
17567    vtab->TestFitsAxisOrder = TestFitsAxisOrder;
17568    vtab->SetFitsAxisOrder = SetFitsAxisOrder;
17569    vtab->GetFitsAxisOrder = GetFitsAxisOrder;
17570    vtab->ClearDefB1950 = ClearDefB1950;
17571    vtab->TestDefB1950 = TestDefB1950;
17572    vtab->SetDefB1950 = SetDefB1950;
17573    vtab->GetDefB1950 = GetDefB1950;
17574    vtab->ClearTabOK = ClearTabOK;
17575    vtab->TestTabOK = TestTabOK;
17576    vtab->SetTabOK = SetTabOK;
17577    vtab->GetTabOK = GetTabOK;
17578    vtab->ClearCarLin = ClearCarLin;
17579    vtab->TestCarLin = TestCarLin;
17580    vtab->SetCarLin = SetCarLin;
17581    vtab->GetCarLin = GetCarLin;
17582    vtab->ClearPolyTan = ClearPolyTan;
17583    vtab->TestPolyTan = TestPolyTan;
17584    vtab->SetPolyTan = SetPolyTan;
17585    vtab->GetPolyTan = GetPolyTan;
17586    vtab->ClearIwc = ClearIwc;
17587    vtab->TestIwc = TestIwc;
17588    vtab->SetIwc = SetIwc;
17589    vtab->GetIwc = GetIwc;
17590    vtab->ClearWarnings = ClearWarnings;
17591    vtab->TestWarnings = TestWarnings;
17592    vtab->SetWarnings = SetWarnings;
17593    vtab->GetWarnings = GetWarnings;
17594    vtab->GetCardType = GetCardType;
17595    vtab->GetCardName = GetCardName;
17596    vtab->GetCardComm = GetCardComm;
17597    vtab->GetNcard = GetNcard;
17598    vtab->GetNkey = GetNkey;
17599    vtab->GetAllWarnings = GetAllWarnings;
17600    vtab->ClearEncoding = ClearEncoding;
17601    vtab->TestEncoding = TestEncoding;
17602    vtab->SetEncoding = SetEncoding;
17603    vtab->GetEncoding = GetEncoding;
17604    vtab->ClearClean = ClearClean;
17605    vtab->TestClean = TestClean;
17606    vtab->SetClean = SetClean;
17607    vtab->GetClean = GetClean;
17608    vtab->ClearCDMatrix = ClearCDMatrix;
17609    vtab->TestCDMatrix = TestCDMatrix;
17610    vtab->SetCDMatrix = SetCDMatrix;
17611    vtab->GetCDMatrix = GetCDMatrix;
17612 
17613 /* Save the inherited pointers to methods that will be extended, and
17614    replace them with pointers to the new member functions. */
17615    object = (AstObjectVtab *) vtab;
17616    channel = (AstChannelVtab *) vtab;
17617    parent_getobjsize = object->GetObjSize;
17618    object->GetObjSize = GetObjSize;
17619 #if defined(THREAD_SAFE)
17620    parent_managelock = object->ManageLock;
17621    object->ManageLock = ManageLock;
17622 #endif
17623    parent_clearattrib = object->ClearAttrib;
17624    object->ClearAttrib = ClearAttrib;
17625    parent_getattrib = object->GetAttrib;
17626    object->GetAttrib = GetAttrib;
17627    parent_setattrib = object->SetAttrib;
17628    object->SetAttrib = SetAttrib;
17629    parent_testattrib = object->TestAttrib;
17630    object->TestAttrib = TestAttrib;
17631    parent_write = channel->Write;
17632    channel->Write = Write;
17633    parent_read = channel->Read;
17634    channel->Read = Read;
17635    parent_getskip = channel->GetSkip;
17636    channel->GetSkip = GetSkip;
17637    parent_getfull = channel->GetFull;
17638    channel->GetFull = GetFull;
17639    channel->WriteBegin = WriteBegin;
17640    channel->WriteIsA = WriteIsA;
17641    channel->WriteEnd = WriteEnd;
17642    channel->WriteInt = WriteInt;
17643    channel->WriteDouble = WriteDouble;
17644    channel->WriteString = WriteString;
17645    channel->WriteObject = WriteObject;
17646    channel->GetNextData = GetNextData;
17647    parent_setsourcefile = channel->SetSourceFile;
17648    channel->SetSourceFile = SetSourceFile;
17649 
17650 /* Declare the class dump, copy and delete functions.*/
17651    astSetDump( vtab, Dump, "FitsChan", "I/O channels to FITS files" );
17652    astSetCopy( (AstObjectVtab *) vtab, Copy );
17653    astSetDelete( (AstObjectVtab *) vtab, Delete );
17654 
17655 /* Max number of characters needed to format an int. */
17656    LOCK_MUTEX4
17657    sprintf( buf, "%d", INT_MAX );
17658    int_dig = strlen( buf );
17659 
17660 /* Create a pair of MJD TimeFrames which will be used for converting to and
17661    from TDB. */
17662    astBeginPM;
17663    if( !tdbframe ) tdbframe = astTimeFrame( "system=MJD,timescale=TDB", status );
17664    if( !timeframe ) timeframe = astTimeFrame( "system=MJD", status );
17665    astEndPM;
17666    UNLOCK_MUTEX4
17667 
17668 /* If we have just initialised the vtab for the current class, indicate
17669    that the vtab is now initialised, and store a pointer to the class
17670    identifier in the base "object" level of the vtab. */
17671    if( vtab == &class_vtab ) {
17672       class_init = 1;
17673       astSetVtabClassIdentifier( vtab, &(vtab->id) );
17674    }
17675 }
17676 
InsCard(AstFitsChan * this,int overwrite,const char * name,int type,void * data,const char * comment,const char * method,const char * class,int * status)17677 static void InsCard( AstFitsChan *this, int overwrite, const char *name,
17678                      int type, void *data, const char *comment,
17679                      const char *method, const char *class, int *status ){
17680 
17681 /*
17682 *  Name:
17683 *     InsCard
17684 
17685 *  Purpose:
17686 *     Inserts a card into a FitsChan.
17687 
17688 *  Type:
17689 *     Private function.
17690 
17691 *  Synopsis:
17692 *     #include "fitschan.h"
17693 
17694 *     void InsCard( AstFitsChan *this, int overwrite, const char *name,
17695 *                   int type, void *data, const char *comment,
17696 *                   const char *method, const char *class, int *status )
17697 
17698 *  Class Membership:
17699 *     FitsChan member function.
17700 
17701 *  Description:
17702 *     Either appends a new card to a FitsChan, or over-writes an existing
17703 *     card, holding the supplied keyword name, value and comment.
17704 
17705 *  Parameters:
17706 *     this
17707 *        Pointer to the FitsChan containing the filters to apply to the
17708 *        keyword name. If a NULL pointer is supplied, no filtering is applied.
17709 *     overwrite
17710 *        If non-zero, the new card over-writes the current card given by
17711 *        the "Card" attribute, and the current card is incremented so
17712 *        that it refers to the next card. Otherwise, the new card is
17713 *        inserted in front of the current card and the current card is
17714 *        left unchanged.
17715 *     name
17716 *        Pointer to a string holding the keyword name of the new card.
17717 *     type
17718 *        An integer value representing the data type of the keyword.
17719 *     data
17720 *        Pointer to the data associated with the keyword.
17721 *     comment
17722 *        Pointer to a null-terminated string holding a comment.
17723 *     method
17724 *        Pointer to a string holding the name of the calling method.
17725 *        This is only for use in constructing error messages.
17726 *     class
17727 *        Pointer to a string holding the name of the supplied object class.
17728 *        This is only for use in constructing error messages.
17729 *     status
17730 *        Pointer to the inherited status variable.
17731 
17732 *  Notes:
17733 *     -  An error is reported if an attempt is made to change the data type
17734 *     of an existing card.
17735 *     -  If a type of AST__COMMENT is supplied, then any data value (of any
17736 *     type) associated with an existing card is left unchanged.
17737 */
17738 
17739 /* Local Variables: */
17740    astDECLARE_GLOBALS     /* Declare the thread specific global data */
17741    int flags;             /* Flags to assign to new card */
17742 
17743 /* Check the global status. */
17744    if( !astOK ) return;
17745 
17746 /* Get a pointer to the structure holding thread-specific global data. */
17747    astGET_GLOBALS(this);
17748 
17749 /* If the current card is to be over-written, delete the current card (the
17750    next card in the list, if any, will become the new current card). */
17751    if( overwrite ) DeleteCard( this, method, class, status );
17752 
17753 /* If requested, set both NEW flags for the new card. */
17754    flags = ( mark_new ) ? ( NEW1 | NEW2 ): 0;
17755 
17756 /* Insert the new card into the list, just before the current card. */
17757    NewCard( this, name, type, data, comment, flags, status );
17758 }
17759 
IRAFFromStore(AstFitsChan * this,FitsStore * store,const char * method,const char * class,int * status)17760 static int IRAFFromStore( AstFitsChan *this, FitsStore *store,
17761                           const char *method, const char *class, int *status ){
17762 
17763 /*
17764 *  Name:
17765 *     IRAFFromStore
17766 
17767 *  Purpose:
17768 *     Store WCS keywords in a FitsChan using FITS-IRAF encoding.
17769 
17770 *  Type:
17771 *     Private function.
17772 
17773 *  Synopsis:
17774 
17775 *     int IRAFFromStore( AstFitsChan *this, FitsStore *store,
17776 *                        const char *method, const char *class, int *status )
17777 
17778 *  Class Membership:
17779 *     FitsChan
17780 
17781 *  Description:
17782 *     A FitsStore is a structure containing a generalised represention of
17783 *     a FITS WCS FrameSet. Functions exist to convert a FitsStore to and
17784 *     from a set of FITS header cards (using a specified encoding), or
17785 *     an AST FrameSet. In other words, a FitsStore is an encoding-
17786 *     independant intermediary staging post between a FITS header and
17787 *     an AST FrameSet.
17788 *
17789 *     This function copies the WCS information stored in the supplied
17790 *     FitsStore into the supplied FitsChan, using FITS-IRAF encoding.
17791 *
17792 *     IRAF encoding is like FITS-WCS encoding but with the following
17793 
17794 *     restrictions:
17795 *
17796 *     1) The celestial projection must not have any projection parameters
17797 *     which are not set to their default values. The one exception to this
17798 *     is that SIN projections are acceptable if the associated projection
17799 *     parameter PV<axlat>_1 is zero and PV<axlat>_2 = cot( reference point
17800 *     latitude). This is encoded using the string "-NCP". The SFL projection
17801 *     is encoded using the string "-GLS". Note, the original IRAF WCS
17802 *     system only recognised a small subset of the currently available
17803 *     projections, but some more recent IRAF-like software recognizes some
17804 *     of the new projections included in the FITS-WCS encoding.
17805 *
17806 *     2) The celestial axes must be RA/DEC, galactic or ecliptic.
17807 *
17808 *     3) LONPOLE and LATPOLE cannot be used.
17809 *
17810 *     4) Only primary axis descriptions are written out.
17811 *
17812 *     5) RADECSYS is used in place of RADESYS.
17813 *
17814 *     6) PC/CDELT keywords are not allowed (CD must be used)
17815 
17816 *  Parameters:
17817 *     this
17818 *        Pointer to the FitsChan.
17819 *     store
17820 *        Pointer to the FitsStore.
17821 *     method
17822 *        Pointer to a string holding the name of the calling method.
17823 *        This is only for use in constructing error messages.
17824 *     class
17825 *        Pointer to a string holding the name of the supplied object class.
17826 *        This is only for use in constructing error messages.
17827 *     status
17828 *        Pointer to the inherited status variable.
17829 
17830 *  Returned Value:
17831 *     A value of 1 is returned if succesfull, and zero is returned
17832 *     otherwise.
17833 */
17834 
17835 /* Local Variables: */
17836    char *comm;         /* Pointer to comment string */
17837    char *cval;         /* Pointer to string keyword value */
17838    char combuf[80];    /* Buffer for FITS card comment */
17839    char lattype[MXCTYPELEN];/* Latitude axis CTYPE */
17840    char lontype[MXCTYPELEN];/* Longitude axis CTYPE */
17841    char s;             /* Co-ordinate version character */
17842    char sign[2];       /* Fraction's sign character */
17843    double cdelt;       /* A CDELT value */
17844    double fd;          /* Fraction of a day */
17845    double mjd99;       /* MJD at start of 1999 */
17846    double p1, p2;      /* Projection parameters */
17847    double val;         /* General purpose value */
17848    int axlat;          /* Index of latitude FITS WCS axis */
17849    int axlon;          /* Index of longitude FITS WCS axis */
17850    int axspec;         /* Index of spectral FITS WCS axis */
17851    int i;              /* Axis index */
17852    int ihmsf[ 4 ];     /* Hour, minute, second, fractional second */
17853    int iymdf[ 4 ];     /* Year, month, date, fractional day */
17854    int j;              /* Axis index */
17855    int jj;             /* SlaLib status */
17856    int naxis;          /* No. of axes */
17857    int ok;             /* Is FitsSTore OK for IRAF encoding? */
17858    int prj;            /* Projection type */
17859    int ret;            /* Returned value. */
17860 
17861 /* Initialise */
17862    ret = 0;
17863 
17864 /* Check the inherited status. */
17865    if( !astOK ) return ret;
17866 
17867 /* First check that the values in the FitsStore conform to the
17868    requirements of the IRAF encoding. Assume they do to begin with. */
17869    ok = 1;
17870 
17871 /* Just do primary axes. */
17872    s = ' ';
17873 
17874 /* Look for the primary celestial and spectral axes. */
17875    FindLonLatSpecAxes( store, s, &axlon, &axlat, &axspec, method, class, status );
17876 
17877 /* If both longitude and latitude axes are present and thereis no
17878    spectral axis...*/
17879    if( axlon >= 0 && axlat >= 0 ) {
17880 
17881 /* Get the CTYPE values for both axes. */
17882       cval = GetItemC( &(store->ctype), axlon, 0, s, NULL, method, class, status );
17883       if( !cval ) return ret;
17884       strcpy( lontype, cval );
17885       cval = GetItemC( &(store->ctype), axlat, 0, s, NULL, method, class, status );
17886       if( !cval ) return ret;
17887       strcpy( lattype, cval );
17888 
17889 /* Extract the projection type as specified by the last 4 characters
17890    in the CTYPE keyword value. */
17891       prj = astWcsPrjType( lattype + 4 );
17892 
17893 /* Check the projection type is OK. Assume not initially. */
17894       ok = 0;
17895 
17896 /* FITS-IRAF cannot handle the AST-specific TPN projection. */
17897       if( prj == AST__TPN ||  prj == AST__WCSBAD ) {
17898          ok = 0;
17899 
17900 /* SIN projections are handled later. */
17901       } else if( prj != AST__SIN ){
17902 
17903 /* There must be no projection parameters. */
17904          if( GetMaxJM( &(store->pv), ' ', status ) == -1 ) ok = 1;
17905 
17906 /* Change the new SFL projection code to to the older equivalent GLS */
17907          if( prj == AST__SFL ){
17908             (void) strcpy( lontype + 4, "-GLS" );
17909             (void) strcpy( lattype + 4, "-GLS" );
17910          }
17911 
17912 /* SIN projections are only acceptable if the associated projection
17913    parameters are both zero, or if the first is zero and the second
17914    = cot( reference point latitude )  (the latter case is equivalent to
17915    the old NCP projection). */
17916       } else {
17917          p1 = GetItem( &( store->pv ), axlat, 1, s, NULL, method, class, status );
17918          p2 = GetItem( &( store->pv ), axlat, 2, s, NULL, method, class, status );
17919          if( p1 == AST__BAD ) p1 = 0.0;
17920          if( p2 == AST__BAD ) p2 = 0.0;
17921          val = GetItem( &( store->crval ), axlat, 0, s, NULL, method, class, status );
17922          if( val != AST__BAD ) {
17923             if( p1 == 0.0 ) {
17924                if( p2 == 0.0 ) {
17925                   ok = 1;
17926                } else if( fabs( p2 ) >= 1.0E14 && val == 0.0 ){
17927                   ok = 1;
17928                   (void) strcpy( lontype + 4, "-NCP" );
17929                   (void) strcpy( lattype + 4, "-NCP" );
17930                } else if( fabs( p2*tan( AST__DD2R*val ) - 1.0 )
17931                           < 0.01 ){
17932                   ok = 1;
17933                   (void) strcpy( lontype + 4, "-NCP" );
17934                   (void) strcpy( lattype + 4, "-NCP" );
17935                }
17936             }
17937          }
17938       }
17939 
17940 /* Identify the celestial coordinate system from the first 4 characters of the
17941    longitude CTYPE value. Only RA, galactic longitude, and ecliptic
17942    longitude can be stored using FITS-IRAF. */
17943       if( strncmp( lontype, "RA--", 4 ) &&
17944           strncmp( lontype, "GLON", 4 ) &&
17945           strncmp( lontype, "ELON", 4 ) ) ok = 0;
17946 
17947 /* If the physical Frame requires a LONPOLE or LATPOLE keyword, it cannot
17948    be encoded using FITS-IRAF. */
17949       if( GetItem( &(store->latpole), 0, 0, s, NULL, method, class, status )
17950           != AST__BAD ||
17951           GetItem( &(store->lonpole), 0, 0, s, NULL, method, class, status )
17952           != AST__BAD ) ok = 0;
17953 
17954 /* If there are no celestial axes, the physical Frame can be written out
17955    using FITS-IRAF. */
17956    } else {
17957       ok = 1;
17958    }
17959 
17960 /* Save the number of axes */
17961    naxis = GetMaxJM( &(store->crpix), ' ', status ) + 1;
17962 
17963 /* If this is different to the value of NAXIS abort since this encoding
17964    does not support WCSAXES keyword. */
17965    if( naxis != store->naxis ) ok = 0;
17966 
17967 /* Return if the FitsStore does not conform to IRAF encoding. */
17968    if( !ok ) return ret;
17969 
17970 /* Get and save CRPIX for all pixel axes. These are required, so return
17971    if they are not available. */
17972    for( i = 0; i < naxis; i++ ){
17973       val = GetItem( &(store->crpix), 0, i, s, NULL, method, class, status );
17974       if( val == AST__BAD ) return ret;
17975       sprintf( combuf, "Reference pixel on axis %d", i + 1 );
17976       SetValue( this, FormatKey( "CRPIX", i + 1, -1, s, status ), &val, AST__FLOAT,
17977                 combuf, status );
17978    }
17979 
17980 /* Get and save CRVAL for all intermediate axes. These are required, so return
17981    if they are not available. */
17982    for( j = 0; j < naxis; j++ ){
17983       val = GetItem( &(store->crval), j, 0, s, NULL, method, class, status );
17984       if( val == AST__BAD ) return ret;
17985       sprintf( combuf, "Value at ref. pixel on axis %d", j + 1 );
17986       SetValue( this, FormatKey( "CRVAL", j + 1, -1, s, status ), &val, AST__FLOAT,
17987                 combuf, status );
17988    }
17989 
17990 /* Get and save CTYPE for all intermediate axes. These are required, so return
17991    if they are not available. Use the potentially modified versions saved
17992    above for the celestial axes. */
17993    for( i = 0; i < naxis; i++ ){
17994       if( i == axlat ) {
17995          cval = lattype;
17996       } else if( i == axlon ) {
17997          cval = lontype;
17998       } else {
17999          cval = GetItemC( &(store->ctype), i, 0, s, NULL, method, class, status );
18000          if( !cval ) return ret;
18001       }
18002       if( !strcmp( cval + 4, "-TAB" ) ) return ret;
18003       comm = GetItemC( &(store->ctype_com), i, 0, s, NULL, method, class, status );
18004       if( !comm ) {
18005          sprintf( combuf, "Type of co-ordinate on axis %d", i + 1 );
18006          comm = combuf;
18007       }
18008       SetValue( this, FormatKey( "CTYPE", i + 1, -1, s, status ), &cval, AST__STRING,
18009                 comm, status );
18010    }
18011 
18012 /* CD matrix (the product of the CDELT and PC matrices). */
18013    for( i = 0; i < naxis; i++ ){
18014       cdelt = GetItem( &(store->cdelt), i, 0, s, NULL, method, class, status );
18015       if( cdelt == AST__BAD ) cdelt = 1.0;
18016       for( j = 0; j < naxis; j++ ){
18017          val = GetItem( &(store->pc), i, j, s, NULL, method, class, status );
18018          if( val == AST__BAD ) val = ( i == j ) ? 1.0 : 0.0;
18019          val *= cdelt;
18020          if( val != 0.0 ) {
18021              SetValue( this, FormatKey( "CD", i + 1, j + 1, s, status ), &val,
18022                        AST__FLOAT, "Transformation matrix element", status );
18023          }
18024       }
18025    }
18026 
18027 /* Get and save CUNIT for all intermediate axes. These are NOT required, so
18028    do not return if they are not available. */
18029    for( i = 0; i < naxis; i++ ){
18030       cval = GetItemC( &(store->cunit), i, 0, s, NULL, method, class, status );
18031       if( cval ) {
18032          sprintf( combuf, "Units for axis %d", i + 1 );
18033          SetValue( this, FormatKey( "CUNIT", i + 1, -1, s, status ), &cval, AST__STRING,
18034                    combuf, status );
18035       }
18036    }
18037 
18038 /* Get and save RADECSYS. This is NOT required, so do not return if it is
18039    not available. */
18040    cval = GetItemC( &(store->radesys), 0, 0, s, NULL, method, class, status );
18041    if( cval ) SetValue( this, "RADECSYS", &cval, AST__STRING,
18042                         "Reference frame for RA/DEC values", status );
18043 
18044 /* Reference equinox */
18045    val = GetItem( &(store->equinox), 0, 0, s, NULL, method, class, status );
18046    if( val != AST__BAD ) SetValue( this, "EQUINOX", &val, AST__FLOAT,
18047                                    "Epoch of reference equinox", status );
18048 
18049 /* Date of observation */
18050    val = GetItem( &(store->mjdobs), 0, 0, ' ', NULL, method, class, status );
18051    if( val != AST__BAD ) {
18052 
18053 /* The format used for the DATE-OBS keyword depends on the value of the
18054    keyword. For DATE-OBS < 1999.0, use the old "dd/mm/yy" format.
18055    Otherwise, use the new "ccyy-mm-ddThh:mm:ss[.ssss]" format. */
18056       palCaldj( 99, 1, 1, &mjd99, &jj );
18057       if( val < mjd99 ) {
18058          palDjcal( 0, val, iymdf, &jj );
18059          sprintf( combuf, "%2.2d/%2.2d/%2.2d", iymdf[ 2 ], iymdf[ 1 ],
18060                   iymdf[ 0 ] - ( ( iymdf[ 0 ] > 1999 ) ? 2000 : 1900 ) );
18061       } else {
18062          palDjcl( val, iymdf, iymdf+1, iymdf+2, &fd, &jj );
18063          palDd2tf( 3, fd, sign, ihmsf );
18064          sprintf( combuf, "%4.4d-%2.2d-%2.2dT%2.2d:%2.2d:%2.2d.%3.3d",
18065                   iymdf[0], iymdf[1], iymdf[2], ihmsf[0], ihmsf[1],
18066                   ihmsf[2], ihmsf[3] );
18067       }
18068 
18069 /* Now store the formatted string in the FitsChan. */
18070       cval = combuf;
18071       SetValue( this, "DATE-OBS", (void *) &cval, AST__STRING,
18072                 "Date of observation", status );
18073    }
18074 
18075 /* If we get here we have succeeded. */
18076    ret = 1;
18077 
18078 /* Return zero or ret depending on whether an error has occurred. */
18079    return astOK ? ret : 0;
18080 }
18081 
IsMapLinear(AstMapping * smap,const double lbnd_in[],const double ubnd_in[],int coord_out,int * status)18082 static int IsMapLinear( AstMapping *smap, const double lbnd_in[],
18083                         const double ubnd_in[], int coord_out, int *status ) {
18084 /*
18085 *  Name:
18086 *     IsMapLinear
18087 
18088 *  Purpose:
18089 *     See if a specified Mapping output is linearly related to the
18090 *     Mapping inputs.
18091 
18092 *  Type:
18093 *     Private function.
18094 
18095 *  Synopsis:
18096 *     #include "fitschan.h"
18097 *     int IsMapLinear( AstMapping *smap, const double lbnd_in[],
18098 *                      const double ubnd_in[], int coord_out, int *status )
18099 
18100 *  Class Membership:
18101 *     FitsChan member function.
18102 
18103 *  Description:
18104 *     Returns a flag indicating if the specified output of the supplied
18105 *     Mapping is a linear function of the Mapping inputs. A set of output
18106 *     positions are created which are evenly spaced along the specified
18107 *     output coordinate. The spacing is chosen so that the entire range
18108 *     of the output coordinate is covered in 20 steps. The other output
18109 *     coordinates are held fixed at arbitrary values (actually, values
18110 *     at which the specified output coordinate achieves its minimum value).
18111 *     This set of output positions is transformed into the corresponding
18112 *     set of input coordinates using the inverse of the supplied Mapping.
18113 *     A least squares linear fit is then made which models each input
18114 *     coordinate as a linear function of the specified output coordinate.
18115 *     The residual at every point in this fit must be less than some
18116 *     small fraction of the total range of the corresponding input
18117 *     coordinate for the Mapping to be considered linear.
18118 
18119 *  Parameters:
18120 *     smap
18121 *        Pointer to the Mapping.
18122 *     lbnd_in
18123 *        Pointer to an array of double, with one element for each
18124 *        Mapping input coordinate. This should contain the lower bound
18125 *        of the input box in each input dimension.
18126 *     ubnd_in
18127 *        Pointer to an array of double, with one element for each
18128 *        Mapping input coordinate. This should contain the upper bound
18129 *        of the input box in each input dimension.
18130 *     coord_out
18131 *        The zero-based index of the Mapping output which is to be checked.
18132 *     status
18133 *        Pointer to the inherited status variable.
18134 
18135 *  Returned Value:
18136 *     Non-zero if the specified Mapping output is linear. Zero otherwise.
18137 */
18138 
18139 /* Local Constants: */
18140 #define NP 20
18141 
18142 /* Local Variables: */
18143    AstMapping *map;
18144    AstPointSet *pset1;
18145    AstPointSet *pset2;
18146    double **ptr1;
18147    double **ptr2;
18148    double *p;
18149    double *s;
18150    double *xl;
18151    double c;
18152    double d;
18153    double delta;
18154    double in_lbnd;
18155    double in_ubnd;
18156    double lbnd_out;
18157    double m;
18158    double p0;
18159    double pv;
18160    double sn;
18161    double sp;
18162    double sps;
18163    double ss2;
18164    double ss;
18165    double sv;
18166    double tol;
18167    double ubnd_out;
18168    int *ins;
18169    int boxok;
18170    int i;
18171    int j;
18172    int nin;
18173    int nout;
18174    int oldrep;
18175    int ret;
18176 
18177 /* Initialise */
18178    ret = 0;
18179 
18180 /* Check inherited status */
18181    if( !astOK ) return ret;
18182 
18183 /* Attempt to split off the required output (in case any of the other
18184    outputs are associated with Mappings that do not have an inverse). */
18185    astInvert( smap );
18186    ins = astMapSplit( smap, 1, &coord_out, &map );
18187    astInvert( smap );
18188 
18189 /* If successful, check that the output is fed by only one input. */
18190    if( ins ) {
18191       if( astGetNin( map ) == 1 ) {
18192 
18193 /* If so, invert the map so that it goes from pixel to wcs, and then
18194    modify the supplied arguments so that they refer to the single required
18195    axis. */
18196          astInvert( map );
18197          lbnd_in += coord_out;
18198          ubnd_in += coord_out;
18199          coord_out = 0;
18200 
18201 /* If the output was fed by more than one input, annul the split mapping
18202    and use the supplied nmapping. */
18203       } else {
18204          (void) astAnnul( map );
18205          map = astClone( smap );
18206       }
18207       ins = astFree( ins );
18208 
18209 /* If the supplied Mapping could not be split, use the supplied nmapping. */
18210    } else {
18211       map = astClone( smap );
18212    }
18213 
18214 /* Check the Mapping is defined in both directions. */
18215    if( astGetTranForward( map ) && astGetTranInverse( map ) ) {
18216 
18217 /* Allocate resources. */
18218       nin = astGetNin( map );
18219       nout = astGetNout( map );
18220       xl = astMalloc( sizeof( double )*(size_t) nin );
18221       pset1 = astPointSet( NP, nin, "", status );
18222       ptr1 = astGetPoints( pset1 );
18223       pset2 = astPointSet( NP, nout, "", status );
18224       ptr2 = astGetPoints( pset2 );
18225 
18226 /* Call astMapBox in a new error reporting context. */
18227       boxok = 0;
18228       if( astOK ) {
18229 
18230 /* Temporarily switch off error reporting so that no report is made if
18231    astMapBox cannot find a bounding box (which can legitimately happen with
18232    some non-linear Mappings). */
18233          oldrep = astReporting( 0 );
18234 
18235 /* Find the upper and lower bounds on the specified Mapping output. This also
18236    returns the input coords of a point at which the required output has its
18237    lowest value. */
18238          astMapBox( map, lbnd_in, ubnd_in, 1, coord_out, &lbnd_out, &ubnd_out,
18239                     xl, NULL );
18240 
18241 /* If the box could not be found, clear the error status and pass on. */
18242          if( !astOK ) {
18243             astClearStatus;
18244 
18245 /* If the box was found OK, flag this and check if the bounds are equal.
18246    If so we cannot use them. In this case create new bounds. */
18247          } else {
18248             boxok = 1;
18249             if( EQUAL( lbnd_out, ubnd_out ) ) {
18250                m = 0.5*( lbnd_out + ubnd_out );
18251                if( fabs( m ) > 1.0E-15 ) {
18252                   lbnd_out = 0.9*m;
18253                   ubnd_out = 1.1*m;
18254                } else {
18255                   lbnd_out = -1.0;
18256                   ubnd_out = 1.0;
18257                }
18258             }
18259          }
18260 
18261 /* Re-instate error reporting. */
18262          astReporting( oldrep );
18263       }
18264 
18265 /* Check pointers can be used safely and a box was obtained. */
18266       if( astOK && boxok ) {
18267 
18268 /* Transform the input position returned by astMapBox using the supplied
18269    Mapping to get the corresponding output position. Fill all unused
18270    elements of the PointSet with AST__BAD. */
18271          for( i = 0; i < nin; i++ ){
18272             p = ptr1[ i ];
18273             *(p++) = xl[ i ];
18274             for( j = 1; j < NP; j++ ) *(p++) = AST__BAD;
18275          }
18276          (void) astTransform( map, pset1, 1, pset2 );
18277 
18278 /* Now create a set of NP points evenly spaced in output coordinates. The
18279    first point is at the output position found above. Each subsequent
18280    point is incremented by a fixed amount along the specified output
18281    coordinate (the values on all other output coordinates is held fixed). */
18282          delta = ( ubnd_out - lbnd_out )/ ( NP - 1 );
18283          for( i = 0; i < nout; i++ ){
18284             p = ptr2[ i ];
18285             if( i == coord_out ) {
18286                for( j = 0; j < NP; j++ ) *(p++) = lbnd_out + j*delta;
18287             } else {
18288                p0 = p[ 0 ];
18289                for( j = 0; j < NP; j++ ) *(p++) = p0;
18290             }
18291          }
18292 
18293 /* Transform these output positions into input positions using the
18294    inverse Mapping. */
18295          (void) astTransform( map, pset2, 0, pset1 );
18296 
18297 /* Do a least squares fit to each input coordinate. Each fit gives the
18298    corresponding input coordinate value as a linear function of the
18299    specified output coordinate value. Note, linear function should never
18300    produce bad values so abort if a bad value is found. */
18301          ret = 1;
18302          s = ptr2[ coord_out ];
18303          for( i = 0; i < nin; i++ ) {
18304             p = ptr1[ i ];
18305 
18306 /* Form the required sums. Also find the largest and smallest input
18307    coordinate value achieved. */
18308             sp = 0.0;
18309             ss = 0.0;
18310             sps = 0.0;
18311             sn = 0.0;
18312             ss2 = 0.0;
18313             in_lbnd = DBL_MAX;
18314             in_ubnd = DBL_MIN;
18315             for( j = 0; j < NP; j++ ) {
18316                sv = s[ j ];
18317                pv = p[ j ];
18318                if( pv != AST__BAD && sv != AST__BAD ) {
18319                   sp += pv;
18320                   ss += sv;
18321                   sps += pv*sv;
18322                   sn += 1.0;
18323                   ss2 += sv*sv;
18324                   if( pv < in_lbnd ) in_lbnd = pv;
18325                   if( pv > in_ubnd ) in_ubnd = pv;
18326                } else {
18327                   sn = 0.0;
18328                   break;
18329                }
18330             }
18331 
18332 /* Ignore input axes which are independant of the output axis. */
18333             if( !EQUAL( in_lbnd, in_ubnd ) ) {
18334 
18335 /* Calculate the constants "input coord = m*output coord + c" */
18336                d = ss*ss - sn*ss2;
18337                if( sn > 0.0 && d != 0.0 ) {
18338                   m = ( sp*ss - sps*sn )/d;
18339                   c = ( sps*ss - sp*ss2 )/d;
18340 
18341 /* Subtract off the fit value form the "p" values to get the residuals of
18342    the fit. */
18343                   for( j = 0; j < NP; j++ ) p[ j ] -= m*s[ j ] + c;
18344 
18345 /* We now do a least squares fit to the residuals. This second fit is done
18346    because the first least squares fit sometimes leaves the residuals with a
18347    distinct non-zero gradient. We do not need to worry about bad values
18348    here since we have checked above that there are no bad values. Also we
18349    do not need to recalculate sums which only depend on the "s" values since
18350    they have not changed. */
18351                   sp = 0.0;
18352                   sps = 0.0;
18353                   for( j = 0; j < NP; j++ ) {
18354                      pv = p[ j ];
18355                      sp += pv;
18356                      sps += pv*s[ j ];
18357                   }
18358 
18359 /* Find the constants in "input residual = m*output coord + c" equation. */
18360                   m = ( sp*ss - sps*sn )/d;
18361                   c = ( sps*ss - sp*ss2 )/d;
18362 
18363 /* Subtract off the fit value form the "p residuals" values to get the
18364    residual redisuals of the fit. */
18365                   for( j = 0; j < NP; j++ ) p[ j ] -= m*s[ j ] + c;
18366 
18367 /* The requirement for a linear relationship is that the absolute residual
18368    between the input coord produced by the above linear fit and the input
18369    coord produced by the actual Mapping should be less than some small
18370    fraction of the total range of input coord value, at every point. Test
18371    this. */
18372                   tol = 1.0E-7*( in_ubnd - in_lbnd );
18373                   for( j = 0; j < NP; j++ ) {
18374                      if( fabs( p[ j ] ) > tol ) {
18375                         ret = 0;
18376                         break;
18377                      }
18378                   }
18379                } else {
18380                   ret = 0;
18381                }
18382             }
18383             if( !ret ) break;
18384          }
18385       }
18386 
18387 /* Free resources. */
18388       pset1 = astAnnul( pset1 );
18389       pset2 = astAnnul( pset2 );
18390       xl = astFree( xl );
18391    }
18392    map = astAnnul( map );
18393 
18394 /* Return the answer. */
18395    return ret;
18396 }
18397 
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)18398 static AstMapping *IsMapTab1D( AstMapping *map, double scale, const char *unit,
18399                                AstFrame *wcsfrm, double *dim, int iax,
18400                                int iwcs, AstFitsTable **table, int *icolmain,
18401                                int *icolindex, int *interp, int *status ){
18402 /*
18403 *  Name:
18404 *     IsMapTab1D
18405 
18406 *  Purpose:
18407 *     See if a specified Mapping output is related to a single Mapping input
18408 *     via a FITS -TAB algorithm.
18409 
18410 *  Type:
18411 *     Private function.
18412 
18413 *  Synopsis:
18414 *     #include "fitschan.h"
18415 *     AstMapping *IsMapTab1D( AstMapping *map, double scale, const char *unit,
18416 *                             AstFrame *wcsfrm, double *dim, int iax,
18417 *                             int iwcs, AstFitsTable **table, int *icolmain,
18418 *                             int *icolindex, int *interp, int *status )
18419 
18420 *  Class Membership:
18421 *     FitsChan member function.
18422 
18423 *  Description:
18424 *     A specified axis of the supplied Mapping is tested to see if it
18425 *     can be represented by the -TAB alogirithm described in FITS-WCS
18426 *     paper III. If the test is passed, a Mapping is returned from the
18427 *     specified WCS axis to the corresponding psi axis. A FitsTable is
18428 *     also created holding the information to be stored in the
18429 *     corresponding FITS binary table.
18430 *
18431 *     Note, when creating a -TAB header, AST uses grid coords for the psi
18432 *     axis. See FITS-WCS paper III section 6.1.2 for a definition of the
18433 *     psi axes.
18434 
18435 *  Parameters:
18436 *     map
18437 *        Pointer to the Mapping from pixel coords to WCS coords.
18438 *     scale
18439 *        A scale factor by which to multiply the axis values stored in the
18440 *        returned FitsTable. Note, the returned Mapping is unaffected by
18441 *        this scaling factor.
18442 *     unit
18443 *        Pointer to the unit string to store with the coords column. If
18444 *        NULL, the unit string is extracted form the supplied WCS Frame.
18445 *     wcsfrm
18446 *        Pointer to a Frame describing WCS coords.
18447 *     dim
18448 *        An array holding the array dimensions in pixels. AST__BAD should
18449 *        be supplied for any unknown dimensions.
18450 *     iax
18451 *        The zero-based index of the Mapping output which is to be checked.
18452 *     iwcs
18453 *        The zero-based index of the corresponding FITS WCS axis.
18454 *     table
18455 *        Pointer to a location holding a pointer to the FitsTable describing
18456 *        the -TAB look-up table. If "*table" is NULL on entry, a new
18457 *        FitsTable will be created and returned, otherwise the supplied
18458 *        FitsTable is used.
18459 *     icolmain
18460 *        The one-based index of the column within "*table" that holds the
18461 *        main data array.
18462 *     icolindex
18463 *        The one-based index of the column within "*table" that holds the
18464 *        index vector. Returned equal to -1 if no index is added to the
18465 *        table (i.e. if the index is a unt index).
18466 *     interp
18467 *        The interpolation method (0=linear, other=nearest neighbour).
18468 *     status
18469 *        Pointer to the inherited status variable.
18470 
18471 *  Returned Value:
18472 *     If the specified "map" output can be described using the -TAB
18473 *     algorithm of FITS-WCS paper III, then a 1-input/1-output Mapping
18474 *     from the specified WCS axis to the corresponding psi axis (which is
18475 *     assumed to be equal to grid coords) is returned. NULL is returned
18476 *     otherwise, of if an error occurs.
18477 */
18478 
18479 /* Local Variables: */
18480    AstCmpMap *cm;          /* CmpMap pointer */
18481    AstMapping **map_list;  /* Mapping array pointer */
18482    AstMapping *postmap;    /* Total Mapping after LutMap */
18483    AstMapping *premap;     /* Total Mapping before LutMap */
18484    AstMapping *ret;        /* Returned WCS axis Mapping */
18485    AstMapping *tmap;       /* Temporary Mapping */
18486    AstPermMap *pm;         /* PermMap pointer */
18487    char cellname[ 20 ];    /* Buffer for cell name */
18488    char colname[ 20 ];     /* Buffer for column name */
18489    double *lut;            /* Pointer to table of Y values */
18490    double *work1;          /* Pointer to work array */
18491    double *work2;          /* Pointer to work array */
18492    double inc;             /* X increment between table entries */
18493    double start;           /* X value at first table entry */
18494    double v[ 2 ];          /* Y values at start and end of interval */
18495    double x[ 2 ];          /* X values at start and end of interval */
18496    int *ins;               /* Array of "map" input indices */
18497    int *invert_list;       /* Invert array pointer */
18498    int *outs;              /* Array of "map" output indices */
18499    int dims[ 2 ];          /* Dimensions of the tab coords array */
18500    int iin;                /* Index of Mapping input */
18501    int ilut;               /* Index of the LutMap within the mappings list */
18502    int imap;               /* Index of current Mapping in list */
18503    int iout;               /* Index of Mapping output */
18504    int jout;               /* Index of Mapping output */
18505    int nin;                /* Number of Mapping inputs */
18506    int nlut;               /* Number of elements in "lut" array */
18507    int nmap;               /* Number of Mappings in the list */
18508    int nout;               /* Number of Mapping outputs */
18509    int ok;                 /* Were columns added to the table? */
18510    int old_invert;         /* Original value for Mapping's Invert flag */
18511    int outperm;            /* Index of input that feeds the single output */
18512 
18513 /* Initialise */
18514    ret = NULL;
18515    *icolmain = -1;
18516    *icolindex = -1;
18517    *interp = 0;
18518 
18519 /* Check inherited status */
18520    if( !astOK ) return ret;
18521 
18522 /* Ensure we have aunit string. */
18523    if( !unit ) unit = astGetUnit( wcsfrm, iax );
18524 
18525 /* Check that the requested mapping output is fed by only one mapping
18526    input, identify that input, and extract the input->output mapping from
18527    the total mapping. Since astMapSplit splits off a specified input, we
18528    need to invert the Mapping first so we can split off a specified output.  */
18529    astInvert( map );
18530    ins = astMapSplit( map, 1, &iax, &ret );
18531    astInvert( map );
18532 
18533 /* If the Mapping could not be split, try a different approach in which
18534    each input is checked in turn to see if it feeds the specified output. */
18535    if( !ins ) {
18536 
18537 /* Loop round each input of "map". */
18538       nin = astGetNin( map );
18539       for( iin = 0; iin < nin && !ins; iin++ ) {
18540 
18541 /* Attempt to find a group of outputs (of "map") that are fed by just
18542    this one input. */
18543          outs = astMapSplit( map, 1, &iin, &ret );
18544 
18545 /* If successful, "ret" will be a Mapping with one input corresponding to
18546    input "iin" of "map, and one or more outputs. We loop round these
18547    outputs to see if any of them correspond to output "iax" of "map". */
18548          if( outs ) {
18549             nout = astGetNout( ret );
18550             for( iout = 0; iout < nout; iout++ ) {
18551                if( outs[ iout ] == iax ) break;
18552             }
18553 
18554 /* Did input "iin" feed the output "iax" (and possibly other outputs)? */
18555             if( iout < nout ) {
18556 
18557 /* The "ret" Mapping is now a 1-input (pixel) N-output (WCS) Mapping in which
18558    output "iout" corresponds to output "iax" of Mapping. To be compatible
18559    with the previous approach, we want "ret" to be a 1-input  (WCS) to
18560    1-output (pixel) Mapping in which the input corresponds to output
18561    "iax" of Mapping. To get "ret" into this form, we first append a PermMap
18562    to "ret" that selects a single output ("iout"), and then invert the
18563    whole CmpMap. */
18564                for( jout = 0; jout < nout; jout++ ) {
18565                   outs[ jout ] = -1;
18566                }
18567                outs[ iout ] = 0;
18568                outperm = iout;
18569 
18570                pm = astPermMap( nout, outs, 1, &outperm, NULL, "", status );
18571                cm = astCmpMap( ret, pm, 1, " ", status );
18572                (void) astAnnul( ret );
18573                pm = astAnnul( pm );
18574                ret = (AstMapping *) cm;
18575                astInvert( ret );
18576 
18577 /* The earlier approach leves ins[ 0 ] holding the index of the input to
18578    "map" that feeds output iax. Ensure we have this too. */
18579                ins = outs;
18580                ins[ 0 ] = iin;
18581 
18582 /* Free resources if the current input did not feed the required output. */
18583             } else {
18584                outs = astFree( outs );
18585                ret = astAnnul( ret );
18586             }
18587          }
18588       }
18589    }
18590 
18591 /* If the Mapping still could not be split, try again on a copy of the
18592    Mapping in which all PermMaps provide an alternative implementation of
18593    the astMapSplit method. */
18594    if( !ins ) {
18595       astInvert( map );
18596       tmap = astCopy( map );
18597       ChangePermSplit( tmap, status );
18598       ins = astMapSplit( tmap, 1, &iax, &ret );
18599       tmap = astAnnul( tmap );
18600       astInvert( map );
18601    }
18602 
18603 /* Assume the Mapping cannot be represented by -TAB */
18604    ok = 0;
18605 
18606 /* Check a Mapping was returned by astMapSplit. If so, it will be the
18607    mapping from the requested output of "map" (the WCS axis) to the
18608    corresponding input(s) (grid axes). Check only one "map" input feeds the
18609    requested output. */
18610    if( ins && ret && astGetNout( ret ) == 1 ) {
18611 
18612 /* Invert the Mapping so that the input is grid coord and the output is
18613    WCS coord. */
18614       astInvert( ret );
18615 
18616 /* We now search the "ret" mapping for a non-inverted LutMap, splitting ret
18617    up into three serial components: 1) the mappings before the LutMap, 2) the
18618    LutMap itself, and 3) the mappings following the LutMap. First, decompose
18619    the mapping into a list of series mappings. */
18620       map_list = NULL;
18621       invert_list = NULL;
18622       nmap = 0;
18623       astMapList( ret, 1, astGetInvert( ret ), &nmap, &map_list,
18624                   &invert_list );
18625 
18626 /* Search the list for a non-inverted LutMap. */
18627       ilut = -1;
18628       for( imap = 0; imap < nmap; imap++ ) {
18629          if( astIsALutMap( map_list[ imap ] ) && !(invert_list[ imap ]) ) {
18630             ilut = imap;
18631             break;
18632          }
18633       }
18634 
18635 /* If a LutMap was found, combine all Mappings before the LutMap into a
18636    single Mapping. Remember to set the Mapping Invert flags temporarily to
18637    the values used within the CmpMap. */
18638       if( ilut >= 0 ) {
18639          premap = (AstMapping *) astUnitMap( 1, " ", status );
18640          for( imap = 0; imap < ilut; imap++ ) {
18641             old_invert = astGetInvert( map_list[ imap ] );
18642             astSetInvert( map_list[ imap ], invert_list[ imap ] );
18643             tmap = (AstMapping *) astCmpMap( premap,  map_list[ imap ], 1,
18644                                              " ", status );
18645             astSetInvert( map_list[ imap ], old_invert );
18646             (void) astAnnul( premap );
18647             premap = tmap;
18648          }
18649 
18650 /* Also combine all Mappings after the LutMap into a single Mapping, setting
18651    the Mapping Invert flags temporarily to the values used within the
18652    CmpMap. */
18653          postmap = (AstMapping *) astUnitMap( 1, " ", status );
18654          for( imap = ilut + 1; imap < nmap; imap++ ) {
18655             old_invert = astGetInvert( map_list[ imap ] );
18656             astSetInvert( map_list[ imap ], invert_list[ imap ] );
18657             tmap = (AstMapping *) astCmpMap( postmap,  map_list[ imap ], 1,
18658                                              " ", status );
18659             astSetInvert( map_list[ imap ], old_invert );
18660             (void) astAnnul( postmap );
18661             postmap = tmap;
18662          }
18663 
18664 /* Get the table of values, and other attributes, from the LutMap. */
18665          lut = astGetLutMapInfo( map_list[ ilut ], &start, &inc, &nlut );
18666          *interp = astGetLutInterp(  map_list[ ilut ] );
18667 
18668 /* If required, create a FitsTable to hold the returned table info. */
18669          if( ! *table ) *table = astFitsTable( NULL, "", status );
18670          ok = 1;
18671 
18672 /* Define the properties of the column in the FitsTable that holds the main
18673    coordinate array. Points on a WCS axis are described by a single value
18674    (wavelength, frequency, or whatever), but the coords array has to be
18675    2-dimensional, with an initial degenerate axis, as required by FITS-WCS
18676    paper III. */
18677          dims[ 0 ] = 1;
18678          dims[ 1 ] = nlut;
18679          sprintf( colname, "COORDS%d", iwcs + 1 );
18680          astAddColumn( *table, colname, AST__DOUBLETYPE, 2, dims, unit );
18681 
18682 /* Get the one-based index of the column just added to the table. */
18683          *icolmain = astGetNcolumn( *table );
18684 
18685 /* Get workspace. */
18686          work1 = astMalloc( nlut*sizeof( double ) );
18687          if( astOK ) {
18688 
18689 /* Transform the LutMap table values using the post-lutmap mapping to
18690    get the list of WCS values in AST units. */
18691             astTran1( postmap, nlut, lut, 1, work1 );
18692 
18693 /* Convert them to FITS units (e.g. celestial axis values should be
18694    converted from radians to degrees). */
18695             for( ilut = 0; ilut < nlut; ilut++ ) work1[ ilut ] *= scale;
18696 
18697 /* Store them in row 1, column COORDS, in the FitsTable. */
18698             sprintf( cellname, "COORDS%d(1)", iwcs + 1 );
18699             astMapPut1D( *table, cellname, nlut, work1, NULL );
18700 
18701 /* Create an array holding the LutMap input value at the centre of each
18702    table entry. Re-use the "lut" array since we no longer need it. */
18703             for( ilut = 0; ilut < nlut; ilut++ ) {
18704                lut[ ilut ] = start + ilut*inc;
18705             }
18706 
18707 /* Transform this array using the inverted pre-lutmap mapping to get the
18708    list of grid coord. */
18709             astTran1( premap, nlut, lut, 0, work1 );
18710 
18711 /* Test this list to see if they form a unit index (i.e. index(i) == i+1 ).
18712    (not the "+1" is due to the fact that "i" is zero based). */
18713             for( ilut = 0; ilut < nlut; ilut++ ) {
18714                if( fabs( work1[ ilut ] - ilut - 1.0 ) > 1.0E-6 ) break;
18715             }
18716 
18717 /* if it is not a unit index, we add the index to the table. */
18718             if( ilut < nlut ) {
18719 
18720 /* Define the properties of the column in the FitsTable that holds the
18721    indexing vector. */
18722                sprintf( colname, "INDEX%d", iwcs + 1 );
18723                astAddColumn( *table, colname, AST__DOUBLETYPE, 1, &nlut, " " );
18724 
18725 /* Get the one-based index of the column just added to the table. */
18726                *icolindex = astGetNcolumn( *table );
18727 
18728 /* Store the values in the column. */
18729                sprintf( cellname, "INDEX%d(1)", iwcs + 1 );
18730                astMapPut1D( *table, cellname, nlut, work1, NULL );
18731             }
18732          }
18733 
18734 /* Free resources. */
18735          work1 = astFree( work1 );
18736          lut = astFree( lut );
18737          premap = astAnnul( premap );
18738          postmap = astAnnul( postmap );
18739 
18740 /* If no LutMap was found in the Mapping, then we can create a FitsTable
18741    by sampling the full WCS Mapping at selected input (i.e. grid)
18742    positions. But we can only do this if we know the number of pixels
18743    along the WCS axis. */
18744       } else if( dim[ ins[ 0 ] ] != AST__BAD ) {
18745 
18746 /* Create two works array each holding a single value. The first holds
18747    the grid coords at which the samples are taken. The second holds the
18748    WCS coords at the sampled positions. These arrays are expanded as
18749    required within function AdaptLut. */
18750          work1 = astMalloc( sizeof( double ) );
18751          work2 = astMalloc( sizeof( double ) );
18752          if( astOK ) {
18753 
18754 /* Get the WCS values at the centres of the first and last pixel on
18755    the WCS axis. */
18756             x[ 0 ] = 1.0;
18757             x[ 1 ] = dim[ ins[ 0 ] ];
18758             astTran1( ret, 2, x, 1, v );
18759 
18760 /* Put the lower limit into the work arrays. */
18761             work1[ 0 ] = x[ 0 ];
18762             work2[ 0 ] = v[ 0 ];
18763             nlut = 1;
18764 
18765 /* Expand the arrays by sampling the WCS axis adaptively so that
18766    more samples occur where the WCS value is changing most rapidly.
18767    We require the maximum error introduced by the table to be 0.25 pixels. */
18768             AdaptLut( ret, 3, 0.25, x[ 0 ], x[ 1 ], v[ 0 ], v[ 1 ],
18769                       &work1, &work2, &nlut, status );
18770 
18771 /* Create a FitsTable to hold the returned table info. */
18772             if( ! *table ) *table = astFitsTable( NULL, "", status );
18773             ok = 1;
18774 
18775 /* Define the properties of the column in the FitsTable that holds the main
18776    coordinate array. */
18777             sprintf( colname, "COORDS%d", iwcs + 1 );
18778             dims[ 0 ] = 1;
18779             dims[ 1 ] = nlut;
18780             astAddColumn( *table, colname, AST__DOUBLETYPE, 2, dims, unit );
18781             *icolmain = astGetNcolumn( *table );
18782 
18783 /* Convert the axis values to FITS units (e.g. celestial axis values should be
18784    converted from radians to degrees). */
18785             for( ilut = 0; ilut < nlut; ilut++ ) work2[ ilut ] *= scale;
18786 
18787 /* Store the scaled axis values in row 1 of the column. */
18788             sprintf( cellname, "COORDS%d(1)", iwcs + 1 );
18789             astMapPut1D( *table, cellname, nlut, work2, NULL );
18790 
18791 /* Test the index vector to see if they form a unit index (i.e. index(i) ==
18792    i+1 ). If not the "+1" is due to the fact that "i" is zero based). If not, store
18793    them as the index vector in the FitsTable. */
18794             for( ilut = 0; ilut < nlut; ilut++ ) {
18795                if( fabs( work1[ ilut ] - ilut - 1.0 ) > 1.0E-6 ) break;
18796             }
18797 
18798 /* If the index vector is not a unit index, define the properties of the
18799    column in the FitsTable that holds the indexing vector. Then store values
18800    in row 1 of the column. */
18801             if( ilut < nlut ) {
18802                sprintf( colname, "INDEX%d", iwcs + 1 );
18803                astAddColumn( *table, colname, AST__DOUBLETYPE, 1, &nlut, " " );
18804                *icolindex = astGetNcolumn( *table );
18805                sprintf( cellname, "INDEX%d(1)", iwcs + 1 );
18806                astMapPut1D( *table, cellname, nlut, work1, NULL );
18807             }
18808          }
18809 
18810 /* Free resources */
18811          work1 = astFree( work1 );
18812          work2 = astFree( work2 );
18813       }
18814 
18815 /* If columns were added to the table, invert the returned Mapping again
18816    so that the input is wcs coord and the output is grid coord. Otherwise,
18817    annul the returned Mapping. */
18818       if( ok ) {
18819          astInvert( ret );
18820       } else {
18821          ret = astAnnul( ret );
18822       }
18823 
18824 /* Loop to annul all the Mapping pointers in the list. */
18825       for ( imap = 0; imap < nmap; imap++ ) map_list[ imap ] = astAnnul( map_list[ imap ] );
18826 
18827 /* Free the dynamic arrays. */
18828       map_list = astFree( map_list );
18829       invert_list = astFree( invert_list );
18830    }
18831 
18832 /* Free resources. */
18833    ins = astFree( ins );
18834 
18835 /* If an error occurred, free the returned Mapping. */
18836    if( !astOK ) ret = astAnnul( ret );
18837 
18838 /* Return the result. */
18839    return ret;
18840 }
18841 
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)18842 static AstMapping *IsMapTab2D( AstMapping *map, double scale, const char *unit,
18843                                AstFrame *wcsfrm, double *dim, int iax1,
18844                                int iax2, int iwcs1, int iwcs2,
18845                                AstFitsTable **table, int *icolmain1,
18846                                int *icolmain2, int *icolindex1,
18847                                int *icolindex2, int *max1, int *max2,
18848                                int *interp1, int *interp2, int *status ){
18849 /*
18850 *  Name:
18851 *     IsMapTab2D
18852 
18853 *  Purpose:
18854 *     See if a specified pair of Mapping outputs are related to a pair of
18855 *     Mapping inputs via a FITS -TAB algorithm.
18856 
18857 *  Type:
18858 *     Private function.
18859 
18860 *  Synopsis:
18861 *     #include "fitschan.h"
18862 *     AstMapping *IsMapTab2D( AstMapping *map, double scale, const char *unit,
18863 *                             AstFrame *wcsfrm, double *dim, int iax1,
18864 *                             int iax2, int iwcs1, int iwcs2,
18865 *                             AstFitsTable **table, int *icolmain1,
18866 *                             int *icolmain2, int *icolindex1,
18867 *                             int *icolindex2, int *max1, int *max2,
18868 *                             int *interp1, int *interp2, int *status )
18869 
18870 *  Class Membership:
18871 *     FitsChan member function.
18872 
18873 *  Description:
18874 *     A specified pair of outputs axes of the supplied Mapping are tested
18875 *     to see if they can be represented by the -TAB alogirithm described in
18876 *     FITS-WCS paper III. If the test is passed, a Mapping is returned from
18877 *     the specified WCS axes to the corresponding psi axes. A FitsTable is
18878 *     also created holding the information to be stored in the corresponding
18879 *     FITS binary table. Note, when creating a header, AST assumes a unit
18880 *     transformaton between psi axes and grid axes (psi axes are defined
18881 *     in FITS-WCS paper III section 6.1.2).
18882 
18883 *  Parameters:
18884 *     map
18885 *        Pointer to the Mapping from pixel coords to WCS coords.
18886 *     scale
18887 *        A scale factor by which to multiply the axis values stored in the
18888 *        returned FitsTable. Note, the returned Mapping is unaffected by
18889 *        this scaling factor.
18890 *     unit
18891 *        A unit string for the axis values. If supplied, the same
18892 *        string is stored for both axes. If NULL, the unit strings are
18893 *        extracted from the relavent axes of the supplied WCS Frame.
18894 *     wcsfrm
18895 *        Pointer to a Frame describing WCS coords.
18896 *     dim
18897 *        An array holding the array dimensions in pixels. AST__BAD should
18898 *        be supplied for any unknown dimensions.
18899 *     iax1
18900 *        The zero-based index of the first Mapping output which is to be
18901 *        checked.
18902 *     iax2
18903 *        The zero-based index of the second Mapping output which is to be
18904 *        checked.
18905 *     iwcs1
18906 *        The zero-based index of the FITS WCS axis corresponding to "iax1".
18907 *     iwcs2
18908 *        The zero-based index of the FITS WCS axis corresponding to "iax2".
18909 *     table
18910 *        Pointer to a location holding a pointer to the FitsTable describing
18911 *        the -TAB look-up table. If "*table" is NULL on entry, a new
18912 *        FitsTable will be created and returned, otherwise the supplied
18913 *        FitsTable is used.
18914 *     icolmain1
18915 *        The one-based index of the column within "*table" that holds the
18916 *        main coord array for the first Mapping output.
18917 *     icolmain2
18918 *        The one-based index of the column within "*table" that holds the
18919 *        main coord array for the second Mapping output.
18920 *     icolindex1
18921 *        The one-based index of the column within "*table" that holds the
18922 *        index vector for the first Mapping output. Returned equal to -1
18923 *        if no index is added to the table (e.g. because the index is a
18924 *        unit index).
18925 *     icolindex2
18926 *        The one-based index of the column within "*table" that holds the
18927 *        index vector for the second Mapping output. Returned equal to -1
18928 *        if no index is added to the table (e.g. because the index is a
18929 *        unit index).
18930 *     max1
18931 *        The one-based index of the dimension describing the first Mapping
18932 *        output within the main coord array specified by "icolmain1".
18933 *     max2
18934 *        The one-based index of the dimension describing the second Mapping
18935 *        output within the main coord array specified by "icolmain1".
18936 *     interp1
18937 *        The interpolation method (0=linear, other=nearest neighbour) for
18938 *        the first mapping output.
18939 *     interp2
18940 *        The interpolation method (0=linear, other=nearest neighbour) for
18941 *        the second mapping output.
18942 *     status
18943 *        Pointer to the inherited status variable.
18944 
18945 *  Returned Value:
18946 *     If the specified "map" outputs can be described using the -TAB
18947 *     algorithm of FITS-WCS paper III, then a 2-input/2-output Mapping
18948 *     from the specified WCS axes to the corresponding psi axes (i.e.
18949 *     grid axes) is returned. NULL is returned otherwise, of if an error
18950 *     occurs.
18951 */
18952 
18953 /* Local Variables: */
18954    AstMapping *ret1;       /* WCS->IWC Mapping for first output */
18955    AstMapping *ret2;       /* WCS->IWC Mapping for second output */
18956    AstMapping *ret;        /* Returned WCS axis Mapping */
18957    AstMapping *tmap;
18958    AstPermMap *pm;
18959    int *pix_axes;          /* Zero-based indicies of corresponding pixel axes */
18960    int wcs_axes[ 2 ];      /* Zero-based indicies of selected WCS axes */
18961    int inperm[ 1 ];
18962    int outperm[ 2 ];
18963 
18964 /* Initialise */
18965    ret = NULL;
18966 
18967 /* Check inherited status */
18968    if( !astOK ) return ret;
18969 
18970 /* First see if the two required Mapping outputs are separable, in which case
18971    they can be described by two 1D tables. */
18972    ret1 = IsMapTab1D( map, scale, unit, wcsfrm, dim, iax1, iwcs1, table, icolmain1,
18973                       icolindex1, interp1, status );
18974    ret2 = IsMapTab1D( map, scale, unit, wcsfrm, dim, iax2, iwcs2, table, icolmain2,
18975                       icolindex2, interp2, status );
18976 
18977 /* If both outputs are seperable... */
18978    if( ret1 && ret2 ) {
18979 
18980 /* Both axes are stored as the first dimension in the corresponding main
18981    coords array. */
18982       *max1 = 1;
18983       *max2 = 1;
18984 
18985 /* Get a Mapping from the required pair of WCS axes to the corresponding
18986    pair of grid axes. First try to split the supplied grid->wcs mapping. */
18987       wcs_axes[ 0 ] = iax1;
18988       wcs_axes[ 1 ] = iax2;
18989 
18990       astInvert( map );
18991       pix_axes = astMapSplit( map, 2, wcs_axes, &ret );
18992       astInvert( map );
18993 
18994       if( pix_axes ) {
18995          pix_axes = astFree( pix_axes );
18996          if( astGetNout( ret ) > 2 ) {
18997             ret = astAnnul( ret );
18998 
18999 /* If the two output WCS axes are fed by the same grid axis, we need to
19000    add another pixel axis to form the pair. */
19001          } else if( astGetNout( ret ) == 1 ) {
19002             inperm[ 0 ] = 0;
19003             outperm[ 0 ] = 0;
19004             outperm[ 1 ] = 0;
19005             pm = astPermMap( 1, inperm, 2, outperm, NULL, " ", status );
19006             tmap = (AstMapping *) astCmpMap( ret, pm, 1, " ", status );
19007             ret = astAnnul( ret );
19008             pm = astAnnul( pm );
19009             ret = tmap;
19010          }
19011       }
19012 
19013 /* If this was unsuccessful, combine the Mappings returned by IsMapTab1D.
19014    We only do this if the above astMapSplit call failed, since the IsMapTab1D
19015    mappings may well not be independent of each other, and we may end up
19016    sticking together in parallel two mappings that are basically the same
19017    except for ending with PermMapa that select different axes. Is is hard
19018    then to simplify such a parallel CmpMap back into the simpler form
19019    that uses only one of the two identical mappings, without a PermMap. */
19020       if( !ret ) {
19021          ret = (AstMapping *) astCmpMap( ret1, ret2, 0, " ", status );
19022       }
19023 
19024 /* Free resources. */
19025       ret1 = astAnnul( ret1 );
19026       ret2 = astAnnul( ret2 );
19027 
19028 /* If only one output is separable, remove the corresponding columns from
19029    the returned table. */
19030    } else if( ret1 ) {
19031       ret1 = astAnnul( ret1 );
19032       astRemoveColumn( *table, astColumnName( *table, *icolmain1 ) );
19033       if( icolindex1 >= 0 ) astRemoveColumn( *table, astColumnName( *table, *icolindex1 ) );
19034    } else if( ret2 ) {
19035       ret2 = astAnnul( ret2 );
19036       astRemoveColumn( *table, astColumnName( *table, *icolmain2 ) );
19037       if( icolindex1 >= 0 ) astRemoveColumn( *table, astColumnName( *table, *icolindex2 ) );
19038    }
19039 
19040 /* If the required Mapping outputs were not separable, create a single
19041    2D coords array describing both outputs. */
19042    if( !ret ) {
19043 
19044 /* TO BE DONE... Until then non-separable Mappings will result in a
19045    failure to create a -TAB header. No point in doing this until AST has
19046    an N-dimensional LutMap class (otherwise AST could never read the
19047    resulting FITS header). */
19048    }
19049 
19050 /* If an error occurred, free the returned Mapping. */
19051    if( !astOK ) ret = astAnnul( ret );
19052 
19053 /* Return the result. */
19054    return ret;
19055 }
19056 
IsAIPSSpectral(const char * ctype,char ** wctype,char ** wspecsys,int * status)19057 static int IsAIPSSpectral( const char *ctype, char **wctype, char **wspecsys, int *status ){
19058 /*
19059 *  Name:
19060 *     IsAIPSSpectral
19061 
19062 *  Purpose:
19063 *     See if a given CTYPE value describes a FITS-AIPS spectral axis.
19064 
19065 *  Type:
19066 *     Private function.
19067 
19068 *  Synopsis:
19069 *     #include "fitschan.h"
19070 *     int IsAIPSSpectral( const char *ctype, char **wctype, char **wspecsys, int *status )
19071 
19072 *  Class Membership:
19073 *     FitsChan member function.
19074 
19075 *  Description:
19076 *     The given CTYPE value is checked to see if it conforms to the
19077 *     requirements of a spectral axis CTYPE value as specified by
19078 *     FITS-AIPS encoding. If so, the equivalent FITS-WCS CTYPE and
19079 *     SPECSYS values are returned.
19080 
19081 *  Parameters:
19082 *     ctype
19083 *        Pointer to a null terminated string holding the CTYPE value to
19084 *        check.
19085 *     wctype
19086 *        The address of a location at which to return a pointer to a
19087 *        static string holding the corresponding FITS-WCS CTYPE value. A
19088 *        NULL pointer is returned if the supplied CTYPE string is not an
19089 *        AIPS spectral CTYPE value.
19090 *     wspecsys
19091 *        The address of a location at which to return a pointer to a
19092 *        static string holding the corresponding FITS-WCS SPECSYS value. A
19093 *        NULL pointer is returned if the supplied CTYPE string is not an
19094 *        AIPS spectral CTYPE value.
19095 *     status
19096 *        Pointer to the inherited status variable.
19097 
19098 *  Retuned Value:
19099 *     Non-zero fi the supplied CTYPE was an AIPS spectral CTYPE value.
19100 
19101 *  Note:
19102 *     - These translations are also used by the FITS-CLASS encoding.
19103 */
19104 
19105 /* Local Variables: */
19106    int ret;
19107 
19108 /* Initialise */
19109    ret = 0;
19110    *wctype = NULL;
19111    *wspecsys = NULL;
19112 
19113 /* Check the inherited status. */
19114    if( !astOK ) return ret;
19115 
19116 /* If the length of the string is not 8, then it is not an AIPS spectral axis. */
19117    if( strlen( ctype ) == 8 ) {
19118 
19119 /* Translate AIPS spectral CTYPE values to FITS-WCS paper III equivalents.
19120    These are of the form AAAA-BBB, where "AAAA" can be "FREQ", "VELO" (=VRAD!)
19121    or "FELO" (=VOPT-F2W), and BBB can be "LSR", "LSD", "HEL" (=*Bary*centric!)
19122    or "GEO". */
19123       if( !strncmp( ctype, "FREQ", 4 ) ){
19124          *wctype = "FREQ    ";
19125       } else if( !strncmp( ctype, "VELO", 4 ) ){
19126          *wctype = "VRAD    ";
19127       } else if( !strncmp( ctype, "FELO", 4 ) ){
19128          *wctype = "VOPT-F2W";
19129       } else if( !strncmp( ctype, "WAVELENG", 8 ) ){
19130          *wctype = "WAVE    ";
19131       }
19132       if( !strcmp( ctype + 4, "-LSR" ) ){
19133          *wspecsys = "LSRK";
19134       } else if( !strcmp( ctype + 4, "LSRK" ) ){
19135          *wspecsys = "LSRK";
19136       } else if( !strcmp( ctype + 4, "-LSRK" ) ){
19137          *wspecsys = "LSRK";
19138       } else if( !strcmp( ctype + 4, "-LSD" ) ){
19139          *wspecsys = "LSRD";
19140       } else if( !strcmp( ctype + 4, "-HEL" ) ){
19141          *wspecsys = "BARYCENT";
19142       } else if( !strcmp( ctype + 4, "-EAR" ) || !strcmp( ctype + 4, "-GEO" ) ){
19143          *wspecsys = "GEOCENTR";
19144       } else if( !strcmp( ctype + 4, "-OBS" ) || !strcmp( ctype + 4, "-TOP" ) ){
19145          *wspecsys = "TOPOCENT";
19146       }
19147       if( *wctype && *wspecsys ) {
19148          ret = 1;
19149       } else {
19150          *wctype = NULL;
19151          *wspecsys = NULL;
19152       }
19153    }
19154 
19155 /* Return the result. */
19156    return ret;
19157 }
19158 
IsSkyOff(AstFrameSet * fset,int iframe,int * status)19159 static int IsSkyOff( AstFrameSet *fset, int iframe, int *status ){
19160 /*
19161 *  Name:
19162 *     IsSkyOff
19163 
19164 *  Purpose:
19165 *     See if a given Frame contains an offset SkyFrame.
19166 
19167 *  Type:
19168 *     Private function.
19169 
19170 *  Synopsis:
19171 *     #include "fitschan.h"
19172 *     int IsSkyOff( AstFrameSet *fset, int iframe, int *status )
19173 
19174 *  Class Membership:
19175 *     FitsChan member function.
19176 
19177 *  Description:
19178 *     Returns a flag indicating if the specified Frame within the
19179 *     supplied FrameSet is, or contains, a SkyFrame that represents
19180 *     offset coordinates. This is the case if the Frame is a SkyFrame
19181 *     and its SkyRefIs attribute is "Pole" or "Origin", or is a CmpFrame
19182 *     containing such a SkyFrame.
19183 
19184 *  Parameters:
19185 *     fset
19186 *        The FrameSet.
19187 *     iframe
19188 *        Index of the Frame to check within "fset"
19189 *     status
19190 *        Pointer to the inherited status variable.
19191 
19192 *  Retuned Value:
19193 *     +1 if the Frame is an offset SkyFrame. Zero otherwise.
19194 
19195 *  Notes:
19196 *     - Zero is returned if an error has already occurred.
19197 */
19198 
19199 /* Local Variables: */
19200    AstFrame *frm;
19201    const char *skyrefis;
19202    int oldrep;
19203    int result;
19204 
19205 /* Initialise. */
19206    result = 0;
19207 
19208 /* Check the inherited status. */
19209    if( !astOK ) return result;
19210 
19211 /* Get a pointer to the required Frame in the FrameSet */
19212    frm = astGetFrame( fset, iframe );
19213 
19214 /* Since the current Frame may not contain a SkyFrame, we temporarily
19215    switch off error reporting. */
19216    oldrep = astReporting( 0 );
19217 
19218 /* Get the SkyRefIs attribute value. */
19219    skyrefis = astGetC( frm, "SkyRefIs" );
19220 
19221 /* If it is "Pole" or "Origin", return 1. */
19222    if( skyrefis && ( !Ustrcmp( skyrefis, "POLE", status ) ||
19223                      !Ustrcmp( skyrefis, "ORIGIN", status ) ) ) result = 1;
19224 
19225 /* Cancel any error and switch error reporting back on again. */
19226    astClearStatus;
19227    astReporting( oldrep );
19228 
19229 /* Annul the Frame pointer. */
19230    frm = astAnnul( frm );
19231 
19232 /* Return the result. */
19233    return result;
19234 }
19235 
IsSpectral(const char * ctype,char stype[5],char algcode[5],int * status)19236 static const char *IsSpectral( const char *ctype, char stype[5], char algcode[5], int *status ) {
19237 /*
19238 *  Name:
19239 *     IsSpectral
19240 
19241 *  Purpose:
19242 *     See if a given FITS-WCS CTYPE value describes a spectral axis.
19243 
19244 *  Type:
19245 *     Private function.
19246 
19247 *  Synopsis:
19248 *     #include "fitschan.h"
19249 *     char *IsSpectral( const char *ctype, char stype[5], char algcode[5], int *status )
19250 
19251 *  Class Membership:
19252 *     FitsChan member function.
19253 
19254 *  Description:
19255 *     The given CTYPE value is checked to see if it conforms to the
19256 *     requirements of a spectral axis CTYPE value as specified by
19257 *     FITS-WCS paper 3. If so, the spectral system and algorithm codes
19258 *     are extracted from it and returned, together with the default units
19259 *     for the spectral system.
19260 
19261 *  Parameters:
19262 *     ctype
19263 *        Pointer to a null terminated string holding the CTYPE value to
19264 *        check.
19265 *     stype
19266 *        An array in which to return the null-terminated spectral system type
19267 *        (e.g. "FREQ", "VELO", "WAVE", etc). A null string is returned if
19268 *        the CTYPE value does not describe a spectral axis.
19269 *     algcode
19270 *        An array in which to return the null-terminated algorithm code
19271 *        (e.g. "-LOG", "", "-F2W", etc). A null string is returned if the
19272 *        spectral axis is linear. A null string is returned if the CTYPE
19273 *        value does not describe a spectral axis.
19274 *     status
19275 *        Pointer to the inherited status variable.
19276 
19277 *  Retuned Value:
19278 *     A point to a static string holding the default units associated
19279 *     with the spectral system specified by the supplied CTYPE value.
19280 *     NULL is returned if the CTYPE value does not describe a spectral
19281 *     axis.
19282 
19283 *  Notes:
19284 *     - The axis is considered to be a spectral axis if the first 4
19285 *     characters form one of the spectral system codes listed in FITS-WCS
19286 *     paper 3. The algorithm code is not checked, except to ensure that
19287 *     it begins with a minus sign, or is blank.
19288 *     - A NULL pointer is returned if an error has already occurred.
19289 */
19290 
19291 /* Local Variables: */
19292    astDECLARE_GLOBALS
19293    int ctype_len;
19294 
19295 /* Initialise */
19296    stype[ 0 ] = 0;
19297    algcode[ 0 ] = 0;
19298 
19299 /* Check the inherited status. */
19300    if( !astOK ) return NULL;
19301 
19302 /* Get a pointer to the structure holding thread-specific global data. */
19303    astGET_GLOBALS(NULL);
19304 
19305 /* Initialise more stuff */
19306    isspectral_ret = NULL;
19307 
19308 /* If the length of the string is less than 4, then it is not a spectral
19309    axis. */
19310    ctype_len = strlen( ctype );
19311    if( ctype_len >= 4 ) {
19312 
19313 /* Copy the first 4 characters (the coordinate system described by the
19314    axis) into a null-terminated buffer. */
19315       strncpy( stype, ctype, 4 );
19316       stype[ 4 ] = 0;
19317       stype[ astChrLen( stype ) ] = 0;
19318 
19319 /* Copy any remaining characters (the algorithm code) into a null-terminated
19320    buffer. Only copy a maximum of 4 characters. */
19321       if( ctype_len > 4 ) {
19322          if( ctype_len <= 8 ) {
19323             strcpy( algcode, ctype + 4 );
19324          } else {
19325             strncpy( algcode, ctype + 4, 4 );
19326             algcode[ 4 ] = 0;
19327          }
19328          algcode[ astChrLen( algcode ) ] = 0;
19329       } else {
19330          algcode[ 0 ] = 0;
19331       }
19332 
19333 /* See if the first 4 characters of the CTYPE value form one of the legal
19334    spectral coordinate type codes listed in FITS-WCS Paper III. Also note
19335    the default units associated with the system. */
19336       if( !strcmp( stype, "FREQ" ) ) {
19337          isspectral_ret = "Hz";
19338       } else if( !strcmp( stype, "ENER" ) ) {
19339          isspectral_ret = "J";
19340       } else if( !strcmp( stype, "WAVN" ) ) {
19341          isspectral_ret = "/m";
19342       } else if( !strcmp( stype, "VRAD" ) ) {
19343          isspectral_ret = "m/s";
19344       } else if( !strcmp( stype, "WAVE" ) ) {
19345          isspectral_ret = "m";
19346       } else if( !strcmp( stype, "VOPT" ) ) {
19347          isspectral_ret = "m/s";
19348       } else if( !strcmp( stype, "ZOPT" ) ) {
19349          isspectral_ret = "";
19350       } else if( !strcmp( stype, "AWAV" ) ) {
19351          isspectral_ret = "m";
19352       } else if( !strcmp( stype, "VELO" ) ) {
19353          isspectral_ret = "m/s";
19354       } else if( !strcmp( stype, "BETA" ) ) {
19355          isspectral_ret = "";
19356       }
19357 
19358 /* Also check that the remaining part of CTYPE (the algorithm code) begins
19359    with a minus sign or is blank. */
19360       if( algcode[ 0 ] != '-' && strlen( algcode ) > 0 ) isspectral_ret = NULL;
19361    }
19362 
19363 /* Return null strings if the axis is not a spectral axis. */
19364    if( ! isspectral_ret ) {
19365       stype[ 0 ] = 0;
19366       algcode[ 0 ] = 0;
19367    }
19368 
19369 /* Return the result. */
19370    return isspectral_ret;
19371 }
19372 
LinearWcs(FitsStore * store,int i,char s,const char * method,const char * class,int * status)19373 static AstMapping *LinearWcs( FitsStore *store, int i, char s,
19374                               const char *method, const char *class, int *status ) {
19375 /*
19376 *  Name:
19377 *     LinearWcs
19378 
19379 *  Purpose:
19380 *     Create a Mapping describing a FITS-WCS linear algorithm
19381 
19382 *  Type:
19383 *     Private function.
19384 
19385 *  Synopsis:
19386 *     #include "fitschan.h"
19387 *     AstMapping *LinearWcs( FitsStore *store, int i, char s,
19388 *                            const char *method, const char *class, int *status )
19389 
19390 *  Class Membership:
19391 *     FitsChan member function.
19392 
19393 *  Description:
19394 *     This function uses the contents of the supplied FitsStore to create
19395 *     a Mapping which goes from Intermediate World Coordinate (known as "w"
19396 *     in the context of FITS-WCS paper III) to a linearly related axis.
19397 *
19398 *     The returned Mapping is a ShiftMap which simply adds on the value of
19399 *     CRVALi.
19400 
19401 *  Parameters:
19402 *     store
19403 *        Pointer to the FitsStore structure holding the values to use for
19404 *        the WCS keywords.
19405 *     i
19406 *        The zero-based index of the spectral axis within the FITS header
19407 *     s
19408 *        A character identifying the co-ordinate version to use. A space
19409 *        means use primary axis descriptions. Otherwise, it must be an
19410 *        upper-case alphabetical characters ('A' to 'Z').
19411 *     method
19412 *        A pointer to a string holding the name of the calling method.
19413 *        This is used only in the construction of error messages.
19414 *     class
19415 *        A pointer to a string holding the class of the object being
19416 *        read. This is used only in the construction of error messages.
19417 *     status
19418 *        Pointer to the inherited status variable.
19419 
19420 *  Returned Value:
19421 *     A pointer to a Mapping, or NULL if an error occurs.
19422 */
19423 
19424 /* Local Variables: */
19425    AstMapping *ret;
19426    double crv;
19427 
19428 /* Check the global status. */
19429    ret = NULL;
19430    if( !astOK ) return ret;
19431 
19432 /* Get the CRVAL value for the specified axis. */
19433    crv = GetItem( &(store->crval), i, 0, s, NULL, method, class, status );
19434    if( crv == AST__BAD ) crv = 0.0;
19435 
19436 /* Create a 1D ShiftMap which adds this value onto the IWCS value. */
19437    if( crv != 0.0 ) {
19438       ret = (AstMapping *) astShiftMap( 1, &crv, "", status );
19439    } else {
19440       ret = (AstMapping *) astUnitMap( 1, "", status );
19441    }
19442    return ret;
19443 }
19444 
LogAxis(AstMapping * map,int iax,int nwcs,double * lbnd_p,double * ubnd_p,double crval,int * status)19445 static AstMapping *LogAxis( AstMapping *map, int iax, int nwcs, double *lbnd_p,
19446                             double *ubnd_p, double crval, int *status ){
19447 /*
19448 *  Name:
19449 *     LogAxes
19450 
19451 *  Purpose:
19452 *     Test a Frame axis to see if it logarithmically spaced in pixel coords.
19453 
19454 *  Type:
19455 *     Private function.
19456 
19457 *  Synopsis:
19458 *     #include "fitschan.h"
19459 *     AstMapping *LogAxis( AstMapping *map, int iax, int nwcs, double *lbnd_p,
19460 *                          double *ubnd_p, double crval )
19461 
19462 *  Class Membership:
19463 *     FitsChan member function.
19464 
19465 *  Description:
19466 *     A specified axis of the supplied Mappinhg is tested to see if it
19467 *     corresponds to the form
19468 *
19469 *        S = Sr.exp( w/Sr )
19470 *
19471 *     where "w" is one of the Mapping inputs, "S" is the specified
19472 *     Mapping output, and "Sr" is the supplied value of "crval". This
19473 *     is the form for a FITS log axis as defined in FITS-WCS paper III.
19474 *
19475 *     If the above test is passed, a Mapping is returned from "S" to "w"
19476 *     (the inverseof the above expression).
19477 
19478 *  Parameters:
19479 *     map
19480 *        Pointer to the Mapping. This will usually be a Mapping from
19481 *        pixel coords to WCS coords.
19482 *     iax
19483 *        The index of the output of "map" which correspoinds to "S".
19484 *     nwcs
19485 *        The number of outputs from "map".
19486 *     lbnd_p
19487 *        Pointer to an array of double, with one element for each
19488 *        Mapping input coordinate. This should contain the lower bound
19489 *        of the input pixel box in each input dimension.
19490 *     ubnd_p
19491 *        Pointer to an array of double, with one element for each
19492 *        Mapping input coordinate. This should contain the upper bound
19493 *        of the input pixel box in each input dimension.
19494 *     crval
19495 *        The reference value ("Sr") to use. Must not be zero.
19496 
19497 *  Returned Value:
19498 *     If the specified axis is logarithmically spaced, a Mapping with
19499 *     "nwcs" inputs and "nwcs" outputs is returned. This Mapping transforms
19500 
19501 *     its "iax"th input using the transformation:
19502 *
19503 *        w = Sr.Log( S/Sr )
19504 *
19505 *     (where "S" is the Mapping is the "iax"th input and "w" is the
19506 *     "iax"th output). Other inputs are copied to the corresponding
19507 *     output without change. NULL is returned if the specified axis is
19508 *     not logarithmically spaced.
19509 */
19510 
19511 /* Local Variables: */
19512    AstMapping *result;     /* Returned Mapping */
19513    AstMapping *tmap0;      /* A temporary Mapping */
19514    AstMapping *tmap1;      /* A temporary Mapping */
19515    AstMapping *tmap2;      /* A temporary Mapping */
19516    AstMapping *tmap3;      /* A temporary Mapping */
19517    AstMapping *tmap4;      /* A temporary Mapping */
19518    const char *fexps[ 1 ]; /* Forward MathMap expressions */
19519    const char *iexps[ 1 ]; /* Inverse MathMap expressions */
19520 
19521 /* Initialise */
19522    result = NULL;
19523 
19524 /* Check the inherited status and crval value. */
19525    if( !astOK || crval == 0.0 ) return result;
19526 
19527 /* If the "log" algorithm is appropriate, the supplied axis (s) is related
19528    to pixel coordinate (p) by s = Sr.EXP( a*p - b ). If this is the case,
19529    then the log of s will be linearly related to pixel coordinates. To test
19530    this, we create a CmpMap which produces log(s). */
19531    fexps[ 0 ] = "logs=log(s)";
19532    iexps[ 0 ] = "s=exp(logs)";
19533    tmap1 = (AstMapping *) astMathMap( 1, 1, 1, fexps, 1, iexps,
19534                                       "simpfi=1,simpif=1", status );
19535    tmap2 = AddUnitMaps( tmap1, iax, nwcs, status );
19536    tmap0 = (AstMapping *) astCmpMap( map, tmap2, 1, "", status );
19537    tmap2 = astAnnul( tmap2 );
19538 
19539 /* See if this Mapping is linear. */
19540    if( IsMapLinear( tmap0, lbnd_p, ubnd_p, iax, status ) ) {
19541 
19542 /* Create the Mapping which defines the IWC axis. This is the Mapping from
19543    WCS to IWCS - "W = Sr.log( S/Sr )". Other axes are left unchanged by the
19544    Mapping. The IWC axis has the same axis index as the WCS axis. */
19545       tmap2 = (AstMapping *) astZoomMap( 1, 1.0/crval, "", status );
19546       tmap3 = (AstMapping *) astCmpMap( tmap2, tmap1, 1, "", status );
19547       tmap2 = astAnnul( tmap2 );
19548       tmap2 = (AstMapping *) astZoomMap( 1, crval, "", status );
19549       tmap4 = (AstMapping *) astCmpMap( tmap3, tmap2, 1, "", status );
19550       tmap3 = astAnnul( tmap3 );
19551       tmap2 = astAnnul( tmap2 );
19552       result = AddUnitMaps( tmap4, iax, nwcs, status );
19553       tmap4 = astAnnul( tmap4 );
19554    }
19555 
19556 /* Free resources. */
19557    tmap0 = astAnnul( tmap0 );
19558    tmap1 = astAnnul( tmap1 );
19559 
19560 /* Return the result. */
19561    return result;
19562 }
19563 
LogWcs(FitsStore * store,int i,char s,const char * method,const char * class,int * status)19564 static AstMapping *LogWcs( FitsStore *store, int i, char s,
19565                            const char *method, const char *class, int *status ) {
19566 /*
19567 *  Name:
19568 *     LogWcs
19569 
19570 *  Purpose:
19571 *     Create a Mapping describing a FITS-WCS logarithmic algorithm
19572 
19573 *  Type:
19574 *     Private function.
19575 
19576 *  Synopsis:
19577 *     #include "fitschan.h"
19578 *     AstMapping *LogWcs( FitsStore *store, int i, char s,
19579 *                         const char *method, const char *class, int *status )
19580 
19581 *  Class Membership:
19582 *     FitsChan member function.
19583 
19584 *  Description:
19585 *     This function uses the contents of the supplied FitsStore to create
19586 *     a Mapping which goes from Intermediate World Coordinate (known as "w"
19587 *     in the context of FITS-WCS paper III) to a logarthmic version of w
19588 
19589 *     called "S" given by:
19590 *
19591 *     S = Sr.exp( w/Sr )
19592 *
19593 *     where Sr is the value of S corresponding to w=0.
19594 
19595 *  Parameters:
19596 *     store
19597 *        Pointer to the FitsStore structure holding the values to use for
19598 *        the WCS keywords.
19599 *     i
19600 *        The zero-based index of the axis within the FITS header
19601 *     s
19602 *        A character identifying the co-ordinate version to use. A space
19603 *        means use primary axis descriptions. Otherwise, it must be an
19604 *        upper-case alphabetical characters ('A' to 'Z').
19605 *     method
19606 *        A pointer to a string holding the name of the calling method.
19607 *        This is used only in the construction of error messages.
19608 *     class
19609 *        A pointer to a string holding the class of the object being
19610 *        read. This is used only in the construction of error messages.
19611 *     status
19612 *        Pointer to the inherited status variable.
19613 
19614 *  Returned Value:
19615 *     A pointer to a Mapping, or NULL if an error occurs.
19616 */
19617 
19618 /* Local Variables: */
19619    AstMapping *ret;
19620    char forexp[ 12 + DBL_DIG*2 ];
19621    char invexp[ 12 + DBL_DIG*2 ];
19622    const char *fexps[ 1 ];
19623    const char *iexps[ 1 ];
19624    double crv;
19625 
19626 /* Check the global status. */
19627    ret = NULL;
19628    if( !astOK ) return ret;
19629 
19630 /* Get the CRVAL value for the specified axis. Use a default of zero. */
19631    crv = GetItem( &(store->crval), i, 0, s, NULL, method, class, status );
19632    if( crv == AST__BAD ) crv = 0.0;
19633 
19634 /* Create the MathMap, if possible. */
19635    if( crv != 0.0 ) {
19636       sprintf( forexp, "s=%.*g*exp(w/%.*g)", DBL_DIG, crv, DBL_DIG, crv );
19637       sprintf( invexp, "w=%.*g*log(s/%.*g)", DBL_DIG, crv, DBL_DIG, crv );
19638       fexps[ 0 ] = forexp;
19639       iexps[ 0 ] = invexp;
19640       ret = (AstMapping *) astMathMap( 1, 1, 1, fexps, 1, iexps, "simpfi=1,simpif=1", status );
19641    }
19642 
19643 /* Return the result */
19644    return ret;
19645 }
19646 
LooksLikeClass(AstFitsChan * this,const char * method,const char * class,int * status)19647 static int LooksLikeClass( AstFitsChan *this, const char *method,
19648                            const char *class, int *status ){
19649 
19650 /*
19651 *  Name:
19652 *     LooksLikeClass
19653 
19654 *  Purpose:
19655 *     Does the FitsChan seem to use FITS-CLASS encoding?
19656 
19657 *  Type:
19658 *     Private function.
19659 
19660 *  Synopsis:
19661 *     #include "fitschan.h"
19662 *     int LooksLikeClass( AstFitsChan *this, const char *method,
19663 *                         const char *class, int *status )
19664 
19665 *  Class Membership:
19666 *     FitsChan member function.
19667 
19668 *  Description:
19669 *     Returns non-zero if the supplied FitsChan probably uses FITS-CLASS
19670 *     encoding. This is the case if it contains a DELTAV keyword and a
19671 *     keyword of the form VELO-xxx", where xxx is one of the accepted
19672 *     standards of rest, or "VLSR".
19673 
19674 *  Parameters:
19675 *     this
19676 *        Pointer to the FitsChan.
19677 *     method
19678 *        Pointer to a string holding the name of the calling method.
19679 *        This is only for use in constructing error messages.
19680 *     class
19681 *        Pointer to a string holding the name of the supplied object class.
19682 *        This is only for use in constructing error messages.
19683 *     status
19684 *        Pointer to the inherited status variable.
19685 
19686 *  Returned Value:
19687 *     Non-zero if the encoding in use lookslike FITS-CLASS.
19688 */
19689 
19690 /* Local Variables... */
19691    int ret;            /* Returned value */
19692 
19693 /* Initialise */
19694    ret = 0;
19695 
19696 /* Check the global status. */
19697    if( !astOK ) return ret;
19698 
19699 /* See if there is a "DELTAV" card, and a "VELO-xxx" or "VLSR" card. */
19700    if( astKeyFields( this, "DELTAV", 0, NULL, NULL ) && (
19701           astKeyFields( this, "VLSR", 0, NULL, NULL ) ||
19702           astKeyFields( this, "VELO-OBS", 0, NULL, NULL ) ||
19703           astKeyFields( this, "VELO-HEL", 0, NULL, NULL ) ||
19704           astKeyFields( this, "VELO-EAR", 0, NULL, NULL ) ||
19705           astKeyFields( this, "VELO-LSR", 0, NULL, NULL ) ) ) {
19706       ret = 1;
19707    }
19708 
19709 /* Return  the result. */
19710    return ret;
19711 }
19712 
MakeBanner(const char * prefix,const char * middle,const char * suffix,char banner[AST__FITSCHAN_FITSCARDLEN-FITSNAMLEN+1],int * status)19713 static void MakeBanner( const char *prefix, const char *middle,
19714                         const char *suffix,
19715                         char banner[ AST__FITSCHAN_FITSCARDLEN -
19716                                      FITSNAMLEN + 1 ], int *status ) {
19717 /*
19718 *  Name:
19719 *     MakeBanner
19720 
19721 *  Purpose:
19722 *     Create a string containing a banner comment.
19723 
19724 *  Type:
19725 *     Private function.
19726 
19727 *  Synopsis:
19728 *     #include "fitschan.h"
19729 *     void MakeBanner( const char *prefix, const char *middle,
19730 *                      const char *suffix,
19731 *                      char banner[ AST__FITSCHAN_FITSCARDLEN - FITSNAMLEN + 1 ], int *status )
19732 
19733 *  Class Membership:
19734 *     FitsChan member function.
19735 
19736 *  Description:
19737 *     This function creates a string which can be written as a FITS
19738 *     comment card to produce a banner heading (or tail) for an AST
19739 *     Object when it is written to a FitsChan. The banner will occupy
19740 *     the maximum permitted width for text in a FITS comment card.
19741 
19742 *  Parameters:
19743 *     prefix
19744 *        A pointer to a constant null-terminated string containing the
19745 *        first part of the text to appear in the banner.
19746 *     middle
19747 *        A pointer to a constant null-terminated string containing the
19748 *        second part of the text to appear in the banner.
19749 *     suffix
19750 *        A pointer to a constant null-terminated string containing the
19751 *        third part of the text to appear in the banner.
19752 *     banner
19753 *        A character array to receive the null-terminated result string.
19754 *     status
19755 *        Pointer to the inherited status variable.
19756 
19757 *  Notes:
19758 *     - The text to appear in the banner is constructed by
19759 *     concatenating the three input strings supplied.
19760 */
19761 
19762 /* Local Variables: */
19763    char token[] = "AST";         /* Identifying token */
19764    int i;                        /* Loop counter for input characters */
19765    int len;                      /* Number of output characters */
19766    int ltok;                     /* Length of token string */
19767    int mxlen;                    /* Maximum permitted output characters */
19768    int start;                    /* Column number where text starts */
19769 
19770 /* Check the global error status. */
19771    if ( !astOK ) return;
19772 
19773 /* Calculate the maximum number of characters that the output banner
19774    can hold and the length of the token string. */
19775    mxlen = AST__FITSCHAN_FITSCARDLEN - FITSNAMLEN;
19776    ltok = (int) strlen( token );
19777 
19778 /* Calculate the column in which to start the text, so that it is
19779    centred in the banner (with 4 non-text characters on each side). */
19780    start = ltok + 2 + ( mxlen - ltok - 1 -
19781                         (int) ( strlen( prefix ) +
19782                                 strlen( middle ) +
19783                                 strlen( suffix ) ) - 1 - ltok ) / 2;
19784    if ( start < ltok + 2 ) start = ltok + 2;
19785 
19786 /* Start building the banner with the token string. */
19787    len = 0;
19788    for ( i = 0; token[ i ] && ( len < mxlen ); i++ ) {
19789       banner[ len++ ] = token[ i ];
19790    }
19791 
19792 /* Then pad with spaces up to the start of the text. */
19793    while ( len < start - 1 ) banner[ len++ ] = ' ';
19794 
19795 /* Insert the prefix data, truncating it if it is too long. */
19796    for ( i = 0; prefix[ i ] && ( len < mxlen - ltok - 1 ); i++ ) {
19797       banner[ len++ ] = prefix[ i ];
19798    }
19799 
19800 /* Insert the middle data, truncating it if it is too long. */
19801    for ( i = 0; middle[ i ] && ( len < mxlen - ltok - 1 ); i++ ) {
19802       banner[ len++ ] = middle[ i ];
19803    }
19804 
19805 /* Insert the suffix data, truncating it if it is too long. */
19806    for ( i = 0; suffix[ i ] && ( len < mxlen - ltok - 1 ); i++ ) {
19807       banner[ len++ ] = suffix[ i ];
19808    }
19809 
19810 /* Pad the end of the text with spaces. */
19811    while ( len < mxlen - ltok ) banner[ len++ ] = ' ';
19812 
19813 /* Finish the banner with the token string. */
19814    for ( i = 0; token[ i ] && ( len < mxlen ); i++ ) {
19815       banner[ len++ ] = token[ i ];
19816    }
19817 
19818 /* Terminate the output string. */
19819    banner[ len ] = '\0';
19820 }
19821 
MakeColumnMap(AstFitsTable * table,const char * col,int isindex,int interp,const char * method,const char * class,int * status)19822 static AstMapping *MakeColumnMap( AstFitsTable *table, const char *col,
19823                                   int isindex, int interp, const char *method,
19824                                   const char *class, int *status ){
19825 /*
19826 *  Name:
19827 *     MakeColumnMap
19828 
19829 *  Purpose:
19830 *     Create a Mapping describing a look-up table supplied in a cell of a
19831 *     FITS binary table.
19832 
19833 *  Type:
19834 *     Private function.
19835 
19836 *  Synopsis:
19837 *     #include "fitschan.h"
19838 *     AstMapping *MakeColumnMap( AstFitsTable *table, const char *col,
19839 *                                int isindex, int interp, const char *method,
19840 *                                const char *class, int *status )
19841 
19842 *  Class Membership:
19843 *     FitsChan member function.
19844 
19845 *  Description:
19846 *     This function returns a Mapping representing the array of values
19847 *     stored in row 1 of a named column of a supplied FitsTable. The
19848 *     array of values is treated as a look-up table following the prescription
19849 *     of FITS-WCS paper III (the "-TAB" algorithm). If the array has (N+1)
19850 *     dimensions (where N is one or more), the returned Mapping has N
19851 *     inputs and N outputs. The inputs correspond to FITS GRID coords
19852 *     within the array. FITS-WCS paper III requires that the first dimension
19853 *     in the array has a length of "N" and contains the N output values
19854 *     at each input values.
19855 
19856 *  Parameters:
19857 *     table
19858 *        Pointer to the Fitstable.
19859 *     col
19860 *        A string holding the name of the column to use.
19861 *     isindex
19862 *        Non-zero if the column hold an index array, zero if it holds a
19863 *        coordinate array.
19864 *     interp
19865 *        The value to use for the Interp attribute of the LutMap. A value
19866 *        of zero tells the LutMap class to use linear interpolation. Other
19867 *        values tell the LutMap class to use nearest neighbour interpolation.
19868 *     method
19869 *        A pointer to a string holding the name of the calling method.
19870 *        This is used only in the construction of error messages.
19871 *     class
19872 *        A pointer to a string holding the class of the object being
19873 *        read. This is used only in the construction of error messages.
19874 *     status
19875 *        Pointer to the inherited status variable.
19876 
19877 *  Returned Value:
19878 *     A pointer to the Mapping, or NULL if an error occurs.
19879 */
19880 
19881 /* Local Variables: */
19882    AstMapping *result;
19883    char *key;
19884    double *lut;
19885    int *dims;
19886    int ndim;
19887    int nel;
19888 
19889 /* Initialise */
19890    result = NULL;
19891 
19892 /* Check the inherited status */
19893    if( !astOK ) return result;
19894 
19895 /* Get the number of dimensions spanned by the value in the named column. */
19896    ndim = astGetColumnNdim( table, col );
19897 
19898 /* First deal with index vectors. */
19899    if( isindex ) {
19900 
19901 /* FITS-WCS paper II mandates that index arrays must be 1-dimensional. */
19902       if( ndim != 1 && astOK ) {
19903          astError( AST__BADTAB, "%s(%s): Column '%s' has %d dimensions but it "
19904                    "holds an index vector and should therefore be 1-dimensional.",
19905                    status, method, class, col, ndim );
19906       }
19907 
19908 /* Get the length of the index vector. */
19909       nel = astGetColumnLength( table, col );
19910 
19911 /* Allocate memory to hold the array values, and to hold the cell key. */
19912       lut = astMalloc( nel*sizeof( double ) );
19913       key = astMalloc( strlen( col ) + 5 );
19914       if( astOK ) {
19915 
19916 /* Create the key for the table cell holding the required array. FITS-WCS
19917    paper III mandates that tables always occur in the first row of the
19918    table (and that the table only has one row). Ignore trailing spaces in
19919    the column name. */
19920          sprintf( key, "%.*s(1)", (int) astChrLen( col ), col );
19921 
19922 /* Copy the array values into the above memory. */
19923          if( astMapGet1D( table, key, nel, &nel, lut ) ) {
19924 
19925 /* Create a 1D LutMap. FITS-WCS paper III (sec 6.1.2) mandates that the input
19926    corresponds to FITS grid coord (i.e. 1.0 at the centre of the first entry).
19927    Ensure the LutMap uses linear interpolation. */
19928             result = (AstMapping *) astLutMap( nel, lut, 1.0, 1.0,
19929                                                "LutInterp=%d", status, interp );
19930 
19931 /* Report an error if the table cell was empty. */
19932          } else if( astOK ) {
19933             astError( AST__BADTAB, "%s(%s): Row 1 of the binary table "
19934                       "contains no value for column '%s'.", status, method,
19935                       class, col );
19936          }
19937       }
19938 
19939 /* Free memory. */
19940       lut = astFree( lut );
19941       key = astFree( key );
19942 
19943 /* Now deal with coordinate arrays. */
19944    } else {
19945 
19946 /* Get the shape of the array. */
19947       dims = astMalloc( sizeof( int )*ndim );
19948       astColumnShape( table, col, ndim, &ndim, dims );
19949 
19950 /* For coordinate arrays, check the length of the first axis is "ndim-1", as
19951    required by FITS-WCS paper III. */
19952       if( astOK && dims[ 0 ] != ndim - 1 && !isindex ) {
19953          astError( AST__BADTAB, "%s(%s): The first dimension of the coordinate "
19954                    "array has length %d (should be %d since the array has %d "
19955                    "dimensions).", status, method, class, dims[ 0 ], ndim - 1,
19956                    ndim );
19957       }
19958 
19959 /* We can currently only handle 1D look-up tables. These are stored in
19960    notionally two-dimensional arrays in which the first dimension is
19961    degenarate (i.e. spans only a single element). */
19962       if( ndim > 2 ) {
19963          if( astOK ) astError( AST__INTER, "%s(%s): AST can currently only "
19964                                "handle 1-dimensional coordinate look-up tables "
19965                                "(the supplied table has %d dimensions).", status,
19966                                method, class, ndim - 1 );
19967 
19968 /* Handle 1-dimensional  look-up tables. */
19969       } else if( astOK ){
19970 
19971 /* Allocate memory to hold the array values, and to hold the cell key. */
19972          lut = astMalloc( dims[ 1 ]*sizeof( double ) );
19973          key = astMalloc( strlen( col ) + 5 );
19974          if( astOK ) {
19975 
19976 /* Create the key for the table cell holding the required array. FITS-WCS
19977    paper III mandates that tables always occur in the first row of the
19978    table (and that the table only has one row). Ignore trailing spaces in
19979    the column name. */
19980             sprintf( key, "%.*s(1)", (int) astChrLen( col ), col );
19981 
19982 /* Copy the array values into the above memory. */
19983             if( astMapGet1D( table, key, dims[ 1 ], dims, lut ) ) {
19984 
19985 /* Create a 1D LutMap. FITS-WCS paper III (sec 6.1.2) mandates that the input
19986    corresponds to FITS grid coord (i.e. 1.0 at the centre of the first entry).
19987    Ensure the LutMap uses linear interpolation. */
19988                result = (AstMapping *) astLutMap( dims[ 1 ], lut, 1.0, 1.0,
19989                                                   "LutInterp=%d", status,
19990                                                   interp );
19991 
19992 /* Report an error if the table cell was empty. */
19993             } else if( astOK ) {
19994                astError( AST__BADTAB, "%s(%s): Row 1 of the binary table "
19995                          "contains no value for column '%s'.", status, method,
19996                          class, col );
19997             }
19998          }
19999 
20000 /* Free memory. */
20001          lut = astFree( lut );
20002          key = astFree( key );
20003       }
20004       dims = astFree( dims );
20005    }
20006 
20007 /* Issue a context message and annul the returned Mapping if an error
20008    has occurred. */
20009    if( !astOK ) {
20010       astError( astStatus, "%s(%s): Cannot read a look-up table for a "
20011                 "tabular WCS axis from column '%s' of a FITS binary table.",
20012                 status, method, class, col );
20013       result = astAnnul( result );
20014    }
20015 
20016 /* Return the result. */
20017    return result;
20018 }
20019 
MakeFitsFrameSet(AstFitsChan * this,AstFrameSet * fset,int ipix,int iwcs,int encoding,const char * method,const char * class,int * status)20020 static AstFrameSet *MakeFitsFrameSet( AstFitsChan *this, AstFrameSet *fset,
20021                                       int ipix, int iwcs, int encoding,
20022                                       const char *method, const char *class,
20023                                       int *status ) {
20024 /*
20025 *  Name:
20026 *     MakeFitsFrameSet
20027 
20028 *  Purpose:
20029 *     Create a FrameSet which conforms to the requirements of the FITS-WCS
20030 *     papers.
20031 
20032 *  Type:
20033 *     Private function.
20034 
20035 *  Synopsis:
20036 *     #include "fitschan.h"
20037 *     AstFrameSet *MakeFitsFrameSet( AstFitsChan *this, AstFrameSet *fset,
20038 *                                    int ipix, int iwcs, int encoding,
20039 *                                    const char *method, const char *class,
20040 *                                    int *status )
20041 
20042 *  Class Membership:
20043 *     FitsChan member function.
20044 
20045 *  Description:
20046 *     This function constructs a new FrameSet holding the pixel and WCS
20047 *     Frames from the supplied FrameSet, but optionally extends the WCS
20048 *     Frame to include any extra axes needed to conform to the FITS model.
20049 
20050 *     Currently, this function does the following:
20051 *
20052 *     - if the WCS Frame contains a spectral axis with a defined celestial
20053 *     reference position (SpecFrame attributes RefRA and RefDec), then
20054 *     it ensures that the WCS Frame also contains a pair of celestial
20055 *     axes (such axes are added if they do not already exist within the
20056 *     supplied WCS Frame). The pixel->WCS Mapping is adjusted accordingly.
20057 *
20058 *     - if the WCS Frame contains a spectral axis and a pair of celestial
20059 *     axes, then the SpecFrame attributes RefRA and RefDec are set to the
20060 *     reference position defined by the celestial axes. The pixel->WCS
20061 *     Mapping is adjusted accordingly.
20062 *
20063 *     - NULL is returned if the WCS Frame contains more than one spectral
20064 *     axis.
20065 *
20066 *     - NULL is returned if the WCS Frame contains more than one pair of
20067 *     celestial axes.
20068 
20069 *  Parameters:
20070 *     this
20071 *        The FitsChan.
20072 *     fset
20073 *        The FrameSet to check.
20074 *     ipix
20075 *        The index of the FITS pixel Frame within "fset".
20076 *     iwcs
20077 *        The index of the WCS Frame within "fset".
20078 *     encoding
20079 *        The encoding in use.
20080 *     method
20081 *        Pointer to a string holding the name of the calling method.
20082 *        This is only for use in constructing error messages.
20083 *     class
20084 *        Pointer to a string holding the name of the supplied object class.
20085 *        This is only for use in constructing error messages.
20086 *     status
20087 *        Pointer to the inherited status variable.
20088 
20089 *  Returned Value:
20090 *     A new FrameSet which confoms to the requirements of the FITS-WCS
20091 *     papers. The base Frame in this FrameSet will be the FITS pixel
20092 *     Frame, and the current Frame will be the WCS Frame. NULL is
20093 *     returned if an error has already occurred, or if the FrameSet cannot
20094 *     be produced for any reason.
20095 */
20096 
20097 /* Local Variables: */
20098    AstFitsChan *fc;        /* Pointer to temporary FitsChan */
20099    AstFrame *pframe;       /* Pointer to the primary Frame */
20100    AstFrame *pixfrm;       /* Pointer to the FITS pixel Frame */
20101    AstFrame *tfrm0;        /* Pointer to a temporary Frame */
20102    AstFrame *tfrm;         /* Pointer to a temporary Frame */
20103    AstFrame *wcsfrm;       /* Pointer to the FITS WCS Frame */
20104    AstFrameSet *ret;       /* The returned FrameSet */
20105    AstFrameSet *tfs;       /* Pointer to a temporary FrameSet */
20106    AstMapping *map1;       /* Pointer to pre-WcsMap Mapping */
20107    AstMapping *map3;       /* Pointer to post-WcsMap Mapping */
20108    AstMapping *map;        /* Pointer to the pixel->wcs Mapping */
20109    AstMapping *tmap0;      /* Pointer to a temporary Mapping */
20110    AstMapping *tmap1;      /* Pointer to a temporary Mapping */
20111    AstMapping *tmap2;      /* Pointer to a temporary Mapping */
20112    AstMapping *tmap;       /* Pointer to a temporary Mapping */
20113    AstSpecFrame *skyfrm;   /* Pointer to the SkyFrame within WCS Frame */
20114    AstSpecFrame *specfrm;  /* Pointer to the SpecFrame within WCS Frame */
20115    AstWcsMap *map2;        /* Pointer to WcsMap */
20116    char card[ AST__FITSCHAN_FITSCARDLEN + 1 ]; /* A FITS header card */
20117    char equinox_attr[ 13 ];/* Name of Equinox attribute for sky axes */
20118    char system_attr[ 12 ]; /* Name of System attribute for sky axes */
20119    const char *eqn;        /* Pointer to original sky Equinox value */
20120    const char *skysys;     /* Pointer to original sky System value */
20121    double con;             /* Constant axis value */
20122    double reflat;          /* Celestial latitude at reference point */
20123    double reflon;          /* Celestial longitude at reference point */
20124    int *perm;              /* Pointer to axis permutation array */
20125    int iax;                /* Axis inex */
20126    int icurr;              /* Index of original current Frame in returned FrameSet */
20127    int ilat;               /* Celestial latitude index within WCS Frame */
20128    int ilon;               /* Celestial longitude index within WCS Frame */
20129    int npix;               /* Number of pixel axes */
20130    int nwcs;               /* Number of WCS axes */
20131    int ok;                 /* Is the supplied FrameSet usable? */
20132    int paxis;              /* Axis index within the primary Frame */
20133 
20134 /* Initialise */
20135    ret = NULL;
20136 
20137 /* Check the inherited status. */
20138    if( !astOK ) return ret;
20139 
20140 /* Get copies of the pixel Frame, the WCS Frame and the Mapping. */
20141    tfrm = astGetFrame( fset, ipix );
20142    pixfrm = astCopy( tfrm );
20143    tfrm = astAnnul( tfrm );
20144    tfrm = astGetFrame( fset, iwcs );
20145    wcsfrm = astCopy( tfrm );
20146    tfrm = astAnnul( tfrm );
20147    tmap = astGetMapping( fset, ipix, iwcs );
20148    map = astCopy( tmap );
20149    tmap = astAnnul( tmap );
20150 
20151 /* Store the number of pixel and WCS axes. */
20152    npix = astGetNaxes( pixfrm );
20153    nwcs = astGetNaxes( wcsfrm );
20154 
20155 /* Search the WCS Frame for SkyFrames and SpecFrames. */
20156    specfrm = NULL;
20157    skyfrm = NULL;
20158    ok = 1;
20159    ilat = -1;
20160    ilon = -1;
20161    for( iax = 0; iax < nwcs; iax++ ) {
20162 
20163 /* Obtain a pointer to the primary Frame containing the current WCS axis. */
20164       astPrimaryFrame( wcsfrm, iax, &pframe, &paxis );
20165 
20166 /* If the current axis is a SpecFrame, save a pointer to it. If we have already
20167    found a SpecFrame, abort. */
20168       if( astIsASpecFrame( pframe ) ) {
20169          if( specfrm ) {
20170             ok = 0;
20171             break;
20172          }
20173          specfrm = astClone( pframe );
20174 
20175 /* If the current axis is a SkyFrame, save a pointer to it, and its WCS
20176    index. If we have already found a different SkyFrame, abort. */
20177       } else if( astIsASkyFrame( pframe ) ) {
20178          if( skyfrm ) {
20179             if( pframe != (AstFrame *) skyfrm ) {
20180                ok = 0;
20181                break;
20182             }
20183          } else {
20184             skyfrm = astClone( pframe );
20185          }
20186          if( paxis == 0 ) {
20187             ilon = iax;
20188          } else {
20189             ilat = iax;
20190          }
20191       }
20192 
20193 /* Free resources. */
20194       pframe = astAnnul( pframe );
20195    }
20196 
20197 /* If the supplied FrameSet is usable... */
20198    if( ok ) {
20199 
20200 /* If we did not find a SpecFrame, return a FrameSet made from the base
20201    and current Frames in the supplied FrameSet. */
20202       if( !specfrm ) {
20203          ret = astFrameSet( pixfrm, "", status );
20204          astAddFrame( ret, AST__BASE, map, wcsfrm );
20205 
20206 /* If we have a SpecFrame, proceed. */
20207       } else {
20208 
20209 /* Check that both the RefRA and RefDec attributes of the SpecFrame are set.
20210    If not, return a FrameSet made from the base and current Frames in the
20211    supplied FrameSet.*/
20212          if( !astTestRefRA( specfrm ) || !astTestRefDec( specfrm ) ) {
20213             ret = astFrameSet( pixfrm, "", status );
20214             astAddFrame( ret, AST__BASE, map, wcsfrm );
20215 
20216 /* If we have a celestial reference position for the spectral axis, ensure
20217    it is described correctly by a pair of celestial axes. */
20218          } else {
20219 
20220 /* If the WCS Frame does not contain any celestial axes, we add some now. */
20221             if( !skyfrm ) {
20222 
20223 /* The easiest way to create the required mapping from pixel to celestial
20224    to create a simple FITS header and read it in via a FitsChan to create a
20225    FrameSet. */
20226                fc = astFitsChan( NULL, NULL, "", status );
20227                astPutFits( fc, "CRPIX1  = 0", 0 );
20228                astPutFits( fc, "CRPIX2  = 0", 0 );
20229                astPutFits( fc, "CDELT1  = 0.0003", 0 );
20230                astPutFits( fc, "CDELT2  = 0.0003", 0 );
20231                astPutFits( fc, "CTYPE1  = 'RA---TAN'", 0 );
20232                astPutFits( fc, "CTYPE2  = 'DEC--TAN'", 0 );
20233                astPutFits( fc, "RADESYS = 'FK5'", 0 );
20234                astPutFits( fc, "EQUINOX = 2000.0", 0 );
20235                sprintf( card, "CRVAL1  = %.*g", DBL_DIG,
20236                         AST__DR2D*astGetRefRA( specfrm ) );
20237                astPutFits( fc, card, 0 );
20238                sprintf( card, "CRVAL2  = %.*g", DBL_DIG,
20239                         AST__DR2D*astGetRefDec( specfrm ) );
20240                astPutFits( fc, card, 0 );
20241                sprintf( card, "MJD-OBS = %.*g", DBL_DIG,
20242                         TDBConv( astGetEpoch( specfrm ), AST__UTC, 1,
20243                                  "astWrite", "FitsChan", status ) );
20244                astPutFits( fc, card, 0 );
20245                astClearCard( fc );
20246                tfs = astRead( fc );
20247                if( tfs ) {
20248 
20249 /* Create the new pixel->wcs Mapping. First get the 2-input,2-output
20250    Mapping between pixel and sky coords from the above FrameSet. Then add
20251    this Mapping in parallel with the original pixel->wcs Mapping. */
20252                   tmap0 = astGetMapping( tfs, AST__BASE, AST__CURRENT );
20253                   tmap1 = (AstMapping *) astCmpMap( map, tmap0, 0, "", status );
20254                   tmap0 = astAnnul( tmap0 );
20255 
20256 /* We now have a (npix+2)-input,(nwcs+2)-output Mapping. We now add a
20257    PermMap in series with this which feeds the constant value 0.0 (the
20258    CRPIX value in the above set of FITS headers) into the 2 pixel axes
20259    corresponding to RA and Dec. This PermMap has npix-inputs and (npix+2)
20260    outputs. The total Mapping then has npix inputs and (nwcs+2) outputs. */
20261                   perm = astMalloc( sizeof( int )*(size_t) ( npix + 2 ) );
20262                   if( astOK ) {
20263                      for( iax = 0; iax < npix; iax++ ) perm[ iax ] = iax;
20264                      perm[ npix ] = -1;
20265                      perm[ npix + 1 ] = -1;
20266                      con = 0.0;
20267                      tmap0 = (AstMapping *) astPermMap( npix, perm, npix + 2, perm, &con, "", status );
20268                      tmap2 = (AstMapping *) astCmpMap( tmap0, tmap1, 1, "", status );
20269                      tmap0 = astAnnul( tmap0 );
20270                      tmap1 = astAnnul( tmap1 );
20271 
20272 /* We now create the new WCS Frame with the extra RA and Dec axes. This
20273    is just a CmpFrame made up of the original WCS Frame and the new
20274    SkyFrame. */
20275                      tfrm = astGetFrame( tfs, AST__CURRENT );
20276                      tfrm0 = (AstFrame *) astCmpFrame( wcsfrm, tfrm, "", status );
20277                      tfrm = astAnnul( tfrm );
20278 
20279 /* Construct the returned FrameSet. */
20280                      ret = astFrameSet( pixfrm, "", status );
20281                      astAddFrame( ret, AST__BASE, tmap2, tfrm0 );
20282                      tmap2 = astAnnul( tmap2 );
20283                      tfrm0 = astAnnul( tfrm0 );
20284 
20285 /* Free remaining resources. */
20286                      perm = astFree( perm );
20287                   }
20288                   tfs = astAnnul( tfs );
20289                }
20290                fc = astAnnul( fc );
20291 
20292 /* If the WCS Frame does contain celestial axes we make sure that the
20293    SpecFrame uses the same reference point. */
20294             } else {
20295 
20296 /* The returned FrameSet has no extra Frames (although some attributes
20297    may be changed) so just create a new FrameSet equaivalent to the supplied
20298    FrameSet. */
20299                tfs = astFrameSet( pixfrm, "", status );
20300                astAddFrame( tfs, AST__BASE, map, wcsfrm );
20301 
20302 /* The RefRA and RefDec attributes of the SpecFrame must be set in FK5
20303    J2000. Therefore we need to know the celestial reference point in
20304    FK5 J2000. Modify the SkyFrame within the FrameSet to represent FK5
20305    J2000, noting the original sky system and equinox first so that they
20306    can be re-instated (if set) later on. */
20307                sprintf( system_attr, "System(%d)", ilon + 1 );
20308                if( astTest( tfs, system_attr ) ) {
20309                   skysys = astGetC( tfs, system_attr );
20310                } else {
20311                   skysys = NULL;
20312                }
20313                astSetC( tfs, system_attr, "FK5" );
20314                sprintf( equinox_attr, "Equinox(%d)", ilon + 1 );
20315                if( astTest( tfs, equinox_attr ) ) {
20316                   eqn = astGetC( tfs, equinox_attr );
20317                } else {
20318                   eqn = NULL;
20319                }
20320                astSetC( tfs, equinox_attr, "J2000" );
20321 
20322 /* The reference point for the celestial axes is defined by the WcsMap
20323    contained within the Mapping. Split the mapping up into a list of serial
20324    component mappings, and locate the first WcsMap in this list. The first
20325    Mapping returned by this call is the result of compounding all the
20326    Mappings up to (but not including) the WcsMap, the second returned Mapping
20327    is the (inverted) WcsMap, and the third returned Mapping is anything
20328    following the WcsMap. Only proceed if one and only one WcsMap is found. */
20329                tmap0 = astGetMapping( tfs, AST__BASE, AST__CURRENT );
20330                if( SplitMap( tmap0, astGetInvert( tmap0 ), ilon, ilat, &map1, &map2, &map3, status ) ){
20331 
20332 /* The reference point in the celestial coordinate system is found by
20333    transforming the fiducial point in native spherical co-ordinates
20334    into absolute physical coordinates using map3. */
20335                   if( GetFiducialWCS( map2, map3, ilon,  ilat, &reflon, &reflat, status ) ){
20336 
20337 /* Use reflon and reflat (which represent FK5 J2000 RA and Dec) to set
20338    the values of the SpecFrame RefRA and RefDec attributes. Format the
20339    values first so that we can use the FrameSet astSetC method, and so
20340    maintain the FrameSet integrity. */
20341                      astSetC( tfs, "RefRA", astFormat( wcsfrm, ilon, reflon ) );
20342                      astSetC( tfs, "RefDec", astFormat( wcsfrm, ilat, reflat ) );
20343 
20344 /* If succesfull, return a pointer to the FrameSet. */
20345                      if( astOK ) ret = astClone( tfs );
20346                   }
20347 
20348 /* Release resources. */
20349                   map1 = astAnnul( map1 );
20350                   map2 = astAnnul( map2 );
20351                   map3 = astAnnul( map3 );
20352 
20353 /* If no WcsMap was found, the celestial axes have no reference point and
20354    so we can retain the original spectral reference point, so just return
20355    the temporary FrameSet. */
20356                } else if( astOK ) {
20357                   ret = astClone( tfs );
20358                }
20359                tmap0 = astAnnul( tmap0 );
20360 
20361 /* Re-instate the original sky system and equinox. */
20362                if( skysys ) astSetC( tfs, system_attr, skysys );
20363                if( eqn ) astSetC( tfs, equinox_attr, eqn );
20364 
20365 /* Release resources. */
20366                tfs = astAnnul( tfs );
20367             }
20368          }
20369       }
20370    }
20371 
20372 /* Add a new current Frame into the FrameSet which increases the chances of
20373    the requested encoding being usable. The index of the original current
20374    Frame is returned, or AST__NOFRAME if no new Frame was added. */
20375    icurr = AddEncodingFrame( this, ret, encoding, method, class, status );
20376 
20377 /* If a new Frame was added, remove the original current Frame. */
20378    if( icurr != AST__NOFRAME ) astRemoveFrame( ret, icurr );
20379 
20380 /* Free resources. */
20381    if( specfrm ) specfrm = astAnnul( specfrm );
20382    if( skyfrm ) skyfrm = astAnnul( skyfrm );
20383    pixfrm = astAnnul( pixfrm );
20384    wcsfrm = astAnnul( wcsfrm );
20385    map = astAnnul( map );
20386 
20387 /* Return NULL if an error has occurred. */
20388    if( !astOK && ret ) ret = astAnnul( ret );
20389 
20390 /* Return the result. */
20391    return ret;
20392 }
20393 
MakeIndentedComment(int indent,char token,const char * comment,const char * data,char string[AST__FITSCHAN_FITSCARDLEN-FITSNAMLEN+1],int * status)20394 static void MakeIndentedComment( int indent, char token,
20395                                  const char *comment, const char *data,
20396                                  char string[ AST__FITSCHAN_FITSCARDLEN -
20397                                               FITSNAMLEN + 1 ], int *status ) {
20398 /*
20399 *  Name:
20400 *     MakeIndentedComment
20401 
20402 *  Purpose:
20403 *     Create a comment string containing an indentation bar.
20404 
20405 *  Type:
20406 *     Private function.
20407 
20408 *  Synopsis:
20409 *     #include "fitschan.h"
20410 *     void MakeIndentedComment( int indent, char token,
20411 *                               const char *comment, const char *data,
20412 *                               char string[ AST__FITSCHAN_FITSCARDLEN -
20413 *                                            FITSNAMLEN + 1 ], int *status )
20414 
20415 *  Class Membership:
20416 *     FitsChan member function.
20417 
20418 *  Description:
20419 *     This function creates a string that may be used as text in a
20420 *     FITS comment card. The string contains a textual comment
20421 *     preceded by a bar (a line of characters) whose length can be
20422 *     used to indicate a level of indentation (in the absence of any
20423 *     way of indenting FITS keywords).
20424 
20425 *  Parameters:
20426 *     indent
20427 *        The level of indentation, in characters.
20428 *     token
20429 *        The character used to form the indentation bar.
20430 *     comment
20431 *        A pointer to a constant null-terminated string containing the text
20432 *        of the comment to be included.
20433 *     data
20434 *        A pointer to a constant null-terminated string containing any
20435 *        textual data to be appended to the comment.
20436 *     string
20437 *        A character array to receive the output string.
20438 *     status
20439 *        Pointer to the inherited status variable.
20440 
20441 *  Notes:
20442 *    - The comment text that appears in the output string is formed by
20443 *   concatenating the "comment" and "data" strings.
20444 */
20445 
20446 /* Local Variables: */
20447    int i;                        /* Loop counter for input characters */
20448    int len;                      /* Number of output characters */
20449    int mxlen;                    /* Maximum length of output string */
20450 
20451 /* Check the global error status. */
20452    if ( !astOK ) return;
20453 
20454 /* Calculate the maximum number of characters that the output string
20455    can accommodate. */
20456    mxlen = AST__FITSCHAN_FITSCARDLEN - FITSNAMLEN;
20457 
20458 /* Start the string with "indent" copies of the token character, but
20459    without exceeding the output string length. */
20460    len = 0;
20461    while ( ( len < indent ) && ( len < mxlen ) ) string[ len++ ] = token;
20462 
20463 /* Pad with spaces up to the start of the comment, if necessary. */
20464    while ( len < ( FITSCOMCOL - FITSNAMLEN - 1 ) ) {
20465       string[ len++ ] = ' ';
20466    }
20467 
20468 /* Add "/ " to introduce the comment (strictly not necessary as the
20469    whole card will be a comment, but it matches the other non-comment
20470    cards). Truncate if necessary. */
20471    for ( i = 0; ( i < 2 ) && ( len < mxlen ); i++ ) {
20472       string[ len++ ] = "/ "[ i ];
20473    }
20474 
20475 /* Append the comment string, truncating it if it is too long. */
20476    for ( i = 0; comment[ i ] && ( len < mxlen ); i++ ) {
20477       string[ len++ ] = comment[ i ];
20478    }
20479 
20480 /* Append the data string, again truncating if too long. */
20481    for ( i = 0; data[ i ] && ( len < mxlen ); i++ ) {
20482       string[ len++ ] = data[ i ];
20483    }
20484 
20485 /* Terminate the output string. */
20486    string[ len ] = '\0';
20487 }
20488 
MakeIntoComment(AstFitsChan * this,const char * method,const char * class,int * status)20489 static void MakeIntoComment( AstFitsChan *this, const char *method,
20490                              const char *class, int *status ){
20491 
20492 /*
20493 *  Name:
20494 *     MakeIntoComment
20495 
20496 *  Purpose:
20497 *     Convert a card into a FITS COMMENT card.
20498 
20499 *  Type:
20500 *     Private function.
20501 
20502 *  Synopsis:
20503 *     #include "fitschan.h"
20504 
20505 *     void MakeIntoComment( AstFitsChan *this, const char *method,
20506 *                           const char *class, int *status )
20507 
20508 *  Class Membership:
20509 *     FitsChan member function.
20510 
20511 *  Description:
20512 *     This function formats the card stored just prior to the current card,
20513 *     and re-stores it as a COMMENT card. It is used (when writing an Object
20514 *     to a FitsChan) to output values that are not "set" and which are
20515 *     therefore provided for information only, and should not be read back.
20516 *     the COMMENT card has the effect of "commenting out" the value.
20517 
20518 *  Parameters:
20519 *     this
20520 *        Pointer to the FitsChan.
20521 *     method
20522 *        Calling method.
20523 *     class
20524 *        Object class.
20525 *     status
20526 *        Pointer to the inherited status variable.
20527 */
20528 
20529 /* Local Variables: */
20530    char card[ AST__FITSCHAN_FITSCARDLEN + 1 ]; /* Character buffer for FITS card data */
20531 
20532 /* Check the global error status. */
20533    if ( !astOK ) return;
20534 
20535 /* Move the current card backwards by one card. */
20536    MoveCard( this, -1, method, class, status );
20537 
20538 /* Format the new current card. */
20539    FormatCard( this, card, method, status );
20540 
20541 /* Write the resulting string to the FitsChan as the contents of a COMMENT
20542    card, overwriting the existing card. The current card is incremented
20543    by this call so that it refers to the same card as on entry. */
20544    astSetFitsCom( this, "COMMENT", card, 1 );
20545 }
20546 
MakeIntWorld(AstMapping * cmap,AstFrame * fr,int * wperm,char s,FitsStore * store,double * dim,const char * method,const char * class,int * status)20547 static int MakeIntWorld( AstMapping *cmap, AstFrame *fr, int *wperm, char s,
20548                          FitsStore *store, double *dim,
20549                          const char *method, const char *class, int *status ){
20550 /*
20551 *  Name:
20552 *     MakeIntWorld
20553 
20554 *  Purpose:
20555 *     Create FITS header values which map grid into intermediate world
20556 *     coords.
20557 
20558 *  Type:
20559 *     Private function.
20560 
20561 *  Synopsis:
20562 *     #include "fitschan.h"
20563 *     int MakeIntWorld( AstMapping *cmap, AstFrame *fr, int *wperm, char s,
20564 *                       FitsStore *store, double *dim,
20565 *                       const char *method, const char *class, int *status )
20566 
20567 *  Class Membership:
20568 *     FitsChan member function.
20569 
20570 *  Description:
20571 *     This function adds values to the supplied FitsStore which describe
20572 *     the transformation from grid (pixel) coords to intermediate world
20573 *     coords. The values added to the FitsStore correspond to the CRPIXj,
20574 *     PCi_j, CDELTi and WCSAXES keywords, and are determined by examining the
20575 *     suppliedMapping, which must be linear with an optional shift of
20576 *     origin (otherwise a value of zero is returned).
20577 *
20578 *     Much of the complication in the algorithm arises from the need to
20579 *     support cases where the supplied Mapping has more outputs than
20580 *     inputs. In these case we add some "degenerate" axes to the grid
20581 *     coord system, choosing their unit vectors to be orthogonal to all
20582 *     the other grid axes. It is assumed that degenerate axes will never
20583 *     be used to find a position other than at the axis value of 1.0.
20584 *
20585 *     NOTE, appropriate values for CRVAL keywords should have been stored
20586 *     in the FitsStore before calling this function (since this function may
20587 *     modify them).
20588 
20589 *  Parameters:
20590 *     cmap
20591 *        A pointer to a Mapping which transforms grid coordinates into
20592 *        intermediate world coordinates. The number of outputs must be
20593 *        greater than or equal to the number of inputs.
20594 *     fr
20595 *        Pointer to the final WCS coordinate Frame.
20596 *     wperm
20597 *        Pointer to an array of integers with one element for each axis of
20598 *        the "fr" Frame. Each element holds the zero-based index of the
20599 *        FITS-WCS axis (i.e. the value of "i" in the keyword names "CTYPEi",
20600 *        "CDi_j", etc) which describes the Frame axis.
20601 *     s
20602 *        The co-ordinate version character. A space means the primary
20603 *        axis descriptions. Otherwise the supplied character should be
20604 *        an upper case alphabetical character ('A' to 'Z').
20605 *     store
20606 *        A pointer to the FitsStore into which the calculated CRPIX and
20607 *        CDi_j values are to be put.
20608 *     dim
20609 *        An array holding the image dimensions in pixels. AST__BAD can be
20610 *        supplied for any unknwon dimensions.
20611 *     method
20612 *        Pointer to a string holding the name of the calling method.
20613 *        This is only for use in constructing error messages.
20614 *     class
20615 *        Pointer to a string holding the name of the supplied object class.
20616 *        This is only for use in constructing error messages.
20617 *     status
20618 *        Pointer to the inherited status variable.
20619 
20620 *  Returned Value:
20621 *     A value of 1 is returned if the CRPIX and CDi_j values are
20622 *     succesfully calculated. Zero is returned otherwise.
20623 
20624 *  Notes:
20625 *     -  Zero is returned if an error occurs.
20626 */
20627 
20628 /* Local Variables: */
20629    AstFrame *pfrm;
20630    AstFrame *sfrm;
20631    AstMapping *map;
20632    AstPointSet *psetw;
20633    AstPointSet *psetg;
20634    double **fullmat;
20635    double **partmat;
20636    double **ptrg;
20637    double **ptrw;
20638    double *c;
20639    double *cdelt;
20640    double *cdmat;
20641    double *colvec;
20642    double *d;
20643    double *g;
20644    double *g0;
20645    double *m;
20646    double *mat;
20647    double *tol;
20648    double *w0;
20649    double *y;
20650    double cd;
20651    double crp;
20652    double crv;
20653    double cv;
20654    double det;
20655    double err;
20656    double k;
20657    double mxcv;
20658    double skydiag1;
20659    double skydiag0;
20660    double val;
20661    int *iw;
20662    int *lin;
20663    int *pperm;
20664    int *skycol;
20665    int i;
20666    int ii;
20667    int j;
20668    int jax;
20669    int jj;
20670    int nin;
20671    int nout;
20672    int nwcs;
20673    int paxis;
20674    int ret;
20675    int sing;
20676    int skycol0;
20677    int skycol1;
20678 
20679 /* Initialise */
20680    ret = 0;
20681 
20682 /* Check the inherited status. */
20683    if( !astOK ) return ret;
20684 
20685 /* Simplify the supplied Mapping to reduce rounding errors when
20686    transforming points. */
20687    map = astSimplify( cmap );
20688 
20689 /* Get the number of inputs and outputs for the Mapping. Return if the
20690    number of outputs is smaller than the number of inputs. */
20691    nin = astGetNin( map );
20692    nout = astGetNout( map );
20693    if( nout < nin ) return ret;
20694 
20695 /* Note the number of final World Coordinate axes (not necessarily the
20696    same as "nout", since some intermediate axes may be discarded by a
20697    later PermMap. */
20698    nwcs = astGetNaxes( fr );
20699 
20700 /* Allocate work space. */
20701    g = astMalloc( sizeof(double)*(size_t) nin );
20702    g0 = astMalloc( sizeof(double)*(size_t) nin );
20703    w0 = astMalloc( sizeof(double)*(size_t) nout );
20704    tol = astMalloc( sizeof(double)*(size_t) nout );
20705    partmat = astMalloc( sizeof(double *)*(size_t) nout );
20706    lin = astMalloc( sizeof(int)*(size_t) nout );
20707    pperm = astMalloc( sizeof(int)*(size_t) nout );
20708    skycol = astMalloc( sizeof(int)*(size_t) nout );
20709    cdmat = astMalloc( sizeof(double)*(size_t) (nout*nout) );
20710    cdelt = astMalloc( sizeof(double)*(size_t) nout );
20711 
20712 /* For safety, initialise all other pointers. */
20713    if( partmat ) for( j = 0; j < nout; j++ ) partmat[ j ] = NULL;
20714    fullmat = NULL;
20715 
20716 /* Create a PointSet to hold an input (grid) position for each axis, plus
20717    an extra one. Create two other PointSets to hold corresponding
20718    output (IWC) coordinates. */
20719    psetg = astPointSet( nin + 1, nin, "", status );
20720    ptrg = astGetPoints( psetg );
20721    psetw = astPointSet( nin + 1, nout, "", status );
20722    ptrw = astGetPoints( psetw );
20723 
20724 /* Check the pointers can be used safely. */
20725    if( astOK ) {
20726 
20727 /* Assume success. */
20728       ret = 1;
20729 
20730 /* The next section finds a 'root' grid position for which the
20731    corresponding IWC coordinates are all good. It also finds these IWC
20732    coordinates, together with the IWC coordinates of "nin" points which
20733    are a unit distance away from the root grid position along each
20734    grid axis. It also finds an estimate of the rounding error in each
20735    Mapping output.
20736    ================================================================= */
20737       ret = FindBasisVectors( map, nin, nout, dim, psetg, psetw, status );
20738 
20739 /* Save the grid root position in "g0". */
20740       for( j = 0; j < nin; j++ ) g0[ j ] = ptrg[ j ][ 0 ];
20741 
20742 /* Save the transformed root position in "w0". This is the grid root
20743    position represented as a vector within the Intermediate World
20744    Coordinate system. */
20745       for( j = 0; j < nout; j++ ) {
20746          w0[ j ] = ptrw[ j ][ 0 ];
20747 
20748 /* Find the tolerance for positions on the j'th IWC axis. This is one
20749    hundredth of the largest change in the j'th IWC axis value caused by
20750    moving out 1 pixel along any grid axis. */
20751          tol[ j ] = 0.0;
20752          for( i = 0; i < nin; i++ ) {
20753             err = fabs( ptrw[ j ][ i + 1 ] - w0[ j ] );
20754             if( err > tol[ j ] ) tol[ j ] = err;
20755          }
20756          tol[ j ] *= 0.01;
20757 
20758 /* If the tolerance is zero (e.g. as is produced for degenerate axes),
20759    then use a tolerance equal to a very small fraction of hte degenerate
20760    axis value. If the axis value is zero use a fixed small value. */
20761          if( tol[ j ] == 0.0 ) tol[ j ] = w0[ j ]*DBL_EPSILON*1.0E5;
20762          if( tol[ j ] == 0.0 ) tol[ j ] = sqrt( DBL_MIN );
20763       }
20764 
20765 /* The next section finds the CD matrix.
20766    ===================================== */
20767 
20768 /* Initialise the CD matrix elements to "all missing". */
20769       for( i = 0; i < nout*nout; i++ ) cdmat[ i ] = AST__BAD;
20770 
20771 /* The elements of column "j" of the CD matrix form a vector (in Intermediate
20772    World Coords) which corresponds to a unit vector along grid axis "j".
20773    We now find these vectors for all the grid axes represented by the
20774    inputs to the supplied Mapping. */
20775       for( i = 0; i < nin && ret; i++ ) {
20776 
20777 /* Form a unit vector along the current input axis. */
20778          for( ii = 0; ii < nin; ii++ ) g[ ii ] = 0.0;
20779          g[ i ] = 1.0;
20780 
20781 /* Fit a straight line (within IWC) to the current input axis of the Mapping.
20782    The IWC vector corresponding to a unit vector along the current input axis
20783    is returned if the Mapping is linear. A NULL pointer is returned if the
20784    Mapping is not linear. */
20785          partmat[ i ] = FitLine( map, g, g0, w0, dim[ i ], tol, status );
20786 
20787 /* If unsuccesful, indicate failure and break out of the loop. */
20788          if( !partmat[ i ] ) {
20789             ret = 0;
20790             break;
20791          }
20792       }
20793 
20794 /* If the number of outputs for "map" is larger than the number of inputs,
20795    then we will still be missing some column vectors for the CDi_j matrix
20796    (which has to be square). We invent these such that the they are
20797    orthogonal to all the other column vectors. Only do this if the
20798    Mapping is linear. */
20799       if( ret ) {
20800          fullmat = OrthVectorSet( nout, nin, partmat, status );
20801          if( !fullmat ) ret = 0;
20802       }
20803 
20804 /* Check everything is OK. */
20805       if( ret ) {
20806 
20807 /* Check that the full matrix is invertable, and if not, see if there is
20808    any way to make it invertable. */
20809          MakeInvertable( fullmat, nout, dim, status );
20810 
20811 /* Set up an array holding index of the Mapping output corresponding to
20812    each IWC axis (the inverse of "wperm"). Also look for matching pairs of
20813    celestial WCS axes. For the first such pair, note the corresponding
20814    column indices and the diagonal element of the matrix which gives the
20815    scaling for the axis (taking account of the permutation of WCS axes).
20816    Also note if the Mapping from intermediate world coords to final world
20817    coords is linear for each axis (this is assumed to be the case if the
20818    axis is part of a simple Frame). */
20819          sfrm = NULL;
20820          skydiag0 = AST__BAD;
20821          skydiag1 = AST__BAD;
20822          skycol0 = -1;
20823          skycol1 = -1;
20824          for( i = 0; i < nout; i++ ) {
20825             pperm[ wperm[ i ] ] = i;
20826             astPrimaryFrame( fr, i, &pfrm, &paxis );
20827             if( astIsASkyFrame( pfrm ) ) {
20828                skycol[ wperm[ i ] ] = paxis + 1;
20829                lin[ i ] = 0;
20830                if( !sfrm ) {
20831                   sfrm = astClone( pfrm );
20832                   skycol0 = wperm[ i ];
20833                   skydiag0 = fullmat[ skycol0 ][ i ];
20834                } else if( sfrm == pfrm ) {
20835                   skycol1 = wperm[ i ];
20836                   skydiag1 = fullmat[ skycol1 ][ i ];
20837                }
20838             } else {
20839                skycol[ wperm[ i ] ] = 0;
20840                lin[ i ] = !strcmp( astGetClass( pfrm ), "Frame" );
20841             }
20842             pfrm = astAnnul( pfrm );
20843          }
20844          if( sfrm ) sfrm = astAnnul( sfrm );
20845 
20846 /* We now have the complete CDi_j matrix. Now to find the CRPIX values.
20847    These are the grid coords of the reference point (which corresponds to
20848    the origin of Intermediate World Coords). The "w0" array currently holds
20849    the position of the root position, as a position within IWC, and the
20850    "g0" array holds the corresponding position in grid coordinates. We
20851    also have IWC vectors which correspond to unit vectors on each grid
20852    axis. The CRPIX values are defined by the matrix equation
20853         w0 = fullmat*( g0 - crpix )
20854    The "g0" array only contains "nin" values. If nout>nin, then the
20855    missing g0 values will be assumed to be zero when we come to find the
20856    CRPIX values below.
20857    We use palDmat to solve this system of simultaneous equations to get
20858    crpix. The "y" array initially holds "w0" but is over-written to hold
20859    "g0 - crpix". */
20860          mat = astMalloc( sizeof( double )*(size_t)( nout*nout ) );
20861          y = astMalloc( sizeof( double )*(size_t) nout );
20862          iw = astMalloc( sizeof( int )*(size_t) nout );
20863          if( astOK ) {
20864             m = mat;
20865             for( i = 0; i < nout; i++ ) {
20866                for( j = 0; j < nout; j++ ) *(m++) = fullmat[ j ][ i ];
20867                y[ i ] = w0[ i ];
20868             }
20869             palDmat( nout, mat, y, &det, &sing, iw );
20870          }
20871          mat = astFree( mat );
20872          iw = astFree( iw );
20873 
20874 /* Loop round all axes, storing the column vector pointer. */
20875          for( j = 0; j < nout; j++ ) {
20876             colvec = fullmat[ j ];
20877 
20878 /* Get the CRPIX values from the "y" vector created above by palDmat.
20879    First deal with axes for which there are Mapping inputs. */
20880             if( j < nin ) {
20881                crp = g0[ j ] - y[ j ];
20882 
20883 /* If this is a grid axis which has been created to represent a "missing"
20884    input to the mapping, we need to add on 1.0 to the crpix value found
20885    above. This is because the "w0" vector corresponds to a value of zero
20886    on any missing axes, but the FITS grid value for any missing axes is
20887    1.0. */
20888             } else {
20889                crp = 1.0 - y[ j ];
20890             }
20891 
20892 /* Store the CD and CRPIX values for axes which correspond to inputs
20893    of "map". The CD matrix elements are stored in an array and are
20894    converted later to the corresponding PC and CDELT values. */
20895             if( j < nin || crp == 0.0 ) {
20896                for( i = 0; i < nout; i++ ) {
20897                   cdmat[ wperm[ i ]*nout+j ] = colvec[ i ] ;
20898                }
20899                SetItem( &(store->crpix), 0, j, s, crp, status );
20900 
20901 /* The length of the unit vector along any "degenerate" axes was fixed
20902    arbitrarily at 1.0 by the call to OrthVectorSet. We can probably
20903    choose a more appropriate vector length. The choice shouldn't make any
20904    difference to the transformation, but an appropriate value will look
20905    more natural to human readers. */
20906             } else {
20907 
20908 /* First, try to arrange for longitude/latitude axis pairs to have the same
20909    scale. Do we have a matching pair of celestial axes? */
20910                k = AST__BAD;
20911                if( skydiag0 != AST__BAD && skydiag1 != AST__BAD ) {
20912 
20913 /* Is the current column the one which corresponds to the first celestial
20914    axis, and does the other sky column correspond to a Mapping input? */
20915                   if( skycol0 == j && skycol1 < nin ) {
20916 
20917 /* If so, scale this column so that its diagonal element is the negative
20918    of the diagonal element of the other axis. This is on the assumption that
20919    the scales on the two axes should be equal, and that longitude increases
20920    east whilst latitude increases north, and that the CD matrix does not
20921    introduce an axis permutation. */
20922                      if( skydiag0 != 0.0 ) k = -skydiag1/skydiag0;
20923 
20924 /* Now see if the current column the one which corresponds to the second
20925    celestial axis. Do the same as above. */
20926                   } else if( skycol1 == j && skycol0 < nin ) {
20927                      if( skydiag1 != 0.0 ) k = -skydiag0/skydiag1;
20928 
20929 /* If neither of the above conditions was met, assume a diagonal element
20930    value of 1.0 degrees for latitude axes, and -1.0 degrees for longitude
20931    axes. */
20932                   }
20933                }
20934 
20935 /* If this failed, the next choice is to arrange for diagonally opposite
20936    elements to be equal and opposite in value. Look for the element of the
20937    column which has the largest diagonally opposite element, and choose a
20938    scaling factor which makes this column element equal to the negative value
20939    of its diagonally opposite element. Be careful to take axis permutations
20940    into account when finding the value of the diagonal element. */
20941                if( k == AST__BAD ) {
20942                   mxcv = 0.0;
20943                   ii = pperm[ j ];
20944                   for( i = 0; i < nout; i++ ) {
20945                      jj = wperm[ i ];
20946                      if( jj < nin ) {
20947                         cv = fullmat[ jj ][ ii ];
20948                         if( !EQUAL( colvec[ i ], 0.0 ) && fabs( cv ) > mxcv ) {
20949                            mxcv = fabs( cv );
20950                            k = -cv/colvec[ i ];
20951                         }
20952                      }
20953                   }
20954                }
20955 
20956 /* If still no scaling factor is available, use a scaling factor which
20957    produces a diagonal element of 1.0 degree if the corresponding row is a
20958    sky latitude axis, -1.0 degree of sky longitude axes, and 1.0 for other
20959    axes. */
20960                if( k == AST__BAD && colvec[ pperm[ j ] ] != 0.0 ) {
20961                   if( skycol[ j ] ) {
20962                      k = AST__DD2R/colvec[ pperm[ j ] ];
20963                      if( skycol[ j ] == 1 ) k = -k;
20964                   } else {
20965                      k = 1.0/colvec[ pperm[ j ] ];
20966                   }
20967                }
20968 
20969 /* If we still do not have a scaling, use 1.0 (no scaling). */
20970                if( k == AST__BAD ) k = 1.0;
20971 
20972 /* Now scale and store the column elements. */
20973                for( i = 0; i < nout; i++ ) {
20974                   cdmat[ wperm[ i ]*nout+j ] = k*colvec[ i ];
20975                }
20976 
20977 /* Find the corresponding modified CRPIX value and store it. */
20978                crp = 1.0 + ( crp - 1.0 )/k;
20979                SetItem( &(store->crpix), 0, j, s, crp, status );
20980             }
20981 
20982 /* Free resources */
20983             if( pfrm ) pfrm = astAnnul( pfrm );
20984          }
20985 
20986 /* Any "degenerate" axes added in the above process for which the
20987    intermediate->world mapping is linear, and which depend only on one
20988    pixel axis, can be adjusted so that the reference point is at grid
20989    coord 1.0. */
20990          for( i = 0; i < nout; i++ ) {
20991             if( lin[ i ] ) {
20992 
20993 /* Check only one pixel axis contributes to this intermediate world axis
20994    and find which one it is. */
20995                jax = -1;
20996                for( j = 0; j < nout; j++ ) {
20997                   if( !EQUAL( fullmat[ j ][ i ], 0.0 ) ) {
20998                      if( jax == -1 ) {
20999                         jax = j;
21000                      } else {
21001                         jax = -1;
21002                         break;
21003                      }
21004                   }
21005                }
21006 
21007 /* We only adjust values for "degenerate" axes. */
21008                if( jax >= nin ) {
21009 
21010 /* Check that this pixel axis only contributes to the single world axis
21011    currently being considered. */
21012                   for( ii = 0; ii < nout; ii++ ) {
21013                      if( ii != i ) {
21014                         if( !EQUAL( fullmat[ jax ][ ii ], 0.0 ) ) {
21015                            jax = -1;
21016                            break;
21017                         }
21018                      }
21019                   }
21020                   if( jax != -1 ) {
21021 
21022 /* Get the original CRVAL, CRPIX and CD values. Check they are defined.*/
21023                      crv = GetItem( &(store->crval), wperm[ i ], 0, s, NULL,
21024                                     method, class, status );
21025                      cd = cdmat[ wperm[ i ]*nout + jax ];
21026                      crp = GetItem( &(store->crpix), 0, jax, s, NULL, method, class, status );
21027                      if( crv != AST__BAD && crp != AST__BAD &&
21028                          cd != AST__BAD ) {
21029 
21030 /* Modify the CRPIX to be 1.0 and modify the CRVAL value accordingly. */
21031                         SetItem( &(store->crpix), 0, jax, s, 1.0, status );
21032                         SetItem( &(store->crval), wperm[ i ], 0, s,
21033                                  cd*( 1.0 - crp ) + crv, status );
21034                      }
21035                   }
21036                }
21037             }
21038          }
21039 
21040 /* Finally, if there are fewer input axes than output axes, put a value for
21041    the WCSAXES keyword into the store. */
21042          if( nin < nwcs ) SetItem( &(store->wcsaxes), 0, 0, s, nwcs, status );
21043 
21044 /* Release resources. */
21045          y = astFree( y );
21046       }
21047 
21048 /* Produce and store PC and CDELT values from the above CD matrix */
21049       SplitMat( nout, cdmat, cdelt, status );
21050       c = cdmat;
21051       d = cdelt;
21052       for( i = 0; i < nout; i++ ){
21053          for( j = 0; j < nout; j++ ){
21054             val = *(c++);
21055             if( i == j ){
21056                if( EQUAL( val, 1.0 ) ) val = AST__BAD;
21057             } else {
21058                if( EQUAL( val, 0.0 ) ) val = AST__BAD;
21059             }
21060             if( val != AST__BAD ) SetItem( &(store->pc), i, j, s, val, status );
21061          }
21062          SetItem( &(store->cdelt), i, 0, s, *(d++), status );
21063       }
21064    }
21065 
21066 /* Annul pointsets. */
21067    psetg = astAnnul( psetg );
21068    psetw = astAnnul( psetw );
21069 
21070 /* Free other resources*/
21071    map = astAnnul( map );
21072    if( fullmat ) for( j = 0; j < nout; j++ ) fullmat[ j ] = astFree( fullmat[ j ] );
21073    if( partmat ) for( j = 0; j < nout; j++ ) partmat[ j ] = astFree( partmat[ j ] );
21074    fullmat = astFree( fullmat );
21075    partmat = astFree( partmat );
21076    cdmat = astFree( cdmat );
21077    cdelt = astFree( cdelt );
21078    g = astFree( g );
21079    g0 = astFree( g0 );
21080    w0 = astFree( w0 );
21081    tol = astFree( tol );
21082    lin = astFree( lin );
21083    skycol = astFree( skycol );
21084    pperm = astFree( pperm );
21085 
21086 /* If an error has occurred, return zero. */
21087    if( !astOK ) ret = 0;
21088 
21089 /* Return the answer. */
21090    return ret;
21091 }
21092 
MakeInvertable(double ** fullmat,int n,double * dim,int * status)21093 static void MakeInvertable( double **fullmat, int n, double *dim, int *status ){
21094 /*
21095 *  Name:
21096 *     MakeInvertable
21097 
21098 *  Purpose:
21099 *     Modify a supplied square CD matrix if possible to make it invertable.
21100 
21101 *  Type:
21102 *     Private function.
21103 
21104 *  Synopsis:
21105 *     void MakeInvertable( double **fullmat, int n, double *dim, int *status )
21106 
21107 *  Class Membership:
21108 *     FitsChan
21109 
21110 *  Description:
21111 *     A search is made for matrix inputs that have no effect on any
21112 *     matrix outputs. if any such matrix inputs are associated with
21113 *     degenerate pixel axes (i.e. pixel axes that span only a single
21114 *     pixel), then the matrix input should always have the value zero and
21115 *     so the corresponding diagonal element of the matrix can be set to
21116 *     1.0 without changing and of the outputs.
21117 
21118 *  Parameters:
21119 *     fullmat
21120 *        A pointer to an array with "n" elements corresponding to the n
21121 *        inputs of the matrix, each element being a pointer to an array
21122 *        with "n" elements corresponding to the n outputs of the matrix.
21123 *     n
21124 *        The number of inputs and outputs for the square matrix.
21125 *     dim
21126 *        Pointer to an array of "n" input (i.e. pixel) axis dimensions.
21127 *        Individual elements will be AST__BAD if dimensions are not known.
21128 *     status
21129 *        Pointer to the inherited status variable.
21130 */
21131 
21132 /* Local Variables: */
21133    int i;          /* Input index */
21134    int j;          /* Output index */
21135    int unused;     /* Does the current input have no effect on any output? */
21136 
21137 /* Check inherited status */
21138    if( !astOK ) return;
21139 
21140 /* Look for any inputs that have no effect on any of the outputs. If such
21141    an input is associated with a degenerate grid axis (i.e. a grid axis
21142    with a dimension of 1.0), then the input value will always be zero and
21143    so the corresponding diagonal element of the matrix can eb set to 1.0
21144    without affecting the output value (which will always be zero since
21145    zero times anything is zero). Loop over all inputs. */
21146    for( i = 0; i < n; i++ ) {
21147 
21148 /* Assume this input has no effect on any output. */
21149       unused = 1;
21150 
21151 /* Loop over all outputs. */
21152       for( j = 0; j < n; j++ ) {
21153 
21154 /* If the corresponding matrix term is non-zero, the the input will have
21155    an effect on the output, so set the unused flag false and break out of
21156    the output loop. */
21157          if( fullmat[ i ][ j ] != 0.0 ) {
21158             unused = 0;
21159             break;
21160          }
21161       }
21162 
21163 /* If the input is unused, and it is associated with a degenerate pixel
21164    axis, we can set the corresponding diagonal element of the matrix to
21165    1.0. */
21166       if( unused && dim[ i ] == 1.0 ) fullmat[ i ][ i ] = 1.0;
21167    }
21168 }
21169 #if defined(THREAD_SAFE)
21170 
ManageLock(AstObject * this_object,int mode,int extra,AstObject ** fail,int * status)21171 static int ManageLock( AstObject *this_object, int mode, int extra,
21172                        AstObject **fail, int *status ) {
21173 /*
21174 *  Name:
21175 *     ManageLock
21176 
21177 *  Purpose:
21178 *     Manage the thread lock on an Object.
21179 
21180 *  Type:
21181 *     Private function.
21182 
21183 *  Synopsis:
21184 *     #include "object.h"
21185 *     AstObject *ManageLock( AstObject *this, int mode, int extra,
21186 *                            AstObject **fail, int *status )
21187 
21188 *  Class Membership:
21189 *     FitsChan member function (over-rides the astManageLock protected
21190 *     method inherited from the parent class).
21191 
21192 *  Description:
21193 *     This function manages the thread lock on the supplied Object. The
21194 *     lock can be locked, unlocked or checked by this function as
21195 *     deteremined by parameter "mode". See astLock for details of the way
21196 *     these locks are used.
21197 
21198 *  Parameters:
21199 *     this
21200 *        Pointer to the Object.
21201 *     mode
21202 
21203 *        An integer flag indicating what the function should do:
21204 *
21205 *        AST__LOCK: Lock the Object for exclusive use by the calling
21206 *        thread. The "extra" value indicates what should be done if the
21207 *        Object is already locked (wait or report an error - see astLock).
21208 *
21209 *        AST__UNLOCK: Unlock the Object for use by other threads.
21210 *
21211 *        AST__CHECKLOCK: Check that the object is locked for use by the
21212 *        calling thread (report an error if not).
21213 *     extra
21214 *        Extra mode-specific information.
21215 *     fail
21216 *        If a non-zero function value is returned, a pointer to the
21217 *        Object that caused the failure is returned at "*fail". This may
21218 *        be "this" or it may be an Object contained within "this". Note,
21219 *        the Object's reference count is not incremented, and so the
21220 *        returned pointer should not be annulled. A NULL pointer is
21221 *        returned if this function returns a value of zero.
21222 *     status
21223 *        Pointer to the inherited status variable.
21224 
21225 *  Returned Value:
21226 
21227 *    A local status value:
21228 *        0 - Success
21229 *        1 - Could not lock or unlock the object because it was already
21230 *            locked by another thread.
21231 *        2 - Failed to lock a POSIX mutex
21232 *        3 - Failed to unlock a POSIX mutex
21233 *        4 - Bad "mode" value supplied.
21234 
21235 *  Notes:
21236 *     - This function attempts to execute even if an error has already
21237 *     occurred.
21238 */
21239 
21240 /* Local Variables: */
21241    AstFitsChan *this;         /* Pointer to FitsChan structure */
21242    int result;                /* Returned status value */
21243 
21244 /* Initialise */
21245    result = 0;
21246 
21247 /* Check the supplied pointer is not NUL. */
21248    if( ! this_object ) return result;
21249 
21250 /* Obtain a pointers to the FitsChan structure. */
21251    this = (AstFitsChan *) this_object;
21252 
21253 /* Invoke the ManageLock method inherited from the parent class. */
21254    if( !result ) result = (*parent_managelock)( this_object, mode, extra,
21255                                                 fail, status );
21256 
21257 /* Invoke the astManageLock method on any Objects contained within
21258    the supplied Object. */
21259    if( !result ) result = astManageLock( this->keyseq, mode, extra, fail );
21260    if( !result ) result = astManageLock( this->keywords, mode, extra, fail );
21261    return result;
21262 }
21263 #endif
21264 
Match(const char * test,const char * temp,int maxfld,int * fields,int * nfld,const char * method,const char * class,int * status)21265 static int Match( const char *test, const char *temp, int maxfld, int *fields,
21266                   int *nfld, const char *method, const char *class, int *status ){
21267 /*
21268 *  Name:
21269 *     Match
21270 
21271 *  Purpose:
21272 *     Sees if a test keyword name matches a template.
21273 
21274 *  Type:
21275 *     Private function.
21276 
21277 *  Synopsis:
21278 *     #include "fitschan.h"
21279 *     int Match( const char *test, const char *temp, int maxfld, int *fields,
21280 *                int *nfld, const char *method, const char *class, int *status )
21281 
21282 *  Class Membership:
21283 *     FitsChan member function.
21284 
21285 *  Description:
21286 *     All characters in the template other than "%" (and the field width
21287 *     and type specifiers which follow a "%") must be matched by an
21288 *     identical character (ignoring case) in the test string. If a "%" occurs
21289 *     in the template, then the next character in the template should be a
21290 *     single digit specifying a field width. If it is zero, then the test
21291 *     string may contain zero or more matching characters. Otherwise,
21292 *     the test string must contain exactly the specified number of matching
21293 *     characters (i.e. 1 to 9). The field width digit may be omitted, in
21294 *     which case the test string must contain one or more matching
21295 *     characters. The next character in the template specifies the type of
21296 *     matching characters and must be one of "d", "c" or "f". Decimal digits
21297 *     are matched by "d", all upper (but not lower) case alphabetical
21298 *     characters are matched by "c", and all characters which are legal within
21299 *     a FITS keyword (i.e. upper case letters, digits, underscores and
21300 *     hyphens) are matched by "f".
21301 
21302 *  Parameters:
21303 *     test
21304 *        Pointer to a null terminated string holding the keyword name to
21305 *        be tested.
21306 *     temp
21307 *        Pointer to a null terminated string holding the template.
21308 *     maxfld
21309 *        The maximum number of integer field values which should be
21310 *        returned in "fields".
21311 *     fields
21312 *        A pointer to an array of at least "maxfld" integers. This is
21313 *        returned holding the values of any integer fields specified
21314 *        in the template. The values are extracted from the test string,
21315 *        and stored in the order they appear in the template string.
21316 *     nfld
21317 *        Pointer to a location at which is returned the total number of
21318 *        integer fields in the test string. This may be more than the
21319 *        number returned in "fields" if "maxfld" is smaller than "*nfld".
21320 *     method
21321 *        Pointer to a string holding the name of the calling method.
21322 *        This is only for use in constructing error messages.
21323 *     class
21324 *        Pointer to a string holding the name of the supplied object class.
21325 *        This is only for use in constructing error messages.
21326 *     status
21327 *        Pointer to the inherited status variable.
21328 
21329 *  Returned Value:
21330 *     Zero is returned if the test string does not match the template
21331 *     string, and one is returned if it does.
21332 */
21333 
21334 /* Local Variables: */
21335    astDECLARE_GLOBALS     /* Declare the thread specific global data */
21336    char type;             /* Field type specifier */
21337    const char *a;         /* Pointer to next test character */
21338    const char *b;         /* Pointer to next template character */
21339    int extend;            /* Can the width of the first field be extended? */
21340    int i;                 /* Field index */
21341    int match;             /* Does "test" match "temp"? */
21342    int nfret;             /* No. of fields returned */
21343    int tmp;               /* Field value */
21344 
21345 /* Check global status. */
21346    if( !astOK ) return 0;
21347 
21348 /* Get a pointer to the structure holding thread-specific global data. */
21349    astGET_GLOBALS(NULL);
21350 
21351 /* On the first entry to this function, indicate that no integer fields
21352    have yet been returned, and save a pointer to the start of the template
21353    string. */
21354    if( !match_nentry ) {
21355       *nfld = 0;
21356       match_template = temp;
21357    }
21358 
21359 /* Increment the number of entries into this function. */
21360    match_nentry++;
21361 
21362 /* Initialise pointers to the start of each string. */
21363    a = test;
21364    b = temp;
21365 
21366 /* Initialise the returned flag to indicate that the two strings do not
21367    match. */
21368    match = 0;
21369 
21370 /* Check that the initial part of the test string can match the first
21371    field in the template. */
21372    if( MatchFront( a, b, &type, &extend, &match_na, &match_nb, method, class, match_template, status ) ){
21373 
21374 /* If it does, increment the pointers to skip over the characters
21375    used up in the comparison. */
21376       a += match_na;
21377       b += match_nb;
21378 
21379 /* If the ends of both strings have been reached, they match. */
21380       if( *a == 0 && *b == 0 ){
21381          match = 1;
21382 
21383 /* Otherwise, if the end of the template has been reached but there are
21384    still characters to be read from the test string, we could still have
21385    a match if all the remaining test characters match an extandable field. */
21386       } else if( *b == 0 && *a != 0 && extend ){
21387 
21388 /* Loop until all the matching characters have been read from the end of
21389    the test string. */
21390          while( *a != 0 && MatchChar( *a, type, method, class, match_template, status ) ) a++;
21391 
21392 /* If we reached the end of the test string, we have a match. */
21393          if( *a == 0 ) match = 1;
21394 
21395 /* Otherwise, we need to carry on checking the remaining fields. */
21396       } else {
21397 
21398 /* Call this function recursively to see if the remainder of the
21399    strings match. */
21400          if( Match( a, b, maxfld, fields, nfld, method, class, status ) ){
21401             match = 1;
21402 
21403 /* If the remainder of the strings do not match, we may be able to make
21404    them match by using up some extra test characters on the first field.
21405    This can only be done if the first field has an unspecified field width,
21406    and if the next test character if of a type which matches the first
21407    field in the template. */
21408          } else if( extend ){
21409 
21410 /* Loop until all the suitable characters have been read from the
21411    test string. Break out of the loop early if we find a field width
21412    which results in the whole string matching. */
21413             while( MatchChar( *a, type, method, class, match_template, status ) ){
21414                a++;
21415                if( Match( a, b, maxfld, fields, nfld, method, class, status ) ){
21416                   match = 1;
21417                   break;
21418                }
21419             }
21420          }
21421       }
21422    }
21423 
21424 /* If the strings match and the leading field is an integer, decode
21425    the field and store it in the supplied array (if there is room). */
21426    if( match && type == 'd' && a > test ){
21427       if( *nfld < maxfld ){
21428          sprintf( match_fmt, "%%%dd", (int) ( a - test ) );
21429          astSscanf( test, match_fmt, fields + *nfld );
21430       }
21431       (*nfld)++;
21432    }
21433 
21434 /* Decrement the number of entries into this function. */
21435    match_nentry--;
21436 
21437 /* If we are leaving this function for the last time, reverse the
21438    order of the returned integer fields so that they are returned
21439    in the same order that they occur in the template. */
21440    if( !match_nentry ){
21441       nfret = ( *nfld < maxfld ) ? (*nfld) : maxfld;
21442       match_pa = fields;
21443       match_pb = fields + nfret - 1;
21444       for( i = 0; i < nfret/2; i++ ){
21445          tmp = *match_pa;
21446          *(match_pa++) = *match_pb;
21447          *(match_pb--) = tmp;
21448       }
21449    }
21450 
21451 /* Return the result. */
21452    return match;
21453 }
21454 
MatchChar(char test,char type,const char * method,const char * class,const char * template,int * status)21455 static int MatchChar( char test, char type, const char *method,
21456                       const char *class, const char *template, int *status ){
21457 /*
21458 *  Name:
21459 *     MatchChar
21460 
21461 *  Purpose:
21462 *     See if a given character is of a specified type.
21463 
21464 *  Type:
21465 *     Private function.
21466 
21467 *  Synopsis:
21468 *     #include "fitschan.h"
21469 *     int MatchChar( char test, char type, const char *method,
21470 *                    const char *class, const char *template, int *status )
21471 
21472 *  Class Membership:
21473 *     FitsChan member function.
21474 
21475 *  Description:
21476 *     This function checks that the supplied test character belongs
21477 *     to the set of characters specified by the parameter "type".
21478 
21479 *  Parameters:
21480 *     test
21481 *        The character to test.
21482 *     type
21483 *        The character specifying the set of acceptable characters. This
21484 *        should be one of the field type characters accepted by function
21485 *        Match (e.g. "d", "c" or "f").
21486 *     method
21487 *        Pointer to a string holding the name of the calling method.
21488 *        This is only for use in constructing error messages.
21489 *     class
21490 *        Pointer to a string holding the name of the supplied object class.
21491 *        This is only for use in constructing error messages.
21492 *     template
21493 *        Pointer to the start of the whole template string, for use in error
21494 *        messages.
21495 *     status
21496 *        Pointer to the inherited status variable.
21497 
21498 *  Returned Value:
21499 *     Zero is returned if the test character does not belongs to the
21500 *     specified character set, and one is returned if it does.
21501 
21502 *  Notes:
21503 *     -  An error is reported if the type specifier is not legal.
21504 *     -  Zero is returned if an error has already occurred, or if ths
21505 *     function fails for any reason.
21506 */
21507 
21508 /* Local Variables: */
21509    int ret;            /* Returned flag */
21510 
21511 /* Check global status. */
21512    ret = 0;
21513    if( !astOK ) return ret;
21514 
21515 /* Check for "d" specifiers (digits). */
21516    if( type == 'd' ){
21517       ret = isdigit( (int) test );
21518 
21519 /* Check for "c" specifiers (upper case letters). */
21520    } else if( type == 'c' ){
21521       ret = isupper( (int) test );
21522 
21523 /* Check for "s" specifiers (any legal FITS keyword character). */
21524    } else if( type == 'f' ){
21525       ret = isFits( (int) test );
21526 
21527 /* Report an error for any other specifier. */
21528    } else if( astOK ){
21529       ret = 0;
21530       astError( AST__BDFMT, "%s(%s): Illegal field type or width "
21531                 "specifier '%c' found in filter template '%s'.", status,
21532                 method, class, type, template );
21533    }
21534 
21535 /* Return the answer. */
21536    return ret;
21537 }
21538 
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)21539 static int MatchFront( const char *test, const char *temp, char *type,
21540                        int *extend, int *ntest, int *ntemp,
21541                        const char *method, const char *class,
21542                        const char *template, int *status ){
21543 /*
21544 *  Name:
21545 *     MatchFront
21546 
21547 *  Purpose:
21548 *     Sees if the start of a test string matches the start of a template.
21549 
21550 *  Type:
21551 *     Private function.
21552 
21553 *  Synopsis:
21554 *     #include "fitschan.h"
21555 *     int MatchFront( const char *test, const char *temp, char *type,
21556 *                     int *extend, int *ntest, int *ntemp,
21557 *                     const char *method, const char *class,
21558 *                     const char *template )
21559 
21560 *  Class Membership:
21561 *     FitsChan member function.
21562 
21563 *  Description:
21564 *     This function looks for a match between the first field in the
21565 *     template string and the string at the start of the test string,
21566 *     using the syntax described in function Match.
21567 
21568 *  Parameters:
21569 *     test
21570 *        Pointer to a null terminated string holding the keyword name to
21571 *        be tested.
21572 *     temp
21573 *        Pointer to a null terminated string holding the template.
21574 *     type
21575 *        Pointer to a location at which to return a character specifying the
21576 *        sort of field that was matched. This will be one of the legal field
21577 *        types accepted by Match (e.g. "d", "c" or "f"), or null (zero) if
21578 *        the first field in the template string was a literal character (i.e.
21579 *        did not start with a "%").
21580 *     extend
21581 *        Pointer to a location at which to return a flag which will be non-zero
21582 *        if the further test characters could be matched by the first field in
21583 *        the template. This will be the case if the template field only
21584 *        specifies a minimum number of matching characters (i.e. if the field
21585 *        width can be extended). For instance, "%d" can be extended, but "%1d"
21586 *        cannot.
21587 *     ntest
21588 *        Pointer to a location at which to return the number of characters
21589 *        matched in the test string. This will be the minimum number allowed
21590 *        by the template field.
21591 *     ntemp
21592 *        Pointer to a location at which to return the number of characters
21593 *        read from the template string (i.e. the number of characters in the
21594 *        field specification).
21595 *     method
21596 *        Pointer to a string holding the name of the calling method.
21597 *        This is only for use in constructing error messages.
21598 *     class
21599 *        Pointer to a string holding the name of the supplied object class.
21600 *        This is only for use in constructing error messages.
21601 *     template
21602 *        Pointer to the start of the whole template string, for use in error
21603 *        messages.
21604 
21605 *  Returned Value:
21606 *     Zero is returned if the test string starts with fewer than the
21607 *     minimum number of characters matching the template string, and one
21608 *     is returned if it does.
21609 
21610 *  Notes:
21611 *     -  Zero is returned if an error has already occurred, or if this
21612 *     function fails for any reason.
21613 */
21614 
21615 /* Local Variables: */
21616    const char *a;     /* Pointer to next test character */
21617    const char *b;     /* Pointer to next template character */
21618    int i;             /* Character index */
21619    int match;         /* Does "test" match "temp"? */
21620 
21621 /* Check global status. */
21622    if( !astOK ) return 0;
21623 
21624 /* Initialise pointers to the start of each string. */
21625    a = test;
21626    b = temp;
21627 
21628 /* Initialise the returned value to indicate that the strings match. */
21629    match = 1;
21630 
21631 /* If the current character in the template is not a % sign, it must
21632    match the current character in the test string (except for case). */
21633    if( *b != '%' ){
21634       if( toupper( (int) *b ) != toupper( (int) *a ) ) {
21635          match = 0;
21636 
21637 /* If the characters match, return all the required information. */
21638       } else {
21639          *type = 0;
21640          *extend = 0;
21641          *ntest = 1;
21642          *ntemp = 1;
21643       }
21644 
21645 /* If the current character of the template is a %, we need to match
21646    a field. */
21647    } else {
21648       *ntemp = 3;
21649 
21650 /* The next character in the template string determines the field width.
21651    Get the lowest number of characters which must match in the test string,
21652    and set a flag indicating if this lowest limit can be extended. */
21653       b++;
21654       if( *b == '0' ){
21655          *ntest = 0;
21656          *extend = 1;
21657       } else if( *b == '1' ){
21658          *ntest = 1;
21659          *extend = 0;
21660       } else if( *b == '2' ){
21661          *ntest = 2;
21662          *extend = 0;
21663       } else if( *b == '3' ){
21664          *ntest = 3;
21665          *extend = 0;
21666       } else if( *b == '4' ){
21667          *ntest = 4;
21668          *extend = 0;
21669       } else if( *b == '5' ){
21670          *ntest = 5;
21671          *extend = 0;
21672       } else if( *b == '6' ){
21673          *ntest = 6;
21674          *extend = 0;
21675       } else if( *b == '7' ){
21676          *ntest = 7;
21677          *extend = 0;
21678       } else if( *b == '8' ){
21679          *ntest = 8;
21680          *extend = 0;
21681       } else if( *b == '9' ){
21682          *ntest = 9;
21683          *extend = 0;
21684 
21685 /* If no field width was given, one or more test characters are matched.
21686    Step back a character so that the current character will be re-used as
21687    the type specifier. */
21688       } else {
21689          *ntest = 1;
21690          *extend = 1;
21691          b--;
21692          (*ntemp)--;
21693       }
21694 
21695 /* The next template character gives the type of character which should
21696    be matched. */
21697       b++;
21698       *type = *b;
21699 
21700 /* Report an error if the template string ended within the field
21701    specifier. */
21702       if( !*b ){
21703          match = 0;
21704          astError( AST__BDFMT, "%s(%s): Incomplete field specifier found "
21705                    "at end of filter template '%s'.", status, method, class,
21706                    template );
21707 
21708 /* Otherwise, check that the test string starts with the minimum allowed
21709    number of characters matching the specified type. */
21710       } else {
21711          for( i = 0; i < *ntest; i++ ){
21712             if( !MatchChar( *a, *type, method, class, template, status ) ){
21713                match = 0;
21714                break;
21715             }
21716             a++;
21717          }
21718       }
21719    }
21720 
21721 /* Return the answer. */
21722    return match;
21723 }
21724 
MarkCard(AstFitsChan * this,int * status)21725 static void MarkCard( AstFitsChan *this, int *status ){
21726 
21727 /*
21728 *  Name:
21729 *     MarkCard
21730 
21731 *  Purpose:
21732 *     Mark the current card as having been read into an AST object.
21733 
21734 *  Type:
21735 *     Private function.
21736 
21737 *  Synopsis:
21738 *     #include "fitschan.h"
21739 
21740 *     void MarkCard( AstFitsChan *this, int *status )
21741 
21742 *  Class Membership:
21743 *     FitsChan member function.
21744 
21745 *  Description:
21746 *     The current card is marked as having been "provisionally used" in
21747 *     the construction of an AST object. If the Object is constructed
21748 *     succesfully, such cards are marked as having been definitely used,
21749 *     and they are then considered to have been removed from the FitsChan.
21750 
21751 *  Parameters:
21752 *     this
21753 *        Pointer to the FitsChan containing the list of cards.
21754 *     status
21755 *        Pointer to the inherited status variable.
21756 
21757 *  Notes:
21758 *     -  The card remains the current card even though it is now marked
21759 *     as having been read.
21760 */
21761    int flags;
21762 
21763 /* Return if the global error status has been set, or the current card
21764    is not defined. */
21765    if( !astOK || !this->card ) return;
21766 
21767 /* Set the PROVISIONALLY_USED flag in the current card, but only if the
21768    PROTECTED flag is not set. */
21769    flags = ( (FitsCard *) this->card )->flags;
21770    if( !( flags & PROTECTED ) ) {
21771       ( (FitsCard *) this->card )->flags = flags | PROVISIONALLY_USED;
21772    }
21773 }
21774 
MoveCard(AstFitsChan * this,int move,const char * method,const char * class,int * status)21775 static int MoveCard( AstFitsChan *this, int move, const char *method,
21776                       const char *class, int *status ){
21777 
21778 /*
21779 *  Name:
21780 *     MoveCard
21781 
21782 *  Purpose:
21783 *     Move the current card a given number of cards forward or backwards.
21784 
21785 *  Type:
21786 *     Private function.
21787 
21788 *  Synopsis:
21789 *     #include "fitschan.h"
21790 
21791 *     int MoveCard( AstFitsChan *this, int move, const char *method,
21792 *                    const char *class, int *status )
21793 
21794 *  Class Membership:
21795 *     FitsChan member function.
21796 
21797 *  Description:
21798 *     The current card is increment by the given number of cards, ignoring
21799 *     cards which have been read into an AST object if the ignore_used flag
21800 *     is set non-zero.
21801 
21802 *  Parameters:
21803 *     this
21804 *        Pointer to the FitsChan containing the list of cards.
21805 *     move
21806 *        The number of cards by which to move the current card. Positive
21807 *        values move towards the end-of-file. Negative values move
21808 *        towards the start of the file (i.e. the list head).
21809 *     method
21810 *        Pointer to string holding name of calling method.
21811 *     class
21812 *        Pointer to string holding object class.
21813 *     status
21814 *        Pointer to the inherited status variable.
21815 
21816 *  Returned Value:
21817 *     The number of cards actually moved. This may not always be equal to
21818 *     the requested number (for instance, if the end or start of the
21819 *     FitsChan is encountered first).
21820 
21821 *  Notes:
21822 *     -  If the end-of-file is reached before the required number of
21823 *     cards have been skipped, the current card is set NULL, to indicate
21824 *     an end-of-file condition.
21825 *     -  If the start of the file is reached before the required number of
21826 *     cards have been skipped, the current card is left pointing to the
21827 *     first usable card.
21828 *     -  This function attempts to execute even if an error has occurred.
21829 */
21830 
21831 /* Local Variables: */
21832    astDECLARE_GLOBALS      /* Declare the thread specific global data */
21833    FitsCard *card;         /* The current card */
21834    FitsCard *card0;        /* The previous non-deleted card */
21835    int moved;              /* The number of cards moved by so far */
21836 
21837 /* Return if the supplied object is NULL or the FitsChan is
21838    empty, or zero movement is requested. */
21839    if( !this || !this->head || !move ) return 0;
21840 
21841 /* Get a pointer to the structure holding thread-specific global data. */
21842    astGET_GLOBALS(this);
21843 
21844 /* Get a pointer to the current card. */
21845    card = (FitsCard *) this->card;
21846 
21847 /* Initialise the number of cards moved so far. */
21848    moved = 0;
21849 
21850 /* First deal with positive movements (towards the end-of-file). */
21851    if( move > 0 ){
21852 
21853 /* Loop round moving on to the next card until the correct number of
21854    moves have been made, or the end-of-file is reached. */
21855       while( moved < move && card ){
21856 
21857 /* Get a pointer to the next card in the list, reporting an error if the
21858    links are inconsistent. */
21859          card = GetLink( card, NEXT, method, class, status );
21860 
21861 /* If we have moved past the last card and are now pointing back at the
21862    list head, then indicate that we are at end-of-file by setting the
21863    card pointer NULL. */
21864          if( (void *) card == this->head ){
21865             card = NULL;
21866 
21867 /* Otherwise, increment the number of cards moved. We ignore cards which
21868    have been read into an AST object if the external "ignore_used" flag is
21869    set. */
21870          } else if( card ){
21871             if( !CARDUSED(card) ) moved++;
21872          }
21873       }
21874 
21875 /* Now deal with negative movements (towards the list head), so long as
21876    we are not currently at the list head. */
21877    } else if( (void *) card != this->head ){
21878 
21879 /* If we are currently at end-of-file, replace the NULL pointer for the
21880    current card with a pointer to the list head. The first step backwards
21881    will make the last card the current card. */
21882       if( !card ) card = (FitsCard *) this->head;
21883 
21884 /* Loop round until the correct number of cards have been moved. */
21885       while( moved < -move && card ){
21886 
21887 /* If cards which have been read into an AST object are to be included in the
21888    count of moved cards, get a pointer to the previous card in the list,
21889    reporting an error if the links are inconsistent. */
21890          if( !ignore_used ){
21891             card = GetLink( card, PREVIOUS, method, class, status );
21892 
21893 /* If cards which have been read into an AST object are to be ignored... */
21894          } else {
21895 
21896 /* We need to find the previous card which has not been read into an AST
21897    object. We do not search beyond the start of the list. */
21898             card0 = GetLink( card, PREVIOUS, method, class, status );
21899             while( card0 && CARDUSED(card0) && (void *) card0 != this->head ){
21900                card0 = GetLink( card0, PREVIOUS, method, class, status );
21901             }
21902 
21903 /* If no such card was found we leave the card where it is. */
21904             if( card0 && ( card0->flags & USED ) ) {
21905                break;
21906 
21907 /* Otherwise, move back to card found above. */
21908             } else {
21909                card = card0;
21910             }
21911          }
21912 
21913 /* Increment the number of cards moved. */
21914          moved++;
21915 
21916 /* If the current card is the list head, break out of the loop. */
21917          if( (void *) card == this->head ) break;
21918       }
21919    }
21920 
21921 /* Store the new current card. */
21922    this->card = (void *) card;
21923 
21924 /* Return the answer. */
21925    return moved;
21926 }
21927 
NearestPix(AstMapping * map,double val,int axis,int * status)21928 static double NearestPix( AstMapping *map, double val, int axis, int *status ){
21929 /*
21930 *  Name:
21931 *     NearestPix
21932 
21933 *  Purpose:
21934 *     Find an axis value which corresponds to an integer pixel value.
21935 
21936 *  Type:
21937 *     Private function.
21938 
21939 *  Synopsis:
21940 *     #include "fitschan.h"
21941 *     double NearestPix( AstMapping *map, double val, int axis, int *status )
21942 
21943 *  Class Membership:
21944 *     FitsChan member function.
21945 
21946 *  Description:
21947 *     The supplied axis value is transformed using the inverse of the
21948 *     supplied Mapping (other axes are given the value AST__BAD). The
21949 *     resulting axis values are rounded to the nearest whole number, and
21950 *     then transformed back using the supplied Mapping in the forward
21951 *     direction. If the nominated axis value is good, it is returned as
21952 *     the function value, otherwise the supplied value is returned unchanged.
21953 
21954 *  Parameters:
21955 *     map
21956 *        A Mapping (usually the input coordinates will correspond to
21957 *        pixel coordinates).
21958 *     val
21959 *        A value for one of the outputs of the "map" Mapping.
21960 *     axis
21961 *        The index of the Mapping output to which "val" refers.
21962 *     status
21963 *        Pointer to the inherited status variable.
21964 
21965 *  Retuned Value:
21966 *     The modified output axis value.
21967 */
21968 
21969 /* Local Variables: */
21970    AstMapping *tmap;       /* Mapping to be used */
21971    AstPointSet *pset1;     /* Pixel coords PointSet */
21972    AstPointSet *pset2;     /* WCS coords PointSet */
21973    double **ptr1;          /* Pointer to data in pset1 */
21974    double **ptr2;          /* Pointer to data in pset2 */
21975    double result;          /* Returned value */
21976    int *ins;               /* Array holding input axis indicies */
21977    int i;                  /* Loop count */
21978    int nin;                /* Number of Mapping inputs */
21979    int nout;               /* Number of Mapping outputs */
21980 
21981 /* Initialise. */
21982    result = val;
21983 
21984 /* Check inherited status, and that the supplied value is good. */
21985    if( !astOK || result == AST__BAD ) return result;
21986 
21987 /* If the supplied Mapping has no inverse, trying splitting off the
21988    transformation for the required axis, which may have an inverse.
21989    If succesful, use the 1-in,1-out Mapping returned by astMapSPlit
21990    instead of the supplied Mapping, and adjust the axis index accordingly. */
21991    if( !astGetTranInverse( map ) ) {
21992       astInvert( map );
21993       ins = astMapSplit( map, 1, &axis, &tmap );
21994       if( tmap ) {
21995          astInvert( tmap );
21996          axis = 0;
21997       } else {
21998          tmap = astClone( map );
21999       }
22000       ins = astFree( ins );
22001       astInvert( map );
22002    } else {
22003       tmap = astClone( map );
22004    }
22005 
22006 /* If the Mapping still has no inverse, return the supplied value
22007    unchanged. */
22008    if( astGetTranInverse( tmap ) ) {
22009 
22010 /* Get the number of input and output coordinates. */
22011       nin = astGetNin( tmap );
22012       nout = astGetNout( tmap );
22013 
22014 /* Create PointSets to hold a single input position and the corresponding
22015    output position. */
22016       pset1 = astPointSet( 1, nin, "", status );
22017       ptr1 = astGetPoints( pset1 );
22018       pset2 = astPointSet( 1, nout, "", status );
22019       ptr2 = astGetPoints( pset2 );
22020       if( astOK ) {
22021 
22022 /* Assign AST__BAD values to all output axes, except for the specified
22023    axis, which is given the supplied axis value. */
22024          for( i = 0; i < nout; i++ ) ptr2[ i ][ 0 ] = AST__BAD;
22025          ptr2[ axis ][ 0 ] = val;
22026 
22027 /* Transform this output position into an input position. */
22028          (void) astTransform( tmap, pset2, 0, pset1 );
22029 
22030 /* Round all good axis values in the resulting input position to the nearest
22031    integer. */
22032          for( i = 0; i < nin; i++ ) {
22033             if( ptr1[ i ][ 0 ] != AST__BAD ) {
22034                ptr1[ i ][ 0 ] = (int) ( ptr1[ i ][ 0 ] + 0.5 );
22035             }
22036          }
22037 
22038 /* Transform this input position back into output coords. */
22039          (void) astTransform( tmap, pset1, 1, pset2 );
22040 
22041 /* If the resulting axis value is good, return it. */
22042          if( ptr2[ axis ] [ 0 ] != AST__BAD ) result = ptr2[ axis ] [ 0 ];
22043       }
22044 
22045 /* Free resources. */
22046       pset1 = astAnnul( pset1 );
22047       pset2 = astAnnul( pset2 );
22048    }
22049    tmap = astAnnul( tmap );
22050 
22051 /* Return the result. */
22052    return result;
22053 }
22054 
NewCard(AstFitsChan * this,const char * name,int type,const void * data,const char * comment,int flags,int * status)22055 static void NewCard( AstFitsChan *this, const char *name, int type,
22056                      const void *data, const char *comment, int flags,
22057                      int *status ){
22058 
22059 /*
22060 *  Name:
22061 *     NewCard
22062 
22063 *  Purpose:
22064 *     Insert a new card in front of the current card.
22065 
22066 *  Type:
22067 *     Private function.
22068 
22069 *  Synopsis:
22070 *     #include "fitschan.h"
22071 
22072 *     void NewCard( AstFitsChan *this, const char *name, int type,
22073 *                   const void *data, const char *comment, int flags,
22074 *                   int *status )
22075 
22076 *  Class Membership:
22077 *     FitsChan member function.
22078 
22079 *  Description:
22080 *     The supplied keyword name, data type and value, and comment are
22081 *     stored in a new FitsCard structure, and this structure is
22082 *     inserted into the circular linked list stored in the supplied
22083 *     FitsChan. It is inserted in front of the current card.
22084 
22085 *  Parameters:
22086 *     this
22087 *        Pointer to the FitsChan containing the list of cards.
22088 *     name
22089 *        Pointer to a string holding the keyword name of the new card.
22090 *     type
22091 *        An integer value representing the data type of the keyword.
22092 *     data
22093 *        Pointer to the data associated with the keyword.
22094 *     comment
22095 *        Pointer to a null-terminated string holding a comment.
22096 *     flags
22097 *        The flags to assign to the card.
22098 *     status
22099 *        Pointer to the inherited status variable.
22100 
22101 *  Notes:
22102 *     -  The new card is inserted into the list in front of the current card,
22103 *     so that the "next" link from the new card points to the current card.
22104 *     If the FitsChan is currently at end-of-file (indicated by a NULL
22105 *     pointer being stored for the current card), then the card is appended
22106 *     to the end of the list. The pointer to the current card is left
22107 *     unchanged.
22108 *     -  Keyword names are converted to upper case before being stored.
22109 *     -  Any trailing white space in a string value is saved as supplied.
22110 *     -  Logical values are converted to zero or one before being stored.
22111 *     -  The "comment" and/or "data" pointers may be supplied as NULL.
22112 */
22113 
22114 /* Local Variables: */
22115    FitsCard *new;             /* Pointer to the new card */
22116    FitsCard *prev;            /* Pointer to the previous card in the list */
22117    char *b;                   /* Pointer to next stored character */
22118    const char *a;             /* Pointer to next supplied character */
22119    int lval;                  /* Logical data value restricted to 0 or 1 */
22120    int nc;                    /* No. of characters to store */
22121 
22122 /* Check the global status. */
22123    if( !astOK ) return;
22124 
22125 /* Get memory to hold the new FitsCard structure. */
22126    new = (FitsCard *) astMalloc( sizeof( FitsCard ) );
22127 
22128 /* Check the pointer can be used. */
22129    if( astOK ){
22130 
22131 /* Copy the keyword name, converting to upper case. */
22132       a = name;
22133       b = new->name;
22134       while( *a ) *(b++) = (char) toupper( (int) *(a++) );
22135       *b = 0;
22136 
22137 /* Ensure that a KeyMap exists to hold the keywords currently in the
22138    FitsChan. */
22139       if( !this->keywords ) this->keywords = astKeyMap( " ", status );
22140 
22141 /* Add the keyword name to the KeyMap. The value associated with the
22142    KeyMap entry is not used and is set arbitrarily to zero. */
22143       astMapPut0I( this->keywords, new->name, 0, NULL );
22144 
22145 /* Copy the data type. */
22146       new->type = type;
22147 
22148 /* Copy any data (ignore any data supplied for an UNDEF value). */
22149       if( data && type != AST__UNDEF ){
22150 
22151 /* Logical values are converted to zero or one before being stored. */
22152          if( type == AST__LOGICAL ){
22153             lval = *( (int *) data ) ? 1 : 0;
22154             new->size = sizeof( int );
22155             new->data = astStore( NULL, (void *) &lval, sizeof( int ) );
22156 
22157 /* String values... */
22158          } else if( type == AST__STRING || type == AST__CONTINUE ){
22159 
22160 /* Find the number of characters excluding the trailing null character. */
22161             nc = strlen( data );
22162 
22163 /* Store the string, reserving room for a terminating null. */
22164             new->size = (size_t)( nc + 1 );
22165             new->data = astStore( NULL, (void *) data, (size_t)( nc + 1 ) );
22166 
22167 /* Terminate it. */
22168             ( (char *) new->data)[ nc ] = 0;
22169 
22170 /* Other types are stored as supplied. */
22171          } else if( type == AST__INT ){
22172             new->size = sizeof( int );
22173             new->data = astStore( NULL, (void *) data, sizeof( int ) );
22174          } else if( type == AST__FLOAT ){
22175             new->size = sizeof( double );
22176             new->data = astStore( NULL, (void *) data, sizeof( double ) );
22177          } else if( type == AST__COMPLEXF ){
22178             if( *( (double *) data ) != AST__BAD ) {
22179                new->size = 2*sizeof( double );
22180                new->data = astStore( NULL, (void *) data, 2*sizeof( double ) );
22181             } else {
22182                nc = strlen( BAD_STRING );
22183                new->size = (size_t)( nc + 1 );
22184                new->data = astStore( NULL, BAD_STRING, (size_t)( nc + 1 ) );
22185                ( (char *) new->data)[ nc ] = 0;
22186             }
22187          } else if( type == AST__COMPLEXI ){
22188             new->size = 2*sizeof( int );
22189             new->data = astStore( NULL, (void *) data, 2*sizeof( int ) );
22190          } else {
22191             new->size = 0;
22192             new->data = NULL;
22193          }
22194       } else {
22195          new->size = 0;
22196          new->data = NULL;
22197       }
22198 
22199 /* Find the first non-blank character in the comment, and find the used
22200    length of the remaining string. We retain leading and trailing white
22201    space if the card is a COMMENT card. */
22202       if( comment ){
22203          a = comment;
22204          if( type != AST__COMMENT ) {
22205             while( isspace( *a ) ) a++;
22206             nc = ChrLen( a, status );
22207          } else {
22208             nc = strlen( a );
22209          }
22210       } else {
22211          nc = 0;
22212       }
22213 
22214 /* Copy any comment, excluding leading and trailing white space unless
22215    this is a COMMENT card */
22216       if( nc > 0 ){
22217          new->comment = astStore( NULL, (void *) a, (size_t)( nc + 1 ) );
22218          ( (char *) new->comment)[ nc ] = 0;
22219       } else {
22220          new->comment = NULL;
22221       }
22222 
22223 /* Set the supplied flag values. */
22224       new->flags = flags;
22225 
22226 /* Insert the copied card into the list, in front of the current card. If
22227    the current card is the list head, make the new card the list head. */
22228       if( this->card ){
22229          prev = ( ( FitsCard *) this->card )->prev;
22230          ( ( FitsCard *) this->card )->prev = new;
22231          new->prev = prev;
22232          prev->next = new;
22233          new->next = (FitsCard *) this->card;
22234          if( this->card == this->head ) this->head = (void *) new;
22235 
22236 /* If the FitsChan is at end-of-file, append the new card to the end of
22237    the list (i.e. insert it just before the list head). */
22238       } else {
22239          if( this->head ){
22240             prev = ( (FitsCard *) this->head )->prev;
22241             ( (FitsCard *) this->head )->prev = new;
22242             new->prev = prev;
22243             prev->next = new;
22244             new->next = (FitsCard *) this->head;
22245 
22246 /* If there are no cards in the list, start a new list. */
22247          } else {
22248             new->prev = new;
22249             new->next = new;
22250             this->head = (void *) new;
22251             this->card = NULL;
22252          }
22253       }
22254    }
22255 
22256 /* Return. */
22257    return;
22258 }
22259 
NonLinSpecWcs(AstFitsChan * this,char * algcode,FitsStore * store,int i,char s,AstSpecFrame * specfrm,const char * method,const char * class,int * status)22260 static AstMapping *NonLinSpecWcs( AstFitsChan *this, char *algcode,
22261                                   FitsStore *store, int i, char s,
22262                                   AstSpecFrame *specfrm, const char *method,
22263                                   const char *class, int *status ) {
22264 
22265 /*
22266 *  Name:
22267 *     NonLinSpecWcs
22268 
22269 *  Purpose:
22270 *     Create a Mapping describing a FITS-WCS non-linear spectral algorithm
22271 
22272 *  Type:
22273 *     Private function.
22274 
22275 *  Synopsis:
22276 *     #include "fitschan.h"
22277 
22278 *     AstMapping *NonLinSpecWcs( AstFitsChan *this, char *algcode,
22279 *                                FitsStore *store, int i, char s,
22280 *                                AstSpecFrame *specfrm, const char *method,
22281 *                                const char *class, int *status )
22282 
22283 *  Class Membership:
22284 *     FitsChan member function.
22285 
22286 *  Description:
22287 *     This function uses the contents of the supplied FitsStore to create
22288 *     a Mapping which goes from Intermediate World Coordinate (known as "w"
22289 *     in the context of FITS-WCS paper III) to the spectral system
22290 *     described by the supplied SpecFrame.
22291 *
22292 *     The returned Mapping implements the non-linear "X2P" algorithms
22293 *     described in FITS-WCS paper III. The axis is linearly sampled in
22294 *     system "X" but expressed in some other system (specified by the
22295 *     supplied SpecFrame).
22296 
22297 *  Parameters:
22298 *     this
22299 *        Pointer to the FitsChan.
22300 *     algcode
22301 *        Pointer to a string holding the non-linear "-X2P" code for the
22302 *        required algorithm. This includes aleading "-" character.
22303 *     store
22304 *        Pointer to the FitsStore structure holding the values to use for
22305 *        the WCS keywords.
22306 *     i
22307 *        The zero-based index of the spectral axis within the FITS header
22308 *     s
22309 *        A character identifying the co-ordinate version to use. A space
22310 *        means use primary axis descriptions. Otherwise, it must be an
22311 *        upper-case alphabetical characters ('A' to 'Z').
22312 *     specfrm
22313 *        Pointer to the SpecFrame. This specified the "S" system - the
22314 *        system in which the CRVAL kewyords (etc) are specified.
22315 *     method
22316 *        A pointer to a string holding the name of the calling method.
22317 *        This is used only in the construction of error messages.
22318 *     class
22319 *        A pointer to a string holding the class of the object being
22320 *        read. This is used only in the construction of error messages.
22321 *     status
22322 *        Pointer to the inherited status variable.
22323 
22324 *  Returned Value:
22325 *     A pointer to a Mapping, or NULL if an error occurs.
22326 */
22327 
22328 /* Local Variables: */
22329    AstFrameSet *fs;
22330    AstMapping *map1;
22331    AstMapping *ret;
22332    AstSpecFrame *xfrm;
22333    AstMapping *map2;
22334    char buf[ 100 ];
22335    char pc;
22336    double crv;
22337    double ds;
22338    double in_a;
22339    double in_b;
22340    double out_a;
22341    double out_b;
22342    int ok;
22343    int s_sys;
22344 
22345 /* Check the global status. */
22346    ret = NULL;
22347    if( !astOK ) return ret;
22348 
22349 /* Identify the spectral "X" system within the "X2P" algorithm code, and
22350    create a SpecFrame describing the X system ("X" is the system in
22351    which the axis is linearly sampled). This is done by copying the
22352    supplied SpecFrame and then setting its System attribute. Copying
22353    the supplied SpecFrame ensures that all the other attributes (RestFreq,
22354    etc.) are set correctly. */
22355    ok = 1;
22356    xfrm = astCopy( specfrm );
22357    if( algcode[ 1 ] == 'F' ) {
22358       astSetSystem( xfrm, AST__FREQ );
22359       astSetUnit( xfrm, 0, "Hz" );
22360    } else if( algcode[ 1 ] == 'W' ) {
22361       astSetSystem( xfrm, AST__WAVELEN );
22362       astSetUnit( xfrm, 0, "m" );
22363    } else if( algcode[ 1 ] == 'V' ) {
22364       astSetSystem( xfrm, AST__VREL );
22365       astSetUnit( xfrm, 0, "m/s" );
22366    } else if( algcode[ 1 ] == 'A' ) {
22367       astSetSystem( xfrm, AST__AIRWAVE );
22368       astSetUnit( xfrm, 0, "m" );
22369    } else {
22370       ok = 0;
22371    }
22372 
22373 /* If the X system was identified, find a Mapping from the "S" (specfrm)
22374    system to the X system. */
22375    map1 = NULL;
22376    if( ok ) {
22377       ok = 0;
22378       fs = astConvert( specfrm, xfrm, "" );
22379       if( fs ) {
22380          map1 = astGetMapping( fs, AST__BASE, AST__CURRENT );
22381          fs = astAnnul( fs );
22382          ok = 1;
22383       }
22384 
22385 /* Issue a warning if the "P" system is not the correct one for the given
22386    "S" system. We can however continue, sine AST interprets illegal "P"
22387    systems correctly. */
22388       pc = 0;
22389       s_sys = astGetSystem( specfrm );
22390       if( s_sys == AST__FREQ || s_sys == AST__ENERGY ||
22391           s_sys == AST__WAVENUM ||  s_sys == AST__VRADIO ) {
22392          pc = 'F';
22393       } else if( s_sys == AST__WAVELEN || s_sys == AST__VOPTICAL ||
22394                  s_sys == AST__REDSHIFT ){
22395          pc = 'W';
22396       } else if( s_sys == AST__AIRWAVE ) {
22397          pc = 'A';
22398       } else if( s_sys == AST__BETA || s_sys == AST__VREL ) {
22399          pc = 'V';
22400       } else if( astOK ) {
22401          pc = algcode[ 3 ];
22402          astError( AST__INTER, "%s: Function NonLinSpecWcs does not yet "
22403                    "support spectral axes of type %s (internal AST "
22404                    "programming error).", status, method, astGetC( specfrm, "System" ) );
22405       }
22406       if( algcode[ 3 ] != pc ) {
22407          sprintf( buf, "The spectral CTYPE value %s%s is not legal - "
22408                  "using %s%.3s%c instead.", astGetC( specfrm, "System" ),
22409                  algcode,  astGetC( specfrm, "System" ), algcode, pc );
22410          Warn( this, "badctype", buf, method, class, status );
22411       }
22412    }
22413 
22414 /* If succesfull, use this Mapping to find the reference value (CRVAL)
22415    in the "X" system. */
22416    if( ok ) {
22417 
22418 /* Get the CRVAL value for the spectral axis (this will be in the S system). */
22419       crv = GetItem( &(store->crval), i, 0, s, NULL, method, class, status );
22420       if( crv == AST__BAD ) crv = 0.0;
22421 
22422 /* Convert it to the X system. */
22423       astTran1( map1, 1, &crv, 1, &crv );
22424 
22425 /* Invert this Mapping so that it forward transformation goes from X to S. */
22426       astInvert( map1 );
22427 
22428 /* Find the rate of change of S with respect to X (dS/dX) at the reference
22429    point (x = crv). */
22430       ds = astRate( map1, &crv, 0, 0 );
22431       if( ds != AST__BAD && ds != 0.0 ) {
22432 
22433 /* FITS-WCS paper III says that dS/dw must be 1.0 at the reference point.
22434    Therefore dX/dw = dX/dS at the reference point. Also, since the spectral
22435    axis is linear in X, dX/dw must be constant. Therefore the Mapping from
22436    IWC to X is a WinMap which scales the IWC axis ("w") by dX/dw and adds
22437    on the X value at the reference point. */
22438          if( crv != 0.0 ) {
22439             in_a = 0.0;
22440             out_a = crv;
22441             in_b = crv*ds;
22442             out_b = 2.0*crv;
22443             map2 = (AstMapping *) astWinMap( 1, &in_a, &in_b, &out_a, &out_b, "", status );
22444          } else {
22445             map2 = (AstMapping *) astZoomMap( 1, 1.0/ds, "", status );
22446          }
22447 
22448 /* The Mapping to be returned is the concatenation of the above Mapping
22449    (from w to X) with the Mapping from X to S. */
22450          ret = (AstMapping *) astCmpMap( map2, map1, 1, "", status );
22451          map1 = astAnnul( map1 );
22452          map2 = astAnnul( map2 );
22453       }
22454    }
22455    xfrm = astAnnul( xfrm );
22456 
22457 /* Return the result */
22458    return ret;
22459 }
22460 
OrthVector(int n,int m,double ** in,int * status)22461 static double *OrthVector( int n, int m, double **in, int *status ){
22462 /*
22463 *  Name:
22464 *     OrthVector
22465 
22466 *  Purpose:
22467 *     Find a unit vector which is orthogonal to a set of supplied vectors.
22468 
22469 *  Type:
22470 *     Private function.
22471 
22472 *  Synopsis:
22473 *     #include "fitschan.h"
22474 *     double *OrthVector( int n, int m, double **in, int *status )
22475 
22476 *  Class Membership:
22477 *     FitsChan member function.
22478 
22479 *  Description:
22480 *     A set of M vectors is supplied, each vector being N-dimensional.
22481 *     It is assumed that M < N and that the supplied vectors span M
22482 *     axes within the N dimensional space. An N-dimensional unit vector is
22483 *     returned which is orthogonal to all the supplied vectors.
22484 *
22485 *     The required vector is orthogonal to all the supplied vectors.
22486 *     Therefore the dot product of the required vector with each of the
22487 *     supplied vectors must be zero. This gives us M equations of the
22488 
22489 *     form:
22490 *
22491 *     a1*r1 + a2*r2 + a3*r3 + .... + aN*rN = 0.0
22492 *     b1*r1 + b2*r2 + b3*r3 + .... + bN*rN = 0.0
22493 *     ...
22494 *
22495 *     where (a1,a2,..,aN), (b1,b2,..,bN), ... are the supplied vectors
22496 *     and (r1,r2,...,rN) is the required vector. Since M is less
22497 *     than N the system of linear simultaneous equations is under
22498 *     specified and we need to assign arbitrary values to some of the
22499 *     components of the required vector in order to allow the equations
22500 *     to be solved. We arbitrarily assume that 1 element of the required
22501 *     vector has value 1.0 and (N-M-1) have value zero. The selection of
22502 *     *which* elements to set constant is based on the magnitudes of the
22503 *     columns of coefficients (a1,b1...), (a2,b2,...), etc. The M components
22504 *     of the required vector which are *not* set constant are the ones which
22505 *     have coefficient columns with the *largest* magnitude. This choice is
22506 *     made in order to minimise the risk of the remaining matrix of
22507 *     coefficients being singular (for instance, if a component of the
22508 *     required vector has a coefficient of zero in every supplied vector
22509 *     then the column magnitude will be zero and that component will be
22510 *     set to 1.0). After choosing the M largest columns, the largest
22511 *     remaining column is assigned a value of 1.0 in the required vector,
22512 *     and all other columns are assigned the value zero in the required
22513 
22514 *     vector. This means that the above equations becomes:
22515 *
22516 *     a1*r1 + a2*r2 + a3*r3 + .... + aM*rM = -aM+1
22517 *     b1*r1 + b2*r2 + b3*r3 + .... + bM*rM = -bM+1
22518 *     ...
22519 *
22520 *     Where the indices are now not direct indices into the supplied and
22521 *     returned vectors, but indices into an array of indices which have
22522 *     been sorted into column magnitude order. This is now a set of MxM
22523 
22524 *     simultaneous linear equations which we can solve using palDmat:
22525 *
22526 *     MAT.R = V
22527 *
22528 *     where MAT is the the matrix of columns (coefficients) on the left
22529 *     hand side of the above set of simultaneous equations, R is the
22530 *     required vector (just the components which have *not* been set
22531 *     constant), and V is a constant vector equal to the column of values
22532 *     on the right hand side in the above set of simultaneous equations.
22533 *     The palDmat function solves this equation to obtain R.
22534 
22535 *  Parameters:
22536 *     n
22537 *        The number of dimensions
22538 *     m
22539 *        The number of supplied vectors.
22540 *     in
22541 *        A pointer to an array with "m" elements, each element being a
22542 *        pointer to an array with "n" elements. Each of these "n" element
22543 *        array holds one of the supplied vectors.
22544 *     status
22545 *        Pointer to the inherited status variable.
22546 
22547 *  Returned Value:
22548 *     The pointer to some newly allocated memory holding the returned N
22549 *     dimensional unit vector. The memory should be freed using astFree when
22550 *     no longer needed.
22551 
22552 *  Notes:
22553 *     -  NULL is returned if an error occurs.
22554 *     -  NULL is returned (without error) if the required vector cannot
22555 *     be found (.e.g becuase the supplied M vectors span less than M axes).
22556 */
22557 
22558 /* Local Variables: */
22559    double *colmag;
22560    double *d;
22561    double *e;
22562    double *mat;
22563    double *mel;
22564    double *ret;
22565    double *rhs;
22566    double det;
22567    double sl;
22568    int *colperm;
22569    int *iw;
22570    int done;
22571    int i;
22572    int ih;
22573    int ii;
22574    int il;
22575    int j;
22576    int sing;
22577 
22578 /* Initialise */
22579    ret = NULL;
22580 
22581 /* Check the inherited status. */
22582    if( !astOK ) return ret;
22583 
22584 /* Return if any of the M supplied vectors are NULL. */
22585    for( i = 0; i < m; i++ ) {
22586       if( !in[ i ] ) return ret;
22587    }
22588 
22589 /* Allocate rquired memory. */
22590    ret = astMalloc( sizeof( double )*(size_t) n );
22591    rhs = astMalloc( sizeof( double )*(size_t) m );
22592    mat = astMalloc( sizeof( double )*(size_t) m*m );
22593    iw = astMalloc( sizeof( int )*(size_t) m );
22594    colmag = astMalloc( sizeof( double )*(size_t) n );
22595    colperm = astMalloc( sizeof( int )*(size_t) n );
22596 
22597 /* Check memory can be used safely. */
22598    if( astOK ) {
22599 
22600 /* Find the magnitude of each column of coefficients in the full set of
22601    simultaneous linear equations (before setting any components of the
22602    required vector constant). Also initialise the column permutation array
22603    to indicate that the columns are in their original order. The outer
22604    loop loops through the columns and the inner loop loops through rows
22605    (i.e. equations). */
22606       for( i = 0; i < n; i++ ) {
22607          colperm[ i ] = i;
22608          colmag[ i ] = 0.0;
22609          for( j = 0; j < m; j++ ) {
22610             colmag[ i ] += in[ j ][ i ]*in[ j ][ i ];
22611          }
22612       }
22613 
22614 /* Now re-arrange the column indices within the permutation array so that
22615    they are in order of decreasing ciolumn magnitude (i.e. colperm[0] will
22616    be left holding the index of the column with the largest magnitude). A
22617    simple bubble sort is used. */
22618       ii = 1;
22619       done = 0;
22620       while( !done ) {
22621          done = 1;
22622          for( i = ii; i < n; i++ ) {
22623             ih = colperm[ i ];
22624             il = colperm[ i - 1 ];
22625             if( colmag[ ih ] > colmag[ il ] ) {
22626                colperm[ i ] = il;
22627                colperm[ i - 1 ] = ih;
22628                done = 0;
22629             }
22630          }
22631          ii++;
22632       }
22633 
22634 /* The first M elements in "colperm" now hold the indices of the
22635    columns which are to be used within the MAT matrix, the next element
22636    of "colperm" hold the index of the column which is to be included in the
22637    V vector (other elements hold the indices of the columns which are
22638    being ignored because they will be mutiplied by a value of zero - the
22639    assumed value of the corresponding components of the returned vector). We
22640    now copy the these values into arrays which can be passed to palDmat.
22641    First, initialise a pointer used to step through the mat array. */
22642       mel = mat;
22643 
22644 /* Loop through all the supplied vectors. Get a pointer to the first
22645    element of the vector. */
22646       for( i = 0; i < m; i++ ) {
22647          d = in[ i ];
22648 
22649 /* Copy the required M elements of this supplied vector into the work array
22650    which will be passed to palDmat. */
22651          for( j = 0; j < m; j++ ) *(mel++) = d[ colperm[ j ] ];
22652 
22653 /* Put the next right-hand side value into the "rhs" array. */
22654          rhs[ i ] = -d[ colperm[ m ] ];
22655       }
22656 
22657 /* Use palDmat to find the first M elements of the returned array. These
22658    are stored in "rhs", over-writing the original right-hand side values. */
22659       palDmat( m, mat, rhs, &det, &sing, iw );
22660 
22661 /* If the supplied vectors span fewer than M axes, the above call will fail.
22662    In this case, annul the returned vector. */
22663       if( sing != 0 ) {
22664          ret = astFree( ret );
22665 
22666 /* If succesful, copy the M elements of the solution vector into the
22667    required M elements of the returned vector. Also find the squared length
22668    of the vector. */
22669       } else {
22670          sl = 0.0;
22671          e = rhs;
22672          for( j = 0; j < m; j++ ) {
22673             sl += (*e)*(*e);
22674             ret[ colperm[ j ] ] = *(e++);
22675          }
22676 
22677 /* Put 1.0 into the next element of the returned vector. */
22678          sl += 1.0;
22679          ret[ colperm[ m ] ] = 1.0;
22680 
22681 /* Fill up the rest of the returned vector with zeros. */
22682          for( j = m + 1; j < n; j++ ) ret[ colperm[ j ] ] = 0.0;
22683 
22684 /* Normalise the returned vector so that it is a unit vector.Also ensure
22685    that any zeros are "+0.0" insteasd of "-0.0". */
22686          e = ret;
22687          sl = sqrt( sl );
22688          for( j = 0; j < n; e++,j++ ) {
22689             *e /= sl;
22690             if( *e == 0.0 ) *e = 0.0;
22691          }
22692       }
22693    }
22694 
22695 /* Free workspace. */
22696    rhs = astFree( rhs );
22697    mat = astFree( mat );
22698    iw = astFree( iw );
22699    colmag = astFree( colmag );
22700    colperm = astFree( colperm );
22701 
22702 /* Free the returned vector if an error has occurred. */
22703    if( !astOK ) ret = astFree( ret );
22704 
22705 /* Return the answer. */
22706    return ret;
22707 }
22708 
OrthVectorSet(int n,int m,double ** in,int * status)22709 static double **OrthVectorSet( int n, int m, double **in, int *status ){
22710 /*
22711 *  Name:
22712 *     OrthVectorSet
22713 
22714 *  Purpose:
22715 *     Find a set of mutually orthogonal vectors.
22716 
22717 *  Type:
22718 *     Private function.
22719 
22720 *  Synopsis:
22721 *     #include "fitschan.h"
22722 *     double **OrthVectorSet( int n, int m, double **in, int *status )
22723 
22724 *  Class Membership:
22725 *     FitsChan member function.
22726 
22727 *  Description:
22728 *     A set of M vectors is supplied, each vector being N-dimensional.
22729 *     It is assumed that the supplied vectors span M axes within the
22730 *     N dimensional space. A pointer to a set of N vectors is returned.
22731 *     The first M returned vectors are copies of the M supplied vectors.
22732 *     The remaining returned vectors are unit vectors chosen to be
22733 *     orthogonal to all other vectors in the returned set.
22734 
22735 *  Parameters:
22736 *     n
22737 *        The number of dimensions
22738 *     m
22739 *        The number of supplied vectors.
22740 *     in
22741 *        A pointer to an array with "m" elements, each element being a
22742 *        pointer to an array with "n" elements. Each of these "n" element
22743 *        array holds one of the supplied vectors.
22744 *     status
22745 *        Pointer to the inherited status variable.
22746 
22747 *  Returned Value:
22748 *     The pointer to some newly allocated memory holding the returned N
22749 *     vectors. The pointer locates an array of N elements, each of which
22750 *     is a pointer to an array holding the N elements of a single vector.
22751 *     The memory (including the inner pointers) should be freed using
22752 *     astFree when no longer needed.
22753 
22754 *  Notes:
22755 *     -  NULL is returned if an error occurs.
22756 *     -  NULL is returned (without error) if the required vectors cannot
22757 *     be found (e.g. becuase the supplied M vectors span less than M axes).
22758 */
22759 
22760 /* Local Variables: */
22761    double **ret;
22762    int i;
22763    int bad;
22764 
22765 /* Initialise */
22766    ret = NULL;
22767 
22768 /* Check the inherited status. */
22769    if( !astOK ) return ret;
22770 
22771 /* Allocate required memory. */
22772    ret = astMalloc( sizeof( double * )*(size_t) n );
22773 
22774 /* Check memory can be used safely. */
22775    bad = 0;
22776    if( astOK ) {
22777 
22778 /* Copy the supplied vectors into the returned array. */
22779       for( i = 0; i < m; i++ ) {
22780          ret[ i ] = astStore( NULL, in[ i ], sizeof( double )*n );
22781       }
22782 
22783 /* For the remaining vectors, find a vector which is orthogonal to all
22784    the vectors currently in the returned set. */
22785       for( ; i < n; i++ ) {
22786          ret[ i ] = OrthVector( n, i, ret, status );
22787          if( !ret[ i ] ) bad = 1;
22788       }
22789    }
22790 
22791 /* Free the returned vectors if an error has occurred. */
22792    if( bad || !astOK ) {
22793       for( i = 0; ret && i < n; i++ ) ret[ i ] = astFree( ret[ i ] );
22794       ret = astFree( ret );
22795    }
22796 
22797 /* Return the answer. */
22798    return ret;
22799 }
22800 
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)22801 static AstMapping *OtherAxes( AstFitsChan *this, AstFrameSet *fs, double *dim,
22802                               int *wperm, char s, FitsStore *store,
22803                               double *crvals, int *axis_done,
22804                               const char *method, const char *class,
22805                               int *status ){
22806 
22807 /*
22808 *  Name:
22809 *     OtherAxes
22810 
22811 *  Purpose:
22812 *     Add values to a FitsStore describing unknown axes in a Frame.
22813 
22814 *  Type:
22815 *     Private function.
22816 
22817 *  Synopsis:
22818 *     #include "fitschan.h"
22819 
22820 *     AstMapping *OtherAxes( AstFitsChan *this, AstFrameSet *fs, double *dim,
22821 *                            int *wperm, char s, FitsStore *store,
22822 *                            double *crvals, int *axis_done,
22823 *                            const char *method, const char *class,
22824 *                            int *status )
22825 
22826 *  Class Membership:
22827 *     FitsChan member function.
22828 
22829 *  Description:
22830 *     FITS WCS keyword values are added to the supplied FitsStore which
22831 *     describe any as yet undescribed axes in the supplied FrameSet. These
22832 *     axes are assumed to be linear and to follow the conventions
22833 *     of FITS-WCS paper I (if in fact they are not linear, then the
22834 *     grid->iwc mapping determined by MakeIntWorld will not be linear and
22835 *     so the axes will be rejected).
22836 *
22837 *     Note, this function does not store values for keywords which define
22838 *     the transformation from pixel coords to Intermediate World Coords
22839 *     (CRPIX, PC and CDELT), but a Mapping is returned which embodies these
22840 *     values. This Mapping is from the current Frame in the FrameSet (WCS
22841 *     coords) to a Frame representing IWC. The IWC Frame has the same number
22842 *     of axes as the WCS Frame which may be greater than the number of base
22843 *     Frame (i.e. pixel) axes.
22844 
22845 *  Parameters:
22846 *     this
22847 *        Pointer to the FitsChan.
22848 *     fs
22849 *        Pointer to the FrameSet. The base Frame should represent FITS pixel
22850 *        coordinates, and the current Frame should represent FITS WCS
22851 *        coordinates. The number of base Frame axes should not exceed the
22852 *        number of current Frame axes.
22853 *     dim
22854 *        An array holding the image dimensions in pixels. AST__BAD can be
22855 *        supplied for any unknwon dimensions.
22856 *     wperm
22857 *        Pointer to an array of integers with one element for each axis of
22858 *        the current Frame. Each element holds the zero-based
22859 *        index of the FITS-WCS axis (i.e. the value of "i" in the keyword
22860 *        names "CTYPEi", "CRVALi", etc) which describes the Frame axis.
22861 *     s
22862 *        The co-ordinate version character. A space means the primary
22863 *        axis descriptions. Otherwise the supplied character should be
22864 *        an upper case alphabetical character ('A' to 'Z').
22865 *     store
22866 *        The FitsStore in which to store the FITS WCS keyword values.
22867 *     crvals
22868 *        Pointer to an array holding the default CRVAL value for each
22869 *        axis in the WCS Frame.
22870 *     axis_done
22871 *        An array of flags, one for each Frame axis, which indicate if a
22872 *        description of the corresponding axis has yet been stored in the
22873 *        FitsStore.
22874 *     method
22875 *        Pointer to a string holding the name of the calling method.
22876 *        This is only for use in constructing error messages.
22877 *     class
22878 *        Pointer to a string holding the name of the supplied object class.
22879 *        This is only for use in constructing error messages.
22880 *     status
22881 *        Pointer to the inherited status variable.
22882 
22883 *  Returned Value:
22884 *     If any axis descriptions were added to the FitsStore, a Mapping from
22885 *     the current Frame of the supplied FrameSet, to the IWC Frame is returned.
22886 *     Otherwise, a UnitMap is returned. Note, the Mapping only defines the IWC
22887 *     transformation for the described axes. Any other (previously
22888 *     described) axes are passed unchanged by the returned Mapping.
22889 */
22890 
22891 /* Local Variables: */
22892    AstFitsTable *table;    /* Pointer to structure holding -TAB table info */
22893    AstFrame *wcsfrm;       /* WCS Frame within FrameSet */
22894    AstMapping *axmap;      /* Mapping from WCS to IWC */
22895    AstMapping *map;        /* FITS pixel->WCS Mapping */
22896    AstMapping *ret;        /* Returned Mapping */
22897    AstMapping *tmap0;      /* Pointer to a temporary Mapping */
22898    AstMapping *tmap1;      /* Pointer to a temporary Mapping */
22899    AstPermMap *pm;         /* PermMap pointer */
22900    AstPointSet *pset1;     /* PointSet holding central pixel position */
22901    AstPointSet *pset2;     /* PointSet holding reference WCS position */
22902    char buf[80];           /* Text buffer */
22903    const char *lab;        /* Pointer to axis Label */
22904    const char *sym;        /* Pointer to axis Symbol */
22905    double **ptr1;          /* Pointer to data for pset1 */
22906    double **ptr2;          /* Pointer to data for pset2 */
22907    double *lbnd_p;         /* Pointer to array of lower pixel bounds */
22908    double *ubnd_p;         /* Pointer to array of upper pixel bounds */
22909    double crval;           /* The value for the FITS CRVAL keyword */
22910    int *inperm;            /* Pointer to permutation array for input axes */
22911    int *outperm;           /* Pointer to permutation array for output axes */
22912    int extver;             /* Table version number for -TAB headers */
22913    int fits_i;             /* FITS WCS axis index */
22914    int i;                  /* Loop count */
22915    int iax;                /* WCS Frame axis index */
22916    int icolindex;          /* Index of table column holding index vector */
22917    int icolmain;           /* Index of table column holding main coord array */
22918    int interp;             /* Interpolation method for look-up tables */
22919    int log_axis;           /* Is the axis logarithmically spaced? */
22920    int nother;             /* Number of axes still to be described */
22921    int npix;               /* Number of pixel axes */
22922    int nwcs;               /* Number of WCS axes */
22923    int tab_axis;           /* Can the axis be described by the -TAB algorithm? */
22924 
22925 /* Initialise */
22926    ret = NULL;
22927 
22928 /* Check the inherited status. */
22929    if( !astOK ) return ret;
22930 
22931 /* Get the number of WCS axes. */
22932    nwcs = astGetNaxes( fs );
22933 
22934 /* Count the number of WCS axes which have not yet been described. */
22935    nother = 0;
22936    for( iax = 0; iax < nwcs; iax++ ) {
22937       if( ! axis_done[ iax ] ) nother++;
22938    }
22939 
22940 /* Only proceed if there are some axes to described. */
22941    if( nother ) {
22942 
22943 /* Get a pointer to the WCS Frame. */
22944       wcsfrm = astGetFrame( fs, AST__CURRENT );
22945 
22946 /* Get a pointer to the pixel->wcs Mapping. */
22947       map = astGetMapping( fs, AST__BASE, AST__CURRENT );
22948 
22949 /* Store the number of pixel and WCS axes. */
22950       npix = astGetNin( fs );
22951       nwcs = astGetNout( fs );
22952 
22953 /* Store the upper and lower pixel bounds. */
22954       lbnd_p = astMalloc( sizeof( double )*(size_t) npix );
22955       ubnd_p = astMalloc( sizeof( double )*(size_t) npix );
22956       if( astOK ) {
22957          for( iax = 0; iax < npix; iax++ ) {
22958             lbnd_p[ iax ] = 1.0;
22959             ubnd_p[ iax ] = ( dim[ iax ] != AST__BAD ) ? dim[ iax ] : 500;
22960          }
22961       }
22962 
22963 /* Transform the central pixel coords into WCS coords */
22964       pset1 = astPointSet( 1, npix, "", status );
22965       ptr1 = astGetPoints( pset1 );
22966       pset2 = astPointSet( 1, nwcs, "", status );
22967       ptr2 = astGetPoints( pset2 );
22968       if( astOK ) {
22969          for( iax = 0; iax < npix; iax++ ) {
22970             ptr1[ iax ][ 0 ] = ( dim[ iax ] != AST__BAD ) ? floor( 0.5*dim[ iax ] ) : 1.0;
22971          }
22972          (void) astTransform( map, pset1, 1, pset2 );
22973       }
22974 
22975 /* Loop round all WCS axes, producing descriptions of any axes which have not
22976    yet been described. */
22977       for( iax = 0; iax < nwcs && astOK; iax++ ) {
22978          if( ! axis_done[ iax ] ) {
22979 
22980 /* Get the (one-based) FITS WCS axis index to use for this Frame axis. */
22981             fits_i = wperm[ iax ];
22982 
22983 /* Use the supplied default CRVAL value. If bad, use the WCS value
22984    corresponding to the central pixel found above (if this value is bad,
22985    abort). */
22986             crval = crvals ? crvals[ iax ] : AST__BAD;
22987             if( crval == AST__BAD ) crval = ptr2[ iax ][ 0 ];
22988             if( crval == AST__BAD ) {
22989                break;
22990             } else {
22991                SetItem( &(store->crval), fits_i, 0, s, crval, status );
22992             }
22993 
22994 /* Initialise flags indicating the type of the axis. */
22995             log_axis = 0;
22996             tab_axis = 0;
22997 
22998 /* Get the table version number to use if we end up using the -TAB
22999    algorithm. This is the set value of the TabOK attribute (if positive). */
23000             extver = astGetTabOK( this );
23001 
23002 /* See if the axis is linear. If so, create a ShiftMap which subtracts off
23003    the CRVAL value. */
23004             if( IsMapLinear( map, lbnd_p, ubnd_p, iax, status ) ) {
23005                crval = -crval;
23006                tmap0 = (AstMapping *) astShiftMap( 1, &crval, "", status );
23007                axmap = AddUnitMaps( tmap0, iax, nwcs, status );
23008                tmap0 = astAnnul( tmap0 );
23009                crval = -crval;
23010 
23011 /* If it is not linear, see if it is logarithmic. If the "log" algorithm is
23012    appropriate (as defined in FITS-WCS paper III), the supplied Frame (s) is
23013    related to pixel coordinate (p) by
23014       s = Sr.EXP( a*p - b ). If this
23015    is the case, the log of s will be linearly related to pixel coordinates.
23016    Test this. If the test is passed a Mapping is returned from WCS to IWC. */
23017             } else if( (axmap = LogAxis( map, iax, nwcs, lbnd_p, ubnd_p,
23018                                          crval, status ) ) ) {
23019                log_axis = 1;
23020 
23021 /* If it is not linear or logarithmic, and the TabOK attribute is
23022    non-zero, describe it using the -TAB algorithm. */
23023             } else if( extver > 0 ){
23024 
23025 /* Get any pre-existing FitsTable from the FitsStore. This is the table
23026    in which the tabular data will be stored (if the Mapping can be expressed
23027    in -TAB form). */
23028                if( !astMapGet0A( store->tables, AST_TABEXTNAME, &table ) ) table = NULL;
23029 
23030 /* See if the Mapping can be expressed in -TAB form. */
23031                tmap0 = IsMapTab1D( map, 1.0, NULL, wcsfrm, dim, iax, fits_i,
23032                                    &table, &icolmain, &icolindex, &interp,
23033                                    status );
23034                if( tmap0 ) {
23035                   tab_axis = 1;
23036 
23037 /* The values stored in the table index vector are GRID coords. So we
23038    need to ensure that IWC are equivalent to GRID coords. So set CRVAL
23039    to zero. */
23040                   crval = 0.0;
23041 
23042 /* Store TAB-specific values in the FitsStore. First the name of the
23043    FITS binary table extension holding the coordinate info. */
23044                   SetItemC( &(store->ps), fits_i, 0, s, AST_TABEXTNAME, status );
23045 
23046 /* Next the table version number. This is the set (positive) value for the
23047    TabOK attribute. */
23048                   SetItem( &(store->pv), fits_i, 1, s, extver, status );
23049 
23050 /* Also store the table version in the binary table header. */
23051                   astSetFitsI( table->header, "EXTVER", extver,
23052                                "Table version number", 0 );
23053 
23054 /* Next the name of the table column containing the main coords array. */
23055                   SetItemC( &(store->ps), fits_i, 1, s,
23056                             astColumnName( table, icolmain ), status );
23057 
23058 /* Next the name of the column containing the index array */
23059                   if( icolindex >= 0 ) SetItemC( &(store->ps), fits_i, 2, s,
23060                                   astColumnName( table, icolindex ), status );
23061 
23062 /* The interpolation method (an AST extension to the published -TAB
23063    algorithm, communicated through the QVi_4a keyword). */
23064                   SetItem( &(store->pv), fits_i, 4, s, interp, status );
23065 
23066 /* Also store the FitsTable itself in the FitsStore. */
23067                   astMapPut0A( store->tables, AST_TABEXTNAME, table, NULL );
23068 
23069 /* Create the WCS -> IWC Mapping (AST uses grid coords as IWC coords for
23070    the -TAB algorithm). First, get a Mapping that combines the TAB axis
23071    Mapping( tmap0) in parallel with one or two UnitMaps in order to put
23072    the TAB axis at the required index. */
23073                   tmap1 = AddUnitMaps( tmap0, iax, nwcs, status );
23074 
23075 /* Now get a PermMap that permutes the WCS axes into the FITS axis order. */
23076                   inperm = astMalloc( sizeof( double )*nwcs );
23077                   outperm = astMalloc( sizeof( double )*nwcs );
23078                   if( astOK ) {
23079                      for( i = 0; i < nwcs; i++ ) {
23080                         inperm[ i ] = wperm[ i ];
23081                         outperm[ wperm[ i ] ] = i;
23082                      }
23083                   }
23084                   pm = astPermMap( nwcs, inperm, nwcs, outperm, NULL, "",
23085                                    status );
23086 
23087 /* Combine these two Mappings in series, to get the Mapping from WCS to
23088    IWC. */
23089                   axmap = (AstMapping *) astCmpMap( pm, tmap1, 1, " ",
23090                                                     status );
23091 
23092 /* Free resources. */
23093                   inperm = astFree( inperm );
23094                   outperm = astFree( outperm );
23095                   pm = astAnnul( pm );
23096                   tmap0 = astAnnul( tmap0 );
23097                   tmap1 = astAnnul( tmap1 );
23098                }
23099                if( table ) table = astAnnul( table );
23100             }
23101 
23102 /* If the axis cannot be described by any of the above methods, we
23103    pretend it is linear. This will generate a non-linear PIXEL->IWC
23104    mapping later (in MakeIntWorld) which will cause the write operation
23105    to fail. */
23106             if( !axmap ) {
23107                crval = -crval;
23108                tmap0 = (AstMapping *) astShiftMap( 1, &crval, "", status );
23109                axmap = AddUnitMaps( tmap0, iax, nwcs, status );
23110                tmap0 = astAnnul( tmap0 );
23111                crval = -crval;
23112             }
23113 
23114 /* Combine the Mapping for this axis in series with those of earlier axes. */
23115             if( ret ) {
23116                tmap0 = (AstMapping *) astCmpMap( ret, axmap, 1, "", status );
23117                (void) astAnnul( ret );
23118                ret = tmap0;
23119             } else {
23120                ret = astClone( axmap );
23121             }
23122 
23123 /* Get axis label and symbol. */
23124             sym =  astGetSymbol( wcsfrm, iax );
23125             lab =  astGetLabel( wcsfrm, iax );
23126 
23127 /* The axis symbols are taken as the CTYPE values. Append "-LOG" or "-TAB" if
23128    the axis is logarithmic or tabular. */
23129             if( sym && strlen( sym ) ) {
23130                (void) sprintf( buf, "%s", sym );
23131             } else {
23132                (void) sprintf( buf, "AXIS%d", iax + 1 );
23133             }
23134             if( log_axis ) {
23135                SetAlgCode( buf, "-LOG", status );
23136             } else if( tab_axis ) {
23137                SetAlgCode( buf, "-TAB", status );
23138             }
23139             SetItemC( &(store->ctype), fits_i, 0, s, buf, status );
23140 
23141 /* The axis labels are taken as the comment for the CTYPE keywords and as
23142    the CNAME keyword (but only if a label has been set and is different to
23143    the symbol). */
23144             if( lab && lab[ 0 ] && astTestLabel( wcsfrm, iax ) && strcmp( sym, lab ) ) {
23145                SetItemC( &(store->ctype_com), fits_i, 0, s, (char *) lab, status );
23146                SetItemC( &(store->cname), fits_i, 0, s, (char *) lab, status );
23147             } else {
23148                sprintf( buf, "Type of co-ordinate on axis %d", iax + 1 );
23149                SetItemC( &(store->ctype_com), fits_i, 0, s, buf, status );
23150             }
23151 
23152 /* If a value has been set for the axis units, use it as CUNIT. */
23153             if( astTestUnit( wcsfrm, iax ) ){
23154                SetItemC( &(store->cunit), fits_i, 0, s, (char *) astGetUnit( wcsfrm, iax ), status );
23155             }
23156 
23157 /* Indicate this axis has now been described. */
23158             axis_done[ iax ] = 1;
23159 
23160 /* Release Resources. */
23161             axmap = astAnnul( axmap );
23162          }
23163       }
23164 
23165 /* Release Resources. */
23166       wcsfrm = astAnnul( wcsfrm );
23167       map = astAnnul( map );
23168       pset1 = astAnnul( pset1 );
23169       pset2 = astAnnul( pset2 );
23170       lbnd_p = astFree( lbnd_p );
23171       ubnd_p = astFree( ubnd_p );
23172    }
23173 
23174 /* If we have a Mapping to return, simplify it. Otherwise, create
23175    a UnitMap to return. */
23176    if( ret ) {
23177       tmap0 = ret;
23178       ret = astSimplify( tmap0 );
23179       tmap0 =  astAnnul( tmap0 );
23180    } else {
23181       ret = (AstMapping *) astUnitMap( nwcs, "", status );
23182    }
23183 
23184 /* Return the result. */
23185    return ret;
23186 }
23187 
PCFromStore(AstFitsChan * this,FitsStore * store,const char * method,const char * class,int * status)23188 static int PCFromStore( AstFitsChan *this, FitsStore *store,
23189                         const char *method, const char *class, int *status ){
23190 
23191 /*
23192 *  Name:
23193 *     PCFromStore
23194 
23195 *  Purpose:
23196 *     Store WCS keywords in a FitsChan using FITS-PC encoding.
23197 
23198 *  Type:
23199 *     Private function.
23200 
23201 *  Synopsis:
23202 *     int PCFromStore( AstFitsChan *this, FitsStore *store,
23203 *                      const char *method, const char *class, int *status )
23204 
23205 *  Class Membership:
23206 *     FitsChan
23207 
23208 *  Description:
23209 *     A FitsStore is a structure containing a generalised represention of
23210 *     a FITS WCS FrameSet. Functions exist to convert a FitsStore to and
23211 *     from a set of FITS header cards (using a specified encoding), or
23212 *     an AST FrameSet. In other words, a FitsStore is an encoding-
23213 *     independant intermediary staging post between a FITS header and
23214 *     an AST FrameSet.
23215 *
23216 *     This function copies the WCS information stored in the supplied
23217 *     FitsStore into the supplied FitsChan, using FITS-PC encoding.
23218 *
23219 *     Zero is returned if the primary axis descriptions cannot be produced.
23220 *     Whether or not secondary axis descriptions can be produced does not
23221 *     effect the returned value (i.e. failure to produce a specific set of
23222 *     secondary axes does not prevent other axis descriptions from being
23223 *     produced).
23224 
23225 *  Parameters:
23226 *     this
23227 *        Pointer to the FitsChan.
23228 *     store
23229 *        Pointer to the FitsStore.
23230 *     method
23231 *        Pointer to a string holding the name of the calling method.
23232 *        This is only for use in constructing error messages.
23233 *     class
23234 *        Pointer to a string holding the name of the supplied object class.
23235 *        This is only for use in constructing error messages.
23236 *     status
23237 *        Pointer to the inherited status variable.
23238 
23239 *  Returned Value:
23240 *     A value of 1 is returned if succesfull, and zero is returned
23241 *     otherwise.
23242 */
23243 
23244 /* Local Variables: */
23245    char *comm;         /* Pointer to comment string */
23246    char *cval;         /* Pointer to string keyword value */
23247    char combuf[80];    /* Buffer for FITS card comment */
23248    char keyname[10];   /* Buffer for keyword name string */
23249    char primsys[20];   /* Buffer for primnary RADECSYS value */
23250    char type[MXCTYPELEN];/* Buffer for CTYPE value */
23251    char s;             /* Co-ordinate version character */
23252    char sign[2];       /* Fraction's sign character */
23253    char sup;           /* Upper limit on s */
23254    double *c;          /* Pointer to next array element */
23255    double *d;          /* Pointer to next array element */
23256    double *matrix;     /* Pointer to Frame PC/CD matrix */
23257    double *primpc;     /* Pointer to primary PC/CD matrix */
23258    double fd;          /* Fraction of a day */
23259    double mjd99;       /* MJD at start of 1999 */
23260    double primdt;      /* Primary mjd-obs value */
23261    double primeq;      /* Primary equinox value */
23262    double primln;      /* Primary lonpole value */
23263    double primlt;      /* Primary latpole value */
23264    double primpv[10];  /* Primary projection parameter values */
23265    double val;         /* General purpose value */
23266    int axlat;          /* Index of latitude FITS WCS axis */
23267    int axlon;          /* Index of longitude FITS WCS axis */
23268    int axspec;         /* Index of spectral FITS WCS axis */
23269    int i;              /* Axis index */
23270    int ihmsf[ 4 ];     /* Hour, minute, second, fractional second */
23271    int is;             /* Co-ordinate version index */
23272    int iymdf[ 4 ];     /* Year, month, date, fractional day */
23273    int j;              /* Axis index */
23274    int jj;             /* SlaLib status */
23275    int m;              /* Parameter index */
23276    int maxm;           /* Upper limit on m */
23277    int naxis;          /* No. of axes */
23278    int ok;             /* Frame written out succesfully? */
23279    int prj;            /* Projection type */
23280    int ret;            /* Returned value. */
23281 
23282 /* Initialise */
23283    ret = 0;
23284 
23285 /* Check the inherited status. */
23286    if( !astOK ) return ret;
23287 
23288 /* Find the number of co-ordinate versions in the FitsStore. FITS-PC
23289    can only encode 10 axis descriptions (including primary). */
23290    sup = GetMaxS( &(store->crval), status );
23291    if( sup > 'I' ) return ret;
23292 
23293 /* Initialise */
23294    primdt = AST__BAD;
23295    primeq = AST__BAD;
23296    primln = AST__BAD;
23297    primlt = AST__BAD;
23298 
23299 /* Loop round all co-ordinate versions (0-9) */
23300    primpc = NULL;
23301    for( s = ' '; s <= sup && astOK; s++ ){
23302       is = s - 'A' + 1;
23303 
23304 /* Assume the Frame can be created succesfully. */
23305       ok = 1;
23306 
23307 /* Save the number of wcs axes */
23308       val = GetItem( &(store->wcsaxes), 0, 0, s, NULL, method, class, status );
23309       if( val != AST__BAD ) {
23310          naxis = (int) ( val + 0.5 );
23311          SetValue( this, FormatKey( "WCSAXES", -1, -1, s, status ),
23312                    &naxis, AST__INT, "Number of WCS axes", status );
23313       } else {
23314          naxis = GetMaxJM( &(store->crpix), s, status ) + 1;
23315       }
23316 
23317 /* PC matrix:
23318    --------- */
23319 
23320 /* This encoding does not allow the PC matrix to be specified for each
23321    version - instead they all share the primary PC matrix. Therefore we
23322    need to check that all versions can use the primary PC matrix. Allocate
23323    memory to hold the PC matrix for this version. */
23324       matrix = (double *) astMalloc( sizeof(double)*naxis*naxis );
23325       if( matrix ){
23326 
23327 /* Fill these array with the values supplied in the FitsStore. */
23328          c = matrix;
23329          for( i = 0; i < naxis; i++ ){
23330             for( j = 0; j < naxis; j++ ){
23331                *c = GetItem( &(store->pc), i, j, s, NULL, method, class, status );
23332                if( *c == AST__BAD ) *c = ( i == j ) ? 1.0 : 0.0;
23333                c++;
23334             }
23335          }
23336 
23337 /* If we are currently processing the primary axis description, take
23338    a copy of the PC matrix. */
23339          if( s == ' ' ) {
23340             primpc = (double *) astStore(  NULL, (void *) matrix,
23341                                            sizeof(double)*naxis*naxis );
23342 
23343 /* Store each matrix element in turn. */
23344             c = matrix;
23345             for( i = 0; i < naxis; i++ ){
23346                for( j = 0; j < naxis; j++ ){
23347 
23348 /* Set the element bad if it takes its default value. */
23349                   val = *(c++);
23350                   if( i == j ){
23351                      if( EQUAL( val, 1.0 ) ) val = AST__BAD;
23352                   } else {
23353                      if( EQUAL( val, 0.0 ) ) val = AST__BAD;
23354                   }
23355 
23356 /* Only store elements which do not take their default values. */
23357                   if( val != AST__BAD ){
23358                      sprintf( keyname, "PC%.3d%.3d", i + 1, j + 1 );
23359                      SetValue( this, keyname, &val, AST__FLOAT, NULL, status );
23360                   }
23361                }
23362             }
23363 
23364 /* For secondary axis descriptions, a check is made that the PC values are
23365    the same as the primary PC values stored earlier. If not, the current
23366    Frame cannot be stored as a secondary axis description so continue on
23367    to the next Frame. */
23368          } else {
23369             if( primpc ){
23370                c = matrix;
23371                d = primpc;
23372                for( i = 0; i < naxis; i++ ){
23373                   for( j = 0; j < naxis; j++ ){
23374                      if( !EQUAL( *c, *d ) ){
23375                         ok = 0;
23376                      } else {
23377                         c++;
23378                         d++;
23379                      }
23380                   }
23381                }
23382 
23383 /* Continue with the next Frame if the PC matrix for this Frame is different
23384    to the primary PC matrix. */
23385                if( !ok ) goto next;
23386             }
23387          }
23388          matrix = (double *) astFree( (void *) matrix );
23389       }
23390 
23391 /* CDELT:
23392    ------ */
23393       for( i = 0; i < naxis; i++ ){
23394          val = GetItem( &(store->cdelt), i, 0, s, NULL, method, class, status );
23395          if( val == AST__BAD ) {
23396             ok = 0;
23397             goto next;
23398          }
23399          sprintf( combuf, "Pixel scale on axis %d", i + 1 );
23400          if( s == ' ' ) {
23401             sprintf( keyname, "CDELT%d", i + 1 );
23402          } else {
23403             sprintf( keyname, "C%dELT%d", is, i + 1 );
23404          }
23405          SetValue( this, keyname, &val, AST__FLOAT, combuf, status );
23406       }
23407 
23408 /* CRPIX:
23409    ------ */
23410       for( j = 0; j < naxis; j++ ){
23411          val = GetItem( &(store->crpix), 0, j, s, NULL, method, class, status );
23412          if( val == AST__BAD ) {
23413             ok = 0;
23414             goto next;
23415          }
23416          sprintf( combuf, "Reference pixel on axis %d", j + 1 );
23417          if( s == ' ' ) {
23418             sprintf( keyname, "CRPIX%d", j + 1 );
23419          } else {
23420             sprintf( keyname, "C%dPIX%d", is, j + 1 );
23421          }
23422          SetValue( this, keyname, &val, AST__FLOAT, combuf, status );
23423       }
23424 
23425 /* CRVAL:
23426    ------ */
23427       for( i = 0; i < naxis; i++ ){
23428          val = GetItem( &(store->crval), i, 0, s, NULL, method, class, status );
23429          if( val == AST__BAD ) {
23430             ok = 0;
23431             goto next;
23432          }
23433          sprintf( combuf, "Value at ref. pixel on axis %d", i + 1 );
23434          if( s == ' ' ) {
23435             sprintf( keyname, "CRVAL%d", i + 1 );
23436          } else {
23437             sprintf( keyname, "C%dVAL%d", is, i + 1 );
23438          }
23439          SetValue( this, keyname, &val, AST__FLOAT, combuf, status );
23440       }
23441 
23442 /* CTYPE:
23443    ------ */
23444       for( i = 0; i < naxis; i++ ){
23445          cval = GetItemC( &(store->ctype), i, 0, s, NULL, method, class, status );
23446          if( !cval || !strcmp( cval + 4, "-TAB" ) ) {
23447             ok = 0;
23448             goto next;
23449          }
23450          comm = GetItemC( &(store->ctype_com), i, 0, s, NULL, method, class, status );
23451          if( !comm ) {
23452             sprintf( combuf, "Type of co-ordinate on axis %d", i + 1 );
23453             comm = combuf;
23454          }
23455          if( s == ' ' ) {
23456             sprintf( keyname, "CTYPE%d", i + 1 );
23457          } else {
23458             sprintf( keyname, "C%dYPE%d", is, i + 1 );
23459          }
23460 
23461 /* FITS-PC cannot handle celestial axes of type "xxLT" or "xxLN".
23462    Neither can it handle the "-TAB". */
23463          if( !strncmp( cval + 2, "LT-", 3 ) ||
23464              !strncmp( cval + 2, "LN-", 3 ) ||
23465              !strncmp( cval + 4, "-TAB", 4 ) ){
23466             ok = 0;
23467             goto next;
23468          }
23469 
23470 /* Extract the projection type as specified by the last 4 characters
23471    in the CTYPE keyword value. This will be AST__WCSBAD for non-celestial
23472    axes. */
23473          prj = astWcsPrjType( cval + 4 );
23474 
23475 /* Change the new SFL projection code to to the older equivalent GLS */
23476          if( prj == AST__SFL ) {
23477             strcpy( type, cval );
23478             (void) strcpy( type + 4, "-GLS" );
23479             cval = type;
23480          }
23481 
23482 /* FITS-PC cannot handle the AST-specific TPN projection. */
23483          if( prj == AST__TPN ) {
23484             ok = 0;
23485             goto next;
23486          }
23487 
23488 /* Store the CTYPE value */
23489          SetValue( this, keyname, &cval, AST__STRING, comm, status );
23490       }
23491 
23492 /* Get and save CUNIT for all intermediate axes. These are NOT required, so
23493    do not pass on if they are not available. */
23494       for( i = 0; i < naxis; i++ ){
23495          cval = GetItemC( &(store->cunit), i, 0, s, NULL, method, class, status );
23496          if( cval ) {
23497             sprintf( combuf, "Units for axis %d", i + 1 );
23498             if( s == ' ' ) {
23499                sprintf( keyname, "CUNIT%d", i + 1 );
23500             } else {
23501                sprintf( keyname, "C%dNIT%d", is, i + 1 );
23502             }
23503             SetValue( this, keyname, &cval, AST__STRING, combuf, status );
23504          }
23505       }
23506 
23507 /* Get and save RADESYS. This is NOT required, so do not pass on if it is
23508    not available. If RADECSYS is provided for a secondary axis, it must
23509    be the same as the primary axis RADECSYS value. If it is not, pass on to
23510    the next Frame. */
23511       cval = GetItemC( &(store->radesys), 0, 0, s, NULL, method, class, status );
23512       if( cval ) {
23513          if( s == ' ' ) {
23514             strcpy( primsys, cval );
23515             SetValue( this, "RADECSYS", &cval, AST__STRING,
23516                       "Reference frame for RA/DEC values", status );
23517          } else if( strcmp( cval, primsys ) ) {
23518             ok = 0;
23519             goto next;
23520          }
23521       }
23522 
23523 /* Reference equinox. This is NOT required, so do not pass on if it is
23524    not available. If equinox is provided for a secondary axis, it must
23525    be the same as the primary axis equinox value. If it is not, pass on to
23526    the next Frame. */
23527       val = GetItem( &(store->equinox), 0, 0, s, NULL, method, class, status );
23528       if( s == ' ' ) {
23529          primeq = val;
23530          if( val != AST__BAD ) SetValue( this, "EQUINOX", &val, AST__FLOAT,
23531                                          "Epoch of reference equinox", status );
23532       } else if( !EQUAL( val, primeq ) ){
23533          ok = 0;
23534          goto next;
23535       }
23536 
23537 /* Latitude of native north pole. This is NOT required, so do not pass on
23538    if it is not available. If latpole is provided for a secondary axis, it
23539    must be the same as the primary axis value. If it is not, pass on to
23540    the next Frame. */
23541       val = GetItem( &(store->latpole), 0, 0, s, NULL, method, class, status );
23542       if( s == ' ' ) {
23543          primlt = val;
23544          if( val != AST__BAD ) SetValue( this, "LATPOLE", &val, AST__FLOAT,
23545                                          "Latitude of native north pole", status );
23546       } else if( !EQUALANG( val, primlt ) ){
23547          ok = 0;
23548          goto next;
23549       }
23550 
23551 /* Longitude of native north pole. This is NOT required, so do not pass on
23552    if it is not available. If lonpole is provided for a secondary axis, it
23553    must be the same as the primary axis value. If it is not, pass on to
23554    the next Frame. */
23555       val = GetItem( &(store->lonpole), 0, 0, s, NULL, method, class, status );
23556       if( s == ' ' ) {
23557          primln = val;
23558          if( val != AST__BAD ) SetValue( this, "LONGPOLE", &val, AST__FLOAT,
23559                                          "Longitude of native north pole", status );
23560       } else if( !EQUALANG( val, primln ) ){
23561          ok = 0;
23562          goto next;
23563       }
23564 
23565 /* Date of observation. This is NOT required, so do not pass on if it is
23566    not available. If mjd-obs is provided for a secondary axis, it must be
23567    the same as the primary axis value. If it is not, pass on to the next
23568    Frame. */
23569       val = GetItem( &(store->mjdobs), 0, 0, s, NULL, method, class, status );
23570       if( s == ' ' ) {
23571          primdt = val;
23572          if( val != AST__BAD ) {
23573             SetValue( this, "MJD-OBS", &val, AST__FLOAT,
23574                       "Modified Julian Date of observation", status );
23575 
23576 /* The format used for the DATE-OBS keyword depends on the value of the
23577    keyword. For DATE-OBS < 1999.0, use the old "dd/mm/yy" format.
23578    Otherwise, use the new "ccyy-mm-ddThh:mm:ss[.ssss]" format. */
23579             palCaldj( 99, 1, 1, &mjd99, &jj );
23580             if( val < mjd99 ) {
23581                palDjcal( 0, val, iymdf, &jj );
23582                sprintf( combuf, "%2.2d/%2.2d/%2.2d", iymdf[ 2 ], iymdf[ 1 ],
23583                         iymdf[ 0 ] - ( ( iymdf[ 0 ] > 1999 ) ? 2000 : 1900 ) );
23584             } else {
23585                palDjcl( val, iymdf, iymdf+1, iymdf+2, &fd, &jj );
23586                palDd2tf( 3, fd, sign, ihmsf );
23587                sprintf( combuf, "%4.4d-%2.2d-%2.2dT%2.2d:%2.2d:%2.2d.%3.3d",
23588                         iymdf[0], iymdf[1], iymdf[2], ihmsf[0], ihmsf[1],
23589                         ihmsf[2], ihmsf[3] );
23590             }
23591 
23592 /* Now store the formatted string in the FitsChan. */
23593             cval = combuf;
23594             SetValue( this, "DATE-OBS", &cval, AST__STRING,
23595                       "Date of observation", status );
23596          }
23597       } else if( !EQUAL( val, primdt ) ){
23598          ok = 0;
23599          goto next;
23600       }
23601 
23602 /* Look for the celestial and spectral axes. */
23603       FindLonLatSpecAxes( store, s, &axlon, &axlat, &axspec, method, class, status );
23604 
23605 /* If both longitude and latitude axes are present ...*/
23606       if( axlon >= 0 && axlat >= 0 ) {
23607 
23608 /* Get the CTYPE values for the latitude axis. */
23609          cval = GetItemC( &(store->ctype), axlat, 0, s, NULL, method, class, status );
23610 
23611 /* Extract the projection type as specified by the last 4 characters
23612    in the CTYPE keyword value. */
23613          prj = ( cval ) ? astWcsPrjType( cval + 4 ) : AST__WCSBAD;
23614 
23615 /* Projection parameters. If provided for a secondary axis, they must be
23616    the same as the primary axis value. If it is not, pass on to the next
23617    Frame. PC encoding ignores parameters associated with the longitude
23618    axis. The old PC TAN projection did not have any parameters.
23619    Pass on if a TAN projection with parameters is found.  The number of
23620    parameters was limited to 10. Pass on if more than 10 are supplied. */
23621          maxm = GetMaxJM( &(store->pv), ' ', status );
23622          for( i = 0; i < naxis; i++ ){
23623             if( i != axlon ) {
23624                for( m = 0; m <= maxm; m++ ){
23625                   val = GetItem( &(store->pv), i, m, s, NULL, method, class, status );
23626                   if( s == ' ' ){
23627                      if( val != AST__BAD ) {
23628                         if( i != axlat || prj == AST__TAN || m >= 10 ){
23629                            ok = 0;
23630                            goto next;
23631                         } else {
23632                            SetValue( this, FormatKey( "PROJP", m, -1, ' ', status ), &val,
23633                                      AST__FLOAT, "Projection parameter", status );
23634                         }
23635                      }
23636                      if( i == axlat && m < 10 ) primpv[m] = val;
23637                   } else {
23638                      if( ( ( i != axlat || m >= 10 ) && val != AST__BAD ) ||
23639                          ( i == axlat && m < 10 && !EQUAL( val, primpv[m] ) ) ){
23640                         ok = 0;
23641                         goto next;
23642                      }
23643                   }
23644                }
23645             }
23646          }
23647       }
23648 
23649 /* See if a Frame was sucessfully written to the FitsChan. */
23650 next:
23651       ok = ok && astOK;
23652 
23653 /* If so, indicate we have something to return. */
23654       if( ok ) ret = 1;
23655 
23656 /* Clear any error status so we can continue to produce the next Frame.
23657    Retain the error if the primary axes could not be produced. After the
23658    primary axes, do the A axes. */
23659       if( s != ' ' ) {
23660          astClearStatus;
23661       } else {
23662          s = 'A' - 1;
23663       }
23664 
23665 /* Remove the secondary "new" flags from the FitsChan. This flag is
23666    associated with cards which have been added to the FitsChan during
23667    this pass through the main loop in this function. If the Frame was
23668    written out succesfully, just clear the flags. If anything went wrong
23669    with this Frame, remove the flagged cards from the FitsChan. */
23670       FixNew( this, NEW2, !ok, method, class, status );
23671 
23672 /* Set the current card so that it points to the last WCS-related keyword
23673    in the FitsChan (whether previously read or not). */
23674       FindWcs( this, 1, 1, 0, method, class, status );
23675    }
23676 
23677 /* Annul the array holding the primary PC matrix. */
23678    primpc = (double *) astFree( (void *) primpc );
23679 
23680 /* Return zero or ret depending on whether an error has occurred. */
23681    return astOK ? ret : 0;
23682 }
23683 
PreQuote(const char * value,char string[AST__FITSCHAN_FITSCARDLEN-FITSNAMLEN-3],int * status)23684 static void PreQuote( const char *value,
23685                       char string[ AST__FITSCHAN_FITSCARDLEN - FITSNAMLEN - 3 ], int *status ) {
23686 
23687 /*
23688 *  Name:
23689 *     PreQuote
23690 
23691 *  Purpose:
23692 *     Pre-quote FITS character data.
23693 
23694 *  Type:
23695 *     Private function.
23696 
23697 *  Synopsis:
23698 *     #include "fitschan.h"
23699 *     void PreQuote( const char *value,
23700 *                    char string[ AST__FITSCHAN_FITSCARDLEN - FITSNAMLEN - 3 ] )
23701 
23702 *  Class Membership:
23703 *     FitsChan member function.
23704 
23705 *  Description:
23706 *     This function processes a string value in such a way that it can
23707 *     be stored as a FITS character value (associated with a keyword)
23708 *     and later retrieved unchanged, except for possible truncation.
23709 *
23710 *     This pre-processing is necessary because FITS does not regard
23711 *     trailing white space as significant, so it is lost. This
23712 *     function adds double quote (") characters around the string if
23713 *     it is necessary in order to prevent this loss. These quotes are
23714 *     also added to zero-length strings and to strings that are
23715 *     already quoted (so that the original quotes are not lost when
23716 *     they are later un-quoted).
23717 *
23718 *     This function will silently truncate any string that is too long
23719 *     to be stored as a FITS character value, but will ensure that the
23720 *     maximum number of characters are retained, taking account of any
23721 *     quoting required.
23722 
23723 *  Parameters:
23724 *     value
23725 *        Pointer to a constant null-terminated string containing the
23726 *        input character data to be quoted. All white space is
23727 *        significant.
23728 *     string
23729 *        A character array into which the result string will be
23730 *        written, with a terminating null. The maximum number of
23731 *        characters from the input string that can be accommodated in
23732 *        this is (AST__FITSCHAN_FITSCARDLEN - FITSNAMLEN - 4), but this
23733 *        will be reduced if quoting is necessary.
23734 
23735 *  Notes:
23736 *     - The UnPreQuote function should be used to reverse the effect
23737 *     of this function on a string (apart from any truncation).
23738 */
23739 
23740 /* Local Variables: */
23741    int dq;                       /* Number of double quotes needed */
23742    int dquotes;                  /* Final number of double quotes */
23743    int i;                        /* Loop counter for input characters */
23744    int j;                        /* Counter for output characters */
23745    int nc;                       /* Number of characters to be accommodated */
23746    int sq;                       /* Number of single quotes needed */
23747 
23748 /* Check the global error status. */
23749    if ( !astOK ) return;
23750 
23751 /* Initialise, setting the default number of double quotes (which
23752    applies to a zero-length string) to 2. */
23753    dquotes = 2;
23754    nc = 0;
23755    sq = 0;
23756 
23757 /* Loop to consider each input character to see if it will fit into
23758    the result string. */
23759    for ( i = 0; value[ i ]; i++ ) {
23760 
23761 /* If a single quote character is to be included, count it. When the
23762    string is encoded as FITS character data, these quotes will be
23763    doubled, so will increase the overall string length by one. */
23764       if ( value[ i ] == '\'' ) sq++;
23765 
23766 /* See how many double quotes are needed around the string (0 or
23767    2). These are needed if there is trailing white space that needs
23768    protecting (this is not significant in FITS and will be removed),
23769    or if the string already has quotes at either end (in which case an
23770    extra set is needed to prevent the original ones being removed when
23771    it is later un-quoted). Note we do not need to double existing
23772    double quote characters within the string, because the position of
23773    the ends of the string are known (from the quoting supplied by
23774    FITS) so only the first and last characters need be inspected when
23775    un-quoting the string.
23776    In assessing the number of double quotes, assume the string will be
23777    truncated after the current character. */
23778       dq = ( isspace( value[ i ] ) ||
23779              ( ( value[ 0 ] == '"' ) && ( value[ i ] == '"' ) ) ) ? 2 : 0;
23780 
23781 /* See if the length of the resulting string, including the current
23782    character and all necessary quotes, is too long. If so, give up
23783    here. */
23784       if ( ( nc + 1 + dq + sq ) >
23785            ( AST__FITSCHAN_FITSCARDLEN - FITSNAMLEN - 4 ) ) break;
23786 
23787 /* If the string is not too long, accept the character and note the
23788    number of double quotes needed. */
23789       nc = i + 1;
23790       dquotes = dq;
23791    }
23792 
23793 /* If double quotes are needed, insert the opening quote into the
23794    output string. */
23795    j = 0;
23796    if ( dquotes ) string[ j++ ] = '"';
23797 
23798 /* Follow this with the maximum number of input string characters that
23799    can be accommodated. */
23800    for ( i = 0; i < nc; i++ ) string[ j++ ] = value[ i ];
23801 
23802 /* Append the closing quote if necessary and terminate the output
23803    string. */
23804    if ( dquotes ) string[ j++ ] = '"';
23805    string[ j ] = '\0';
23806 }
23807 
PurgeWCS(AstFitsChan * this,int * status)23808 static void PurgeWCS( AstFitsChan *this, int *status ){
23809 
23810 /*
23811 *++
23812 *  Name:
23813 c     astPurgeWCS
23814 f     AST_PURGEWCS
23815 
23816 *  Purpose:
23817 *     Delete all cards in the FitsChan describing WCS information.
23818 
23819 *  Type:
23820 *     Public virtual function.
23821 
23822 *  Synopsis:
23823 c     #include "fitschan.h"
23824 c     void astPurgeWCS( AstFitsChan *this )
23825 f     CALL AST_PURGEWCS( THIS, STATUS )
23826 
23827 *  Class Membership:
23828 *     FitsChan method.
23829 
23830 *  Description:
23831 c     This function
23832 f     This routine
23833 *     deletes all cards in a FitsChan that relate to any of the recognised
23834 *     WCS encodings. On exit, the current card is the first remaining card
23835 *     in the FitsChan.
23836 
23837 *  Parameters:
23838 c     this
23839 f     THIS = INTEGER (Given)
23840 *        Pointer to the FitsChan.
23841 f     STATUS = INTEGER (Given and Returned)
23842 f        The global status.
23843 *--
23844 */
23845 
23846 /* Local Variables: */
23847    AstObject *obj;
23848    int oldclean;
23849 
23850 /* Check the global status. */
23851    if( !astOK ) return;
23852 
23853 /* Ensure the source function has been called */
23854    ReadFromSource( this, status );
23855 
23856 /* Ensure the Clean attribute is set so that WCS keywords are removed
23857    even if an error occurs. */
23858    if( astTestClean( this ) ) {
23859       oldclean = astGetClean( this );
23860       astSetClean( this, 1 );
23861    } else {
23862       astSetClean( this, 1 );
23863       oldclean = -1;
23864    }
23865 
23866 /* Loop round attempting to read AST objects form the FitsChan. This will
23867    flag cards as used that are involved in the creation of these object
23868    (including NATIVE encodings). Ignore any error that ocurs whilst doing
23869    this. */
23870    astClearCard( this );
23871    if( astOK ) {
23872       int oldreporting = astReporting( 0 );
23873       obj = astRead( this );
23874       while( obj ) {
23875          obj = astAnnul( obj );
23876          astClearCard( this );
23877          obj = astRead( this );
23878       }
23879       if( !astOK ) astClearStatus;
23880       astReporting( oldreporting );
23881    }
23882 
23883 /* We now loop round to remove any spurious WCS-related cards left in the
23884    FitsChan that did not form part of a complete WCS encoding. Find the
23885    first WCS-related card left in the FitsChan. */
23886    FindWcs( this, 0, 0, 1, "DeleteWcs", "FitsChan", status );
23887 
23888 /* Loop round marking each WCS-related card as used until none are left */
23889    while( this->card && astOK ) {
23890 
23891 /* Mark the current card as having been read. */
23892       ( (FitsCard*) this->card )->flags = USED;
23893 
23894 /* Find the next WCS-related card. */
23895       FindWcs( this, 0, 0, 0, "DeleteWcs", "FitsChan", status );
23896    }
23897 
23898 /* Rewind the FitsChan. */
23899    astClearCard( this );
23900 
23901 /* Reset the Clean attribute. */
23902    if( oldclean == -1 ) {
23903       astClearClean( this );
23904    } else {
23905       astSetClean( this, oldclean );
23906    }
23907 
23908 }
23909 
PutCards(AstFitsChan * this,const char * cards,int * status)23910 static void PutCards( AstFitsChan *this, const char *cards, int *status ) {
23911 
23912 /*
23913 *++
23914 *  Name:
23915 c     astPutCards
23916 f     AST_PUTCARDS
23917 
23918 *  Purpose:
23919 *     Store a set of FITS header cards in a FitsChan.
23920 
23921 *  Type:
23922 *     Public virtual function.
23923 
23924 *  Synopsis:
23925 c     #include "fitschan.h"
23926 
23927 c     void astPutCards( AstFitsChan *this, const char *cards )
23928 f     CALL AST_PUTCARDS( THIS, CARDS, STATUS )
23929 
23930 *  Class Membership:
23931 *     FitsChan method.
23932 
23933 *  Description:
23934 c     This function
23935 f     This routine
23936 *     stores a set of FITS header cards in a FitsChan. The cards are
23937 *     supplied concatenated together into a single character string.
23938 *     Any existing cards in the FitsChan are removed before the new cards
23939 *     are added. The FitsChan is "re-wound" on exit by clearing its Card
23940 *     attribute. This means that a subsequent invocation of
23941 c     astRead
23942 f     AST_READ
23943 *     can be made immediately without the need to re-wind the FitsChan
23944 *     first.
23945 
23946 *  Parameters:
23947 c     this
23948 f     THIS = INTEGER (Given)
23949 *        Pointer to the FitsChan.
23950 c     cards
23951 f     CARDS = CHARACTER * ( * ) (Given)
23952 c        Pointer to a null-terminated character string
23953 f        A character string
23954 *        containing the FITS cards to be stored. Each individual card
23955 *        should occupy 80 characters in this string, and there should be
23956 *        no delimiters, new lines, etc, between adjacent cards. The final
23957 *        card may be less than 80 characters long.
23958 c        This is the format produced by the fits_hdr2str function in the
23959 c        CFITSIO library.
23960 f     STATUS = INTEGER (Given and Returned)
23961 f        The global status.
23962 
23963 *  Notes:
23964 *     - An error will result if the supplied string contains any cards
23965 *     which cannot be interpreted.
23966 *--
23967 */
23968 
23969 /* Local Variables: */
23970    const char *a;         /* Pointer to start of next card */
23971    int clen;              /* Length of supplied string */
23972    int i;                 /* Card index */
23973    int ncard;             /* No. of cards supplied */
23974 
23975 /* Check the global error status. */
23976    if ( !astOK ) return;
23977 
23978 /* Ensure the source function has been called */
23979    ReadFromSource( this, status );
23980 
23981 /* Empty the FitsChan. */
23982    astEmptyFits( this );
23983 
23984 /* Loop round the supplied string in 80 character segments, inserting
23985    each segment into the FitsChan as a header card. Allow the last card
23986    to be less than 80 characters long. */
23987    clen = strlen( cards );
23988    ncard = clen/80;
23989    if( ncard*80 < clen ) ncard++;
23990    a = cards;
23991    for( i = 0; i < ncard; i++, a += 80 ) astPutFits( this, a, 1 );
23992 
23993 /* Rewind the FitsChan. */
23994    astClearCard( this );
23995 }
23996 
PutFits(AstFitsChan * this,const char card[AST__FITSCHAN_FITSCARDLEN+1],int overwrite,int * status)23997 static void PutFits( AstFitsChan *this, const char card[ AST__FITSCHAN_FITSCARDLEN + 1 ],
23998                      int overwrite, int *status ){
23999 
24000 /*
24001 *++
24002 *  Name:
24003 c     astPutFits
24004 f     AST_PUTFITS
24005 
24006 *  Purpose:
24007 *     Store a FITS header card in a FitsChan.
24008 
24009 *  Type:
24010 *     Public virtual function.
24011 
24012 *  Synopsis:
24013 c     #include "fitschan.h"
24014 
24015 c     void astPutFits( AstFitsChan *this, const char card[ 80 ],
24016 c                      int overwrite )
24017 f     CALL AST_PUTFITS( THIS, CARD, OVERWRITE, STATUS )
24018 
24019 *  Class Membership:
24020 *     FitsChan method.
24021 
24022 *  Description:
24023 c     This function stores a FITS header card in a FitsChan. The card
24024 f     This routine stores a FITS header card in a FitsChan. The card
24025 *     is either inserted before the current card (identified by the
24026 *     Card attribute), or over-writes the current card, as required.
24027 
24028 *  Parameters:
24029 c     this
24030 f     THIS = INTEGER (Given)
24031 *        Pointer to the FitsChan.
24032 c     card
24033 f     CARD = CHARACTER * ( 80 ) (Given)
24034 c        Pointer to a possibly null-terminated character string
24035 c        containing the FITS card to be stored. No more than 80
24036 c        characters will be used from this string (or fewer if a null
24037 c        occurs earlier).
24038 f        A character string string containing the FITS card to be
24039 f        stored. No more than 80 characters will be used from this
24040 f        string.
24041 c     overwrite
24042 f     OVERWRITE = LOGICAL (Given)
24043 c        If this value is zero, the new card is inserted in front of
24044 f        If this value is .FALSE., the new card is inserted in front of
24045 *        the current card in the FitsChan (as identified by the
24046 c        initial value of the Card attribute). If it is non-zero, the
24047 f        initial value of the Card attribute). If it is .TRUE., the
24048 *        new card replaces the current card. In either case, the Card
24049 *        attribute is then incremented by one so that it subsequently
24050 *        identifies the card following the one stored.
24051 f     STATUS = INTEGER (Given and Returned)
24052 f        The global status.
24053 
24054 *  Notes:
24055 *     - If the Card attribute initially points at the "end-of-file"
24056 *     (i.e. exceeds the number of cards in the FitsChan), then the new
24057 *     card is appended as the last card in the FitsChan.
24058 *     - An error will result if the supplied string cannot be interpreted
24059 *     as a FITS header card.
24060 *--
24061 */
24062 
24063 /* Local Variables: */
24064    char *comment;         /* The keyword comment */
24065    char *name;            /* The keyword name */
24066    char *value;           /* The keyword value */
24067    const char *class;     /* Object class */
24068    const char *method;    /* Current method */
24069    double cfval[2];       /* Complex floating point keyword value */
24070    double fval;           /* floating point keyword value */
24071    int cival[2];          /* Complex integer keyword value */
24072    int ival;              /* Integer keyword value */
24073    int len;               /* No. of characters to read from the value string */
24074    int nc;                /* No. of characters read from value string */
24075    int type;              /* Keyword data type */
24076 
24077 /* Check the global error status. */
24078    if ( !astOK ) return;
24079 
24080 /* Ensure the source function has been called */
24081    ReadFromSource( this, status );
24082 
24083 /* Store the current method, and the class of the supplied object for use
24084    in error messages.*/
24085    method = "astPutFits";
24086    class = astGetClass( this );
24087 
24088 /* Split the supplied card up into name, value and commment strings, and
24089    get pointers to local copies of them. The data type associated with the
24090    keyword is returned. */
24091    type = Split( card, &name, &value, &comment, method, class, status );
24092 
24093 /* Check that the pointers can be used. */
24094    if( astOK ){
24095 
24096 /* Initialise the number of characters read from the value string. */
24097       nc = 0;
24098 
24099 /* Store the number of characters in the value string. */
24100       len = strlen( value );
24101 
24102 /* Read and store floating point values from the value string. NB, this
24103    list is roughly in the order of descreasing frequency of use (i.e.
24104    most FITS keywords are simple floating point values, the next most
24105    common are strings, etc). */
24106       if( type == AST__FLOAT ){
24107          if( 1 == astSscanf( value, " %lf %n", &fval, &nc ) && nc >= len ){
24108             astSetFitsF( this, name, fval, comment, overwrite );
24109          } else {
24110             astError( AST__BDFTS, "%s(%s): Unable to read a floating point "
24111                       "FITS keyword value.", status, method, class );
24112          }
24113 
24114 /* Read and store string values from the value string. */
24115       } else if( type == AST__STRING ){
24116          astSetFitsS( this, name, value, comment, overwrite );
24117 
24118 /* Read and store string values from the value string. */
24119       } else if( type == AST__CONTINUE ){
24120          astSetFitsCN( this, name, value, comment, overwrite );
24121 
24122 /* Store comment card. */
24123       } else if( type == AST__COMMENT ){
24124          astSetFitsCom( this, name, comment, overwrite );
24125 
24126 /* Read and store integer values from the value string. */
24127       } else if( type == AST__INT ){
24128          if( 1 == astSscanf( value, " %d %n", &ival, &nc ) && nc >= len ){
24129             astSetFitsI( this, name, ival, comment, overwrite );
24130          } else {
24131             astError( AST__BDFTS, "%s(%s): Unable to read an integer FITS "
24132                       "keyword value.", status, method, class );
24133          }
24134 
24135 /* Read and store logical values from the value string. */
24136       } else if( type == AST__LOGICAL ){
24137          astSetFitsL( this, name, (*value == 'T'), comment, overwrite );
24138 
24139 /* Read and store undefined values from the value string. */
24140       } else if( type == AST__UNDEF ){
24141          astSetFitsU( this, name, comment, overwrite );
24142 
24143 /* Read and store complex floating point values from the value string. */
24144       } else if( type == AST__COMPLEXF ){
24145          if( 2 == astSscanf( value, " %lf %lf %n", cfval, cfval + 1, &nc ) &&
24146              nc >= len ){
24147             astSetFitsCF( this, name, cfval, comment, overwrite );
24148          } else {
24149             astError( AST__BDFTS, "%s(%s): Unable to read a complex pair "
24150                       "of floating point FITS keyword values.", status, method, class );
24151          }
24152 
24153 /* Read and store complex integer values from the value string. */
24154       } else if( type == AST__COMPLEXI ){
24155          if( 2 == astSscanf( value, " %d %d %n", cival, cival + 1, &nc ) &&
24156              nc >= len ){
24157             astSetFitsCI( this, name, cival, comment, overwrite );
24158          } else {
24159             astError( AST__BDFTS, "%s(%s): Unable to read a complex pair "
24160                       "of integer FITS keyword values.", status, method, class );
24161          }
24162 
24163 /* Report an error for any other type. */
24164       } else {
24165          astError( AST__INTER, "%s: AST internal programming error - "
24166                    "FITS data-type '%d' not yet supported.", status, method, type );
24167       }
24168 
24169 /* Give a context message if an error occurred. */
24170       if( !astOK ){
24171          astError( astStatus, "%s(%s): Unable to store the following FITS "
24172                    "header card:\n%s\n", status, method, class, card );
24173       }
24174    }
24175 
24176 /* Free the memory used to hold the keyword name, comment and value
24177    strings. */
24178    (void) astFree( (void *) name );
24179    (void) astFree( (void *) comment );
24180    (void) astFree( (void *) value );
24181 }
24182 
PutTable(AstFitsChan * this,AstFitsTable * table,const char * extnam,int * status)24183 static void PutTable( AstFitsChan *this, AstFitsTable *table,
24184                       const char *extnam, int *status ) {
24185 
24186 /*
24187 *++
24188 *  Name:
24189 c     astPutTable
24190 f     AST_PUTTABLE
24191 
24192 *  Purpose:
24193 *     Store a single FitsTable in a FitsChan.
24194 
24195 *  Type:
24196 *     Public virtual function.
24197 
24198 *  Synopsis:
24199 c     #include "fitschan.h"
24200 
24201 c     void astPutTable( AstFitsChan *this, AstFitsTable *table,
24202 c                       const char *extnam )
24203 f     CALL AST_PUTTABLE( THIS, TABLE, EXTNAM, STATUS )
24204 
24205 *  Class Membership:
24206 *     FitsChan method.
24207 
24208 *  Description:
24209 c     This function
24210 f     This routine
24211 *     allows a representation of a single FITS binary table to be
24212 *     stored in a FitsChan. For instance, this may provide the coordinate
24213 *     look-up tables needed subequently when reading FITS-WCS headers
24214 *     for axes described using the "-TAB" algorithm. Since, in general,
24215 *     the calling application may not know which tables will be needed -
24216 *     if any - prior to calling
24217 c     astRead, the astTablesSource function
24218 f     AST_READ, the AST_TABLESOURCE routine
24219 *     provides an alternative mechanism in which a caller-supplied
24220 *     function is invoked to store a named table in the FitsChan.
24221 
24222 *  Parameters:
24223 c     this
24224 f     THIS = INTEGER (Given)
24225 *        Pointer to the FitsChan.
24226 c     table
24227 f     TABLE = INTEGER (Given)
24228 *        Pointer to a FitsTable to be added to the FitsChan. If a FitsTable
24229 *        with the associated extension name already exists in the FitsChan,
24230 *        it is replaced with the new one. A deep copy of the FitsTable is
24231 *        stored in the FitsChan, so any subsequent changes made to the
24232 *        FitsTable will have no effect on the behaviour of the FitsChan.
24233 c     extnam
24234 f     EXTNAM = CHARACTER * ( * ) (Given)
24235 *        The name of the FITS extension associated with the table.
24236 f     STATUS = INTEGER (Given and Returned)
24237 f        The global status.
24238 
24239 *  Notes:
24240 *     - Tables stored in the FitsChan may be retrieved using
24241 c     astGetTables.
24242 f     AST_GETTABLES.
24243 c     - The astPutTables method can add multiple FitsTables in a single call.
24244 f     - The AST_PUTTABLES method can add multiple FitsTables in a single call.
24245 *--
24246 */
24247 
24248 /* Local Variables: */
24249    AstObject *ft;
24250 
24251 /* Check the global error status. */
24252    if ( !astOK ) return;
24253 
24254 /* Create a KeyMap to hold the tables within the FitsChan, if this has not
24255    already been done. */
24256    if( !this->tables ) this->tables = astKeyMap( " ", status );
24257 
24258 /* Store a copy of the FitsTable in the FitsChan. */
24259    ft = astCopy( table );
24260    astMapPut0A( this->tables, extnam, ft, NULL );
24261    ft = astAnnul( ft );
24262 }
24263 
PutTables(AstFitsChan * this,AstKeyMap * tables,int * status)24264 static void PutTables( AstFitsChan *this, AstKeyMap *tables, int *status ) {
24265 
24266 /*
24267 *++
24268 *  Name:
24269 c     astPutTables
24270 f     AST_PUTTABLES
24271 
24272 *  Purpose:
24273 *     Store one or more FitsTables in a FitsChan.
24274 
24275 *  Type:
24276 *     Public virtual function.
24277 
24278 *  Synopsis:
24279 c     #include "fitschan.h"
24280 
24281 c     void astPutTables( AstFitsChan *this, AstKeyMap *tables )
24282 f     CALL AST_PUTTABLES( THIS, TABLES, STATUS )
24283 
24284 *  Class Membership:
24285 *     FitsChan method.
24286 
24287 *  Description:
24288 c     This function
24289 f     This routine
24290 *     allows representations of one or more FITS binary tables to be
24291 *     stored in a FitsChan. For instance, these may provide the coordinate
24292 *     look-up tables needed subequently when reading FITS-WCS headers
24293 *     for axes described using the "-TAB" algorithm. Since, in general,
24294 *     the calling application may not know which tables will be needed -
24295 *     if any - prior to calling
24296 c     astRead, the astTablesSource function
24297 f     AST_READ, the AST_TABLESOURCE routine
24298 *     provides an alternative mechanism in which a caller-supplied
24299 *     function is invoked to store a named table in the FitsChan.
24300 
24301 *  Parameters:
24302 c     this
24303 f     THIS = INTEGER (Given)
24304 *        Pointer to the FitsChan.
24305 c     tables
24306 f     TABLES = INTEGER (Given)
24307 *        Pointer to a KeyMap holding the tables that are to be added
24308 *        to the FitsChan. Each entry should hold a scalar value which is a
24309 *        pointer to a FitsTable to be added to the FitsChan. Any unusable
24310 *        entries are ignored. The key associated with each entry should be
24311 *        the name of the FITS binary extension from which the table was
24312 *        read. If a FitsTable with the associated key already exists in the
24313 *        FitsChan, it is replaced with the new one. A deep copy of each
24314 *        usable FitsTable is stored in the FitsChan, so any subsequent
24315 *        changes made to the FitsTables will have no effect on the
24316 *        behaviour of the FitsChan.
24317 f     STATUS = INTEGER (Given and Returned)
24318 f        The global status.
24319 
24320 *  Notes:
24321 *     - Tables stored in the FitsChan may be retrieved using
24322 c     astGetTables.
24323 f     AST_GETTABLES.
24324 *     - The tables in the supplied KeyMap are added to any tables already
24325 *     in the FitsChan.
24326 c     - The astPutTable
24327 f     - The AST_PUTTABLE
24328 *     method provides a simpler means of adding a single table to a FitsChan.
24329 *--
24330 */
24331 
24332 /* Local Variables: */
24333    AstObject *obj;
24334    const char *key;
24335    int ientry;
24336    int nentry;
24337 
24338 /* Check the global error status. */
24339    if ( !astOK ) return;
24340 
24341 /* Loop through all entries in the supplied KeyMap. */
24342    nentry = astMapSize( tables );
24343    for( ientry = 0; ientry < nentry; ientry++ ) {
24344       key = astMapKey( tables, ientry );
24345 
24346 /* Ignore entries that do not contain AST Object pointers, or are not
24347    scalar. */
24348       if( astMapType( tables, key ) == AST__OBJECTTYPE &&
24349           astMapLength( tables, key ) == 1 ) {
24350 
24351 /* Get the pointer, amd ignore it if it is not a FitsTable. */
24352          astMapGet0A( tables, key, &obj );
24353          if( astIsAFitsTable( obj ) ) {
24354 
24355 /* Store it in the FitsChan. */
24356             astPutTable( this, (AstFitsTable *) obj, key );
24357          }
24358 
24359 /* Annul the Object pointer. */
24360          obj = astAnnul( obj );
24361       }
24362    }
24363 }
24364 
Read(AstChannel * this_channel,int * status)24365 static AstObject *Read( AstChannel *this_channel, int *status ) {
24366 /*
24367 *  Name:
24368 *     Read
24369 
24370 *  Purpose:
24371 *     Read an Object from a Channel.
24372 
24373 *  Type:
24374 *     Private function.
24375 
24376 *  Synopsis:
24377 *     #include "fitschan.h"
24378 *     AstObject *Read( AstChannel *this_channel, int *status )
24379 
24380 *  Class Membership:
24381 *     FitsChan member function (over-rides the astRead method
24382 *     inherited from the Channel class).
24383 
24384 *  Description:
24385 *     This function reads an Object from a FitsChan.
24386 
24387 *  Parameters:
24388 *     this
24389 *        Pointer to the FitsChan.
24390 *     status
24391 *        Pointer to the inherited status variable.
24392 
24393 *  Returned Value:
24394 *     A pointer to the new Object. This will always be a FrameSet.
24395 
24396 *  Notes:
24397 *     -  The pixel Frame is given a title of "Pixel Coordinates", and
24398 *     each axis in the pixel Frame is given a label of the form "Pixel
24399 *     axis <n>", where <n> is the axis index (starting at one).
24400 *     -  The FITS CTYPE keyword values are used to set the labels for any
24401 *     non-celestial axes in the physical coordinate Frames, and the FITS
24402 *     CUNIT keywords are used to set the corresponding units strings.
24403 *     -  On exit, the pixel Frame is the base Frame, and the physical
24404 *     Frame derived from the primary axis descriptions is the current Frame.
24405 *     -  Extra Frames are added to hold any secondary axis descriptions. All
24406 *     axes within such a Frame refer to the same coordinate version ('A',
24407 *     'B', etc).
24408 *     -  For foreign encodings, the first card in the FitsChan must be
24409 *     the current card on entry (otherwise a NULL pointer is returned),
24410 *     and the FitsChan is left at end-of-file on exit.
24411 *     -  For the Native encoding, reading commences from the current card
24412 *     on entry (which need not be the first in the FitsChan), and the
24413 *     current Card on exit is the first card following the last one read
24414 *     (or end-of-file).
24415 */
24416 
24417 /* Local Variables: */
24418    AstObject *new;               /* Pointer to returned Object */
24419    AstFitsChan *this;            /* Pointer to the FitsChan structure */
24420    FitsStore *store;             /* Intermediate storage for WCS information */
24421    const char *method;           /* Pointer to string holding calling method */
24422    const char *class;            /* Pointer to string holding object class */
24423    int encoding;                 /* The encoding scheme */
24424    int remove;                   /* Remove used cards? */
24425 
24426 /* Initialise. */
24427    new = NULL;
24428 
24429 /* Check the global error status. */
24430    if ( !astOK ) return new;
24431 
24432 /* Obtain a pointer to the FitsChan structure. */
24433    this = (AstFitsChan *) this_channel;
24434 
24435 /* Ensure the source function has been called */
24436    ReadFromSource( this, status );
24437 
24438 /* Store the calling method, and object class. */
24439    method = "astRead";
24440    class = astGetClass( this );
24441 
24442 /* Get the encoding scheme used by the FitsChan. */
24443    encoding = astGetEncoding( this );
24444 
24445 /* If we are reading from a FitsChan in which AST objects are encoded using
24446    native AST-specific keywords, use the Read method inherited from the
24447    Channel class. */
24448    if( encoding == NATIVE_ENCODING ){
24449       new = (*parent_read)( this_channel, status );
24450 
24451 /* Indicate that used cards should be removed from the FitsChan. */
24452       remove = 1;
24453 
24454 /* If we are reading from a FitsChan in which AST objects are encoded using
24455    any of the other supported encodings, the header may only contain a
24456    single FrameSet. */
24457    } else {
24458       remove = 0;
24459 
24460 /* Only proceed if the FitsChan is at start-of-file. */
24461       if( !astTestCard( this ) && astOK ){
24462 
24463 /* Extract the required information from the FITS header into a standard
24464    intermediary structure called a FitsStore. */
24465          store = FitsToStore( this, encoding, method, class, status );
24466 
24467 /* Now create a FrameSet from this FitsStore. */
24468          new = FsetFromStore( this, store, method, class, status );
24469 
24470 /* Release the resources used by the FitsStore. */
24471          store = FreeStore( store, status );
24472 
24473 /* Indicate that used cards should be retained in the FitsChan. */
24474          remove = 0;
24475 
24476 /* If no object is being returned, rewind the fitschan in order to
24477    re-instate the original current Card. */
24478          if( !new ) {
24479             astClearCard( this );
24480 
24481 /*  Otherwise, ensure the current card is at "end-of-file". */
24482          } else {
24483             astSetCard( this, INT_MAX );
24484          }
24485       }
24486    }
24487 
24488 /* If an error occurred, clean up by deleting the new Object and
24489    return a NULL pointer. */
24490    if ( !astOK ) new = astDelete( new );
24491 
24492 /* If no object is being returned, clear the "provisionally used" flags
24493    associated with cards which were read. We do not do this if the user
24494    wants to clean WCS cards from the FitsChan even if an error occurs. */
24495    if( !new && !astGetClean( this ) ) {
24496       FixUsed( this, 0, 0, 0, method, class, status );
24497 
24498 /*  Otherwise, indicate that all the "provisionally used" cards have been
24499     "definitely used". If native encoding was used, these cards are
24500     totally removed from the FitsChan. */
24501    } else {
24502       FixUsed( this, 0, 1, remove, method, class, status );
24503    }
24504 
24505 /* Return the pointer to the new Object. */
24506    return new;
24507 }
24508 
ReadCrval(AstFitsChan * this,AstFrame * wcsfrm,char s,const char * method,const char * class,int * status)24509 static double *ReadCrval( AstFitsChan *this, AstFrame *wcsfrm, char s,
24510                           const char *method, const char *class, int *status ){
24511 
24512 /*
24513 *  Name:
24514 *     ReadCrval
24515 
24516 *  Purpose:
24517 *     Obtain the reference point from the supplied FitsChan  in the
24518 *     supplied WCS Frame.
24519 
24520 *  Type:
24521 *     Private function.
24522 
24523 *  Synopsis:
24524 *     #include "fitschan.h"
24525 
24526 *     double *ReadCrval( AstFitsChan *this, AstFrame *wcsfrm, char s,
24527 *                        const char *method, const char *class, int *status )
24528 
24529 *  Class Membership:
24530 *     FitsChan member function.
24531 
24532 *  Description:
24533 *     The original reference point in the "s" coordinate description is read
24534 *     from the CRVAL keywords in the supplied FitsChan, and the original
24535 *     FrameSet is re-read from the FitsChan. If possible, the reference
24536 *     position is then converted from the "s" coordinate description to the
24537 *     supplied WCS Frame, and a pointer to an array holding the axis
24538 *     values for the transformed reference point is returned.
24539 
24540 *  Parameters:
24541 *     this
24542 *        The FitsChan.
24543 *     wcsfrm
24544 *        The WCS Frame in the FitsChan being written to.
24545 *     s
24546 *        The co-ordinate version character. A space means the primary
24547 *        axis descriptions. Otherwise the supplied character should be
24548 *        an upper case alphabetical character ('A' to 'Z').
24549 *     method
24550 *        Pointer to a string holding the name of the calling method.
24551 *        This is only for use in constructing error messages.
24552 *     class
24553 *        Pointer to a string holding the name of the supplied object class.
24554 *        This is only for use in constructing error messages.
24555 *     status
24556 *        Pointer to the inherited status variable.
24557 
24558 *  Returned Value:
24559 *     A pointer to a dynamically allocated array holding the reference
24560 *     point in the supplied WCS Frame. NULL is returned if is is not
24561 *     possible to determine the reference point for any reason (for
24562 *     instance, if the FitsChan does not contain values for the CRVAL
24563 *     keywords).
24564 */
24565 
24566 /* Local Variables: */
24567    AstFitsChan *fc;          /* A copy of the supplied FitsChan */
24568    AstFrame *tfrm;           /* Temporary Frame pointer */
24569    AstFrameSet *fs;          /* The FITS FrameSet */
24570    AstFrameSet *tfs;         /* FrameSet connecting FITS and supplied WCS Frame */
24571    const char *id;           /* Pointer to Object "Id" string */
24572    char buf[ 11 ];           /* FITS keyword template buffer */
24573    double *crval;            /* CRVAL keyword values in supplied FitsChan */
24574    double *ret;              /* Returned array */
24575    int hii;                  /* Highest found FITS axis index */
24576    int iax;                  /* Axis index (zero based) */
24577    int ifr;                  /* Frames index */
24578    int loi;                  /* Lowest found FITS axis index */
24579    int nax;                  /* Axis count */
24580    int nfr;                  /* No. of Frames in FITS FrameSet */
24581    int ok;                   /* Were CRVAL values found? */
24582 
24583 /* Initialise */
24584    ret = NULL;
24585 
24586 /* Check the inherited status. */
24587    if( !astOK ) return ret;
24588 
24589 /* We want to re-create the original FrameSet represented by the original
24590    contents of the supplied FitsChan. Some of the contents of the
24591    FitsChan will already have been marked as "having been read" and so
24592    will be ignored if we attempt to read a FrameSet directly from the
24593    supplied FitsChan. Therefore we take a deep copy of the supplied
24594    FitsChan and clear all the "previusly read" flags in the copy. */
24595    fc = astCopy( this );
24596    astClearEncoding( fc );
24597    FixUsed( fc, 1, 0, 0, method, class, status );
24598 
24599 /* Copy the CRVAL values for the "s" axis descriptions into a dynamically
24600    allocated array ("crval"). */
24601    if( s == ' ' ) {
24602       strcpy( buf, "CRVAL%d" );
24603    } else {
24604       sprintf( buf, "CRVAL%%d%c", s );
24605    }
24606    if( astKeyFields( fc, buf, 1, &hii, &loi ) > 0 ) {
24607       crval = astMalloc( sizeof( double )*(size_t) hii );
24608       ok = 1;
24609       for( iax = 0; iax < hii; iax++ ){
24610          ok = ok && GetValue( fc, FormatKey( "CRVAL", iax + 1, -1, s, status ),
24611                               AST__FLOAT, (void *) (crval + iax), 0, 0, method,
24612                               class, status );
24613       }
24614    } else {
24615       crval = NULL;
24616       ok = 0;
24617    }
24618 
24619 /* If the CRVAL values were obtained succesfully, attempt to read a FrameSet
24620    from the FitsChan copy. Do it in a new error report context so that we
24621    can annull any error when the FrameSet is read. */
24622    if( ok ) {
24623       int oldreporting = astReporting( 0 );
24624       astClearCard( fc );
24625       fs = astRead( fc );
24626       if( fs ) {
24627 
24628 /* We want to find a conversion from the Frame in this FrameSet which
24629    represents the FITS-WCS "s" coordinate descriptions and the supplied WCS
24630    Frame. So first find the Frame which has its Ident attribute set to
24631    "s" and make it the current Frame. */
24632          nfr = astGetNframe( fs );
24633          for( ifr = 1; ifr <= nfr; ifr++ ) {
24634             astSetCurrent( fs, ifr );
24635             tfrm = astGetFrame( fs, ifr );
24636             id = astTestIdent( tfrm ) ? astGetIdent( tfrm ) : NULL;
24637             tfrm = astAnnul( tfrm );
24638             if( id && strlen( id ) == 1 && id[ 0 ] == s ) break;
24639          }
24640 
24641 /* Check a Frame was found, and that we have CRVAL values for all axes in
24642    the Frame. */
24643          if( ifr <= nfr && astGetNaxes( fs ) == hii ) {
24644 
24645 /* Attempt to find a conversion route from the Frame found above to the
24646    supplied WCS Frame. */
24647             tfs = astConvert( fs, wcsfrm, astGetDomain( wcsfrm ) );
24648             if( tfs ) {
24649 
24650 /* Allocate memory to hold the returned reference point. */
24651                nax = astGetNaxes( wcsfrm );
24652                ret = astMalloc( sizeof( double )*(size_t) nax );
24653 
24654 /* Transform the original reference position from the "s" Frame to the
24655    supplied WCS Frame using the Mapping returned by astConvert. */
24656                astTranN( tfs, 1, hii, 1, crval, 1, nax, 1, ret );
24657 
24658 /* Free resources. */
24659                tfs = astAnnul( tfs );
24660             }
24661          }
24662 
24663 /* Free resources. */
24664          fs = astAnnul( fs );
24665 
24666 /* Annul any error that occurred reading the FitsChan. */
24667       } else if( !astOK ) {
24668          astClearStatus;
24669       }
24670 
24671 /* Re-instate error reporting. */
24672       astReporting( oldreporting );
24673    }
24674 
24675 /* Free resources. */
24676    if( crval ) crval = astFree( crval );
24677    fc = astAnnul( fc );
24678 
24679 /* If an error occurred, free the returned array. */
24680    if( !astOK ) ret = astFree( ret );
24681 
24682 /* Return the result. */
24683    return ret;
24684 }
24685 
ReadFits(AstFitsChan * this,int * status)24686 static void ReadFits( AstFitsChan *this, int *status ){
24687 
24688 /*
24689 *++
24690 *  Name:
24691 c     astReadFits
24692 f     AST_READFITS
24693 
24694 *  Purpose:
24695 *     Read cards into a FitsChan from the source function.
24696 
24697 *  Type:
24698 *     Public virtual function.
24699 
24700 *  Synopsis:
24701 c     #include "fitschan.h"
24702 c     void astReadFits( AstFitsChan *this )
24703 f     CALL AST_READFITS( THIS, STATUS )
24704 
24705 *  Class Membership:
24706 *     FitsChan method.
24707 
24708 *  Description:
24709 c     This function
24710 f     This routine
24711 *     reads cards from the source function that was specified when the
24712 *     FitsChan was created, and stores them in the FitsChan. This
24713 *     normally happens once-only, when the FitsChan is accessed for the
24714 *     first time.
24715 c     This function
24716 f     This routine
24717 *     provides a means of forcing a re-read of the external source, and
24718 *     may be useful if (say) new cards have been deposited into the
24719 *     external source. Any newcards read from the source are appended to
24720 *     the end of the current contents of the FitsChan.
24721 
24722 *  Parameters:
24723 c     this
24724 f     THIS = INTEGER (Given)
24725 *        Pointer to the FitsChan.
24726 f     STATUS = INTEGER (Given and Returned)
24727 f        The global status.
24728 
24729 *  Notes:
24730 *     - This function returns without action if no source function was
24731 *     specified when the FitsChan was created.
24732 *     - The SourceFile attribute is ignored by this
24733 c     function.
24734 f     routine.
24735 *     New cards are read from the source file whenever a new value is
24736 *     assigned to the SourceFile attribute.
24737 
24738 *--
24739 */
24740 
24741 /* Check the inherited status */
24742    if( !astOK ) return;
24743 
24744 /* If no source function is available, re-instate any saved source
24745    function pointer. */
24746    if( !this->source ) {
24747       this->source = this->saved_source;
24748       this->saved_source = NULL;
24749    }
24750 
24751 /* Call the source function. */
24752    ReadFromSource( this, status );
24753 }
24754 
ReadFromSource(AstFitsChan * this,int * status)24755 static void ReadFromSource( AstFitsChan *this, int *status ){
24756 
24757 /*
24758 *  Name:
24759 *     ReadFromSource
24760 
24761 *  Purpose:
24762 *     Fill the FitsChan by reading cards from the source function.
24763 
24764 *  Type:
24765 *     Private function.
24766 
24767 *  Synopsis:
24768 *     #include "fitschan.h"
24769 *     void ReadFromSource( AstFitsChan *this, int *status )
24770 
24771 *  Class Membership:
24772 *     FitsChan member function.
24773 
24774 *  Description:
24775 *     The source function specified when the FitsChan was created is
24776 *     called repeatedly until it returns a NULL pointer. The string
24777 *     returned by each such call is assumed to be a FITS header card,
24778 *     and is stored in the FitsChan using astPutFits.
24779 *
24780 *     If no source function was provided, the FitsChan is left as supplied.
24781 *     This is different to a standard Channel, which tries to read data
24782 *     from standard input if no source function is provided.
24783 *
24784 *     This function should be called at the start of most public or protected
24785 *     FitsChan functions, and most private functions that are used to override
24786 *     methods inherited form the Channel class. Previously, this function
24787 *     was called only once, from the FitsChan initialiser (astInitFitschan).
24788 *     However, calling it from astInitFitsChan means that application code
24789 *     cannot use the astPutChannelData function with a FitsChan, since the
24790 *     source function would already have been called by the time the
24791 *     FitsChan constructor returned (and thus before astPutChannelData
24792 *     could have been called). In order to ensure that the source
24793 *     function is called only once, this function now nullifies the source
24794 *     function pointer after its first use.
24795 
24796 *  Parameters:
24797 *     this
24798 *        Pointer to the FitsChan.
24799 *     status
24800 *        Pointer to the inherited status variable.
24801 
24802 *  Notes:
24803 *     -  The new cards are appended to the end of the FitsChan.
24804 *     -  The first of the new cards is made the current card on exit. If no
24805 *     source function is supplied, the current card is left unchanged.
24806 */
24807 
24808 /* Local Variables: */
24809    const char *(* source)( void ); /* Pointer to source function */
24810    const char *card;               /* Pointer to externally-read header card */
24811    int icard;                      /* Current card index on entry */
24812 
24813 /* Check the global status. */
24814    if( !astOK || !this ) return;
24815 
24816 /* Only proceed if source function and wrapper were supplied when the FitsChan
24817    was created and are still available. */
24818    if( this->source && this->source_wrap ){
24819 
24820 /* Save the source function pointer and then nullify the pointer in the
24821    FitsChan structure. This avoids infinte loops. */
24822       source = this->source;
24823       this->source = NULL;
24824 
24825 /* Save the source fubnction pointer in the FitsChan so that it can be
24826    re-instated if required (e.g. by astReadFits). */
24827       this->saved_source = source;
24828 
24829 /* Ensure the FitsChan is at end-of-file. This will result in the
24830    new cards being appended to the end of the FitsChan. */
24831       astSetCard( this, INT_MAX );
24832 
24833 /* Store the current card index. */
24834       icard = astGetCard( this );
24835 
24836 /* Obtain the first header card from the source function. This is an
24837    externally supplied function which may not be thread-safe, so lock a
24838    mutex first. Also store the channel data pointer in a global variable
24839    so that it can be accessed in the source function using macro
24840    astChannelData. */
24841       astStoreChannelData( this );
24842       LOCK_MUTEX2;
24843       card = ( *this->source_wrap )( source, status );
24844       UNLOCK_MUTEX2;
24845 
24846 /* Loop until a NULL pointer is returned by the source function, or an
24847    error occurs. */
24848       while( card && astOK ){
24849 
24850 /* Store the card in the FitsChan. */
24851          astPutFits( this, card, 0 );
24852 
24853 /* Free the memory holding the header card. */
24854          card = (char *) astFree( (void *) card );
24855 
24856 /* Obtain the next header card. Also store the channel data pointer in a
24857    global variable so that it can be accessed in the source function using
24858    macro astChannelData. */
24859          astStoreChannelData( this );
24860          LOCK_MUTEX2;
24861          card = ( *this->source_wrap )( source, status );
24862          UNLOCK_MUTEX2;
24863       }
24864 
24865 /* Set the current card index so that the first of the new cards will be the
24866    next card to be read from the FitsChan. */
24867       astSetCard( this, icard );
24868    }
24869 }
24870 
RemoveTables(AstFitsChan * this,const char * key,int * status)24871 static void RemoveTables( AstFitsChan *this, const char *key, int *status ){
24872 
24873 /*
24874 *++
24875 *  Name:
24876 c     astRemoveTables
24877 f     AST_REMOVETABLES
24878 
24879 *  Purpose:
24880 *     Remove one or more tables from a FitsChan.
24881 
24882 *  Type:
24883 *     Public virtual function.
24884 
24885 *  Synopsis:
24886 c     #include "fitschan.h"
24887 
24888 c     void astRemoveTables( AstFitsChan *this, const char *key )
24889 f     CALL AST_REMOVETABLES( THIS, KEY, STATUS )
24890 
24891 *  Class Membership:
24892 *     FitsChan method.
24893 
24894 *  Description:
24895 c     This function
24896 f     This routine
24897 *     removes the named tables from the FitsChan, it they exist (no error
24898 *     is reported if any the tables do not exist).
24899 
24900 *  Parameters:
24901 c     this
24902 f     THIS = INTEGER (Given)
24903 *        Pointer to the FitsChan.
24904 c     key
24905 f     KEY = CHARACTER * ( * ) (Given)
24906 *        The key indicating which tables to exist. A single key or a
24907 *        comma-separated list of keys can be supplied. If a blank string
24908 *        is supplied, all tables are removed.
24909 f     STATUS = INTEGER (Given and Returned)
24910 f        The global status.
24911 *--
24912 */
24913 
24914 /* Local variables: */
24915    char **words;
24916    int itable;
24917    int ntable;
24918 
24919 /* Return if the global error status has been set, or the FitsChan
24920    contains no tables KeyMap. */
24921    if( !astOK || !this->tables ) return;
24922 
24923 /* If the string is blank, remove all tables. */
24924    if( astChrLen( key ) == 0 ) {
24925       ntable = astMapSize( this->tables );
24926       for( itable = 0; itable < ntable; itable++ ) {
24927          astMapRemove( this->tables, astMapKey( this->tables, itable ) );
24928       }
24929 
24930 /* Otherwise, split the supplied comma-separated string up into individual
24931    items. */
24932    } else {
24933       words = astChrSplitC( key, ',', &ntable );
24934 
24935 /* Attempt to remove each one, and then free the string. */
24936       if( astOK ) {
24937          for( itable = 0; itable < ntable; itable++ ) {
24938             astMapRemove( this->tables, words[ itable ] );
24939             words[ itable ] = astFree( words[ itable ] );
24940          }
24941       }
24942 
24943 /* Free the list. */
24944       words = astFree( words );
24945    }
24946 }
24947 
RetainFits(AstFitsChan * this,int * status)24948 static void RetainFits( AstFitsChan *this, int *status ){
24949 
24950 /*
24951 *++
24952 *  Name:
24953 c     astRetainFits
24954 f     AST_RETAINFITS
24955 
24956 *  Purpose:
24957 *     Indicate that the current card in a FitsChan should be retained.
24958 
24959 *  Type:
24960 *     Public virtual function.
24961 
24962 *  Synopsis:
24963 c     #include "fitschan.h"
24964 c     void astRetainFits( AstFitsChan *this )
24965 f     CALL AST_RETAINFITS( THIS, STATUS )
24966 
24967 *  Class Membership:
24968 *     FitsChan method.
24969 
24970 *  Description:
24971 c     This function
24972 f     This routine
24973 *     stores a flag with the current card in the FitsChan indicating that
24974 *     the card should not be removed from the FitsChan when an Object is
24975 *     read from the FitsChan using
24976 c     astRead.
24977 f     AST_READ.
24978 *
24979 *     Cards that have not been flagged in this way are removed when a
24980 *     read operation completes succesfully, but only if the card was used
24981 *     in the process of creating the returned AST Object. Any cards that
24982 *     are irrelevant to the creation of the AST Object are retained whether
24983 *     or not they are flagged.
24984 
24985 *  Parameters:
24986 c     this
24987 f     THIS = INTEGER (Given)
24988 *        Pointer to the FitsChan.
24989 f     STATUS = INTEGER (Given and Returned)
24990 f        The global status.
24991 
24992 *  Notes:
24993 *     - This function returns without action if the FitsChan is
24994 *     initially positioned at the "end-of-file" (i.e. if the Card
24995 *     attribute exceeds the number of cards in the FitsChan).
24996 *     - The current card is not changed by this function.
24997 *--
24998 */
24999 
25000 /* Local variables: */
25001    int flags;
25002 
25003 /* Ensure the source function has been called */
25004    ReadFromSource( this, status );
25005 
25006 /* Return if the global error status has been set, or the current card
25007    is not defined. */
25008    if( !astOK || !this->card ) return;
25009 
25010 /* Set the PROTECTED flag in the current card. */
25011    flags = ( (FitsCard *) this->card )->flags;
25012    ( (FitsCard *) this->card )->flags = flags | PROTECTED;
25013 }
25014 
RoundFString(char * text,int width,int * status)25015 static void RoundFString( char *text, int width, int *status ){
25016 /*
25017 *  Name:
25018 *     RoundString
25019 
25020 *  Purpose:
25021 *     Modify a formatted floating point number to round out long
25022 *     sequences of zeros or nines.
25023 
25024 *  Type:
25025 *     Private function.
25026 
25027 *  Synopsis:
25028 *     #include "fitschan.h"
25029 *     void RoundFString( char *text, int width )
25030 
25031 *  Class Membership:
25032 *     FitsChan member function.
25033 
25034 *  Description:
25035 *     The supplied string is assumed to be a valid decimal representation of
25036 *     a floating point number. It is searched for sub-strings consisting
25037 *     of NSEQ or more adjacent zeros, or NSEQ or more adjacent nines. If found
25038 *     the string is modified to represent the result of rounding the
25039 *     number to remove the sequence of zeros or nines.
25040 
25041 *  Parameters:
25042 *     text
25043 *        The formatted number. Modified on exit to round out long
25044 *        sequences of zeros or nines. The returned string is right justified.
25045 *     width
25046 *        The minimum field width to use. The value is right justified in
25047 *        this field width. Ignored if zero.
25048 */
25049 
25050 /* Local Constants: */
25051 #define NSEQ  4    /* No. of adjacent 0's or 9's to produce rounding */
25052 
25053 /* Local Variables: */
25054    char *a;
25055    char *c;
25056    char *dot;
25057    char *exp;
25058    char *last;
25059    char *start;
25060    char *end;
25061    int i;
25062    int neg;
25063    int nnine;
25064    int nonzero;
25065    int nzero;
25066    int replace;
25067    int started;
25068    int len;
25069    int bu;
25070    int nls;
25071 
25072 /* Check the inherited status. */
25073    if( !astOK ) return;
25074 
25075 /* Save the original length of the text. */
25076    len = strlen( text );
25077 
25078 /* Locate the start of any exponent string. */
25079    exp = strpbrk( text, "dDeE" );
25080 
25081 /* First check for long strings of adjacent zeros.
25082    =============================================== */
25083 
25084 /* Indicate that we have not yet found a decimal point in the string. */
25085    dot = NULL;
25086 
25087 /* The "started" flag controls whether *leading* zeros should be removed
25088    if there are more than NSEQ of them. They are only removed if there is an
25089    exponent. */
25090    started = ( exp != NULL );
25091 
25092 /* We are not currently replacing digits with zeros. */
25093    replace = 0;
25094 
25095 /* We have not yet found any adjacent zeros. */
25096    nzero = 0;
25097 
25098 /* We have no evidence yet that the number is non-zero. */
25099    nonzero = 0;
25100 
25101 /* Loop round the supplied text string. */
25102    c = text;
25103    while( *c && c != exp ){
25104 
25105 /* If this is a zero, increment the number of adjacent zeros found, so
25106    long as we have previously found a non-zero digit (or there is an
25107    exponent). If this is the NSEQ'th adjacent zero, indicate that
25108    subsequent digits should be replaced by zeros. */
25109       if( *c == '0' ){
25110          if( started && ++nzero >= NSEQ ) replace = 1;
25111 
25112 /* Note if the number contains a decimal point. */
25113       } else if( *c == '.' ){
25114          dot = c;
25115 
25116 /* If this character is a non-zero digit, indicate that we have found a
25117    non-zero digit. If we have previously found a long string of adjacent
25118    zeros, replace the digit by '0'. Otherwise, reset the count of
25119    adjacent zeros, and indicate the final number is non-zero. */
25120       } else if( *c != ' ' && *c != '+' && *c != '-' ){
25121          started = 1;
25122          if( replace ) {
25123             *c = '0';
25124          } else {
25125             nzero = 0;
25126             nonzero = 1;
25127          }
25128       }
25129 
25130 /* Move on to the next character. */
25131       c++;
25132    }
25133 
25134 /* If the final number is zero, just return the most simple decimal zero
25135    value. */
25136    if( !nonzero ) {
25137       strcpy( text, "0.0" );
25138 
25139 /* Otherwise, we remove any trailing zeros which occur to the right of a
25140    decimal point. */
25141    } else if( dot ) {
25142 
25143 /* Find the last non-zero digit. */
25144       while( c-- > text && *c == '0' );
25145 
25146 /* If any trailing zeros were found... */
25147       if( c > text ) {
25148 
25149 /* Retain one trailing zero after a decimal point. */
25150          if( *c == '.' ) c++;
25151 
25152 /* We put a terminator following the last non-zero character. The
25153    terminator is the exponent, if there was one, or a null character.
25154    Remember to update the pointer to the start of the exponent. */
25155          c++;
25156          if( exp ) {
25157             a = exp;
25158             exp = c;
25159             while( ( *(c++) = *(a++) ) );
25160          } else {
25161             *c = 0;
25162          }
25163       }
25164    }
25165 
25166 /* Next check for long strings of adjacent nines.
25167    ============================================= */
25168 
25169 /* We have not yet found any adjacent nines. */
25170    nnine = 0;
25171 
25172 /* We have not yet found a non-nine digit. */
25173    a = NULL;
25174 
25175 /* We have not yet found a non-blank character */
25176    start = NULL;
25177    last = NULL;
25178 
25179 /* Number is assumed positive. */
25180    neg = 0;
25181 
25182 /* Indicate that we have not yet found a decimal point in the string. */
25183    dot = NULL;
25184 
25185 /* Loop round the supplied text string. */
25186    c = text;
25187    while( *c && c != exp ){
25188 
25189 /* Note the address of the first non-blank character. */
25190       if( !start && *c != ' ' ) start = c;
25191 
25192 /* If this is a nine, increment the number of adjacent nines found. */
25193       if( *c == '9' ){
25194          ++nnine;
25195 
25196 /* Note if the number contains a decimal point. */
25197       } else if( *c == '.' ){
25198          dot = c;
25199 
25200 /* Note if the number is negative. */
25201       } else if( *c == '-' ){
25202          neg = 1;
25203 
25204 /* If this character is a non-nine digit, and we have not had a long
25205    sequence of 9's, reset the count of adjacent nines, and update a pointer
25206    to "the last non-nine digit prior to a long string of nines". */
25207       } else if( *c != ' ' && *c != '+' ){
25208          if( nnine < NSEQ ) {
25209             nnine = 0;
25210             a = c;
25211          }
25212       }
25213 
25214 /* Note the address of the last non-blank character. */
25215       if( *c != ' ' ) last = c;
25216 
25217 /* Move on to the next character. */
25218       c++;
25219    }
25220 
25221 /* If a long string of adjacent nines was found... */
25222    if( nnine >= NSEQ ) {
25223       c = NULL;
25224 
25225 /* If we found at least one non-nine digit. */
25226       if( a ) {
25227 
25228 /* "a" points to the last non-nine digit before the first of the group of 9's.
25229    Increment this digit by 1. Since we know the digit is not a nine, there
25230    is no danger of a carry. */
25231          *a = *a + 1;
25232 
25233 /* Fill with zeros up to the decimal point, or to  the end if there is no
25234    decimal point. */
25235          c = a + 1;
25236          if( dot ) {
25237             while( c < dot ) *(c++) = '0';
25238          } else {
25239             while( *c ) *(c++) = '0';
25240          }
25241 
25242 /* Now make "c" point to the first character for the terminator. This is
25243    usually the character following the last non-nine digit. However, if
25244    the last non-nine digit appears immediately before a decimal point, then
25245    we append ".0" to the string before appending the terminator. */
25246          if( *c == '.' ) {
25247             *(++c) = '0';
25248             c++;
25249          }
25250 
25251 /* If all digits were nines, the rounded number will occupy one more
25252    character than the supplied number. We can only do the rounding if there
25253    is a spare character (i.e.a space) in the supplied string. */
25254       } else if( last - start + 1 < len ) {
25255 
25256 /* Put the modified text at the left of the available space. */
25257          c = text;
25258 
25259 /* Start with a minus sing if needed, followed by the leading "1" (caused
25260    by the overflow from the long string of 9's). */
25261          if( neg ) *(c++) = '-';
25262          *(c++) = '1';
25263 
25264 /* Now find the number of zeros to place after the leading "1". This is
25265    the number of characters in front of the terminator marking the end of
25266    the integer part of the number. */
25267          if( dot ) {
25268             nzero = dot - start;
25269          } else if( exp ) {
25270             nzero = exp - start;
25271          } else {
25272             nzero = last - start;
25273          }
25274 
25275 /* If the number is negative, the above count will include the leading
25276    minus sign, which is not a digit. So reduce the count by one. */
25277          if( neg ) nzero--;
25278 
25279 /* Now put in the correct number of zeros. */
25280          for( i = 0; i < nzero; i++ ) *(c++) = '0';
25281 
25282 /* If the original string containsed a decimal point, make sure the
25283    returned string also contains one. */
25284          if( dot ) {
25285             *(c++) = '.';
25286             if( *c ) *(c++) = '0';
25287          }
25288       }
25289 
25290 /* We put a terminator following the last non-zero character. The
25291    terminator is the exponent, if there was one, or a null character. */
25292       if( c ) {
25293          if( exp ) {
25294             while( ( *(c++) = *(exp++) ) );
25295          } else {
25296             *c = 0;
25297          }
25298       }
25299    }
25300 
25301 /* Right justify the returned string in the original field width. */
25302    end = text + len;
25303    c = text + strlen( text );
25304    if( c != end ) {
25305       while( c >= text ) *(end--) = *(c--);
25306       while( end >= text ) *(end--) = ' ';
25307    }
25308 
25309 /* If a minimum field width was given, shunt the text to the left in
25310    order to reduce the used field width to the specified value. This
25311    requires there to be some leading spaces (because we do not want to
25312    loose any non-blank characters from the left hand end of the string).
25313    If there are insufficient leading spaces to allow the field width to
25314    be reduced to the specified value, then reduce the field width as far
25315    as possible. First find the number of spaces we would like to remove
25316    from the front of the string (in order to reduce the used width to the
25317    specified value). */
25318    bu = len - width;
25319 
25320 /* If we need to remove any leading spaces... */
25321    if( width > 0 && bu > 0 ) {
25322 
25323 /* Find the number of leading spaces which are available to be removed. */
25324       c = text - 1;
25325       while( *(++c) == ' ' );
25326       nls = c - text;
25327 
25328 /* If there are insufficient leading spaces, just use however many there
25329    are. */
25330       if( bu > nls ) bu = nls;
25331 
25332 /* Shift the string. */
25333       c = text;
25334       a = c + bu;
25335       while( ( *(c++) = *(a++) ) );
25336    }
25337 
25338 /* Undefine local constants. */
25339 #undef NSEQ
25340 }
25341 
SAOTrans(AstFitsChan * this,AstFitsChan * out,const char * method,const char * class,int * status)25342 static int SAOTrans( AstFitsChan *this, AstFitsChan *out, const char *method,
25343                      const char *class, int *status ){
25344 /*
25345 *  Name:
25346 *     SAOTrans
25347 
25348 *  Purpose:
25349 *     Translate an SAO encoded header into a TPN encoded header.
25350 
25351 *  Type:
25352 *     Private function.
25353 
25354 *  Synopsis:
25355 *     #include "fitschan.h"
25356 *     int SAOTrans( AstFitsChan *this, AstFitsChan *out, const char *method,
25357 *                   const char *class, int *status )
25358 
25359 *  Class Membership:
25360 *     FitsChan member function.
25361 
25362 *  Description:
25363 *     Search "this" for keywords that give a description of a distorted
25364 *     TAN projection using the SAO representation and, if found, write
25365 *     keywords to "out" that describe an equivalent projection using TPN
25366 *     representation. The definition of the SAO polynomial is taken from
25367 *     the platepos.c file included in Doug Mink's WCSTools.
25368 
25369 *  Parameters:
25370 *     this
25371 *        Pointer to the FitsChan to read.
25372 *     out
25373 *        Pointer to a FitsCHan in which to store translated keywords.
25374 *     method
25375 *        Pointer to a string holding the name of the calling method.
25376 *        This is only for use in constructing error messages.
25377 *     class
25378 *        Pointer to a string holding the name of the supplied object class.
25379 *        This is only for use in constructing error messages.
25380 *     status
25381 *        Pointer to the inherited status variable.
25382 
25383 *  Returned Value:
25384 *     Non-zero if "this" contained an SAO encoded header. Zero otherwise.
25385 
25386 */
25387 
25388 #define NC 13
25389 
25390 /* Local Variables: */
25391    char keyname[10];
25392    double co[ 2 ][ NC ];
25393    double pv;
25394    int i;
25395    int is_sao;
25396    int m;
25397    int ok;
25398    int result;
25399 
25400 /* Initialise */
25401    result = 0;
25402 
25403 /* Check the inherited status. */
25404    if( !astOK ) return result;
25405 
25406 /* Check there are exactly two CTYPE keywords in the header. */
25407    if( 2 == astKeyFields( this, "CTYPE%d", 0, NULL, NULL ) ){
25408 
25409 /* Initialise all cooefficients. */
25410       memset( co, 0, sizeof( co ) );
25411 
25412 /* Get the required SAO keywords. */
25413       is_sao = 1;
25414       ok = 1;
25415       for( i = 0; i < 2 && ok && is_sao; i++ ) {
25416 
25417          ok = 0;
25418          for( m = 0; m < NC; m++ ) {
25419 
25420 /* Get the value of the next "COi_j" keyword. If any of the first 3 values
25421    are missing on either axis, we assume this is not an SAO header. */
25422             sprintf( keyname, "CO%d_%d", i + 1, m + 1 );
25423             if( !GetValue( this, keyname, AST__FLOAT, &co[ i ][ m ], 0, 1, method,
25424                            class, status ) ) {
25425                if( m < 3 ) is_sao = 0;
25426                break;
25427             }
25428 
25429 /* Check that we have at least one non-zero coefficient (excluding the
25430    first constant term ). */
25431             if( co[ i ][ m ] != 0.0 && m > 0 ) ok = 1;
25432          }
25433       }
25434 
25435 /* If this is an SAO header..  */
25436       if( is_sao ) {
25437 
25438 /* Issue a warning if all coefficients for this axis are zero. */
25439          if( !ok ) {
25440             Warn( this, "badpv", "This FITS header describes an SAO encoded "
25441                   "distorted TAN projection, but all the distortion "
25442                   "coefficients for at least one axis are zero.", method, class,
25443                   status );
25444 
25445 /* Otherwise, calculate and store the equivalent PV projection parameters. */
25446          } else {
25447             pv = co[ 0 ][ 0 ];
25448             if( pv != AST__BAD ) SetValue( out, "PV1_0", &pv,
25449                                            AST__FLOAT, NULL, status );
25450 
25451             pv = co[ 0 ][ 1 ];
25452             if( pv != AST__BAD ) SetValue( out, "PV1_1", &pv,
25453                                            AST__FLOAT, NULL, status );
25454 
25455             pv = co[ 0 ][ 2 ];
25456             if( pv != AST__BAD ) SetValue( out, "PV1_2", &pv,
25457                                            AST__FLOAT, NULL, status );
25458 
25459             pv = 0.0;
25460             if( co[ 0 ][ 3 ] != AST__BAD ) pv += co[ 0 ][ 3 ];
25461             if( co[ 0 ][ 10 ] != AST__BAD ) pv += co[ 0 ][ 10 ];
25462             if( pv != AST__BAD ) SetValue( out, "PV1_4", &pv,
25463                                            AST__FLOAT, NULL, status );
25464 
25465             pv = co[ 0 ][ 5 ];
25466             if( pv != AST__BAD ) SetValue( out, "PV1_5", &pv,
25467                                            AST__FLOAT, NULL, status );
25468 
25469             pv = 0.0;
25470             if( co[ 0 ][ 4 ] != AST__BAD ) pv += co[ 0 ][ 4 ];
25471             if( co[ 0 ][ 10 ] != AST__BAD ) pv += co[ 0 ][ 10 ];
25472             if( pv != AST__BAD ) SetValue( out, "PV1_6", &pv,
25473                                            AST__FLOAT, NULL, status );
25474 
25475             pv = 0.0;
25476             if( co[ 0 ][ 6 ] != AST__BAD ) pv += co[ 0 ][ 6 ];
25477             if( co[ 0 ][ 11 ] != AST__BAD ) pv += co[ 0 ][ 11 ];
25478             if( pv != AST__BAD ) SetValue( out, "PV1_7", &pv,
25479                                            AST__FLOAT, NULL, status );
25480 
25481             pv = 0.0;
25482             if( co[ 0 ][ 8 ] != AST__BAD ) pv += co[ 0 ][ 8 ];
25483             if( co[ 0 ][ 12 ] != AST__BAD ) pv += co[ 0 ][ 12 ];
25484             if( pv != AST__BAD ) SetValue( out, "PV1_8", &pv,
25485                                            AST__FLOAT, NULL, status );
25486 
25487             pv = 0.0;
25488             if( co[ 0 ][ 9 ] != AST__BAD ) pv += co[ 0 ][ 9 ];
25489             if( co[ 0 ][ 11 ] != AST__BAD ) pv += co[ 0 ][ 11 ];
25490             if( pv != AST__BAD ) SetValue( out, "PV1_9", &pv,
25491                                            AST__FLOAT, NULL, status );
25492 
25493             pv = 0.0;
25494             if( co[ 0 ][ 7 ] != AST__BAD ) pv += co[ 0 ][ 7 ];
25495             if( co[ 0 ][ 12 ] != AST__BAD ) pv += co[ 0 ][ 12 ];
25496             if( pv != AST__BAD ) SetValue( out, "PV1_10", &pv,
25497                                            AST__FLOAT, NULL, status );
25498 
25499             pv = co[ 1 ][ 0 ];
25500             if( pv != AST__BAD ) SetValue( out, "PV2_0", &pv,
25501                                            AST__FLOAT, NULL, status );
25502 
25503             pv = co[ 1 ][ 2 ];
25504             if( pv != AST__BAD ) SetValue( out, "PV2_1", &pv,
25505                                            AST__FLOAT, NULL, status );
25506 
25507             pv = co[ 1 ][ 1 ];
25508             if( pv != AST__BAD ) SetValue( out, "PV2_2", &pv,
25509                                            AST__FLOAT, NULL, status );
25510 
25511             pv = 0.0;
25512             if( co[ 1 ][ 4 ] != AST__BAD ) pv += co[ 1 ][ 4 ];
25513             if( co[ 1 ][ 10 ] != AST__BAD ) pv += co[ 1 ][ 10 ];
25514             if( pv != AST__BAD ) SetValue( out, "PV2_4", &pv,
25515                                            AST__FLOAT, NULL, status );
25516 
25517             pv = co[ 1 ][ 5 ];
25518             if( pv != AST__BAD ) SetValue( out, "PV2_5", &pv,
25519                                            AST__FLOAT, NULL, status );
25520 
25521             pv = 0.0;
25522             if( co[ 1 ][ 3 ] != AST__BAD ) pv += co[ 1 ][ 3 ];
25523             if( co[ 1 ][ 10 ] != AST__BAD ) pv += co[ 1 ][ 10 ];
25524             if( pv != AST__BAD ) SetValue( out, "PV2_6", &pv,
25525                                            AST__FLOAT, NULL, status );
25526 
25527             pv = 0.0;
25528             if( co[ 1 ][ 7 ] != AST__BAD ) pv += co[ 1 ][ 7 ];
25529             if( co[ 1 ][ 12 ] != AST__BAD ) pv += co[ 1 ][ 12 ];
25530             if( pv != AST__BAD ) SetValue( out, "PV2_7", &pv,
25531                                            AST__FLOAT, NULL, status );
25532 
25533             pv = 0.0;
25534             if( co[ 1 ][ 9 ] != AST__BAD ) pv += co[ 1 ][ 9 ];
25535             if( co[ 1 ][ 11 ] != AST__BAD ) pv += co[ 1 ][ 11 ];
25536             if( pv != AST__BAD ) SetValue( out, "PV2_8", &pv,
25537                                            AST__FLOAT, NULL, status );
25538 
25539             pv = 0.0;
25540             if( co[ 1 ][ 8 ] != AST__BAD ) pv += co[ 1 ][ 8 ];
25541             if( co[ 1 ][ 12 ] != AST__BAD ) pv += co[ 1 ][ 12 ];
25542             if( pv != AST__BAD ) SetValue( out, "PV2_9", &pv,
25543                                            AST__FLOAT, NULL, status );
25544 
25545             pv = 0.0;
25546             if( co[ 1 ][ 6 ] != AST__BAD ) pv += co[ 1 ][ 6 ];
25547             if( co[ 1 ][ 11 ] != AST__BAD ) pv += co[ 1 ][ 11 ];
25548             if( pv != AST__BAD ) SetValue( out, "PV2_10", &pv,
25549                                            AST__FLOAT, NULL, status );
25550 
25551 /* From an example header provided by Bill Joye, it seems that the SAO
25552    polynomial includes the rotation and scaling effects of the CD matrix.
25553    Therefore we mark as read all CDi_j, CDELT and CROTA values. Without
25554    this, the rotation and scaling would be applied twice. First, mark the
25555    original values as having been used, no matter which FitsChan they are
25556    in. */
25557             GetValue( this, "CD1_1", AST__FLOAT, &pv, 0, 1, method, class, status );
25558             GetValue( this, "CD1_2", AST__FLOAT, &pv, 0, 1, method, class, status );
25559             GetValue( this, "CD2_1", AST__FLOAT, &pv, 0, 1, method, class, status );
25560             GetValue( this, "CD2_2", AST__FLOAT, &pv, 0, 1, method, class, status );
25561             GetValue( this, "PC1_1", AST__FLOAT, &pv, 0, 1, method, class, status );
25562             GetValue( this, "PC1_2", AST__FLOAT, &pv, 0, 1, method, class, status );
25563             GetValue( this, "PC2_1", AST__FLOAT, &pv, 0, 1, method, class, status );
25564             GetValue( this, "PC2_2", AST__FLOAT, &pv, 0, 1, method, class, status );
25565             GetValue( this, "CDELT1", AST__FLOAT, &pv, 0, 1, method, class, status );
25566             GetValue( this, "CDELT2", AST__FLOAT, &pv, 0, 1, method, class, status );
25567             GetValue( this, "CROTA1", AST__FLOAT, &pv, 0, 1, method, class, status );
25568             GetValue( this, "CROTA2", AST__FLOAT, &pv, 0, 1, method, class, status );
25569 
25570             GetValue( out, "CD1_1", AST__FLOAT, &pv, 0, 1, method, class, status );
25571             GetValue( out, "CD1_2", AST__FLOAT, &pv, 0, 1, method, class, status );
25572             GetValue( out, "CD2_1", AST__FLOAT, &pv, 0, 1, method, class, status );
25573             GetValue( out, "CD2_2", AST__FLOAT, &pv, 0, 1, method, class, status );
25574             GetValue( out, "PC1_1", AST__FLOAT, &pv, 0, 1, method, class, status );
25575             GetValue( out, "PC1_2", AST__FLOAT, &pv, 0, 1, method, class, status );
25576             GetValue( out, "PC2_1", AST__FLOAT, &pv, 0, 1, method, class, status );
25577             GetValue( out, "PC2_2", AST__FLOAT, &pv, 0, 1, method, class, status );
25578             GetValue( out, "CDELT1", AST__FLOAT, &pv, 0, 1, method, class, status );
25579             GetValue( out, "CDELT2", AST__FLOAT, &pv, 0, 1, method, class, status );
25580             GetValue( out, "CROTA1", AST__FLOAT, &pv, 0, 1, method, class, status );
25581             GetValue( out, "CROTA2", AST__FLOAT, &pv, 0, 1, method, class, status );
25582 
25583 /* Now store new default values in the returned FitsChan. */
25584             pv = 1.0;
25585             SetValue( out, "PC1_1", &pv, AST__FLOAT, NULL,
25586                       status );
25587             SetValue( out, "PC2_2", &pv, AST__FLOAT, NULL,
25588                       status );
25589             SetValue( out, "CDELT1", &pv, AST__FLOAT, NULL,
25590                       status );
25591             SetValue( out, "CDELT2", &pv, AST__FLOAT, NULL,
25592                       status );
25593 
25594             pv = 0.0;
25595             SetValue( out, "PC1_2", &pv, AST__FLOAT, NULL,
25596                       status );
25597             SetValue( out, "PC2_1", &pv, AST__FLOAT, NULL,
25598                       status );
25599 
25600 /* Indicate we have converted an SAO header. */
25601             result = 1;
25602          }
25603       }
25604    }
25605 
25606 /* Return a flag indicating if an SAO header was found. */
25607    return result;
25608 }
25609 #undef NC
25610 
SearchCard(AstFitsChan * this,const char * name,const char * method,const char * class,int * status)25611 static int SearchCard( AstFitsChan *this, const char *name,
25612                        const char *method, const char *class, int *status ){
25613 
25614 /*
25615 *  Name:
25616 *     SearchCard
25617 
25618 *  Purpose:
25619 *     Search the whole FitsChan for a card refering to given keyword.
25620 
25621 *  Type:
25622 *     Private function.
25623 
25624 *  Synopsis:
25625 *     #include "fitschan.h"
25626 
25627 *     int SearchCard( AstFitsChan *this, const char *name,
25628 *                     const char *method, const char *class, int *status )
25629 
25630 *  Class Membership:
25631 *     FitsChan member function.
25632 
25633 *  Description:
25634 *     Searches the whole FitsChan for a card refering to the supplied keyword,
25635 *     and makes it the current card. The card following the current card is
25636 *     checked first. If this is not the required card, then a search is
25637 *     performed starting with the first keyword in the FitsChan.
25638 
25639 *  Parameters:
25640 *     this
25641 *        Pointer to the FitsChan.
25642 *     name
25643 *        Pointer to a string holding the keyword name.
25644 *     method
25645 *        Pointer to string holding name of calling method.
25646 *     status
25647 *        Pointer to the inherited status variable.
25648 
25649 *  Returned Value:
25650 *     A value of 1 is returned if a card was found refering to the given
25651 *     keyword. Otherwise zero is returned.
25652 
25653 *  Notes:
25654 *     -  If a NULL pointer is supplied for "name" then the current card
25655 *     is left unchanged.
25656 *     -  The current card is set to NULL (end-of-file) if no card can be
25657 *     found for the supplied keyword.
25658 */
25659 
25660 /* Local Variables: */
25661    int ret;              /* Was a card found? */
25662 
25663 /* Check the global status, and supplied keyword name. */
25664    if( !astOK || !name ) return 0;
25665 
25666 /* Indicate that no card has been found yet. */
25667    ret = 0;
25668 
25669 /* The required card is very often the next card in the FitsChan, so check the
25670    next card, and only search the entire FitsChan if the check fails. */
25671    MoveCard( this, 1, method, class, status );
25672    if( !astFitsEof( this ) &&
25673        !Ustrncmp( CardName( this, status ), name, FITSNAMLEN, status ) ){
25674       ret = 1;
25675 
25676 /* If the next card is not the required card, rewind the FitsChan back to
25677    the first card. */
25678    } else {
25679       astClearCard( this );
25680 
25681 /* Attempt to find the supplied keyword, searching from the first card. */
25682       ret = FindKeyCard( this, name, method, class, status );
25683    }
25684 
25685 /* Return. */
25686    return ret;
25687 }
25688 
SetAlgCode(char * buf,const char * algcode,int * status)25689 static void SetAlgCode( char *buf, const char *algcode, int *status ){
25690 /*
25691 *  Name:
25692 *     SetAlgCode
25693 
25694 *  Purpose:
25695 *    Create a non-linear CTYPE string from a system code and an algorithm
25696 *    code.
25697 
25698 *  Type:
25699 *     Private function.
25700 
25701 *  Synopsis:
25702 *     void SetAlgCode( char *buf, const char *algcode, int *status )
25703 
25704 *  Class Membership:
25705 *     FitsChan
25706 
25707 *  Description:
25708 *     FITS-WCS paper 1 says that non-linear axes must have a CTYPE of the
25709 *     form "4-3" (e.g. "VRAD-TAB"). This function handles the truncation
25710 *     of long system codes, or the padding of short system codes.
25711 
25712 *  Parameters:
25713 *     buf
25714 *        A buffer in which is stored the system code. Modified on exit to
25715 *        hold the combined CTYPE value. It should have a length long
25716 *        enough to hold the system code and the algorithm code.
25717 *     algcode
25718 *        Pointer to a string holding the algorithm code (with a leading
25719 *        "-", e.g. "-TAB").
25720 *     status
25721 *        Pointer to the inherited status variable.
25722 */
25723 
25724 /* Local Variables: */
25725    int nc;
25726 
25727 /* Check inherited status */
25728    if( !astOK ) return;
25729 
25730 /* Pad the supplied string to at least 4 characters using "-" characters. */
25731    nc = strlen( buf );
25732    while( nc < 4 ) buf[ nc++ ] = '-';
25733 
25734 /* Insert the null-terminated code at position 4. */
25735    strcpy( buf + 4, algcode );
25736 }
25737 
SetAttrib(AstObject * this_object,const char * setting,int * status)25738 static void SetAttrib( AstObject *this_object, const char *setting, int *status ) {
25739 /*
25740 *  Name:
25741 *     SetAttrib
25742 
25743 *  Purpose:
25744 *     Set an attribute value for a FitsChan.
25745 
25746 *  Type:
25747 *     Private function.
25748 
25749 *  Synopsis:
25750 *     #include "fitschan.h"
25751 *     void SetAttrib( AstObject *this, const char *setting )
25752 
25753 *  Class Membership:
25754 *     FitsChan member function (over-rides the astSetAttrib protected
25755 *     method inherited from the Channel class).
25756 
25757 *  Description:
25758 *     This function assigns an attribute value for a FitsChan, the
25759 *     attribute and its value being specified by means of a string of
25760 
25761 *     the form:
25762 *
25763 *        "attribute= value "
25764 *
25765 *     Here, "attribute" specifies the attribute name and should be in
25766 *     lower case with no white space present. The value to the right
25767 *     of the "=" should be a suitable textual representation of the
25768 *     value to be assigned and this will be interpreted according to
25769 *     the attribute's data type.  White space surrounding the value is
25770 *     only significant for string attributes.
25771 
25772 *  Parameters:
25773 *     this
25774 *        Pointer to the FitsChan.
25775 *     setting
25776 *        Pointer to a null-terminated string specifying the new attribute
25777 *        value.
25778 */
25779 
25780 /* Local Variables: */
25781    AstFitsChan *this;            /* Pointer to the FitsChan structure */
25782    const char *class;            /* Object class */
25783    int ival;                     /* Integer attribute value */
25784    int len;                      /* Length of setting string */
25785    int nc;                       /* Number of characters read by astSscanf */
25786    int offset;                   /* Offset of attribute string */
25787    int warn;                     /* Offset of Warnings string */
25788 
25789 /* Check the global error status. */
25790    if ( !astOK ) return;
25791 
25792 /* Obtain a pointer to the FitsChan structure. */
25793    this = (AstFitsChan *) this_object;
25794 
25795 /* Obtain the length of the setting string. */
25796    len = (int) strlen( setting );
25797 
25798 /* Obtain the object class. */
25799    class = astGetClass( this );
25800 
25801 /* Card. */
25802 /* ----- */
25803    if ( nc = 0,
25804         ( 1 == astSscanf( setting, "card= %d %n", &ival, &nc ) )
25805         && ( nc >= len ) ) {
25806       astSetCard( this, ival );
25807 
25808 /* Encoding. */
25809 /* --------- */
25810    } else if( nc = 0,
25811         ( 0 == astSscanf( setting, "encoding=%n%*[^\n]%n", &ival, &nc ) )
25812         && ( nc >= len ) ) {
25813       nc = ChrLen( setting + ival, status );
25814       if( !Ustrncmp( setting + ival, NATIVE_STRING, nc, status ) ){
25815          astSetEncoding( this, NATIVE_ENCODING );
25816       } else if( !Ustrncmp( setting + ival, FITSPC_STRING, nc, status ) ){
25817          astSetEncoding( this, FITSPC_ENCODING );
25818       } else if( !Ustrncmp( setting + ival, FITSPC_STRING2, nc, status ) ){
25819          astSetEncoding( this, FITSPC_ENCODING );
25820       } else if( !Ustrncmp( setting + ival, FITSWCS_STRING, nc, status ) ){
25821          astSetEncoding( this, FITSWCS_ENCODING );
25822       } else if( !Ustrncmp( setting + ival, FITSWCS_STRING2, nc, status ) ){
25823          astSetEncoding( this, FITSWCS_ENCODING );
25824       } else if( !Ustrncmp( setting + ival, FITSIRAF_STRING, nc, status ) ){
25825          astSetEncoding( this, FITSIRAF_ENCODING );
25826       } else if( !Ustrncmp( setting + ival, FITSIRAF_STRING2, nc, status ) ){
25827          astSetEncoding( this, FITSIRAF_ENCODING );
25828       } else if( !Ustrncmp( setting + ival, FITSAIPS_STRING, nc, status ) ){
25829          astSetEncoding( this, FITSAIPS_ENCODING );
25830       } else if( !Ustrncmp( setting + ival, FITSAIPS_STRING2, nc, status ) ){
25831          astSetEncoding( this, FITSAIPS_ENCODING );
25832       } else if( !Ustrncmp( setting + ival, FITSAIPSPP_STRING, nc, status ) ){
25833          astSetEncoding( this, FITSAIPSPP_ENCODING );
25834       } else if( !Ustrncmp( setting + ival, FITSAIPSPP_STRING2, nc, status ) ){
25835          astSetEncoding( this, FITSAIPSPP_ENCODING );
25836       } else if( !Ustrncmp( setting + ival, FITSCLASS_STRING, nc, status ) ){
25837          astSetEncoding( this, FITSCLASS_ENCODING );
25838       } else if( !Ustrncmp( setting + ival, FITSCLASS_STRING2, nc, status ) ){
25839          astSetEncoding( this, FITSCLASS_ENCODING );
25840       } else if( !Ustrncmp( setting + ival, DSS_STRING, nc, status ) ){
25841          astSetEncoding( this, DSS_ENCODING );
25842       } else {
25843          astError( AST__BADAT, "astSet(%s): Unknown encoding system '%s' "
25844                    "requested for a %s.", status, class, setting + ival, class );
25845       }
25846 
25847 /* FitsDigits. */
25848 /* ----------- */
25849    } else if ( nc = 0,
25850         ( 1 == astSscanf( setting, "fitsdigits= %d %n", &ival, &nc ) )
25851         && ( nc >= len ) ) {
25852       astSetFitsDigits( this, ival );
25853 
25854 /* FitsAxisOrder. */
25855 /* -------------- */
25856    } else if ( nc = 0,
25857                ( 0 == astSscanf( setting, "fitsaxisorder=%n%*[^\n]%n",
25858                                  &offset, &nc ) )
25859                && ( nc >= len ) ) {
25860       astSetFitsAxisOrder( this, setting + offset );
25861 
25862 /* CDMatrix */
25863 /* -------- */
25864    } else if ( nc = 0,
25865         ( 1 == astSscanf( setting, "cdmatrix= %d %n", &ival, &nc ) )
25866         && ( nc >= len ) ) {
25867       astSetCDMatrix( this, ival );
25868 
25869 /* DefB1950 */
25870 /* -------- */
25871    } else if ( nc = 0,
25872         ( 1 == astSscanf( setting, "defb1950= %d %n", &ival, &nc ) )
25873         && ( nc >= len ) ) {
25874       astSetDefB1950( this, ival );
25875 
25876 /* TabOK */
25877 /* ----- */
25878    } else if ( nc = 0,
25879         ( 1 == astSscanf( setting, "tabok= %d %n", &ival, &nc ) )
25880         && ( nc >= len ) ) {
25881       astSetTabOK( this, ival );
25882 
25883 /* CarLin */
25884 /* ------ */
25885    } else if ( nc = 0,
25886         ( 1 == astSscanf( setting, "carlin= %d %n", &ival, &nc ) )
25887         && ( nc >= len ) ) {
25888       astSetCarLin( this, ival );
25889 
25890 /* PolyTan */
25891 /* ------- */
25892    } else if ( nc = 0,
25893         ( 1 == astSscanf( setting, "polytan= %d %n", &ival, &nc ) )
25894         && ( nc >= len ) ) {
25895       astSetPolyTan( this, ival );
25896 
25897 /* Iwc */
25898 /* --- */
25899    } else if ( nc = 0,
25900         ( 1 == astSscanf( setting, "iwc= %d %n", &ival, &nc ) )
25901         && ( nc >= len ) ) {
25902       astSetIwc( this, ival );
25903 
25904 /* Clean */
25905 /* ----- */
25906    } else if ( nc = 0,
25907         ( 1 == astSscanf( setting, "clean= %d %n", &ival, &nc ) )
25908         && ( nc >= len ) ) {
25909       astSetClean( this, ival );
25910 
25911 /* Warnings. */
25912 /* -------- */
25913    } else if ( nc = 0,
25914                ( 0 == astSscanf( setting, "warnings=%n%*[^\n]%n", &warn, &nc ) )
25915                && ( nc >= len ) ) {
25916       astSetWarnings( this, setting + warn );
25917 
25918 /* Define a macro to see if the setting string matches any of the
25919    read-only attributes of this class. */
25920 #define MATCH(attrib) \
25921         ( nc = 0, ( 0 == astSscanf( setting, attrib "=%*[^\n]%n", &nc ) ) && \
25922                   ( nc >= len ) )
25923 
25924 /* If the attribute was not recognised, use this macro to report an error
25925    if a read-only attribute has been specified. */
25926    } else if ( MATCH( "ncard" ) ||
25927                MATCH( "cardtype" ) ||
25928                MATCH( "cardcomm" ) ||
25929                MATCH( "cardname" ) ||
25930                MATCH( "nkey" ) ||
25931                MATCH( "allwarnings" ) ){
25932       astError( AST__NOWRT, "astSet: The setting \"%s\" is invalid for a %s.", status,
25933                 setting, astGetClass( this ) );
25934       astError( AST__NOWRT, "This is a read-only attribute." , status);
25935 
25936 /* If the attribute is still not recognised, pass it on to the parent
25937    method for further interpretation. */
25938    } else {
25939       (*parent_setattrib)( this_object, setting, status );
25940    }
25941 }
25942 
SetCard(AstFitsChan * this,int icard,int * status)25943 static void SetCard( AstFitsChan *this, int icard, int *status ){
25944 
25945 /*
25946 *+
25947 *  Name:
25948 *     astSetCard
25949 
25950 *  Purpose:
25951 *     Set the value of the Card attribute.
25952 
25953 *  Type:
25954 *     Protected virtual function.
25955 
25956 *  Synopsis:
25957 *     #include "fitschan.h"
25958 
25959 *     void astSetCard( AstFitsChan *this, int icard )
25960 
25961 *  Class Membership:
25962 *     FitsChan method.
25963 
25964 *  Description:
25965 *     This function sets the value of the Card attribute for the supplied
25966 *     FitsChan. This is the index of the next card to be read from the
25967 *     FitsChan. If a value of 1 or less is supplied, the first card in
25968 *     the FitsChan will be read next. If a value greater than the number
25969 *     of cards in the FitsChan is supplied, the FitsChan will be left in an
25970 *     "end-of-file" condition, in which no further read operations can be
25971 *     performed.
25972 
25973 *  Parameters:
25974 *     this
25975 *        Pointer to the FitsChan.
25976 *     icard
25977 *        The index of the next card to read.
25978 
25979 *  Notes:
25980 *     -  This function attempts to execute even if an error has occurred.
25981 *-
25982 */
25983 
25984 /* Check the supplied object. */
25985    if ( !this ) return;
25986 
25987 /* Ensure the source function has been called */
25988    ReadFromSource( this, status );
25989 
25990 /* Rewind the FitsChan. */
25991    astClearCard( this );
25992 
25993 /* Move forward the requested number of cards. */
25994    MoveCard( this, icard - 1, "astSetCard", astGetClass( this ), status );
25995 
25996 /* Return. */
25997    return;
25998 }
25999 
SetItem(double **** item,int i,int jm,char s,double val,int * status)26000 static void SetItem( double ****item, int i, int jm, char s, double val, int *status ){
26001 /*
26002 *  Name:
26003 *     SetItem
26004 
26005 *  Purpose:
26006 *     Store a value for a axis keyword value in a FitStore structure.
26007 
26008 *  Type:
26009 *     Private function.
26010 
26011 *  Synopsis:
26012 *     #include "fitschan.h"
26013 *     void SetItem( double ****item, int i, int jm, char s, double val, int *status )
26014 
26015 *  Class Membership:
26016 *     FitsChan member function.
26017 
26018 *  Description:
26019 *     The supplied keyword value is stored in the specified array,
26020 *     at a position indicated by the axis and co-ordinate version.
26021 *     The array is created or extended as necessary to make room for
26022 *     the new value. Any old value is over-written.
26023 
26024 *  Parameters:
26025 *     item
26026 *        The address of the pointer within the FitsStore which locates the
26027 *        arrays of values for the required keyword (eg &(store->crval) ).
26028 *        The array located by the supplied pointer contains a vector of
26029 *        pointers. Each of these pointers is associated with a particular
26030 *        co-ordinate version (s), and locates an array of pointers for that
26031 *        co-ordinate version. Each such array of pointers has an element
26032 *        for each intermediate axis number (i), and the pointer locates an
26033 *        array of axis keyword values. These arrays of keyword values have
26034 *        one element for every pixel axis (j) or projection parameter (m).
26035 *     i
26036 *        The zero based intermediate axis index in the range 0 to 98. Set
26037 *        this to zero for keywords (e.g. CRPIX) which are not indexed by
26038 *        intermediate axis number.
26039 *     jm
26040 *        The zero based pixel axis index (in the range 0 to 98) or parameter
26041 *        index (in the range 0 to WCSLIB__MXPAR-1). Set this to zero for
26042 *        keywords (e.g. CRVAL) which are not indexed by either pixel axis or
26043 *        parameter number.
26044 *     val
26045 *        The keyword value to store.
26046 *     status
26047 *        Pointer to the inherited status variable.
26048 */
26049 
26050 /* Local Variables: */
26051    int el;               /* Array index */
26052    int nel;              /* Number of elements in array */
26053    int si;               /* Integer co-ordinate version index */
26054 
26055 /* Check the inherited status. */
26056    if( !astOK ) return;
26057 
26058 /* Convert the character co-ordinate version into an integer index, and
26059    check it is within range. The primary axis description (s=' ') is
26060    given index zero. 'A' is 1, 'B' is 2, etc. */
26061    if( s == ' ' ) {
26062       si = 0;
26063    } else if( islower(s) ){
26064       si = (int) ( s - 'a' ) + 1;
26065    } else {
26066       si = (int) ( s - 'A' ) + 1;
26067    }
26068    if( si < 0 || si > 26 ) {
26069       astError( AST__INTER, "SetItem(fitschan): AST internal error; "
26070                 "co-ordinate version '%c' ( char(%d) ) is invalid.", status, s, s );
26071 
26072 /* Check the intermediate axis index is within range. */
26073    } else if( i < 0 || i > 98 ) {
26074       astError( AST__INTER, "SetItem(fitschan): AST internal error; "
26075                 "intermediate axis index %d is invalid.", status, i );
26076 
26077 /* Check the pixel axis or parameter index is within range. */
26078    } else if( jm < 0 || jm > 99 ) {
26079       astError( AST__INTER, "SetItem(fitschan): AST internal error; "
26080                 "pixel axis or parameter index %d is invalid.", status, jm );
26081 
26082 /* Otherwise proceed... */
26083    } else {
26084 
26085 /* Store the current number of coordinate versions in the supplied array */
26086       nel = astSizeOf( (void *) *item )/sizeof(double **);
26087 
26088 /* If required, extend the array located by the supplied pointer so that
26089    it is long enough to hold the specified co-ordinate version. */
26090       if( nel < si + 1 ){
26091          *item = (double ***) astGrow( (void *) *item, si + 1,
26092                                       sizeof(double **) );
26093 
26094 /* Check the pointer can be used. */
26095          if( astOK ){
26096 
26097 /* Initialise the new elements to hold NULL. Note, astGrow may add more
26098    elements to the array than is actually needed, so use the actual current
26099    size of the array as implied by astSize rather than the index si. */
26100             for( el = nel;
26101                  el < astSizeOf( (void *) *item )/sizeof(double **);
26102                  el++ ) (*item)[el] = NULL;
26103          }
26104       }
26105 
26106 /* If the above went OK... */
26107       if( astOK ){
26108 
26109 /* Store the currrent number of intermediate axes in the supplied array */
26110          nel = astSizeOf( (void *) (*item)[si] )/sizeof(double *);
26111 
26112 /* If required, extend the array so that it is long enough to hold the
26113    specified intermediate axis. */
26114          if( nel < i + 1 ){
26115             (*item)[si] = (double **) astGrow( (void *) (*item)[si], i + 1,
26116                                       sizeof(double *) );
26117 
26118 /* Check the pointer can be used. */
26119             if( astOK ){
26120 
26121 /* Initialise the new elements to hold NULL. */
26122                for( el = nel;
26123                     el < astSizeOf( (void *) (*item)[si] )/sizeof(double *);
26124                     el++ ) (*item)[si][el] = NULL;
26125             }
26126          }
26127 
26128 /* If the above went OK... */
26129          if( astOK ){
26130 
26131 /* Store the current number of pixel axis or parameter values in the array. */
26132             nel = astSizeOf( (void *) (*item)[si][i] )/sizeof(double);
26133 
26134 /* If required, extend the array so that it is long enough to hold the
26135    specified axis. */
26136             if( nel < jm + 1 ){
26137                (*item)[si][i] = (double *) astGrow( (void *) (*item)[si][i],
26138                                                     jm + 1, sizeof(double) );
26139 
26140 /* Check the pointer can be used. */
26141                if( astOK ){
26142 
26143 /* Initialise the new elements to hold AST__BAD. */
26144                   for( el = nel;
26145                        el < astSizeOf( (void *) (*item)[si][i] )/sizeof(double);
26146                        el++ ) (*item)[si][i][el] = AST__BAD;
26147                }
26148             }
26149 
26150 /* If the above went OK, store the supplied keyword value. */
26151             if( astOK ) (*item)[si][i][jm] = val;
26152          }
26153       }
26154    }
26155 }
26156 
SetItemC(char ***** item,int i,int jm,char s,const char * val,int * status)26157 static void SetItemC( char *****item, int i, int jm, char s, const char *val,
26158                       int *status ){
26159 /*
26160 *  Name:
26161 *     SetItemC
26162 
26163 *  Purpose:
26164 *     Store a character string for an axis keyword value in a FitStore
26165 *     structure.
26166 
26167 *  Type:
26168 *     Private function.
26169 
26170 *  Synopsis:
26171 *     #include "fitschan.h"
26172 *     void SetItemC( char *****item, int i, int jm, char s, const char *val,
26173 *                    int *status )
26174 
26175 *  Class Membership:
26176 *     FitsChan member function.
26177 
26178 *  Description:
26179 *     The supplied keyword string value is stored in the specified array,
26180 *     at a position indicated by the axis and co-ordinate version.
26181 *     The array is created or extended as necessary to make room for
26182 *     the new value. Any old value is over-written.
26183 
26184 *  Parameters:
26185 *     item
26186 *        The address of the pointer within the FitsStore which locates the
26187 *        arrays of values for the required keyword (eg &(store->ctype) ).
26188 *        The array located by the supplied pointer contains a vector of
26189 *        pointers. Each of these pointers is associated with a particular
26190 *        co-ordinate version (s), and locates an array of pointers for that
26191 *        co-ordinate version. Each such array of pointers has an element
26192 *        for each intermediate axis number (i), and the pointer locates an
26193 *        array of axis keyword string pointers. These arrays of keyword
26194 *        string pointers have one element for every pixel axis (j) or
26195 *        projection parameter (m).
26196 *     i
26197 *        The zero based intermediate axis index in the range 0 to 98. Set
26198 *        this to zero for keywords (e.g. RADESYS) which are not indexed by
26199 *        intermediate axis number.
26200 *     jm
26201 *        The zero based pixel axis index (in the range 0 to 98) or parameter
26202 *        index (in the range 0 to WCSLIB__MXPAR-1). Set this to zero for
26203 *        keywords (e.g. CTYPE) which are not indexed by either pixel axis or
26204 *        parameter number.
26205 *     val
26206 *        The keyword string value to store. A copy of the supplied string
26207 *        is taken.
26208 *     status
26209 *        Pointer to the inherited status variable.
26210 */
26211 
26212 /* Local Variables: */
26213    int el;               /* Array index */
26214    int nel;              /* Number of elements in array */
26215    int si;               /* Integer co-ordinate version index */
26216 
26217 /* Check the inherited status and the supplied pointer. */
26218    if( !astOK || !val ) return;
26219 
26220 /* Convert the character co-ordinate version into an integer index, and
26221    check it is within range. The primary axis description (s=' ') is
26222    given index zero. 'A' is 1, 'B' is 2, etc. */
26223    if( s == ' ' ) {
26224       si = 0;
26225    } else if( islower(s) ){
26226       si = (int) ( s - 'a' ) + 1;
26227    } else {
26228       si = (int) ( s - 'A' ) + 1;
26229    }
26230    if( si < 0 || si > 26 ) {
26231       astError( AST__INTER, "SetItemC(fitschan): AST internal error; "
26232                 "co-ordinate version '%c' ( char(%d) ) is invalid.", status, s, s );
26233 
26234 /* Check the intermediate axis index is within range. */
26235    } else if( i < 0 || i > 98 ) {
26236       astError( AST__INTER, "SetItemC(fitschan): AST internal error; "
26237                 "intermediate axis index %d is invalid.", status, i );
26238 
26239 /* Check the pixel axis or parameter index is within range. */
26240    } else if( jm < 0 || jm > 99 ) {
26241       astError( AST__INTER, "SetItemC(fitschan): AST internal error; "
26242                 "pixel axis or parameter index %d is invalid.", status, jm );
26243 
26244 /* Otherwise proceed... */
26245    } else {
26246 
26247 /* Store the current number of coordinate versions in the supplied array */
26248       nel = astSizeOf( (void *) *item )/sizeof(char ***);
26249 
26250 /* If required, extend the array located by the supplied pointer so that
26251    it is long enough to hold the specified co-ordinate version. */
26252       if( nel < si + 1 ){
26253          *item = (char ****) astGrow( (void *) *item, si + 1,
26254                                       sizeof(char ***) );
26255 
26256 /* Check the pointer can be used. */
26257          if( astOK ){
26258 
26259 /* Initialise the new elements to hold NULL. Note, astGrow may add more
26260    elements to the array than is actually needed, so use the actual current
26261    size of the array as implied by astSize rather than the index si. */
26262             for( el = nel;
26263                  el < astSizeOf( (void *) *item )/sizeof(char ***);
26264                  el++ ) (*item)[el] = NULL;
26265          }
26266       }
26267 
26268 /* If the above went OK... */
26269       if( astOK ){
26270 
26271 /* Store the currrent number of intermediate axes in the supplied array */
26272          nel = astSizeOf( (void *) (*item)[si] )/sizeof(char **);
26273 
26274 /* If required, extend the array so that it is long enough to hold the
26275    specified intermediate axis. */
26276          if( nel < i + 1 ){
26277             (*item)[si] = (char ***) astGrow( (void *) (*item)[si], i + 1,
26278                                               sizeof(char **) );
26279 
26280 /* Check the pointer can be used. */
26281             if( astOK ){
26282 
26283 /* Initialise the new elements to hold NULL. */
26284                for( el = nel;
26285                     el < astSizeOf( (void *) (*item)[si] )/sizeof(char **);
26286                     el++ ) (*item)[si][el] = NULL;
26287             }
26288          }
26289 
26290 /* If the above went OK... */
26291          if( astOK ){
26292 
26293 /* Store the current number of pixel axis or parameter values in the array. */
26294             nel = astSizeOf( (void *) (*item)[si][i] )/sizeof(char *);
26295 
26296 /* If required, extend the array so that it is long enough to hold the
26297    specified axis. */
26298             if( nel < jm + 1 ){
26299                (*item)[si][i] = (char **) astGrow( (void *) (*item)[si][i],
26300                                                     jm + 1, sizeof(char *) );
26301 
26302 /* Check the pointer can be used. */
26303                if( astOK ){
26304 
26305 /* Initialise the new elements to hold NULL. */
26306                   for( el = nel;
26307                        el < astSizeOf( (void *) (*item)[si][i] )/sizeof(char *);
26308                        el++ ) (*item)[si][i][el] = NULL;
26309                }
26310             }
26311 
26312 /* If the above went OK... */
26313             if( astOK ){
26314 
26315 /* Store a copy of the supplied string, using any pre-allocated memory. */
26316                (*item)[si][i][jm] = (char *) astStore( (void *) (*item)[si][i][jm],
26317                                                        (void *) val,
26318                                                        strlen( val ) + 1 );
26319             }
26320          }
26321       }
26322    }
26323 }
26324 
SetSourceFile(AstChannel * this_channel,const char * source_file,int * status)26325 static void SetSourceFile( AstChannel *this_channel, const char *source_file,
26326                            int *status ) {
26327 /*
26328 *  Name:
26329 *     SetSourceFile
26330 
26331 *  Purpose:
26332 *     Set a new value for the SourceFile attribute.
26333 
26334 *  Type:
26335 *     Private function.
26336 
26337 *  Synopsis:
26338 *     #include "fitschan.h"
26339 *     void SetSourceFile( AstChannel *this, const char *source_file,
26340 *                         int *status )
26341 
26342 *  Class Membership:
26343 *     FitsChan member function (over-rides the astSetSourceFile
26344 *     method inherited from the Channel class).
26345 
26346 *  Description:
26347 *     This function stores the supplied string as the new value for the
26348 *     SourceFile attribute. In addition, it also attempts to open the
26349 *     file, read FITS headers from it and append them to the end of the
26350 *     FitsChan. It then closes the SourceFile.
26351 
26352 *  Parameters:
26353 *     this
26354 *        Pointer to the FitsChan.
26355 *     source_file
26356 *        The new attribute value. Should be the path to an existing text
26357 *        file, holding FITS headers (one per line)
26358 *     status
26359 *        Inherited status pointer.
26360 
26361 */
26362 
26363 /* Local Constants: */
26364 #define ERRBUF_LEN 80
26365 
26366 /* Local Variables: */
26367    AstFitsChan *this;            /* Pointer to the FitsChan structure */
26368    FILE *fd;                     /* Descriptor for source file */
26369    char *errstat;                /* Pointer for system error message */
26370    char card[ AST__FITSCHAN_FITSCARDLEN + 2 ]; /* Buffer for source line */
26371    char errbuf[ ERRBUF_LEN ];    /* Buffer for system error message */
26372 
26373 /* Check the global error status. */
26374    if ( !astOK ) return;
26375 
26376 /* Obtain a pointer to the FitsChan structure. */
26377    this = (AstFitsChan *) this_channel;
26378 
26379 /* Invoke the parent astSetSourceFile method to store  the supplied
26380    string in the Channel structure. */
26381    (*parent_setsourcefile)( this_channel, source_file, status );
26382 
26383 /* Attempt to open the file. */
26384    fd = NULL;
26385    if( astOK ) {
26386       fd = fopen( source_file, "r" );
26387       if( !fd ) {
26388          if ( errno ) {
26389 #if HAVE_STRERROR_R
26390             strerror_r( errno, errbuf, ERRBUF_LEN );
26391             errstat = errbuf;
26392 #else
26393             errstat = strerror( errno );
26394 #endif
26395             astError( AST__RDERR, "astSetSourceFile(%s): Failed to open input "
26396                       "SourceFile '%s' - %s.", status, astGetClass( this ),
26397                       source_file, errstat );
26398          } else {
26399             astError( AST__RDERR, "astSetSourceFile(%s): Failed to open input "
26400                       "SourceFile '%s'.", status, astGetClass( this ),
26401                       source_file );
26402          }
26403       }
26404    }
26405 
26406 /* Move the FitsChan to EOF */
26407    astSetCard( this, INT_MAX );
26408 
26409 /* Read each line from the file, remove trailing space, and append to the
26410    FitsChan. */
26411    while( astOK && fgets( card, AST__FITSCHAN_FITSCARDLEN + 2, fd ) ) {
26412       card[ astChrLen( card ) ] = 0;
26413       astPutFits( this, card, 0 );
26414    }
26415 
26416 /* Close the source file. */
26417    if( fd ) fclose( fd );
26418 
26419 }
26420 
SetTableSource(AstFitsChan * this,void (* tabsource)(void),void (* tabsource_wrap)(void (*)(void),AstFitsChan *,const char *,int,int,int *),int * status)26421 static void SetTableSource( AstFitsChan *this,
26422                             void (*tabsource)( void ),
26423                             void (*tabsource_wrap)( void (*)( void ),
26424                                                     AstFitsChan *, const char *,
26425                                                     int, int, int * ),
26426                             int *status ){
26427 
26428 /*
26429 *+
26430 *  Name:
26431 *     astSetTableSource
26432 
26433 *  Purpose:
26434 *     Register source and wrapper function for accessing tables in FITS files.
26435 
26436 *  Type:
26437 *     Protected function.
26438 
26439 *  Synopsis:
26440 *     #include "fitschan.h"
26441 *     void astSetTableSource( AstFitsChan *this,
26442 *                             void (*tabsource)( void ),
26443 *                             void (*tabsource_wrap)( void (*)( void ),
26444 *                                                     AstFitsChan *, const char *,
26445 *                                                     int, int, int * ),
26446 *                             int *status )
26447 
26448 *  Class Membership:
26449 *     FitsChan member function.
26450 
26451 *  Description:
26452 *     This function registers a table source function and its wrapper. A
26453 *     wrapper function exists to adapt the API of the table source
26454 *     function to the needs of different languages. The wrapper is called
26455 *     from the FitsChan code. The wrapper then adjusts the arguments as
26456 *     required and then calls the actualy table source function.
26457 
26458 *  Parameters:
26459 *     this
26460 *        Pointer to the FitsChan.
26461 *     tabsource
26462 *        Pointer to the table source function. The API for this function
26463 *        will depend on the language, and so is cast to void here. It
26464 *        should be cast to the required form within the wrapper function.
26465 *     tabsource_wrap
26466 *        The wrapper function.
26467 *-
26468 */
26469 
26470 /* Local Variables: */
26471 
26472 /* Check the global error status. */
26473    if ( !astOK ) return;
26474    this->tabsource = tabsource;
26475    this->tabsource_wrap = tabsource_wrap;
26476 }
26477 
SetValue(AstFitsChan * this,const char * keyname,void * value,int type,const char * comment,int * status)26478 static void SetValue( AstFitsChan *this, const char *keyname, void *value,
26479                       int type, const char *comment, int *status ){
26480 
26481 /*
26482 *  Name:
26483 *     SetValue
26484 
26485 *  Purpose:
26486 *     Save a FITS keyword value, over-writing any existing keyword value.
26487 
26488 *  Type:
26489 *     Private function.
26490 
26491 *  Synopsis:
26492 *     #include "fitschan.h"
26493 *     void SetValue( AstFitsChan *this, char *keyname, void *value,
26494 *                    int type, const char *comment, int *status )
26495 
26496 *  Class Membership:
26497 *     FitsChan
26498 
26499 *  Description:
26500 *     This function saves a keyword value as a card in the supplied
26501 *     FitsChan. Comment cards are always inserted in-front of the current
26502 *     card. If the keyword is not a comment card, any existing value
26503 *     for the keyword is over-written with the new value (even if it is
26504 *     marked as having been read). Otherwise, (i.e. if it is not a comment
26505 *     card, and no previous value exists) it is inserted in front
26506 *     of the current card.
26507 
26508 *  Parameters:
26509 *     this
26510 *        A pointer to the FitsChan.
26511 *     keyname
26512 *        A pointer to a string holding the keyword name.
26513 *     value
26514 *        A pointer to a buffer holding the keyword value. For strings,
26515 *        the buffer should hold a pointer to the character string.
26516 *     type
26517 *        The FITS data type of the supplied keyword value.
26518 *     comment
26519 *        A comment to store with the keyword.
26520 *     status
26521 *        Pointer to the inherited status variable.
26522 
26523 *   Notes:
26524 *     -  Nothing is stored if a NULL pointer is supplied for "value".
26525 *     -  If the keyword has a value of AST__BAD then nothing is stored,
26526 *     and an error is reported.
26527 */
26528 
26529 /* Local Variables: */
26530    astDECLARE_GLOBALS     /* Declare the thread specific global data */
26531    FitsCard *card;        /* Pointer to original current card */
26532    const char *class;     /* Class name to include in error messages */
26533    const char *method;    /* Method name to include in error messages */
26534    int newcard;           /* Has the original current card been deleted? */
26535    int old_ignore_used;   /* Original setting of external ignore_used variable */
26536    int stored;            /* Has the keyword been stored? */
26537 
26538 /* Check the status and supplied value pointer. */
26539    if( !astOK || !value ) return;
26540 
26541 /* Get a pointer to the structure holding thread-specific global data. */
26542    astGET_GLOBALS(this);
26543 
26544 /* Set up the method and class names for inclusion in error mesages. */
26545    method = "astWrite";
26546    class = astGetClass( this );
26547 
26548 /* Comment card are always inserted in-front of the current card. */
26549    if ( type == AST__COMMENT ) {
26550       SetFits( this, keyname, value, type, comment, 0, status );
26551 
26552 /* Otherwise... */
26553    } else {
26554 
26555 /* Report an error if a bad value is stored for a keyword. */
26556       if( type == AST__FLOAT ){
26557          if( *( (double *) value ) == AST__BAD && astOK ) {
26558             astError( AST__BDFTS, "%s(%s): The required FITS keyword "
26559                       "\"%s\" is indeterminate.", status, method, class, keyname );
26560          }
26561       }
26562 
26563 /* Save a pointer to the current card. */
26564       card = (FitsCard *) this->card;
26565 
26566 /* Indicate that we should not skip over cards marked as having been
26567    read. */
26568       old_ignore_used = ignore_used;
26569       ignore_used = 0;
26570 
26571 /* Indicate that we have not yet stored the keyword value. */
26572       stored = 0;
26573 
26574 /* Attempt to find a card refering to the supplied keyword. If one is
26575    found, it becomes the current card. */
26576       if( SearchCard( this, keyname, "astWrite", astGetClass( this ), status ) ){
26577 
26578 /* If the card which was current on entry to this function will be
26579    over-written, we will need to take account of this when re-instating the
26580    original current card. Make a note of this. */
26581          newcard = ( card == (FitsCard *) this->card );
26582 
26583 /* Replace the current card with a card holding the supplied information. */
26584          SetFits( this, keyname, value, type, comment, 1, status );
26585          stored = 1;
26586 
26587 /* If we have just replaced the original current card, back up a card
26588    so that the replacement card becomes the current card. */
26589          if( newcard ) {
26590             MoveCard( this, -1, "astWrite", astGetClass( this ), status );
26591 
26592 /* Otherwise, re-instate the original current card. */
26593          } else {
26594             this->card = (void *) card;
26595          }
26596       }
26597 
26598 /* If the keyword has not yet been stored (i.e. if it did not exist in the
26599    FitsChan), re-instate the original current card and insert the new card
26600    before the original current card, leaving the current card unchanged. */
26601       if( !stored ) {
26602          this->card = (void *) card;
26603          SetFits( this, keyname, value, type, comment, 0, status );
26604       }
26605 
26606 /* Re-instate the original flag indicating if cards marked as having been
26607    read should be skipped over. */
26608       ignore_used = old_ignore_used;
26609    }
26610 }
26611 
Shpc1(double xmin,double xmax,int n,double * d,double * w,int * status)26612 static void Shpc1( double xmin, double xmax, int n, double *d, double *w,
26613                    int *status ){
26614 /*
26615 *  Name:
26616 *     Shpc1
26617 
26618 *  Purpose:
26619 *     Modifies a one-dimensional polynomial to scale the polynomial argument.
26620 
26621 *  Type:
26622 *     Private function.
26623 
26624 *  Synopsis:
26625 *     #include "fitschan.h"
26626 *     void Shpc1( double xmin, double xmax, int n, double *d, double *w,
26627 *                 int *status )
26628 
26629 *  Description:
26630 *     Given the coefficients of a one-dimensional polynomial P(u) defined on a
26631 *     unit interval (i.e. -1 <= u <= +1 ), find the coefficients of another
26632 *     one-dimensional polynomial Q(x) where:
26633 *
26634 *       Q(x) = P(u)
26635 *       u = ( 2*x - ( xmax + xmin ) ) / ( xmax - xmin )
26636 *
26637 *     That is, u is a scaled version of x, such that the unit interval in u
26638 *     maps onto (xmin:xmax) in x.
26639 
26640 *  Parameters:
26641 *     xmin
26642 *        X value corresponding to u = -1
26643 *     xmax
26644 *        X value corresponding to u = +1
26645 *     n
26646 *        One more than the maximum power of u within P.
26647 *     d
26648 *        An array of n elements supplied holding the coefficients of P such
26649 *        that the coefficient of (u^i) is held in element (i).
26650 *     w
26651 *        An array of n elements returned holding the coefficients of Q such
26652 *        that the coefficient of (x^i) is held in element (i).
26653 *     status
26654 *        Pointer to the inherited status variable.
26655 
26656 *  Notes:
26657 *    - Vaguely inspired by the Numerical Recipes routine "pcshft". But the
26658 *    original had bugs, so I wrote this new version from first principles.
26659 
26660 */
26661 
26662 /* Local Variables: */
26663    double b;
26664    double a;
26665    int j;
26666    int i;
26667 
26668 /* Check inherited status */
26669    if( !astOK ) return;
26670 
26671 /* Get the scale and shift terms so that u = a*x + b */
26672    a = 2.0/( xmax - xmin );
26673    b = ( xmin + xmax )/( xmin - xmax );
26674 
26675 /* Initialise the returned coeffs */
26676    for( i = 0; i < n; i++ ) w[ i ] = 0.0;
26677 
26678 /* The supplied Polynomial is
26679 
26680    P(u) = d0 + d1*u + d2*u^2 + ...
26681 
26682         = d0 + u*( d1 + u*( d2 + ... u*( d{n-1} ) ) )  . . . . . (1)
26683 
26684         = d0 + (a*x+b)*( d1 + (a*x+b)*( d2 + ... (a*x+b)*( d[n-1] ) ) )
26685 
26686    The inner-most parenthesised expression is a polynomial of order zero
26687    (a constant - d[n-1]). Store the coefficients of this zeroth order
26688    polynomial in the returned array. The "w" array is used to hold the
26689    coefficients of Q, i.e. coefficients of powers of "x", not "u", but
26690    since the inner-most polynomial is a constant, it makes no difference
26691    (x^0 == u^0 == 1). */
26692    w[ 0 ] = d[ n - 1 ];
26693 
26694 /* Now loop through each remaining level of parenthetic nesting in (1). At
26695    each level, the parenthesised expression represents a polynomial of order
26696    "i". At the end of each pass though this loop, the returned array "w"
26697    holds the coefficients of this "i"th order polynomial. So on the last
26698    loop, i = n-1, "w" holds the required coefficients of Q. */
26699    for( i = 1; i < n; i++ ) {
26700 
26701 /* If "R" is the polynomial at the "i-1"th level of nesting (the
26702    coefficiemts of which are currently held in "w"), and "S" is the
26703    polynomial at the "i"th level of nesting, we can see from (1) that:
26704 
26705    S = d[ n - 1 - i ] + u*R
26706 
26707    Substituting for "u", this becomes
26708 
26709    S = d[ n - 1 - i ] + ( a*x + b )*R
26710      = d[ n - 1 - i ] + a*R*x + b*R
26711 
26712    Looking at each of these three terms in reverse order:
26713 
26714    1) The "b*R" term is implemented by simply scaling the current contents
26715    of the "w" array by "b"; in the "a*R*x" term.
26716 
26717    2) In "a*R*x", the effect of multiplying by "x" is to move the existing
26718    coefficients in "w" up one element. We then multiply the shifted
26719    coefficients by "a" and add them onto the coefficients produced at
26720    step 1) above.
26721 
26722    We know that "w" still contains the initial zeros at indices higher than
26723    "i" so we only need to scale the bottom "i" elements. We do not do the
26724    zeroth term in this loop since there is no lower term to shift up into
26725    it. */
26726 
26727       for( j = i; j > 0; j-- ){
26728          w[ j ] = b*w[ j ] + a*w[ j - 1 ];
26729       }
26730 
26731 /* Now do the zeroth term. Scale the existing zeroth term by "b" as
26732    required by step 1) and add on the first term, the constant
26733    "d[ n - 1 - i ]". Step 2) is a no-op, since in effect the value of
26734    "w[-1]" is zero. */
26735       w[ 0 ] = d[ n - i - 1 ] + b*w[ 0 ];
26736    }
26737 
26738 }
26739 
ShowFits(AstFitsChan * this,int * status)26740 static void ShowFits( AstFitsChan *this, int *status ){
26741 
26742 /*
26743 *++
26744 *  Name:
26745 c     astShowFits
26746 f     AST_SHOWFITS
26747 
26748 *  Purpose:
26749 *     Display the contents of a FitsChan on standard output.
26750 
26751 *  Type:
26752 *     Public virtual function.
26753 
26754 *  Synopsis:
26755 c     #include "fitschan.h"
26756 c     void astShowFits( AstFitsChan *this )
26757 f     CALL AST_SHOWFITS( THIS, STATUS )
26758 
26759 *  Class Membership:
26760 *     FitsChan method.
26761 
26762 *  Description:
26763 c     This function
26764 f     This routine
26765 *     formats and displays all the cards in a FitsChan on standard output.
26766 
26767 *  Parameters:
26768 c     this
26769 f     THIS = INTEGER (Given)
26770 *        Pointer to the FitsChan.
26771 f     STATUS = INTEGER (Given and Returned)
26772 f        The global status.
26773 
26774 *--
26775 */
26776 
26777 /* Local Variables: */
26778    astDECLARE_GLOBALS           /* Declare the thread specific global data */
26779    char card[ AST__FITSCHAN_FITSCARDLEN + 1]; /* Buffer for header card */
26780    int icard;                   /* Current card index on entry */
26781    int old_ignore_used;         /* Original value of external variable ignore_used */
26782 
26783 /* Check the global status. */
26784    if( !astOK ) return;
26785 
26786 /* Get a pointer to the structure holding thread-specific global data. */
26787    astGET_GLOBALS(this);
26788 
26789 /* Store the current card index. */
26790    icard = astGetCard( this );
26791 
26792 /* Indicate that cards which have been read into an AST object should skipped
26793    over by the functions which navigate the linked list of cards. */
26794    old_ignore_used = ignore_used;
26795    ignore_used = 1;
26796 
26797 /* Ensure that the first card in the FitsChan will be the next one to be
26798    read. */
26799    astSetCard( this, 1 );
26800 
26801 /* Loop round obtaining and writing out each card, until all cards have been
26802    processed. */
26803    while( !astFitsEof( this ) && astOK ){
26804 
26805 /* Get the current card, and display it. The call to astFindFits increments
26806    the current card. */
26807       if( astFindFits( this, "%f", card, 1 ) ) printf( "%s\n", card );
26808    }
26809 
26810 /* Re-instate the original flag indicating if cards marked as having been
26811    read should be skipped over. */
26812    ignore_used = old_ignore_used;
26813 
26814 /* Set the current card index back to what it was on entry. */
26815    astSetCard( this, icard );
26816 
26817 }
26818 
Similar(const char * str1,const char * str2,int * status)26819 static int Similar( const char *str1, const char *str2, int *status ){
26820 /*
26821 *  Name:
26822 *     Similar
26823 
26824 *  Purpose:
26825 *     Are two string effectively the same to human readers?
26826 
26827 *  Type:
26828 *     Private function.
26829 
26830 *  Synopsis:
26831 *     #include "fitschan.h"
26832 *     void Similar( const char *str1, const char *str2, int *status )
26833 
26834 *  Class Membership:
26835 *     FitsChan
26836 
26837 *  Description:
26838 *     This function returns a non-zero value if the two supplied strings
26839 *     are equivalent to a human reader. This is assumed to be the case if
26840 *     the strings are equal apart from leading and trailing white space,
26841 *     multiple embedded space, and case.
26842 
26843 *  Parameters:
26844 *     str1
26845 *        The first string
26846 *     str2
26847 *        The second string
26848 *     status
26849 *        Pointer to the inherited status variable.
26850 
26851 *  Returned Value:
26852 *     Non-zero if the two supplied strings are equivalent, and zero
26853 *     otherwise.
26854 */
26855 
26856 /* Local Variables: */
26857    const char *ea;         /* Pointer to end of string a */
26858    const char *eb;         /* Pointer to end of string b */
26859    const char *a;          /* Pointer to next character in string a */
26860    const char *b;          /* Pointer to next character in string b */
26861    int result;             /* Are the two strings equivalent? */
26862    int ss;                 /* Skip subsequent spaces? */
26863 
26864 /* Initialise */
26865    result = 0;
26866 
26867 /* Check the status and supplied value pointer. */
26868    if( !astOK ) return result;
26869 
26870 /* Initialise pointers into the two strings. */
26871    a = str1;
26872    b = str2;
26873 
26874 /* Get a pointer to the character following the last non-blank character in
26875    each string. */
26876    ea = a + ChrLen( a, status ) - 1;
26877    eb = b + ChrLen( b, status ) - 1;
26878 
26879 /* Set a flag indicating that spaces before the next non-blank character
26880    should be ignored. */
26881    ss = 1;
26882 
26883 /* Compare the strings. */
26884    while( 1 ){
26885 
26886 /* Move on to the next significant character in both strings. */
26887       while( a < ea && *a == ' ' && ss ) a++;
26888       while( b < eb && *b == ' ' && ss ) b++;
26889 
26890 /* If one string has been exhausted but the other has not, the strings
26891    are not equivalent. */
26892       if( ( a < ea && b == eb ) || ( a == ea && b < eb ) ) {
26893          break;
26894 
26895 /* If both strings have been exhausted simultaneously, the strings
26896    are equivalent. */
26897       } else if( b == eb && a == ea ) {
26898          result = 1;
26899          break;
26900 
26901 /* If neither string has been exhausted, compare the current character
26902    for equality, ignoring case. Break if they are different. */
26903       } else if( tolower( *a ) != tolower( *b ) ){
26904          break;
26905 
26906 /* If the two characters are both spaces, indicate that subsequent spaces
26907    should be skipped. */
26908       } else if( *a == ' ' ) {
26909          ss = 1;
26910 
26911 /* If the two characters are not spaces, indicate that subsequent spaces
26912    should not be skipped. */
26913       } else {
26914          ss = 0;
26915       }
26916 
26917 /* Move on to the next characters. */
26918       a++;
26919       b++;
26920    }
26921 
26922 /* Return the result. */
26923    return result;
26924 }
26925 
SinkWrap(void (* sink)(const char *),const char * line,int * status)26926 static void SinkWrap( void (* sink)( const char * ), const char *line, int *status ) {
26927 /*
26928 *  Name:
26929 *     SinkWrap
26930 
26931 *  Purpose:
26932 *     Wrapper function to invoke a C FitsChan sink function.
26933 
26934 *  Type:
26935 *     Private function.
26936 
26937 *  Synopsis:
26938 *     #include "fitschan.h"
26939 *     void SinkWrap( void (* sink)( const char * ), const char *line, int *status )
26940 
26941 *  Class Membership:
26942 *     FitsChan member function.
26943 
26944 *  Description:
26945 *     This function invokes the sink function whose pointer is
26946 *     supplied in order to write an output line to an external data
26947 *     store.
26948 
26949 *  Parameters:
26950 *     sink
26951 *        Pointer to a sink function, whose single parameter is a
26952 *        pointer to a const, null-terminated string containing the
26953 *        text to be written, and which returns void. This is the form
26954 *        of FitsChan sink function employed by the C language interface
26955 *        to the AST library.
26956 *     status
26957 *        Pointer to the inherited status variable.
26958 */
26959 
26960 /* Check the global error status. */
26961    if ( !astOK ) return;
26962 
26963 /* Invoke the sink function. */
26964    ( *sink )( line );
26965 }
26966 
SIPMapping(double * dim,FitsStore * store,char s,int naxes,const char * method,const char * class,int * status)26967 static AstMapping *SIPMapping( double *dim, FitsStore *store, char s,
26968                                int naxes, const char *method,
26969                                const char *class, int *status ){
26970 /*
26971 *  Name:
26972 *     SIPMapping
26973 
26974 *  Purpose:
26975 *     Create a Mapping descriping "-SIP" (Spitzer) distortion.
26976 
26977 *  Type:
26978 *     Private function.
26979 
26980 *  Synopsis:
26981 *     AstMapping *SIPMapping( double *dim, FitsStore *store, char s, int naxes,
26982 *                             const char *method, const char *class, int *status )
26983 
26984 *  Class Membership:
26985 *     FitsChan
26986 
26987 *  Description:
26988 *     This function uses the values in the supplied FitsStore to create a
26989 *     Mapping which implements the "-SIP" distortion code. This is the
26990 
26991 *     code used by the Spitzer project and is described in:
26992 *
26993 *     http://irsa.ipac.caltech.edu/data/SPITZER/docs/files/spitzer/shupeADASS.pdf
26994 *
26995 *     SIP distortion can only be applied to axes 0 and 1. Other axes are
26996 *     passed unchanged by the returned Mapping.
26997 
26998 *  Parameters:
26999 *     dim
27000 *        The dimensions of the array in pixels. AST__BAD is stored for
27001 *        each value if dimensions are not known.
27002 *     store
27003 *        A structure containing information about the requested axis
27004 *        descriptions derived from a FITS header.
27005 *     s
27006 *        A character identifying the co-ordinate version to use. A space
27007 *        means use primary axis descriptions. Otherwise, it must be an
27008 *        upper-case alphabetical characters ('A' to 'Z').
27009 *     naxes
27010 *        The number of intermediate world coordinate axes (WCSAXES).
27011 *     method
27012 *        A pointer to a string holding the name of the calling method.
27013 *        This is used only in the construction of error messages.
27014 *     class
27015 *        A pointer to a string holding the class of the object being
27016 *        read. This is used only in the construction of error messages.
27017 *     status
27018 *        Pointer to the inherited status variable.
27019 
27020 *  Returned Value:
27021 *     A pointer to the Mapping.
27022 */
27023 
27024 /* Local Variables: */
27025    AstMapping   *ret;        /* Pointer to the returned Mapping */
27026    AstPolyMap *pmap;         /* PolyMap describing the distortion */
27027    AstPolyMap *pmap2;        /* New PolyMap describing the distortion */
27028    double ****item;          /* Address of FitsStore item to use */
27029    double *c;                /* Pointer to start of coefficient description */
27030    double *coeff_f;          /* Array of coeffs. for forward transformation */
27031    double *coeff_i;          /* Array of coeffs. for inverse transformation */
27032    double cof;               /* Coefficient value */
27033    double lbnd[ 2 ];         /* Lower bounds of fitted region */
27034    double ubnd[ 2 ];         /* Upper bounds of fitted region */
27035    int def;                  /* Is transformation defined? */
27036    int iin;                  /* Input (u or v) index */
27037    int iout;                 /* Output (U or V) index */
27038    int ncoeff_f;             /* No. of coeffs. for forward transformation */
27039    int ncoeff_i;             /* No. of coeffs. for inverse transformation */
27040    int p;                    /* Power of u or U */
27041    int pmax;                 /* Max power of u or U */
27042    int q;                    /* Power of v or V */
27043    int qmax;                 /* Max power of v or V */
27044 
27045 /* Initialise the pointer to the returned Mapping. */
27046    ret = NULL;
27047 
27048 /* Check the global status. */
27049    if ( !astOK ) return ret;
27050 
27051 /* Store coefficients of the forward transformation:
27052    ================================================ */
27053 
27054 /* Indicate that we have as yet no coefficients for the forward polynomials. */
27055    ncoeff_f = 0;
27056 
27057 /* Indicate that we do not yet have any evidence that the forward
27058    transformation is defined. */
27059    def = 0;
27060 
27061 /* Allocate workspace to hold descriptions of (initially) 20 coefficients used
27062    within the forward polynomials. */
27063    coeff_f = astMalloc( sizeof( double )*20 );
27064 
27065 /* Store the coefficients of the polynomial which produces each output
27066    axis (U or V) in turn. */
27067    for( iout = 0; iout < 2; iout++ ){
27068 
27069 /* Get a pointer to the FitsStore item holding the values defining this
27070    output. */
27071       item = ( iout == 0 ) ? &(store->asip) : &(store->bsip);
27072 
27073 /* Get the largest powers used of u and v. */
27074       pmax = GetMaxI( item, s, status );
27075       qmax = GetMaxJM( item, s, status );
27076 
27077 /* Loop round all combination of powers. */
27078       for( p = 0; p <= pmax; p++ ){
27079          for( q = 0; q <= qmax; q++ ){
27080 
27081 /* Get the polynomial coefficient for this combination of powers. */
27082             cof = GetItem( item, p, q, s, NULL, method, class, status );
27083 
27084 /* If there is no coefficient for this combination of powers, use a value
27085    of zero. Otherwise indicate we have found at least one coefficient. */
27086             if( cof == AST__BAD ) {
27087                cof = 0.0;
27088             } else {
27089                def = 1;
27090             }
27091 
27092 /* The distortion polynomial gives a correction to be added on to the
27093    input value. On the other hand, the returned Mapping is a direct
27094    transformation from input to output. Therefore increment the coefficient
27095    value by 1 for the term which corresponds to the current output axis. */
27096             if( p == ( 1 - iout ) && q == iout ) cof += 1.0;
27097 
27098 /* If the coefficient is not zero, store it in the array of coefficient
27099    descriptions. */
27100             if( cof != 0.0 ) {
27101 
27102 /* Increment the number of coefficients for the forward polynomials. */
27103                ncoeff_f++;
27104 
27105 /* Ensure the "coeff_f" array is large enough to hold the new coefficient. */
27106                coeff_f = astGrow( coeff_f, sizeof( double )*4, ncoeff_f );
27107                if( astOK ) {
27108 
27109 /* Store it. Each coefficient is described by 4 values (since we have 2
27110    inputs to the Mapping). The first is the coefficient value, the second
27111    is the (1-based) index of the output to which the coefficient relates.
27112    The next is the power of input 0, and the last one is the power of input 1. */
27113                   c = coeff_f + 4*( ncoeff_f - 1 );
27114                   c[ 0 ] = cof;
27115                   c[ 1 ] = iout + 1;
27116                   c[ 2 ] = p;
27117                   c[ 3 ] = q;
27118                }
27119             }
27120          }
27121       }
27122    }
27123 
27124 /* If no coefficients were supplied in the FitsStore, the forward
27125    transformation is undefined. */
27126    if( !def ) ncoeff_f = 0;
27127 
27128 /* Store coefficients of the inverse transformation:
27129    ================================================ */
27130 
27131 /* Indicate that we have as yet no coefficients for the inverse polynomials. */
27132    ncoeff_i = 0;
27133 
27134 /* Indicate that we do not yet have any evidence that the forward
27135    transformation is defined. */
27136    def = 0;
27137 
27138 /* Allocate workspace to hold descriptions of (initially) 20 coefficients used
27139    within the inverse polynomials. */
27140    coeff_i = astMalloc( sizeof( double )*20 );
27141 
27142 /* Store the coefficients of the polynomial which produces each input
27143    axis (u or v) in turn. */
27144    for( iin = 0; iin < 2; iin++ ){
27145 
27146 /* Get a pointer to the FitsStore item holding the values defining this
27147    output. */
27148       item = ( iin == 0 ) ? &(store->apsip) : &(store->bpsip);
27149 
27150 /* Get the largest powers used of U and V. */
27151       pmax = GetMaxI( item, s, status );
27152       qmax = GetMaxJM( item, s, status );
27153 
27154 /* Loop round all combination of powers. */
27155       for( p = 0; p <= pmax; p++ ){
27156          for( q = 0; q <= qmax; q++ ){
27157 
27158 /* Get the polynomial coefficient for this combination of powers. */
27159             cof = GetItem( item, p, q, s, NULL, method, class, status );
27160 
27161 /* If there is no coefficient for this combination of powers, use a value
27162    of zero. Otherwise indicate we have found at least one coefficient. */
27163             if( cof == AST__BAD ) {
27164                cof = 0.0;
27165             } else {
27166                def = 1;
27167             }
27168 
27169 /* The distortion polynomial gives a correction to be added on to the
27170    output value. On the other hand, the returned Mapping is a direct
27171    transformation from output to input. Therefore increment the coefficient
27172    value by 1 for the term which corresponds to the current input axis. */
27173             if( p == ( 1 - iin ) && q == iin ) cof += 1.0;
27174 
27175 /* If the coefficient is not zero, store it in the array of coefficient
27176    descriptions. */
27177             if( cof != 0.0 ) {
27178 
27179 /* Increment the number of coefficients for the inverse polynomials. */
27180                ncoeff_i++;
27181 
27182 /* Ensure the "coeff_i" array is large enough to hold the new coefficient. */
27183                coeff_i = astGrow( coeff_i, sizeof( double )*4, ncoeff_i );
27184                if( astOK ) {
27185 
27186 /* Store it. Each coefficient is described by 4 values (since we have 2
27187    outputs to the Mapping). The first is the coefficient value, the second
27188    is the (1-based) index of the input to which the coefficient relates. The
27189    next is the power of output 0, and the last one is the power of output 1. */
27190                   c = coeff_i + 4*( ncoeff_i - 1 );
27191                   c[ 0 ] = cof;
27192                   c[ 1 ] = iin + 1;
27193                   c[ 2 ] = p;
27194                   c[ 3 ] = q;
27195                }
27196             }
27197          }
27198       }
27199    }
27200 
27201 /* If no coefficients were supplied in the FitsStore, the forward
27202    transformation is undefined. */
27203    if( !def ) ncoeff_i = 0;
27204 
27205 /* Create the returned Mapping:
27206    ============================ */
27207 
27208 /* If neither transformation is defined, create a UnitMap. */
27209    if( ncoeff_f == 0 && ncoeff_i == 0 ){
27210       ret = (AstMapping *) astUnitMap( naxes, "", status );
27211 
27212 /* Otherwise, create a PolyMap to describe axes 0 and 1. */
27213    } else {
27214       pmap = astPolyMap( 2, 2, ncoeff_f, coeff_f, ncoeff_i, coeff_i, "", status );
27215 
27216 /* The inverse transformations supplied within SIP headers are often
27217    inaccurate. So replace any existing inverse by sampling the supplied
27218    transformation, and fitting a polynomial to the sampled positions. If
27219    the fit fails to reach 0.01 pixel accuracy, forget it and rely on the
27220    (slower) iterative inverse provided by the PolyMap class. Do the fit
27221    over an area three times the size of the image to provide accurate
27222    values outside the image.*/
27223       lbnd[ 0 ] = ( dim[ 0 ] != AST__BAD ) ? -dim[ 0 ] : -1000.0;
27224       lbnd[ 1 ] = ( dim[ 1 ] != AST__BAD ) ? -dim[ 1 ] : -1000.0;
27225       ubnd[ 0 ] = ( dim[ 0 ] != AST__BAD ) ? 2*dim[ 0 ] : 2000.0;
27226       ubnd[ 1 ] = ( dim[ 1 ] != AST__BAD ) ? 2*dim[ 1 ] : 2000.0;
27227       pmap2 = astPolyTran( pmap, (ncoeff_f == 0), 0.0001, 0.01, 7, lbnd,
27228                            ubnd );
27229       if( pmap2 ) {
27230          (void) astAnnul( pmap );
27231          pmap = pmap2;
27232       } else {
27233          astSet( pmap, "IterInverse=1,NiterInverse=6,TolInverse=1.0E-8",
27234                  status );
27235       }
27236 
27237 /* Add the above Mapping in parallel with a UnitMap which passes any
27238    other axes unchanged. */
27239       ret = AddUnitMaps( (AstMapping *) pmap, 0, naxes, status );
27240       pmap = astAnnul( pmap );
27241    }
27242 
27243 /* Free resources. */
27244    coeff_f = astFree( coeff_f );
27245    coeff_i = astFree( coeff_i );
27246 
27247 /* Return the result. */
27248    return ret;
27249 }
27250 
SkyPole(AstWcsMap * map2,AstMapping * map3,int ilon,int ilat,int * wperm,char s,FitsStore * store,const char * method,const char * class,int * status)27251 static void SkyPole( AstWcsMap *map2, AstMapping *map3, int ilon, int ilat,
27252                      int *wperm, char s, FitsStore *store, const char *method,
27253                      const char *class, int *status ){
27254 /*
27255 *  Name:
27256 *     SkyPole
27257 
27258 *  Purpose:
27259 *     Put values for FITS keywords LONPOLE and LATPOLE into a FitsStore.
27260 
27261 *  Type:
27262 *     Private function.
27263 
27264 *  Synopsis:
27265 *     #include "fitschan.h"
27266 *     void SkyPole( AstWcsMap *map2, AstMapping *map3, int ilon, int ilat,
27267 *                   int *wperm, char s, FitsStore *store, const char *method,
27268 *                   const char *class, int *status )
27269 
27270 *  Class Membership:
27271 *     FitsChan member function.
27272 
27273 *  Description:
27274 *     This function calculates values for the LONPOLE and LATPOLE FITS
27275 *     keywords and stores them in the supplied FitsStore. LONPOLE and
27276 *     LATPOLE are the longitude and latitude of the celestial north pole
27277 *     in native spherical coordinates.
27278 
27279 *  Parameters:
27280 *     map2
27281 *        Pointer to the Mapping from Intermediate World Coordinates to Native
27282 *        Spherical Coordinates.
27283 *     map3
27284 *        Pointer to the Mapping from Native Spherical Coordinates to celestial
27285 *        coordinates.
27286 *     ilon
27287 *        Zero-based index of longitude output from "map3".
27288 *     ilat
27289 *        Zero-based index of latitude output from "map3".
27290 *     wperm
27291 *        Pointer to an array of integers with one element for each axis of
27292 *        the current Frame. Each element holds the zero-based
27293 *        index of the FITS-WCS axis (i.e. the value of "i" in the keyword
27294 *        names "CTYPEi", "CRVALi", etc) which describes the Frame axis.
27295 *     s
27296 *        The co-ordinate version character. A space means the primary
27297 *        axis descriptions. Otherwise the supplied character should be
27298 *        an upper case alphabetical character ('A' to 'Z').
27299 *     store
27300 *        The FitsStore in which to store the FITS WCS keyword values.
27301 *     method
27302 *        Pointer to a string holding the name of the calling method.
27303 *        This is only for use in constructing error messages.
27304 *     class
27305 *        Pointer to a string holding the name of the supplied object class.
27306 *        This is only for use in constructing error messages.
27307 *     status
27308 *        Pointer to the inherited status variable.
27309 */
27310 
27311 /* Local Variables: */
27312    AstPointSet *pset1;      /* PointSet holding intermediate wcs coords */
27313    AstPointSet *pset2;      /* PointSet holding final WCS coords */
27314    double **ptr1;           /* Pointer to coordinate data */
27315    double **ptr2;           /* Pointer to coordinate data */
27316    double alpha0;           /* Long. of fiducial point in standard system */
27317    double alphap;           /* Celestial longitude of native north pole */
27318    double deflonpole;       /* Default value for lonpole */
27319    double delta0;           /* Lat. of fiducial point in standard system */
27320    double latpole;          /* Native latitude of celestial north pole */
27321    double lonpole;          /* Native longitude of celestial north pole */
27322    double phi0;             /* Native longitude at fiducial point */
27323    double theta0;           /* Native latitude at fiducial point */
27324    int axlat;               /* Index of latitude output from "map2" */
27325    int axlon;               /* Index of longitude output from "map2" */
27326    int fits_ilat;           /* FITS WCS axis index for latitude axis */
27327    int fits_ilon;           /* FITS WCS axis index for longitude axis */
27328    int iax;                 /* Axis index */
27329    int nax;                 /* Number of IWC axes */
27330    int nax2;                /* Number of WCS axes */
27331 
27332 /* Check the inherited status. */
27333    if( !astOK ) return;
27334 
27335 /* Store the indices of the native longitude and latitude outputs of the
27336    WcsMap. */
27337    axlon = astGetWcsAxis( map2, 0 );
27338    axlat = astGetWcsAxis( map2, 1 );
27339 
27340 /* Store the indices of the FITS WCS axes for longitude and latitude */
27341    fits_ilon = wperm[ ilon ];
27342    fits_ilat = wperm[ ilat ];
27343 
27344 /* To find the longitude and latitude of the celestial north pole in native
27345    spherical coordinates, we will transform the coords of the celestial north
27346    pole into spherical cords using the inverse of "map2", and if the resulting
27347    native spherical coords differ from the default values of LONPOLE and
27348    LATPOLE, we store them in the FitsStore. However, for zenithal projections,
27349    any value can be used simply by introducing an extra rotation into the
27350    (X,Y) projection plane. If values have been set in the WcsMap (as
27351    projection parameters PVi_3 and PVi_4 for longitude axis "i") uses
27352    them. Otherwise, set the values bad to indicate that the default values
27353    should be used. Note, these projection parameters are used for other
27354    purposes in a TPN projection. */
27355    lonpole = AST__BAD;
27356    latpole = AST__BAD;
27357    if( astIsZenithal( map2 ) ) {
27358       if( astGetWcsType( map2 ) != AST__TPN ) {
27359          lonpole = astTestPV( map2, axlon, 3 ) ? astGetPV( map2, axlon, 3 )
27360                                                 : AST__BAD;
27361          latpole = astTestPV( map2, axlon, 4 ) ? astGetPV( map2, axlon, 4 )
27362                                                 : AST__BAD;
27363       }
27364 
27365 /* For non-zenithal projections, do the full calculation. */
27366    } else {
27367 
27368 /* Allocate resources. */
27369       nax = astGetNin( map2 );
27370       pset1 = astPointSet( 1, nax, "", status );
27371       ptr1 = astGetPoints( pset1 );
27372       nax2 = astGetNout( map3 );
27373       pset2 = astPointSet( 1, nax2, "", status );
27374       ptr2 = astGetPoints( pset2 );
27375       if( astOK ) {
27376 
27377 /* Calculate the longitude and latitude of the celestial north pole
27378    in native spherical coordinates (using the inverse of map3). These
27379    values correspond to the LONPOLE and LATPOLE keywords. */
27380          for( iax = 0; iax < nax2; iax++ ) ptr2[ iax ][ 0 ] = 0.0;
27381          ptr2[ ilat ][ 0 ] = AST__DPIBY2;
27382          (void) astTransform( map3, pset2, 0, pset1 );
27383 
27384 /* Retrieve the latitude and longitude (in the standard system) of the
27385    fiducial point (i.e. CRVAL), in radians. */
27386          delta0 = GetItem( &(store->crval), fits_ilat, 0, s, NULL, method, class, status );
27387          if( delta0 == AST__BAD ) delta0 = 0.0;
27388          delta0 *= AST__DD2R;
27389          alpha0 = GetItem( &(store->crval), fits_ilon, 0, s, NULL, method, class, status );
27390          if( alpha0 == AST__BAD ) alpha0 = 0.0;
27391          alpha0 *= AST__DD2R;
27392 
27393 /* The default value of the LATPOLE is defined by equation 8 of FITS-WCS
27394    paper II (taking the +ve signs). Find this value. */
27395          if( WcsNatPole( NULL, map2, alpha0, delta0, 999.0, ptr1[ axlon ],
27396                          &alphap, &latpole, status ) ){
27397 
27398 /* If the default value is defined, compare it to the latitude of the
27399    north pole found above. If they are equal use a bad value instead to
27400    prevent an explicit keyword from being added to the FitsChan. */
27401             if( EQUALANG( ptr1[ axlat ][ 0 ], latpole ) ) {
27402                latpole = AST__BAD;
27403             } else {
27404                latpole = ptr1[ axlat ][ 0 ];
27405             }
27406 
27407 /* If the default value is not defined, always store an explicit LATPOLE
27408    value. */
27409          } else {
27410             latpole = ptr1[ axlat ][ 0 ];
27411          }
27412 
27413 /* The default LONPOLE value is zero if the celestial latitude at the
27414    fiducial point is greater than or equal to the native latitude at the
27415    fiducial point. Otherwise, the default is (+ or -) 180 degrees. If LONPOLE
27416    takes the default value, replace it with AST__BAD to prevent an explicit
27417    keyword being stored in the FitsChan. */
27418          GetFiducialNSC( map2, &phi0, &theta0, status );
27419          lonpole = palDranrm( ptr1[ axlon ][ 0 ] );
27420          if( delta0 >= theta0 ){
27421             deflonpole = 0.0;
27422          } else {
27423             deflonpole = AST__DPI;
27424          }
27425          if( EQUALANG( lonpole, deflonpole ) ) lonpole = AST__BAD;
27426       }
27427 
27428 /* Convert from radians to degrees. */
27429       if( lonpole != AST__BAD ) lonpole *= AST__DR2D;
27430       if( latpole != AST__BAD ) latpole *= AST__DR2D;
27431 
27432 /* Free resources. */
27433       pset1 = astAnnul( pset1 );
27434       pset2 = astAnnul( pset2 );
27435    }
27436 
27437 /* Store these values. */
27438    SetItem( &(store->lonpole), 0, 0, s, lonpole, status );
27439    SetItem( &(store->latpole), 0, 0, s, latpole, status );
27440 
27441 /* FITS-WCS paper 2 recommends putting a copy of LONPOLE and LATPOLE in
27442    projection parameters 3 and 4 associated with the longitude axis. Only do
27443    this if the projection is not TPN (since this projection uses these
27444    parameters for other purposes). */
27445    if( astGetWcsType( map2 ) != AST__TPN ) {
27446       SetItem( &(store->pv), fits_ilon, 3, s, lonpole, status );
27447       SetItem( &(store->pv), fits_ilon, 4, s, latpole, status );
27448    }
27449 }
27450 
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)27451 static int SkySys( AstFitsChan *this, AstSkyFrame *skyfrm, int wcstype,
27452                    int wcsproj, FitsStore *store, int axlon, int axlat, char s,
27453                    int isoff, const char *method, const char *class, int *status ){
27454 /*
27455 *  Name:
27456 *     SkySys
27457 
27458 *  Purpose:
27459 *     Return FITS-WCS values describing a sky coordinate system.
27460 
27461 *  Type:
27462 *     Private function.
27463 
27464 *  Synopsis:
27465 *     #include "fitschan.h"
27466 *     int SkySys( AstFitsChan *this, AstSkyFrame *skyfrm, int wcstype,
27467 *                 int wcsproj, FitsStore *store, int axlon, int axlat, char s,
27468 *                 int isoff, const char *method, const char *class, int *status )
27469 
27470 *  Class Membership:
27471 *     FitsChan
27472 
27473 *  Description:
27474 *     This function sets values for the following FITS-WCS keywords
27475 *     within the supplied FitsStore structure: CTYPE, CNAME, RADESYS, EQUINOX,
27476 *     MJDOBS, CUNIT, OBSGEO-X/Y/Z. The values are derived from the supplied
27477 *     SkyFrame and WcsMap.
27478 
27479 *  Parameters:
27480 *     this
27481 *        Pointer to the FitsChan.
27482 *     skyfrm
27483 *        A pointer to the SkyFrame to be described.
27484 *     wcstype
27485 *        The type of WCS: 0 = TAB, 1 = WcsMap projection.
27486 *     wcsproj
27487 *        An identifier for the type of WCS projection to use. Should be
27488 *        one of the values defined by the WcsMap class. Only used if "wcstype"
27489 *        is 1.
27490 *     store
27491 *        A pointer to the FitsStore structure in which to store the
27492 *        results.
27493 *     axlon
27494 *        The index of the FITS WCS longitude axis (i.e. the value of "i"
27495 *        in "CTYPEi").
27496 *     axlat
27497 *        The index of the FITS WCS latitude axis (i.e. the value of "i"
27498 *        in "CTYPEi").
27499 *     s
27500 *        Co-ordinate version character.
27501 *     isoff
27502 *        If greater than zero, the description to add to the FitsStore
27503 *        should describe offset coordinates. If less than zero, the
27504 *        description to add to the FitsStore should describe absolute
27505 *        coordinates but should include the SkyRefIs, SkyRef and SkyRefP
27506 *        attributes. If zero, ignore all offset coordinate info. The
27507 *        absolute value indicates the nature of the reference point:
27508 *        1 == "pole", 2 == "origin", otherwise "ignored".
27509 *     method
27510 *        Pointer to a string holding the name of the calling method.
27511 *        This is only for use in constructing error messages.
27512 *     class
27513 *        Pointer to a string holding the name of the supplied object class.
27514 *        This is only for use in constructing error messages.
27515 *     status
27516 *        Pointer to the inherited status variable.
27517 
27518 *  Returned Value:
27519 *     Are the keywords values in the FitsStore usable?
27520 */
27521 
27522 /* Local Variables: */
27523    astDECLARE_GLOBALS     /* Declare the thread specific global data */
27524    char *label;             /* Pointer to axis label string */
27525    char attr[20];           /* Buffer for AST attribute name */
27526    char com[80];            /* Buffer for keyword comment */
27527    char lattype[MXCTYPELEN];/* Latitude axis CTYPE value */
27528    char lontype[MXCTYPELEN];/* Longitude axis CTYPE value */
27529    const char *latsym;      /* SkyFrame latitude axis symbol */
27530    const char *lonsym;      /* SkyFrame longitude axis symbol */
27531    const char *prj_name;    /* Pointer to projection name string */
27532    const char *skyref;      /* Formatted SkyRef position */
27533    const char *skyrefis;    /* SkyRefIs value */
27534    const char *sys;         /* Celestal coordinate system */
27535    const char *timesys;     /* Timescale specified in FitsChan */
27536    double ep;               /* Epoch of observation in required timescale (MJD) */
27537    double ep_tdb;           /* Epoch of observation in TDB timescale (MJD) */
27538    double ep_utc;           /* Epoch of observation in UTC timescale (MJD) */
27539    double eq;               /* Epoch of reference equinox (MJD) */
27540    double geolat;           /* Geodetic latitude of observer (radians) */
27541    double geolon;           /* Geodetic longitude of observer (radians) */
27542    double h;                /* Geodetic altitude of observer (metres) */
27543    double skyref_lat;       /* SkyRef latitude value (rads) */
27544    double skyrefp_lat;      /* SkyRefP latitude value (rads) */
27545    double skyref_lon;       /* SkyRef longitude value (rads) */
27546    double skyrefp_lon;      /* SkyRefP longitude value (rads) */
27547    double xyz[3];           /* Geocentric position vector (in m) */
27548    int defdate;             /* Can the date keywords be defaulted? */
27549    int i;                   /* Character count */
27550    int isys;                /* Celestial coordinate system */
27551    int latax;               /* Index of latitude axis in SkyFrame */
27552    int lonax;               /* Index of longitude axis in SkyFrame */
27553    int ok;                  /* Do axis symbols conform to FITS-WCS CTYPE form? */
27554    int old_ignore_used;     /* Original setting of external ignore_used variable */
27555    int ret;                 /* Returned flag */
27556 
27557 /* Check the status. */
27558    if( !astOK ) return 0;
27559 
27560 /* Get a pointer to the structure holding thread-specific global data. */
27561    astGET_GLOBALS(this);
27562 
27563 /* Check we have a SkyFrame. */
27564    if( !astIsASkyFrame( skyfrm ) ) return 0;
27565 
27566 /* Initialise */
27567    ret = 1;
27568 
27569 /* Get the equinox, epoch of observation, and system of the SkyFrame. The epoch
27570    is in TDB. It is assumed the Equinox is in UTC. */
27571    eq = astGetEquinox( skyfrm );
27572    sys = astGetC( skyfrm, "system" );
27573    ep_tdb = astTestEpoch( skyfrm ) ? astGetEpoch( skyfrm ) : AST__BAD;
27574 
27575 /* Convert the epoch to UTC. */
27576    ep_utc = TDBConv( ep_tdb, AST__UTC, 1, method, class, status );
27577 
27578 /* See if the FitsChan contains a value for the TIMESYS keyword (include
27579    previously used cards in the search). If so, and if it is not UTC, convert
27580    the epoch to the specified time scale, and store a TIMESYS value in the
27581    FitsStore. */
27582    old_ignore_used = ignore_used;
27583    ignore_used = 0;
27584    if( GetValue( this, "TIMESYS", AST__STRING, (void *) &timesys, 0, 0, method,
27585                  class, status ) && strcmp( timesys, "UTC" ) ) {
27586       ep = TDBConv( ep_tdb, TimeSysToAst( this, timesys, method, class,
27587                                           status ),
27588                     1, method, class, status );
27589       SetItemC( &(store->timesys), 0, 0, s, timesys, status );
27590 
27591 /* If no TIMESYS keyword was found in the FitsChan, or the timesys was
27592    UTC, we use the UTC epoch value found above. In this case no TIMESYS value
27593    need be stored in the FitsSTore since UTC is the default for TIMESYS. */
27594    } else {
27595       ep = ep_utc;
27596    }
27597 
27598 /* Reinstate the original value for the flag that indicates whether keywords
27599    in the FitsChan that have been used previously should be ignored. */
27600    ignore_used = old_ignore_used;
27601 
27602 /* The MJD-OBS and DATE-OBS keywords default to the epoch of the
27603    reference equinox if not supplied. Therefore MJD-OBS and DATE-OBS do
27604    not need to be stored in the FitsChan if the epoch of observation is
27605    the same as the epoch of the reference equinox. This can avoid
27606    producing FITS headers which say unlikely things like
27607    DATE-OBS = "01/01/50". Set a flag indicating if MJD-OBS and DATE-OBS
27608    can be defaulted. */
27609    defdate = EQUAL( ep_utc, eq );
27610 
27611 /* Convert the equinox to a Julian or Besselian epoch. Also get the
27612    reference frame and standard system. */
27613    if( !Ustrcmp( sys, "FK4", status ) ){
27614       eq = palEpb( eq );
27615       isys = RADEC;
27616       SetItemC( &(store->radesys), 0, 0, s, "FK4", status );
27617    } else if( !Ustrcmp( sys, "FK4_NO_E", status ) || !Ustrcmp( sys, "FK4-NO-E", status ) ){
27618       eq = palEpb( eq );
27619       isys = RADEC;
27620       SetItemC( &(store->radesys), 0, 0, s, "FK4-NO-E", status );
27621    } else if( !Ustrcmp( sys, "FK5", status ) ){
27622       eq = palEpj( eq );
27623       isys = RADEC;
27624       SetItemC( &(store->radesys), 0, 0, s, "FK5", status );
27625    } else if( !Ustrcmp( sys, "ICRS", status ) ){
27626       eq = AST__BAD;
27627       isys = RADEC;
27628       SetItemC( &(store->radesys), 0, 0, s, "ICRS", status );
27629    } else if( !Ustrcmp( sys, "GAPPT", status ) ||
27630               !Ustrcmp( sys, "Apparent", status ) ||
27631               !Ustrcmp( sys, "Geocentric", status ) ){
27632       eq = AST__BAD;
27633       isys = RADEC;
27634       SetItemC( &(store->radesys), 0, 0, s, "GAPPT", status );
27635    } else if( !Ustrcmp( sys, "Helioecliptic", status ) ){
27636       eq = AST__BAD;
27637       isys = HECLIP;
27638    } else if( !Ustrcmp( sys, "Galactic", status ) ){
27639       eq = AST__BAD;
27640       isys = GALAC;
27641    } else if( !Ustrcmp( sys, "Supergalactic", status ) ){
27642       eq = AST__BAD;
27643       isys = SUPER;
27644    } else if( !Ustrcmp( sys, "AzEl", status ) ){
27645       eq = AST__BAD;
27646       isys = AZEL;
27647    } else {
27648       eq = AST__BAD;
27649       isys = NOCEL;
27650    }
27651 
27652 /* Store these values. Only store the date if it does not take its
27653    default value. */
27654    SetItem( &(store->equinox), 0, 0, s, eq, status );
27655    if( !defdate ) SetItem( &(store->mjdobs), 0, 0, ' ', ep, status );
27656 
27657 /* Only proceed if we have usable values */
27658    if( astOK ) {
27659 
27660 /* Get the indices of the latitude and longitude axes within the
27661    SkyFrame. */
27662       latax = astGetLatAxis( skyfrm );
27663       lonax = 1 - latax;
27664 
27665 /* The first 4 characters in CTYPE are determined by the celestial coordinate
27666    system and the second 4 by the projection type. If we are describing
27667    offset coordinates, then use "OFLN" and "OFLT. Otherwise use the
27668    standard FITS-WCS name of the system. */
27669       if( isoff > 0 ){
27670          strcpy( lontype, "OFLN" );
27671          strcpy( lattype, "OFLT" );
27672       } else if( isys == RADEC ){
27673          strcpy( lontype, "RA--" );
27674          strcpy( lattype, "DEC-" );
27675       } else if( isys == ECLIP ){
27676          strcpy( lontype, "ELON" );
27677          strcpy( lattype, "ELAT" );
27678       } else if( isys == HECLIP ){
27679          strcpy( lontype, "HLON" );
27680          strcpy( lattype, "HLAT" );
27681       } else if( isys == GALAC ){
27682          strcpy( lontype, "GLON" );
27683          strcpy( lattype, "GLAT" );
27684       } else if( isys == SUPER ){
27685          strcpy( lontype, "SLON" );
27686          strcpy( lattype, "SLAT" );
27687       } else if( isys == AZEL ){
27688          strcpy( lontype, "AZ--" );
27689          strcpy( lattype, "EL--" );
27690 
27691 /* For unknown systems, use the axis symbols within CTYPE if they conform
27692    to the requirement of FITS-WCS (i.e. "xxLN/xxLT" or "xLON/xLAT") or use
27693    "UNLN/UNLT" otherwise. */
27694       } else {
27695          latsym = astGetSymbol( skyfrm, latax );
27696          lonsym = astGetSymbol( skyfrm, lonax );
27697          if( astOK ) {
27698 
27699             ok = 0;
27700             if( strlen( latsym ) == 4 && strlen( lonsym ) == 4 ) {
27701                if( !strcmp( latsym + 2, "LT" ) &&
27702                    !strcmp( lonsym + 2, "LN" ) &&
27703                    !strncmp( latsym, lonsym, 2 ) ) {
27704                   ok = 1;
27705                } else if( !strcmp( latsym + 1, "LAT" ) &&
27706                    !strcmp( lonsym + 1, "LON" ) &&
27707                    !strncmp( latsym, lonsym, 1 ) ) {
27708                   ok = 1;
27709                }
27710             }
27711 
27712             if( !ok ) {
27713                latsym = "UNLT";
27714                lonsym = "UNLN";
27715             }
27716 
27717             strncpy( lontype, lonsym, 4 );
27718             for( i = strlen( lonsym ); i < 4; i++ ) {
27719                lontype[ i ] = '-';
27720             }
27721             strncpy( lattype, latsym, 4 );
27722             for( i = strlen( latsym ); i < 4; i++ ) {
27723                lattype[ i ] = '-';
27724             }
27725          }
27726       }
27727 
27728 /* Store the projection strings. */
27729       prj_name = ( wcstype == 0 ) ? "-TAB" : astWcsPrjName( wcsproj );
27730       if( astOK ) {
27731          strcpy( lontype + 4, prj_name );
27732          strcpy( lattype + 4, prj_name );
27733       }
27734 
27735 /* Store the total CTYPE strings */
27736       SetItemC( &(store->ctype), axlon, 0, s, lontype, status );
27737       SetItemC( &(store->ctype), axlat, 0, s, lattype, status );
27738 
27739 /* Store offset coord information. */
27740       if( isoff ) {
27741 
27742 /* If the description is for offset coords store suitable comments for
27743    the CTYPE keywords. */
27744          if( isoff > 0 ) {
27745             skyref = astGetC( skyfrm, "SkyRef" );
27746 
27747             sprintf( attr, "Symbol(%d)", axlon + 1 );
27748             sprintf( com, "%s offset from %s",astGetC( skyfrm, attr )+1, skyref );
27749             SetItemC( &(store->ctype_com), axlon, 0, s, com, status );
27750 
27751             sprintf( attr, "Symbol(%d)", axlat + 1 );
27752             sprintf( com, "%s offset from %s",astGetC( skyfrm, attr )+1, skyref );
27753             SetItemC( &(store->ctype_com), axlat, 0, s, com, status );
27754 
27755 /* If the description is for absolute coords store the SkyFrame attribute
27756    values in AST-specific keywords. */
27757          } else {
27758             sprintf( attr, "SkyRef(%d)", axlon + 1 );
27759             skyref_lon = astGetD( skyfrm, attr );
27760             sprintf( attr, "SkyRef(%d)", axlat + 1 );
27761             skyref_lat = astGetD( skyfrm, attr );
27762 
27763             sprintf( attr, "SkyRefP(%d)", axlon + 1 );
27764             skyrefp_lon = astGetD( skyfrm, attr );
27765             sprintf( attr, "SkyRefP(%d)", axlat + 1 );
27766             skyrefp_lat = astGetD( skyfrm, attr );
27767 
27768             skyrefis = (isoff < -2) ? "IGNORED" :
27769                        ( (isoff < -1) ? "ORIGIN" : "POLE" );
27770 
27771             SetItemC( &(store->skyrefis), 0, 0, s, skyrefis, status );
27772             if( astTest( skyfrm, "SkyRef(1)" ) ) {
27773                SetItem( &(store->skyref), axlon, 0, s, skyref_lon, status );
27774                SetItem( &(store->skyref), axlat, 0, s, skyref_lat, status );
27775             }
27776             if( astTest( skyfrm, "SkyRefP(1)" ) ) {
27777                SetItem( &(store->skyrefp), axlon, 0, s, skyrefp_lon, status );
27778                SetItem( &(store->skyrefp), axlat, 0, s, skyrefp_lat, status );
27779             }
27780          }
27781       }
27782 
27783 /* If the Label attribute  has been set for an axis, use it as the CTYPE
27784    comment and CNAME value. */
27785       if( astTestLabel( skyfrm, latax ) ) {
27786          label = (char *) astGetLabel( skyfrm, latax );
27787          SetItemC( &(store->ctype_com), axlat, 0, s, label, status );
27788          SetItemC( &(store->cname), axlat, 0, s, label, status );
27789       }
27790       if( astTestLabel( skyfrm, lonax ) ) {
27791          label = (char *) astGetLabel( skyfrm, lonax );
27792          SetItemC( &(store->ctype_com), axlon, 0, s, label, status );
27793          SetItemC( &(store->cname), axlon, 0, s, label, status );
27794       }
27795 
27796 /* Nullify any CUNITS strings for the longitude and latitude axes (they
27797    always take the default value of degrees). */
27798       SetItemC( &(store->cunit), axlat, 0, s, NULL, status );
27799       SetItemC( &(store->cunit), axlon, 0, s, NULL, status );
27800    }
27801 
27802 /* Store the Domain name as the WCSNAME keyword (if set). */
27803    if( astTestDomain( skyfrm ) ) {
27804       SetItemC( &(store->wcsname), 0, 0, s, (char *) astGetDomain( skyfrm ), status );
27805    }
27806 
27807 /* Store the observer's position if set (needed for definition of AzEl
27808    systems). */
27809    if( astTestObsLon( skyfrm ) && astTestObsLat( skyfrm ) && s == ' ' ) {
27810       geolon = astGetObsLon( skyfrm );
27811       geolat = astGetObsLat( skyfrm );
27812       h = astGetObsAlt( skyfrm );
27813       if( geolat != AST__BAD && geolon != AST__BAD && h != AST__BAD ) {
27814          eraGd2gc( 1, geolon, geolat, h, xyz );
27815          SetItem( &(store->obsgeox), 0, 0, ' ', xyz[0], status );
27816          SetItem( &(store->obsgeoy), 0, 0, ' ', xyz[1], status );
27817          SetItem( &(store->obsgeoz), 0, 0, ' ', xyz[2], status );
27818       }
27819    }
27820    if( !astOK ) ret = 0;
27821    return ret;
27822 }
27823 
SourceWrap(const char * (* source)(void),int * status)27824 static char *SourceWrap( const char *(* source)( void ), int *status ) {
27825 /*
27826 *  Name:
27827 *     SourceWrap
27828 
27829 *  Purpose:
27830 *     Wrapper function to invoke a C FitsChan source function.
27831 
27832 *  Type:
27833 *     Private function.
27834 
27835 *  Synopsis:
27836 *     #include "fitschan.h"
27837 *     char *SourceWrap( const char *, int *status(* source)( void ) )
27838 
27839 *  Class Membership:
27840 *     FitsChan member function.
27841 
27842 *  Description:
27843 *     This function invokes the source function whose pointer is
27844 *     supplied in order to read the next input line from an external
27845 *     data store. It then returns a pointer to a dynamic string
27846 *     containing a copy of the text that was read.
27847 
27848 *  Parameters:
27849 *     source
27850 *        Pointer to a source function, with no parameters, that
27851 *        returns a pointer to a const, null-terminated string
27852 *        containing the text that it read. This is the form of FitsChan
27853 *        source function employed by the C language interface to the
27854 *        AST library.
27855 *     status
27856 *        Pointer to the inherited status variable.
27857 
27858 *  Returned Value:
27859 *     A pointer to a dynamically allocated, null terminated string
27860 *     containing a copy of the text that was read. This string must be
27861 *     freed by the caller (using astFree) when no longer required.
27862 *
27863 *     A NULL pointer will be returned if there is no more input text
27864 *     to read.
27865 
27866 *  Notes:
27867 *     - A NULL pointer value will be returned if this function is
27868 *     invoked with the global error status set or if it should fail
27869 *     for any reason.
27870 */
27871 
27872 /* Local Variables: */
27873    char *result;                 /* Pointer value to return */
27874    const char *line;             /* Pointer to input line */
27875 
27876 /* Initialise. */
27877    result = NULL;
27878 
27879 /* Check the global error status. */
27880    if ( !astOK ) return result;
27881 
27882 /* Invoke the source function to read the next input line and return a
27883    pointer to the resulting string. */
27884    line = ( *source )();
27885 
27886 /* If a string was obtained, make a dynamic copy of it and save the
27887    resulting pointer. */
27888    if ( line ) result = astString( line, (int) strlen( line ) );
27889 
27890 /* Return the result. */
27891    return result;
27892 }
27893 
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)27894 static AstMapping *SpectralAxes( AstFitsChan *this, AstFrameSet *fs,
27895                                  double *dim, int *wperm,
27896                                  char s, FitsStore *store, double *crvals,
27897                                  int *axis_done, const char *method,
27898                                  const char *class, int *status ){
27899 
27900 /*
27901 *  Name:
27902 *     SpectralAxes
27903 
27904 *  Purpose:
27905 *     Add values to a FitsStore describing spectral axes in a Frame.
27906 
27907 *  Type:
27908 *     Private function.
27909 
27910 *  Synopsis:
27911 *     #include "fitschan.h"
27912 
27913 *     AstMapping *SpectralAxes( AstFitsChan *this, AstFrameSet *fs,
27914 *                               double *dim, int *wperm,
27915 *                               char s, FitsStore *store, double *crvals,
27916 *                               int *axis_done, const char *method,
27917 *                               const char *class, int *status )
27918 
27919 *  Class Membership:
27920 *     FitsChan member function.
27921 
27922 *  Description:
27923 *     The current Frame of the supplied FrameSet is searched for spectral
27924 *     axes. If any are found, FITS WCS keyword values describing the axis
27925 *     are added to the supplied FitsStore, if possible (the conventions
27926 *     of FITS-WCS paper III are used). Note, this function does not store
27927 *     values for keywords which define the transformation from pixel
27928 *     coords to Intermediate World Coords (CRPIX, PC and CDELT), but a
27929 *     Mapping is returned which embodies these values. This Mapping is
27930 *     from the current Frame in the FrameSet (WCS coords) to a Frame
27931 *     representing IWC. The IWC Frame has the same number of axes as the
27932 *     WCS Frame which may be greater than the number of base Frame (i.e.
27933 *     pixel) axes.
27934 *
27935 *     If a spectral axis is found, the RafRA and RefDec attributes of the
27936 *     SpecFrame describing the axis are ignored: it is assumed that the
27937 *     WCS Frame also contains a pair of celestial axes which will result
27938 *     in appropriate celestial reference values being stored in the
27939 *     FitsStore (this asumption should be enforced by calling function
27940 *     MakeFitsFrameSet prior to calling this function).
27941 
27942 *  Parameters:
27943 *     this
27944 *        Pointer to the FitsChan.
27945 *     fs
27946 *        Pointer to the FrameSet. The base Frame should represent FITS pixel
27947 *        coordinates, and the current Frame should represent FITS WCS
27948 *        coordinates. The number of base Frame axes should not exceed the
27949 *        number of current Frame axes. The spectral Unit in the returned
27950 *        FrameSet will always be linearly related to the default Units for
27951 *        the spectral System in use by the axis. If this requires a
27952 *        change to the existing spectral Unit, the integrity of the
27953 *        FrameSet will be maintained by suitable adjustments to the Mappings
27954 *        within the FrameSet.
27955 *     dim
27956 *        An array holding the image dimensions in pixels. AST__BAD can be
27957 *        supplied for any unknwon dimensions.
27958 *     wperm
27959 *        Pointer to an array of integers with one element for each axis of
27960 *        the current Frame. Each element holds the zero-based
27961 *        index of the FITS-WCS axis (i.e. one les than the value of "i" in
27962 *        the keyword names "CTYPEi", "CRVALi", etc) which describes the
27963 *        Frame axis.
27964 *     s
27965 *        The co-ordinate version character. A space means the primary
27966 *        axis descriptions. Otherwise the supplied character should be
27967 *        an upper case alphabetical character ('A' to 'Z').
27968 *     store
27969 *        The FitsStore in which to store the FITS WCS keyword values.
27970 *     crvals
27971 *        Pointer to an array holding the default CRVAL value for each
27972 *        axis in the WCS Frame.
27973 *     axis_done
27974 *        An array of flags, one for each Frame axis, which indicate if a
27975 *        description of the corresponding axis has yet been stored in the
27976 *        FitsStore.
27977 *     method
27978 *        Pointer to a string holding the name of the calling method.
27979 *        This is only for use in constructing error messages.
27980 *     class
27981 *        Pointer to a string holding the name of the supplied object class.
27982 *        This is only for use in constructing error messages.
27983 *     status
27984 *        Pointer to the inherited status variable.
27985 
27986 *  Returned Value:
27987 *     If a spectral axis was found which can be described using the
27988 *     conventions of FITS-WCS paper III, then a Mapping from the current Frame
27989 *     of the supplied FrameSet, to the IWC Frame is returned. Otherwise,
27990 *     a UnitMap is returned. Note, the Mapping only defines the IWC
27991 *     transformation for spectral axes. Any non-spectral axes are passed
27992 *     unchanged by the returned Mapping.
27993 */
27994 
27995 /* Local Variables: */
27996    AstFitsTable *table;    /* Pointer to structure holding -TAB table info */
27997    AstFrame *pframe;       /* Primary Frame containing current WCS axis*/
27998    AstFrame *tfrm1;        /* A temporary Frame */
27999    AstFrame *tfrm;         /* A temporary Frame */
28000    AstFrame *wcsfrm;       /* WCS Frame within FrameSet */
28001    AstFrameSet *tfs;       /* A temporary FrameSet */
28002    AstGrismMap *gmap;      /* GrismMap defining the spectral axis */
28003    AstMapping *axmap;      /* Mapping from WCS to IWC */
28004    AstMapping *map;        /* Pixel -> WCS mapping */
28005    AstMapping *ret;        /* Returned Mapping */
28006    AstMapping *tmap0;      /* A temporary Mapping */
28007    AstMapping *tmap1;      /* A temporary Mapping */
28008    AstMapping *tmap2;      /* A temporary Mapping */
28009    AstMapping *tmap3;      /* A temporary Mapping */
28010    AstMapping *tmap4;      /* A temporary Mapping */
28011    AstMapping *tmap5;      /* A temporary Mapping */
28012    AstMapping *tmap6;      /* A temporary Mapping */
28013    AstPermMap *pm;         /* PermMap pointer */
28014    AstSpecFrame *specfrm;  /* The SpecFrame defining current WCS axis */
28015    char *cname;            /* Pointer to CNAME value */
28016    char ctype[ MXCTYPELEN ]; /* The value for the FITS CTYPE keyword */
28017    char lin_unit[ 20 ];    /* Linear spectral Units being used */
28018    char orig_system[ 40 ]; /* Value of System attribute for current WCS axis */
28019    char system_attr[ 10 ]; /* Name of System attribute for current WCS axis */
28020    char unit_attr[ 10 ];   /* Name of Unit attribute for current WCS axis */
28021    const char *cval;       /* Pointer to temporary character string */
28022    const char *x_sys[ 4 ]; /* Basic spectral systems */
28023    double *lbnd_p;         /* Pointer to array of lower pixel bounds */
28024    double *ubnd_p;         /* Pointer to array of upper pixel bounds */
28025    double crval;           /* The value for the FITS CRVAL keyword */
28026    double dgbyds;          /* Rate of change of grism parameter wrt "S" at ref. point */
28027    double dsbydx;          /* Rate of change of "S" wrt "X" at ref. point */
28028    double geolat;          /* Geodetic latitude of observer (radians) */
28029    double geolon;          /* Geodetic longitude of observer (radians) */
28030    double gval;            /* Value of grism parameter at reference point  */
28031    double h;               /* Geodetic altitude of observer (metres) */
28032    double imagfreq;        /* Image sideband equivalent to the rest frequency (Hz) */
28033    double lbnd_s;          /* Lower bound on spectral axis */
28034    double pv;              /* Value of projection parameter */
28035    double restfreq;        /* Rest frequency (Hz) */
28036    double ubnd_s;          /* Upper bound on spectral axis */
28037    double vsource;         /* Rel.vel. of source (m/s) */
28038    double xval;            /* Value of "X" system at reference point  */
28039    double xyz[3];          /* Geocentric position vector (in m) */
28040    double zsource;         /* Redshift of source */
28041    int *inperm;            /* Pointer to permutation array for input axes */
28042    int *outperm;           /* Pointer to permutation array for output axes */
28043    int extver;             /* Table version number for -TAB headers */
28044    int fits_i;             /* FITS WCS axis index for current WCS axis */
28045    int iax;                /* Axis index */
28046    int icolindex;          /* Index of table column holding index vector */
28047    int icolmain;           /* Index of table column holding main coord array */
28048    int interp;             /* INterpolation method for look-up tables */
28049    int ix;                 /* System index */
28050    int j;                  /* Loop count */
28051    int npix;               /* Number of pixel axes */
28052    int nwcs;               /* Number of WCS axes */
28053    int paxis;              /* Axis index within primary Frame */
28054    int sourcevrf;          /* Rest Frame in which SourceVel is accesed */
28055 
28056 /* Initialise */
28057    ret = NULL;
28058 
28059 /* Check the inherited status. */
28060    if( !astOK ) return ret;
28061 
28062 /* Every supported spectral system is linearly related to one of the
28063    following four systems. */
28064    x_sys[ 0 ] = "FREQ";
28065    x_sys[ 1 ] = "WAVE";
28066    x_sys[ 2 ] = "AWAV";
28067    x_sys[ 3 ] = "VELO";
28068 
28069 /* Get a pointer to the WCS Frame. */
28070    wcsfrm = astGetFrame( fs, AST__CURRENT );
28071 
28072 /* Store the number of pixel and WCS axes. */
28073    npix = astGetNin( fs );
28074    nwcs = astGetNout( fs );
28075 
28076 /* Store the upper and lower pixel bounds. */
28077    lbnd_p = astMalloc( sizeof( double )*(size_t) npix );
28078    ubnd_p = astMalloc( sizeof( double )*(size_t) npix );
28079    if( astOK ) {
28080       for( iax = 0; iax < npix; iax++ ) {
28081          lbnd_p[ iax ] = 1.0;
28082          ubnd_p[ iax ] = ( dim[ iax ] != AST__BAD ) ? dim[ iax ] : 500;
28083       }
28084    }
28085 
28086 /* Check each axis in the WCS Frame to see if it is a spectral axis. */
28087    axmap = NULL;
28088    for( iax = 0; iax < nwcs; iax++ ) {
28089 
28090 /* Obtain a pointer to the primary Frame containing the current WCS axis. */
28091       astPrimaryFrame( wcsfrm, iax, &pframe, &paxis );
28092 
28093 /* If the current axis belongs to a SpecFrame, we have found a spectral
28094    axis. */
28095       if( astIsASpecFrame( pframe ) ) {
28096          specfrm = (AstSpecFrame *) pframe;
28097 
28098 /* Note the (zero-based) FITS WCS axis index to be used for the current
28099    Frame axis. */
28100          fits_i = wperm[ iax ];
28101 
28102 /* Note the name and original value of the System attribute for the spectral
28103    axis within the FrameSet current Frame. */
28104          sprintf( system_attr, "System(%d)", iax + 1 );
28105          cval = astGetC( wcsfrm, system_attr );
28106          if( cval ) strcpy( orig_system, cval );
28107 
28108 /* Note the name of the Unit attribute for the spectral axis within the
28109    FrameSet current Frame. */
28110          sprintf( unit_attr, "Unit(%d)", iax + 1 );
28111 
28112 /* Get a pointer to the Mapping from FITS pixel coordinates to SpecFrame. */
28113          map = astGetMapping( fs, AST__BASE, AST__CURRENT );
28114 
28115 /* Find the bounds of the Spectral axis over the volume of the pixel grid. */
28116          astMapBox( map, lbnd_p, ubnd_p, 1, iax, &lbnd_s, &ubnd_s,
28117                     NULL, NULL );
28118 
28119 /* The Unit attribute of a SpecFrame can be set to arbitrary non-linear
28120    functions of standard linear spectral units.  FITS-WCS paper III requires
28121    CRVAL etc to be given in linear units. So first we ensure that we have a
28122    SpecFrame with linear Units. Create a copy of the SpecFrame and clear
28123    its Unit attribute (this ensures the copy has the default linear units).
28124    Then find a Mapping from the original spectral units to the default
28125    linear units. If the conversion is possible, see if the Mapping
28126    between the units is linear. If it is, then the original Unit attribute
28127    of the SpecFrame is OK (i.e. the units are linear). If not, clear
28128    the Unit attribute of the spectral axis in the FrameSet so that it
28129    uses the default linear units (retaining the original value so that it
28130    can be re-instated later). Using the clear method on the FrameSet
28131    pointer rather than the SpecFrame pointer causes the SpecFrame to be
28132    re-mapped within the FrameSet to maintain its correct relationship with
28133    the other Frames in the FrameSet. Also update the pixel->spectrum
28134    Mapping to take account of the change in units and re-calculate the new
28135    bounds on the spectral axis. Also update any supplied CRVAL value for
28136    the spectral axis. */
28137          tfrm = astCopy( specfrm );
28138          astClearUnit( tfrm, 0 );
28139          tfs = astConvert( specfrm, tfrm, "" );
28140          tfrm = astAnnul( tfrm );
28141          if( tfs ) {
28142             crval = crvals ? crvals[ iax ] : AST__BAD;
28143             tmap1 = astGetMapping( tfs, AST__BASE, AST__CURRENT );
28144             tfs = astAnnul( tfs );
28145             if( !IsMapLinear( tmap1, &lbnd_s, &ubnd_s, 0, status ) ) {
28146                astClear( fs, unit_attr );
28147                (void) astAnnul( map );
28148                map = astGetMapping( fs, AST__BASE, AST__CURRENT );
28149                astMapBox( map, lbnd_p, ubnd_p, 1, iax, &lbnd_s, &ubnd_s,
28150                           NULL, NULL );
28151                astTran1( tmap1, 1, &crval, 1, &crval );
28152             }
28153             tmap1 = astAnnul( tmap1 );
28154 
28155 /* Note the linear spectral Unit currently in use. */
28156             cval = astGetUnit( specfrm, 0 );
28157             if( cval ) strcpy( lin_unit, cval );
28158 
28159 /* For some of the algorithms, the reference value CRVAL is arbitrary.
28160    For these algorithms we choose to use the supplied default CRVAL value.
28161    If no default CRVAL value was suppllied, we use the mid spectral value
28162    if the size of the spectral axis was given, or the lower bound (i.e.
28163    pixel 1) if the size of the spectral axis was not given. */
28164             if( crval == AST__BAD ) {
28165                if( dim[ iax ] != AST__BAD ) {
28166                   crval = 0.5*( lbnd_s + ubnd_s );
28167                } else {
28168                   crval = lbnd_s;
28169                }
28170             }
28171 
28172 /* Modify this crval value so that it correpsonds to an integer pixel
28173    coordinate. */
28174             crval = NearestPix( map, crval, iax, status );
28175 
28176 /* We now check to see if the Mapping from pixel coords -> linear spectral
28177    coords corresponds to one of the algorithms supported in FITS-WCS paper
28178    III. First check for the "linear" algorithm in which the linear spectral
28179    coordinate given by the SpecFrame is related linearly to the pixel
28180    coords. */
28181             ctype[ 0 ] = 0;
28182             if( IsMapLinear( map, lbnd_p, ubnd_p, iax, status ) ) {
28183 
28184 /* The CTYPE value is just the spectral system. */
28185                strcpy( ctype, orig_system );
28186 
28187 /* Create the Mapping which defines the spectral IWC axis. This is
28188    initially the Mapping from WCS to IWCS - it subtracts the CRVAL value
28189    from the spectral WCS value to get the spectral IWC value (other
28190    non-spectral axes are left unchanged by this Mapping). This results
28191    in the spectral IWC axis having the same axis index as the spectral
28192    WCS axis. */
28193                crval = -crval;
28194                tmap0 = (AstMapping *) astShiftMap( 1, &crval, "", status );
28195                crval = -crval;
28196                axmap = AddUnitMaps( tmap0, iax, nwcs, status );
28197                tmap0 = astAnnul( tmap0 );
28198             }
28199 
28200 /* If the "linear" algorithm above is inappropriate, see if the "non-linear"
28201    algorithm defined in FITS-WCS paper III can be used, in which pixel
28202    coords are linearly related to some spectral system (called "X") other
28203    than the one represented by the supplied SpecFrame (called "S"). */
28204             if( !ctype[ 0 ] ) {
28205 
28206 /* Loop round each of the 4 allowed X systems. All other spectral systems
28207    are linearly related to one of these 4 systems and so do not need to be
28208    tested. */
28209                for( ix = 0; ix < 4 && !ctype[ 0 ]; ix++ ) {
28210 
28211 /* Set the system of the spectral WCS axis to the new trial X system. Clear
28212    the Unit attribute to ensure we are using the default linear units.
28213    Using the FrameSet pointer "fs" ensures that the Mappings within the
28214    FrameSet are modified to maintain the correct inter-Frame relationships. */
28215                   astSetC( fs, system_attr, x_sys[ ix ] );
28216                   astClear( fs, unit_attr );
28217 
28218 /* Now we check to see if the current X system is linearly related to
28219    pixel coordinates. */
28220                   tmap3 = astGetMapping( fs, AST__BASE, AST__CURRENT );
28221                   if( IsMapLinear( tmap3, lbnd_p, ubnd_p, iax, status ) ) {
28222 
28223 /* CTYPE: First 4 characters specify the "S" system. */
28224                      strcpy( ctype, orig_system );
28225 
28226 /* The non-linear algorithm code to be appended to the "S" system is of the
28227    form "-X2P" ("P" is the system which is linearly related to "S"). */
28228                      if( !strcmp( x_sys[ ix ], "FREQ" ) ) {
28229                         strcpy( ctype + 4, "-F2" );
28230                      } else if( !strcmp( x_sys[ ix ], "WAVE" ) ) {
28231                         strcpy( ctype + 4, "-W2" );
28232                      } else if( !strcmp( x_sys[ ix ], "AWAV" ) ) {
28233                         strcpy( ctype + 4, "-A2" );
28234                      } else {
28235                         strcpy( ctype + 4, "-V2" );
28236                      }
28237                      if( !strcmp( orig_system, "FREQ" ) ||
28238                          !strcmp( orig_system, "ENER" ) ||
28239                          !strcmp( orig_system, "WAVN" ) ||
28240                          !strcmp( orig_system, "VRAD" ) ) {
28241                         strcpy( ctype + 7, "F" );
28242                      } else if( !strcmp( orig_system, "WAVE" ) ||
28243                                 !strcmp( orig_system, "VOPT" ) ||
28244                                 !strcmp( orig_system, "ZOPT" ) ) {
28245                         strcpy( ctype + 7, "W" );
28246                      } else if( !strcmp( orig_system, "AWAV" ) ) {
28247                         strcpy( ctype + 7, "A" );
28248                      } else {
28249                         strcpy( ctype + 7, "V" );
28250                      }
28251 
28252 /* Create a Mapping which gives S as a function of X. */
28253                      tfrm = astCopy( specfrm );
28254                      astSetC( tfrm, "System(1)", orig_system );
28255                      astSetC( tfrm, "Unit(1)", lin_unit );
28256                      tfs = astConvert( specfrm, tfrm, "" );
28257                      tmap5 = astGetMapping( tfs, AST__BASE, AST__CURRENT );
28258                      tfs = astAnnul( tfs );
28259                      tfrm = astAnnul( tfrm );
28260 
28261 /* Use the inverse of this Mapping to get the X value at the reference S
28262    value. */
28263                      astTran1( tmap5, 1, &crval, 0, &xval );
28264 
28265 /* Also use it to get the rate of change of S with respect to X at the
28266    reference point. */
28267                      dsbydx = astRate( tmap5, &xval, 0, 0 );
28268 
28269 /* Create the Mapping which defines the spectral IWC axis. This is the
28270    Mapping from WCS to IWC - it first converts from S to X, then subtracts
28271    the X reference value value, and then scales the axis to ensure that
28272    the rate of change of S with respect to IWC is unity (as required by
28273    FITS-WCS paper III). Other non-spectral axes are left unchanged by
28274    the Mapping. The spectral IWC axis has the same axis index as the
28275    spectral WCS axis. */
28276                      xval = -xval;
28277                      tmap2 = (AstMapping *) astShiftMap( 1, &xval, "", status );
28278                      astInvert( tmap5 );
28279                      tmap0 = (AstMapping *) astCmpMap( tmap5, tmap2, 1, "", status );
28280                      tmap5 = astAnnul( tmap5 );
28281                      tmap2 = astAnnul( tmap2 );
28282                      tmap2 = (AstMapping *) astZoomMap( 1, dsbydx, "", status );
28283                      tmap1 = (AstMapping *) astCmpMap( tmap0, tmap2, 1, "", status );
28284                      tmap0 = astAnnul( tmap0 );
28285                      tmap2 = astAnnul( tmap2 );
28286                      axmap = AddUnitMaps( tmap1, iax, nwcs, status );
28287                      tmap1 = astAnnul( tmap1 );
28288                   }
28289                   tmap3 = astAnnul( tmap3 );
28290 
28291 /* Re-instate the original system and unit attributes for the spectral axis. */
28292                   astSetC( fs, system_attr, orig_system );
28293                   astSetC( fs, unit_attr, lin_unit );
28294                }
28295             }
28296 
28297 /* If the "non-linear" algorithm above is inappropriate, see if the
28298    "log-linear" algorithm defined in FITS-WCS paper III can be used, in
28299    which the spectral axis is logarithmically spaced in the spectral
28300    system given by the SpecFrame. */
28301             if( !ctype[ 0 ] ) {
28302 
28303 /* If the "log-linear" algorithm is appropriate, the supplied SpecFrame (s)
28304    is related to pixel coordinate (p) by s = Sr.EXP( a*p - b ). If this
28305    is the case, then the log of s will be linearly related to pixel
28306    coordinates. Test this. If the test is passed a Mapping is returned from
28307    WCS to IWC. */
28308                axmap = LogAxis( map, iax, nwcs, lbnd_p, ubnd_p, crval, status );
28309 
28310 /* If the axis is logarithmic... */
28311                if( axmap ) {
28312 
28313 /* CTYPE: First 4 characters specify the "S" system. */
28314                   strcpy( ctype, orig_system );
28315 
28316 /* The rest is "-LOG". */
28317                   strcpy( ctype + 4, "-LOG" );
28318                }
28319             }
28320 
28321 /* If the "log-linear" algorithm above is inappropriate, see if the "grism"
28322    algorithm defined in FITS-WCS paper III can be used, in which pixel
28323    coords are related to wavelength using a grism dispersion function,
28324    implemented in AST by a GrismMap. GrismMaps produce either vacuum
28325    wavelength or air wavelength as output. Temporarily set the SpecFrame
28326    to these two systems in turn before we do the check for a GrismMap. */
28327             for( ix = 0; ix < 2 && !ctype[ 0 ]; ix++ ) {
28328                astSetC( fs, system_attr, ( ix == 0 ) ? "WAVE" : "AWAV" );
28329                astSetC( fs, unit_attr, "m" );
28330 
28331 /* Get the simplified Mapping from pixel to wavelength. If the Mapping is
28332    a CmpMap containing a GrismMap, and if the output of the GrismMap is
28333    scaled by a neighbouring ZoomMap (e.g. into different wavelength units),
28334    then the GrismMap will be modified to incorporate the effect of the
28335    ZoomMap, and the ZoomMap will be removed. */
28336                tmap2 = astGetMapping( fs, AST__BASE, AST__CURRENT );
28337                tmap1 = astSimplify( tmap2 );
28338                tmap2 = astAnnul( tmap2 );
28339 
28340 /* Analyse this Mapping to see if the iax'th output is created diretcly by a
28341    GrismMap (i.e. the output of theGrismMap must not subsequently be
28342    modified by some other Mapping). If so, ExtractGrismMap returns a pointer
28343    to the GrismMap as its function value, and also returns "tmap2" as a copy
28344    of tmap1 in which the GrismMap has been replaced by a UnitMap. */
28345                gmap = ExtractGrismMap( tmap1, iax, &tmap2, status );
28346                if( gmap ) {
28347 
28348 /* The Mapping without the GrismMap must be linear on the spectral axis. */
28349                   if( IsMapLinear( tmap2, lbnd_p, ubnd_p, iax, status ) ) {
28350 
28351 /* Get the reference wavelength (in "m") stored in the GrismMap. */
28352                      crval = astGetGrismWaveR( gmap );
28353 
28354 /* Save a copy of the current Wavelength (in "m") SpecFrame. */
28355                      tfrm1 = astCopy( specfrm );
28356 
28357 /* Re-instate the original System and Unit attributes for the SpecFrame. */
28358                      astSetC( fs, system_attr, orig_system );
28359                      astSetC( fs, unit_attr, lin_unit );
28360 
28361 /* Find the Mapping from the original "S" system to wavelength (in "m"). */
28362                      tfs = astConvert( specfrm, tfrm1, "" );
28363                      tfrm1 = astAnnul( tfrm1 );
28364                      if( tfs ) {
28365                         tmap3 = astGetMapping( tfs, AST__BASE, AST__CURRENT );
28366                         tfs = astAnnul( tfs );
28367 
28368 /* Use the inverse of this Mapping to convert the reference value from
28369    wavelength to the "S" system. */
28370                         astTran1( tmap3, 1, &crval, 0, &crval );
28371 
28372 /* Concatenate the "S"->wavelength Mapping with the inverse GrismMap (from
28373    wavelength to grism parameter), to get the "S" -> "grism parameter"
28374    Mapping. */
28375                         astInvert( gmap );
28376                         tmap4 = (AstMapping *) astCmpMap( tmap3, gmap, 1, "", status );
28377                         tmap3 = astAnnul( tmap3 );
28378 
28379 /* Use this Mapping to find the grism parameter at the reference point. */
28380                         astTran1( tmap4, 1, &crval, 1, &gval );
28381 
28382 /* Also use it to find the rate of change of grism parameter with respect
28383    to "S" at the reference point. */
28384                         dgbyds = astRate( tmap4, &crval, 0, 0 );
28385 
28386 /* FITS-WCS paper III required ds/dw to be unity at the reference point.
28387    Therefore the rate of change of grism parameter with respect to IWC ("w")
28388    is equal to the rate of change of grism parameter with respect to "S"
28389    (at the reference point). The mapping from "w" to grism parameter is a
28390    ZoomMap which scales "w" by "dgbyds" followed by a ShiftMap which adds
28391    on "gval". */
28392                         tmap5 = (AstMapping *) astZoomMap( 1, dgbyds, "", status );
28393                         tmap6 = (AstMapping *) astShiftMap( 1, &gval, "", status );
28394                         tmap3 = (AstMapping *) astCmpMap( tmap5, tmap6, 1, "", status );
28395                         tmap5 = astAnnul( tmap5 );
28396                         tmap6 = astAnnul( tmap6 );
28397 
28398 /* Create the Mapping which defines the spectral IWC axis. This is the
28399    Mapping from WCS "S" to IWCS "w", formed by combining the Mapping from
28400    "S" to grism parameter (tmap4), with the Mapping from grism parameter to
28401    "w" (inverse of tmap3). Other non-spectral axes are left unchanged by the
28402    Mapping. The spectral IWC axis has the same axis index as the spectral
28403    WCS axis. */
28404                         astInvert( tmap3 );
28405                         tmap5 = (AstMapping *) astCmpMap( tmap4, tmap3, 1, "", status );
28406                         tmap3 = astAnnul( tmap3 );
28407                         tmap4 = astAnnul( tmap4 );
28408                         axmap = AddUnitMaps( tmap5, iax, nwcs, status );
28409                         tmap5 = astAnnul( tmap5 );
28410 
28411 /* CTYPE: First 4 characters specify the "S" system. */
28412                         strcpy( ctype, orig_system );
28413 
28414 /* Last 4 characters are "-GRA" or "-GRI". */
28415                         strcpy( ctype + 4, ( ix == 0 ) ? "-GRI" : "-GRA"  );
28416 
28417 /* Store values for the projection parameters in the FitsStore. Ignore
28418    parameters which are set to the default values defined in FITS-WCS
28419    paper III. */
28420                         pv = astGetGrismG( gmap );
28421                         if( pv != 0 ) SetItem( &(store->pv), fits_i, 0, s, pv, status );
28422                         pv = (double) astGetGrismM( gmap );
28423                         if( pv != 0 ) SetItem( &(store->pv), fits_i, 1, s, pv, status );
28424                         pv = astGetGrismAlpha( gmap );
28425                         if( pv != 0 ) SetItem( &(store->pv), fits_i, 2, s, pv*AST__DR2D, status );
28426                         pv = astGetGrismNR( gmap );
28427                         if( pv != 1.0 ) SetItem( &(store->pv), fits_i, 3, s, pv, status );
28428                         pv = astGetGrismNRP( gmap );
28429                         if( pv != 0 ) SetItem( &(store->pv), fits_i, 4, s, pv, status );
28430                         pv = astGetGrismEps( gmap );
28431                         if( pv != 0 ) SetItem( &(store->pv), fits_i, 5, s, pv*AST__DR2D, status );
28432                         pv = astGetGrismTheta( gmap );
28433                         if( pv != 0 ) SetItem( &(store->pv), fits_i, 6, s, pv*AST__DR2D, status );
28434                      }
28435                   }
28436 
28437 /* Release resources. */
28438                   tmap2 = astAnnul( tmap2 );
28439                   gmap = astAnnul( gmap );
28440                }
28441 
28442 /* Release resources. */
28443                tmap1 = astAnnul( tmap1 );
28444 
28445 /* Re-instate the original System and Unit attributes for the SpecFrame. */
28446                astSetC( fs, system_attr, orig_system );
28447                astSetC( fs, unit_attr, lin_unit );
28448             }
28449 
28450 /* If none of the above algorithms are appropriate, we must resort to
28451    using the -TAB algorithm, in which the Mapping is defined by a look-up
28452    table. Check the TabOK attribute to see -TAB is to be supported. */
28453             extver = astGetTabOK( this );
28454             if( !ctype[ 0 ] && extver > 0 ) {
28455 
28456 /* Get any pre-existing FitsTable from the FitsStore. This is the table
28457    in which the tabular data will be stored (if the Mapping can be expressed
28458    in -TAB form). */
28459                if( !astMapGet0A( store->tables, AST_TABEXTNAME, &table ) ) table = NULL;
28460 
28461 /* See if the Mapping can be expressed in -TAB form. */
28462                tmap0 = IsMapTab1D( map, 1.0, NULL, wcsfrm, dim, iax, fits_i, &table,
28463                                    &icolmain, &icolindex, &interp, status );
28464                if( tmap0 ) {
28465 
28466 /* CTYPE: First 4 characters specify the "S" system. Last 4 characters are
28467    "-TAB". */
28468                   strcpy( ctype, orig_system );
28469                   strcpy( ctype + 4, "-TAB" );
28470 
28471 /* The values stored in the table index vector are GRID coords. So we
28472    need to ensure that IWC are equivalent to GRID coords. So set CRVAL
28473    to zero. First store the original CRVAL value (which gives the
28474    observation centre) in AXREF. */
28475                   SetItem( &(store->axref), fits_i, 0, s, crval, status );
28476                   crval = 0.0;
28477 
28478 /* Store TAB-specific values in the FitsStore. First the name of the
28479    FITS binary table extension holding the coordinate info. */
28480                   SetItemC( &(store->ps), fits_i, 0, s, AST_TABEXTNAME, status );
28481 
28482 /* Next the table version number. This is the set (positive) value for the
28483    TabOK attribute. */
28484                   SetItem( &(store->pv), fits_i, 1, s, extver, status );
28485 
28486 /* Also store the table version in the binary table header. */
28487                   astSetFitsI( table->header, "EXTVER", extver,
28488                                "Table version number", 0 );
28489 
28490 /* Next the name of the table column containing the main coords array. */
28491                   SetItemC( &(store->ps), fits_i, 1, s,
28492                             astColumnName( table, icolmain ), status );
28493 
28494 /* Next the name of the column containing the index array */
28495                   if( icolindex >= 0 ) SetItemC( &(store->ps), fits_i, 2, s,
28496                                   astColumnName( table, icolindex ), status );
28497 
28498 /* The interpolation method (an AST extension to the published -TAB
28499    algorithm, communicated through the QVi_4a keyword). */
28500                   SetItem( &(store->pv), fits_i, 4, s, interp, status );
28501 
28502 /* Also store the FitsTable itself in the FitsStore. */
28503                   astMapPut0A( store->tables, AST_TABEXTNAME, table, NULL );
28504 
28505 /* Create the WCS -> IWC Mapping (AST uses grid coords as IWC coords for
28506    the -TAB algorithm). First, get a Mapping that combines the TAB axis
28507    Mapping( tmap0) in parallel with one or two UnitMaps in order to put
28508    the TAB axis at the required index. */
28509                   tmap1 = AddUnitMaps( tmap0, iax, nwcs, status );
28510 
28511 /* Now get a PermMap that permutes the WCS axes into the FITS axis order. */
28512                   inperm = astMalloc( sizeof( double )*nwcs );
28513                   outperm = astMalloc( sizeof( double )*nwcs );
28514                   if( astOK ) {
28515                      for( j = 0; j < nwcs; j++ ) {
28516                         inperm[ j ] = wperm[ j ];
28517                         outperm[ wperm[ j ] ] = j;
28518                      }
28519                   }
28520                   pm = astPermMap( nwcs, inperm, nwcs, outperm, NULL, "",
28521                                    status );
28522 
28523 /* Combine these two Mappings in series, to get the Mapping from WCS to
28524    IWC. */
28525                   axmap = (AstMapping *) astCmpMap( pm, tmap1, 1, " ",
28526                                                     status );
28527 
28528 /* Free resources. */
28529                   inperm = astFree( inperm );
28530                   outperm = astFree( outperm );
28531                   pm = astAnnul( pm );
28532                   tmap0 = astAnnul( tmap0 );
28533                   tmap1 = astAnnul( tmap1 );
28534                }
28535                if( table ) table = astAnnul( table );
28536             }
28537 
28538 /* If this axis is a usable spectral axis... */
28539             if( ctype[ 0 ] ) {
28540 
28541 /* Add the Mapping for this axis in series with any existing result Mapping. */
28542                if( ret ) {
28543                   tmap0 = (AstMapping *) astCmpMap( ret, axmap, 1, "", status );
28544                   (void) astAnnul( ret );
28545                   ret = tmap0;
28546                } else {
28547                   ret = astClone( axmap );
28548                }
28549                axmap = astAnnul( axmap );
28550 
28551 /* Store values for CTYPE, CRVAL and CUNIT in the FitsStore. */
28552                SetItemC( &(store->ctype), fits_i, 0, s, ctype, status );
28553                SetItem( &(store->crval), fits_i, 0, s, crval, status );
28554                SetItemC( &(store->cunit), fits_i, 0, s, lin_unit, status );
28555 
28556 /* If the axis label has been set, use it as the CTYPE comment and CNAME
28557    value. */
28558                if( astTestLabel( specfrm, 0 ) ) {
28559                   cname = (char *) astGetLabel( specfrm, 0 );
28560                   SetItemC( &(store->ctype_com), fits_i, 0, s, cname, status );
28561                   SetItemC( &(store->cname), fits_i, 0, s, cname, status );
28562                }
28563 
28564 /* Store values for the other FITS-WCS keywords which describe the
28565    spectral system. Only store values which have been explicitly set in
28566    the SpecFrame, which are different to the default values defined by
28567    FITS-WCS paper III (if any), and which are not bad. */
28568                if( astTestObsLon( specfrm ) && astTestObsLat( specfrm ) &&
28569                    s == ' ' ) {
28570                   geolon = astGetObsLon( specfrm );
28571                   geolat = astGetObsLat( specfrm );
28572                   h = astGetObsAlt( specfrm );
28573                   if( geolat != AST__BAD && geolon != AST__BAD && h != AST__BAD ) {
28574                      eraGd2gc( 1, geolon, geolat, h, xyz );
28575                      SetItem( &(store->obsgeox), 0, 0, ' ', xyz[0], status );
28576                      SetItem( &(store->obsgeoy), 0, 0, ' ', xyz[1], status );
28577                      SetItem( &(store->obsgeoz), 0, 0, ' ', xyz[2], status );
28578                   }
28579                }
28580                if( astTestRestFreq( specfrm ) ) {
28581                   restfreq = astGetRestFreq( specfrm );
28582                   if( restfreq != AST__BAD ) {
28583                      if( !strcmp( orig_system, "WAVE" ) ||
28584                          !strcmp( orig_system, "VOPT" ) ||
28585                          !strcmp( orig_system, "ZOPT" ) ||
28586                          !strcmp( orig_system, "AWAV" ) ) {
28587                         SetItem( &(store->restwav), 0, 0, s, AST__C/restfreq, status );
28588                      } else {
28589                         SetItem( &(store->restfrq), 0, 0, s, restfreq, status );
28590                      }
28591                   }
28592                   if( astIsADSBSpecFrame( specfrm ) ) {
28593                      imagfreq = astGetImagFreq( (AstDSBSpecFrame *) specfrm );
28594                      if( imagfreq != AST__BAD ) {
28595                         SetItem( &(store->imagfreq), 0, 0, s, imagfreq, status );
28596                      }
28597                   }
28598                }
28599                cval = GetFitsSor( astGetC( specfrm, "StdOfRest" ), status );
28600                if( cval ) SetItemC( &(store->specsys), 0, 0, s, cval, status );
28601                if( astTestSourceVel( specfrm ) ) {
28602                   vsource = astGetSourceVel( specfrm );
28603                   if( vsource != AST__BAD && fabs( vsource ) < AST__C ) {
28604                      zsource = sqrt( (AST__C + vsource)/
28605                                      (AST__C - vsource) ) - 1.0;
28606                      SetItem( &(store->zsource), 0, 0, s, zsource, status );
28607                      cval = GetFitsSor( astGetC( specfrm, "SourceVRF" ), status );
28608                      if( cval ) SetItemC( &(store->ssyssrc), 0, 0, s, cval, status );
28609                   }
28610                } else {
28611                   vsource = AST__BAD;
28612                }
28613 
28614 /* Store the VELOSYS value (not strictly needed since it can be
28615    determined from the other values, but FITS-WCS paper III says it can be
28616    useful). We temporarily change the source velocity to be zero m/s
28617    in the main rest frame (StdOfRest) (unless the main rest frame is
28618    already the source rest frame). We then change the source rest
28619    frame to topocentric and get the source velocity (i.e. the velocity of
28620    the main rest Frame) in the topocentric system. We then re-instate the
28621    original attribute values if they were set. */
28622                if( astGetStdOfRest( specfrm ) != AST__SCSOR ) {
28623                   sourcevrf = astGetSourceVRF( specfrm );
28624                   astSetSourceVRF( specfrm, astGetStdOfRest( specfrm ) );
28625                   astSetSourceVel( specfrm, 0.0 );
28626                } else {
28627                   vsource = AST__BAD;
28628                   sourcevrf = AST__NOSOR;
28629                }
28630                astSetSourceVRF( specfrm, AST__TPSOR );
28631                SetItem( &(store->velosys), 0, 0, s,
28632                         astGetSourceVel( specfrm ), status );
28633                if( vsource != AST__BAD ){
28634                   astSetSourceVRF( specfrm, sourcevrf );
28635                   astSetSourceVel( specfrm, vsource );
28636                }
28637 
28638 /* Indicate that this axis has been described. */
28639                axis_done[ iax ] = 1;
28640             }
28641 
28642 /* Release resources. */
28643             map = astAnnul( map );
28644          }
28645       }
28646       pframe = astAnnul( pframe );
28647    }
28648 
28649 /* Release resources. */
28650    lbnd_p = astFree( lbnd_p );
28651    ubnd_p = astFree( ubnd_p );
28652    wcsfrm = astAnnul( wcsfrm );
28653 
28654 /* If we have a Mapping to return, simplify it. Otherwise, create
28655    a UnitMap to return. */
28656    if( ret ) {
28657       tmap0 = ret;
28658       ret = astSimplify( tmap0 );
28659       tmap0 =  astAnnul( tmap0 );
28660    } else {
28661       ret = (AstMapping *) astUnitMap( nwcs, "", status );
28662    }
28663 
28664 /* Return the result. */
28665    return ret;
28666 }
28667 
SpecTrans(AstFitsChan * this,int encoding,const char * method,const char * class,int * status)28668 static AstFitsChan *SpecTrans( AstFitsChan *this, int encoding,
28669                                const char *method, const char *class, int *status ){
28670 
28671 /*
28672 *  Name:
28673 *     SpecTrans
28674 
28675 *  Purpose:
28676 *     Translated non-standard WCS FITS headers into equivalent standard
28677 *     ones.
28678 
28679 *  Type:
28680 *     Private function.
28681 
28682 *  Synopsis:
28683 *     #include "fitschan.h"
28684 *     AstFitsChan *SpecTrans( AstFitsChan *this, int encoding,
28685 *                             const char *method, const char *class, int *status )
28686 
28687 *  Class Membership:
28688 *     FitsChan member function.
28689 
28690 *  Description:
28691 *     This function checks the supplied FitsChan for selected
28692 *     non-standard WCS keywords and, if found, stores equivalent
28693 *     standard keywords in a newly created FitsChan which is returned as
28694 *     the function value. All the original keywords are marked
28695 *     as having been used, so that they are not written out when the
28696 *     FitsChan is deleted.
28697 *
28698 
28699 *     At the moment, the non-standard keywords checked for are:
28700 *
28701 *     1) RADECSYS is renamed as RADESYS
28702 *
28703 *     2) LONGPOLE is renamed as LONPOLE
28704 *
28705 *     3) CDjjjiii and CDj_i are converted to PCi_j (with unit CDELT)
28706 *
28707 *     4) CROTAj are converted to PCi_j
28708 *
28709 *     5) PROJPi are converted to PV<axlat>_i
28710 *
28711 *     6) CmVALi are converted to CRVALis (s=A,B,,, for m=1,2...). This
28712 *        is also done for CmPIXi, CmYPEi, and CmNITi. CmELTi is converted
28713 *        to a CDj_is array.
28714 *
28715 *     7) EQUINOX keywords with string values equal to a date preceded
28716 *        by the letter B or J (eg "B1995.0"). These are converted to the
28717 *        corresponding Julian floating point value without any epoch
28718 *        specifier.
28719 *
28720 *     8) EPOCH values are converted into Julian EQUINOX values (but only
28721 *        if the FitsChan does not already contain an EQUINOX value).
28722 *
28723 *     9) DATE-OBS values are converted into MJD-OBS values (but only
28724 *        if the FitsChan does not already contain an MJD-OBS value).
28725 *
28726 *     10) EQUINOX or EPOCH keywords with value zero  are converted to
28727 *         B1950.
28728 *
28729 *     11) The AIPS NCP and GLS projections are converted into equivalent SIN
28730 *         or SFL projections.
28731 *
28732 *     12) The IRAF "ZPX" projection. If the last 4 chacaters of CTYPEi
28733 
28734 *       (i = 1, naxis) are "-ZPX", then:
28735 *	- "ZPX" is replaced by "ZPN" within the CTYPEi value
28736 *	- A distortion code of "-ZPX" is appended to the end of the CTYPEi
28737 *       value (this is used later by the DistortMaps function).
28738 *       - If the FitsChan contains no PROJP keywords, then projection
28739 *       parameter valus are read from any WATi_nnn keywords, and
28740 *       corresponding PV keywords are added to the FitsChan.
28741 *
28742 *     13) The IRAF "TNX" projection. If the last 4 chacaters of CTYPEi
28743 
28744 *       (i = 1, naxis) are "-TNX", then:
28745 *	- "TNX" is replaced by "TAN" within the CTYPEi value (the distorted
28746 *       TAN projection included in a pre-final version of FITS-WCS is still
28747 *       supported by AST using the WcsMap AST__TPN projection).
28748 *       - If the FitsChan contains no PROJP keywords, then projection
28749 *       parameter valus are read from any WATi_nnn keywords, and
28750 *       corresponding PV keywords are added to the FitsChan.
28751 *       - If the TNX projection cannot be converted exactly into a TAN
28752 *       projection, ASTWARN keywords are added to the FitsChan
28753 *       containing a warning message. The calling application can (if it
28754 *       wants to) check for this keyword, and report its contents to the
28755 *       user.
28756 *
28757 *     14) Keywords relating to the IRAF "mini-WCS" system are removed.
28758 *       This is the IRAF equivalent of the AST native encoding. Mini-WCS
28759 *       keywords are removed in order to avoid confusion arising between
28760 *       potentially inconsistent encodings.
28761 *
28762 *     15) "QV" parameters for TAN projections (as produced by AUTOASTROM)
28763 *       or "-TAB" (as produced by FitsChan) are renamed to "PV".
28764 *
28765 *     16) RESTFREQ is converted to RESTFRQ.
28766 *
28767 *     17) the "-WAV", "-FRQ" and "-VEL" CTYPE algorithms included in an
28768 *       early draft of FITS-WCS paper III are translated to the
28769 *       corresponding modern "-X2P" form.
28770 *
28771 *     18) AIPS spectral CTYPE values are translated to FITS-WCS paper III
28772 *     equivalents.
28773 *
28774 *     19) AIPS spectral keywords OBSRA and OBSDEC are used to create a
28775 *     pair of celestial axes with reference point at the specified
28776 *     (OBSRA,OBSDEC) position. This is only done if the header does not
28777 *     already contain a pair of celestial axes.
28778 *
28779 *     20) Common case insensitive CUNIT values: "Hz", "Angstrom", "km/s",
28780 *     "M/S"
28781 *
28782 *     21) Various translations specific to the FITS-CLASS encoding.
28783 *
28784 *     22) SAO distorted TAN projections (uses COi_j keywords to store
28785 *     polynomial coefficients) are converted to TPN projections.
28786 
28787 *     23) CTYPE == "LAMBDA" changed to CTYPE = "WAVE"
28788 *
28789 *     24) if the projection is TAN and the PolyTan attribute is non-zero,
28790 *     or if the projection is TPV (produced by SCAMP), the projection is
28791 *     changed to TPN (the AST code for the draft FITS-WCS paper II
28792 *     conventions for a distorted TAN projection).
28793 
28794 *  Parameters:
28795 *     this
28796 *        Pointer to the FitsChan.
28797 *     encoding
28798 *        The FitsChan encoding in use.
28799 *     method
28800 *        Pointer to string holding name of calling method.
28801 *     class
28802 *        Pointer to a string holding the name of the supplied object class.
28803 *     status
28804 *        Pointer to the inherited status variable.
28805 
28806 *  Returned Value:
28807 *     A pointer to the new FitsChan containing the keywords which
28808 *     constitute the standard equivalents to any non-standard keywords in
28809 *     the supplied FitsChan. A NULL pointer is returned if there are no
28810 *     non-standard keywords in the supplied FitsChan.
28811 */
28812 
28813 /* Local Variables: */
28814    AstFitsChan *ret;              /* The returned FitsChan */
28815    char *assys;                   /* AIPS standad of rest type */
28816    char *astype;                  /* AIPS spectral type */
28817    char *comm;                    /* Pointer to comment string */
28818    char *cval;                    /* Pointer to character string */
28819    char *start;                   /* Pointer to start of projp term */
28820    char *watmem;                  /* Pointer to total WAT string */
28821    char bj;                       /* Besselian/Julian indicator */
28822    char format[ 50 ];             /* scanf format string */
28823    char keyname[ FITSNAMLEN + 5 ];/* General keyword name + formats */
28824    char lattype[MXCTYPELEN];      /* CTYPE value for latitude axis */
28825    char lontype[MXCTYPELEN];      /* CTYPE value for longitude axis */
28826    char prj[6];                   /* Spatial projection string */
28827    char s;                        /* Co-ordinate version character */
28828    char spectype[MXCTYPELEN];     /* CTYPE value for spectral axis */
28829    char sprj[6];                  /* Spectral projection string */
28830    char ss;                       /* Co-ordinate version character */
28831    char template[ FITSNAMLEN + 1 ];/* General keyword name template */
28832    double *cvals;                 /* PVi_m values for TPN projection */
28833    double cdelti;                 /* CDELT for longitude axis */
28834    double cdeltj;                 /* CDELT for latitude axis */
28835    double cosrota;                /* Cos( CROTA ) */
28836    double crota;                  /* CROTA Value */
28837    double dval;                   /* General floating value */
28838    double lambda;                 /* Ratio of CDELTs */
28839    double projp;                  /* Projection parameter value */
28840    double rowsum2;                /* Sum of squared CDi_j row elements */
28841    double sinrota;                /* Sin( CROTA ) */
28842    double sinval;                 /* Sin( dec ref ) */
28843    int *mvals;                    /* "m" index of each PVi_m value */
28844    int axlat;                     /* Index of latitude axis */
28845    int axlon;                     /* Index of longitude axis */
28846    int diag;                      /* Sign of diagonal CDi_j element */
28847    int gotpcij;                   /* Does FitsChan contain any PCi_j keywords? */
28848    int i,j;                       /* Indices */
28849    int iaxis;                     /* Axis index */
28850    int icoeff;                    /* Index of next PVi_m value */
28851    int iproj;                     /* Projection parameter index */
28852    int jhi;                       /* Highest axis index */
28853    int jlo;                       /* Lowest axis index */
28854    int lbnd[ 2 ];                 /* Lower index bounds */
28855    int m;                         /* Co-ordinate version index */
28856    int naxis;                     /* Number of axes */
28857    int ncoeff;                    /* Number of PVi_m values */
28858    int ok;                        /* Can projection be represented in FITS-WCS?*/
28859    int shifted;                   /* Non-zero if there is an origin shift */
28860    int tlbnd[ 2 ];                /* Lower index bounds */
28861    int tubnd[ 2 ];                /* Upper index bounds */
28862    int ubnd[ 2 ];                 /* Upper index bounds */
28863    int use_projp;                 /* Use PROJP keywors in favour of PV keywords? */
28864    size_t size;                   /* Length of string value */
28865 
28866 /* Check the global error status. */
28867    if ( !astOK ) return NULL;
28868 
28869 /* Initialise to avoid compiler warnings. */
28870    size = 0;
28871    prj[ 0 ] = 0;
28872 
28873 /* Create the returned FitsChan. */
28874    ret = astFitsChan( NULL, NULL, "", status );
28875 
28876 /* Loop round all axis descriptions, starting with primary (' '). */
28877    for( s = 'A' - 1; s <= 'Z' && astOK; s++ ){
28878       if( s == 'A' - 1 ) s = ' ';
28879 
28880 /* Find the number of axes by finding the highest axis number in any
28881    CRPIXi keyword name. Pass on if there are no axes for this axis
28882    description. */
28883       if( s != ' ' ) {
28884          sprintf( template, "CRPIX%%d%c", s );
28885       } else {
28886          strcpy( template, "CRPIX%d" );
28887       }
28888       if( !astKeyFields( this, template, 1, &naxis, lbnd ) ) {
28889          if( s == ' ' ) s = 'A' - 1;
28890          continue;
28891       }
28892 
28893 /* Find the longitude and latitude axes by examining the CTYPE values.
28894    They are marked as read. Such markings are only provisional, and they
28895    can be read again any number of times until the current astRead
28896    operation is completed. Also note the projection type. */
28897       j = 0;
28898       axlon = -1;
28899       axlat = -1;
28900       while( j < naxis && astOK ){
28901          if( GetValue2( ret, this, FormatKey( "CTYPE", j + 1, -1, s, status ),
28902                        AST__STRING, (void *) &cval, 0, method,
28903                        class, status ) ){
28904             if( !strncmp( cval, "RA--", 4 ) ||
28905                 !strncmp( cval, "AZ--", 4 ) ||
28906                 !strncmp( cval + 1, "LON", 3 ) ||
28907                 !strncmp( cval + 2, "LN", 2 ) ) {
28908                axlon = j;
28909                strncpy( prj, cval + 4, 4 );
28910                strncpy( lontype, cval, 10 );
28911                prj[ 4 ] = 0;
28912             } else if( !strncmp( cval, "DEC-", 4 ) ||
28913                 !strncmp( cval, "EL--", 4 ) ||
28914                 !strncmp( cval + 1, "LAT", 3 ) ||
28915                 !strncmp( cval + 2, "LT", 2 ) ) {
28916                axlat = j;
28917                strncpy( prj, cval + 4, 4 );
28918                strncpy( lattype, cval, 10 );
28919                prj[ 4 ] = 0;
28920 
28921 /* Check for spectral algorithms from early drafts of paper III */
28922             } else {
28923                sprj[ 0 ] = '-';
28924                if( !strncmp( cval + 4, "-WAV", 4 ) ) {
28925                   sprj[ 1 ] = 'W';
28926                } else if( !strncmp( cval + 4, "-FRQ", 4 ) ) {
28927                   sprj[ 1 ] = 'F';
28928                } else if( !strncmp( cval + 4, "-VEL", 4 ) ) {
28929                   sprj[ 1 ] = 'V';
28930                } else {
28931                   sprj[ 0 ] = 0;
28932                }
28933                if( *sprj ) {
28934                   sprj[ 2 ] = '2';
28935                   if( !strncmp( cval, "WAVE", 4 ) ) {
28936                      sprj[ 3 ] = 'W';
28937                   } else if( !strncmp( cval, "FREQ", 4 ) ) {
28938                      sprj[ 3 ] = 'F';
28939                   } else if( !strncmp( cval, "VELO", 4 ) ) {
28940                      sprj[ 3 ] = 'V';
28941                   } else if( !strncmp( cval, "VRAD", 4 ) ) {
28942                      sprj[ 3 ] = 'F';
28943                   } else if( !strncmp( cval, "VOPT", 4 ) ) {
28944                      sprj[ 3 ] = 'W';
28945                   } else if( !strncmp( cval, "ZOPT", 4 ) ) {
28946                      sprj[ 3 ] = 'W';
28947                   } else if( !strncmp( cval, "ENER", 4 ) ) {
28948                      sprj[ 3 ] = 'F';
28949                   } else if( !strncmp( cval, "WAVN", 4 ) ) {
28950                      sprj[ 3 ] = 'F';
28951                   } else if( !strncmp( cval, "BETA", 4 ) ) {
28952                      sprj[ 3 ] = 'V';
28953                   } else {
28954                      sprj[ 0 ] = 0;
28955                   }
28956                }
28957                if( *sprj ) {
28958                   strcpy( spectype, cval );
28959                   if( sprj[ 1 ] == sprj[ 3 ] ) {
28960                      strcpy( sprj, strlen( cval ) > 8 ? "----" : "    " );
28961                   } else {
28962                      sprj[ 4 ] = 0;
28963                   }
28964                   strncpy( spectype + 4, sprj, 4 );
28965                   cval = spectype;
28966                   SetValue( ret, FormatKey( "CTYPE", j + 1, -1, s, status ),
28967                            (void *) &cval, AST__STRING, NULL, status );
28968                }
28969             }
28970             j++;
28971          } else {
28972             break;
28973          }
28974       }
28975 
28976 /* RADECSYS keywords
28977    ----------------- */
28978       if( s == ' ' ) {
28979          if( GetValue2( ret, this, "RADECSYS", AST__STRING, (void *) &cval, 0, method,
28980                        class, status ) ){
28981             if( encoding == FITSPC_ENCODING || encoding == FITSIRAF_ENCODING ){
28982                SetValue( ret, "RADESYS", (void *) &cval, AST__STRING,
28983                          CardComm( this, status ), status );
28984             }
28985          }
28986 
28987 /* LONGPOLE keywords
28988    ----------------- */
28989          if( GetValue2( ret, this, "LONGPOLE", AST__FLOAT, (void *) &dval, 0, method,
28990                        class, status ) ){
28991             if( encoding == FITSPC_ENCODING || encoding == FITSIRAF_ENCODING ){
28992                SetValue( ret, "LONPOLE", (void *) &dval, AST__FLOAT,
28993                          CardComm( this, status ), status );
28994             }
28995          }
28996       }
28997 
28998 /* Zero CDELT values.
28999    ----------------- */
29000 
29001 /* Check there are some CDELT keywords... */
29002       if( s != ' ' ) {
29003          sprintf( template, "CDELT%%d%c", s );
29004       } else {
29005          strcpy( template, "CDELT%d" );
29006       }
29007       if( astKeyFields( this, template, 0, NULL, NULL ) ){
29008 
29009 /* Do each row in the matrix. */
29010          for( j = 0; j < naxis; j++ ){
29011 
29012 /* Get the CDELT value for this row. */
29013             GetValue2( ret, this, FormatKey( "CDELT", j + 1, -1, s, status ), AST__FLOAT,
29014                        (void *) &cdeltj, 0, method, class, status );
29015 
29016 /* If CDELT is zero, use 1.0E-6 of the corresponding CRVAL value
29017    instead, or 1.0 if CRVAL is zero. Otherwise, the zeros could cause the
29018    matrix to be non-invertable. The Mapping could then not be simplified
29019    or used by a Plot. CDELT values of zero are usually used to indicate
29020    "redundant" axes. For instance, a 2D image may be stored as a 3D cube
29021    with a single plane with the "redundant" 3rd axis used to specify the
29022    wavelength of the filter. The actual value used for CDELT shouldn't
29023    matter since the axis only spans a single pixel anyway. */
29024             if( cdeltj == 0.0 ){
29025                GetValue2( ret, this, FormatKey( "CDELT", j + 1, -1, s, status ), AST__FLOAT,
29026                          (void *) &dval, 1, method, class, status );
29027                cdeltj = 1.0E-6*dval;
29028                if( cdeltj == 0.0 ) cdeltj = 1.0;
29029                SetValue( ret, FormatKey( "CDELT", j + 1, -1, s, status ), (void *) &cdeltj,
29030                          AST__FLOAT, NULL, status );
29031             }
29032          }
29033       }
29034 
29035 /* Following conversions produce PCi_j keywords. Only do them if there
29036    are currently no PCi_j keywords in the header. */
29037       if( s != ' ' ) {
29038          sprintf( template, "PC%%d_%%d%c", s );
29039       } else {
29040          strcpy( template, "PC%d_%d" );
29041       }
29042       gotpcij = astKeyFields( this, template, 0, NULL, NULL );
29043       if( !gotpcij ){
29044 
29045 /* CDjjjiii
29046    -------- */
29047          if( s == ' ' && astKeyFields( this, "CD%3d%3d", 0, NULL, NULL ) ){
29048 
29049 /* Do each row in the matrix. */
29050             for( j = 0; j < naxis; j++ ){
29051 
29052 /* Do each column in the matrix. */
29053                for( i = 0; i < naxis; i++ ){
29054 
29055 /* Get the CDjjjiii matrix element */
29056                   sprintf( keyname, "CD%.3d%.3d", j + 1, i + 1 );
29057                   if( GetValue2( ret, this, keyname, AST__FLOAT, (void *) &dval, 0,
29058                                 method, class, status ) ){
29059 
29060 /* If found, save it with name PCj_i, and ensure the default value of 1.0
29061    is used for CDELT. */
29062                      if( encoding == FITSIRAF_ENCODING ){
29063                         SetValue( ret, FormatKey( "PC", j + 1, i + 1, ' ', status ),
29064                                   (void *) &dval, AST__FLOAT, NULL, status );
29065                         dval = 1.0;
29066                         SetValue( ret, FormatKey( "CDELT", j + 1, -1, s, status ),
29067                                   (void *) &dval, AST__FLOAT, NULL, status );
29068                         gotpcij = 1;
29069                      }
29070                   }
29071                }
29072             }
29073          }
29074 
29075 /* CDj_i
29076    ---- */
29077          if( s != ' ' ) {
29078             sprintf( template, "CD%%d_%%d%c", s );
29079          } else {
29080             strcpy( template, "CD%d_%d" );
29081          }
29082          if( !gotpcij && astKeyFields( this, template, 0, NULL, NULL ) ){
29083 
29084 /* Do each row in the matrix. */
29085             for( j = 0; j < naxis; j++ ){
29086 
29087 /* First find the sum of the squared elements in the row. and note the
29088    sign of the diagonal element. */
29089                rowsum2 = 0.0;
29090                diag = +1;
29091                for( i = 0; i < naxis; i++ ){
29092                   if( GetValue2( ret, this, FormatKey( "CD", j + 1, i + 1, s, status ),
29093                                   AST__FLOAT, (void *) &dval, 0, method, class, status ) ){
29094                      rowsum2 += dval*dval;
29095                      if( i == j ) diag = ( dval >= 0.0 ) ? +1 : -1;
29096                   }
29097                }
29098 
29099 /* The CDELT value for this row will be the length of the row vector. This means that
29100    each row will be a unit vector when converted to PCi_j form, and the CDELT will
29101    give a real indication of the pixel size. Ensure that the diagonal
29102    PCi+j element has a positive sign. */
29103                cdelti = sqrt( rowsum2 )*diag;
29104                SetValue( ret, FormatKey( "CDELT", j + 1, -1, s, status ),
29105                          (void *) &cdelti, AST__FLOAT, NULL, status );
29106 
29107 /* Do each column in the matrix. */
29108                for( i = 0; i < naxis; i++ ){
29109 
29110 /* Get the CDj_i matrix element (note default value for all CD elements
29111    is zero (even diagonal elements!). */
29112                   if( !GetValue2( ret, this, FormatKey( "CD", j + 1, i + 1, s, status ),
29113                                   AST__FLOAT, (void *) &dval, 0, method, class, status ) ){
29114                      dval = 0.0;
29115                   }
29116 
29117 /* Divide by the rows cdelt value and save it with name PCj_i. */
29118                   if( cdelti != 0.0 ) dval /= cdelti;
29119                   SetValue( ret, FormatKey( "PC", j + 1, i + 1, s, status ),
29120                             (void *) &dval, AST__FLOAT, NULL, status );
29121                   gotpcij = 1;
29122                }
29123             }
29124          }
29125 
29126 /* PCjjjiii and CROTAi keywords
29127    ---------------------------- */
29128 
29129 /* Check there are some CDELT keywords... */
29130          if( s != ' ' ) {
29131             sprintf( template, "CDELT%%d%c", s );
29132          } else {
29133             strcpy( template, "CDELT%d" );
29134          }
29135          if( !gotpcij && astKeyFields( this, template, 0, NULL, NULL ) ){
29136 
29137 /* See if there is a CROTA keyword. Try to read values for both axes
29138    since they are sometimes both included. This ensures they will not be
29139    included in the output when the FitsChan is deleted. Read the latitude
29140    axis second in order to give it priority in cases where both are
29141    present. */
29142             crota = AST__BAD;
29143             GetValue2( ret, this, FormatKey( "CROTA", axlon + 1, -1, s, status ),
29144                        AST__FLOAT, (void *) &crota, 0, method, class, status );
29145             GetValue2( ret, this, FormatKey( "CROTA", axlat + 1, -1, s, status ),
29146                        AST__FLOAT, (void *) &crota, 0, method, class, status );
29147 
29148 /* If there are any PCjjjiii keywords, rename them as PCj_i. */
29149             if( s == ' ' && astKeyFields( this, "PC%3d%3d", 0, NULL, NULL ) ){
29150 
29151 /* Do each row in the matrix. */
29152                for( j = 0; j < naxis; j++ ){
29153 
29154 /* Do each column in the matrix. */
29155                   for( i = 0; i < naxis; i++ ){
29156 
29157 /* Get the PCiiijjj matrix element */
29158                      sprintf( keyname, "PC%.3d%.3d", j + 1, i + 1 );
29159                      if( GetValue2( ret, this, keyname, AST__FLOAT, (void *) &dval, 0,
29160                                    method, class, status ) ){
29161                      } else if( i == j ) {
29162                         dval = 1.0;
29163                      } else {
29164                         dval = 0.0;
29165                      }
29166 
29167 /* Store it as PCi_j */
29168                      SetValue( ret, FormatKey( "PC", j + 1, i + 1, ' ', status ),
29169                                (void *) &dval, AST__FLOAT, NULL, status );
29170                      gotpcij = 1;
29171                   }
29172                }
29173 
29174 /* If there is a CROTA value and no PCjjjii keywords, create a PCj_i
29175    matrix from the CROTA values. We need to have latitude and longitude
29176    axes for this.  */
29177             } else if( s == ' ' && axlat != -1 && axlon != -1 && crota != AST__BAD ){
29178 
29179 /* Get the sin and cos of CROTA */
29180                cosrota = cos( crota*AST__DD2R );
29181                sinrota = sin( crota*AST__DD2R );
29182 
29183 /* Get the CDELT values for the longitude and latitude axes. */
29184                if( GetValue2( ret, this, FormatKey( "CDELT", axlat + 1, -1, ' ', status ),
29185                              AST__FLOAT, (void *) &cdeltj, 1, method,
29186                              class, status ) &&
29187                    GetValue2( ret, this, FormatKey( "CDELT", axlon + 1, -1, ' ', status ),
29188                              AST__FLOAT, (void *) &cdelti, 1, method,
29189                              class, status ) ){
29190 
29191 /* Save the ratio, needed below. */
29192                   lambda = cdeltj/cdelti;
29193 
29194 /* Save a corresponding set of PCi_j keywords in the FitsChan. First do
29195    the diagonal terms. */
29196                   for( i = 0; i < naxis; i++ ){
29197                      if( i == axlat ) {
29198                         dval = cosrota;
29199                      } else if( i == axlon ) {
29200                         dval = cosrota;
29201                      } else {
29202                         dval = 1.0;
29203                      }
29204                      SetValue( ret, FormatKey( "PC", i + 1, i + 1, ' ', status ),
29205                                (void *) &dval, AST__FLOAT, NULL, status );
29206                      gotpcij = 1;
29207                   }
29208 
29209 /* Now do the non-zero off-diagonal terms. */
29210                   dval = sinrota/lambda;
29211                   SetValue( ret, FormatKey( "PC", axlat + 1, axlon + 1, ' ', status ),
29212                             (void *) &dval, AST__FLOAT, NULL, status );
29213                   dval = -sinrota*lambda;
29214                   SetValue( ret, FormatKey( "PC", axlon + 1, axlat + 1, ' ', status ),
29215                             (void *) &dval, AST__FLOAT, NULL, status );
29216                }
29217             }
29218          }
29219       }
29220 
29221 /* Conversion of old PROJP, etc, is done once on the "primary" pass. */
29222       if( s == ' ' ) {
29223 
29224 /* PROJP keywords
29225    -------------- */
29226          if( astKeyFields( this, "PROJP%d", 1, ubnd, lbnd ) && axlat != -1 ) {
29227 
29228 /* Some people produce headers with both PROJP and PV. Even worse, the
29229    PROJP and PV values are sometimes inconsistent. In this case we trust
29230    the PV values rather than the PROJP values, but only if the PV values
29231    are not obviously incorrect for some reason. In particularly, we check
29232    that, *if* either PVi_1 or PVi_2 (where i=longitude axis) is present,
29233    then PVi_0 is also present. Conversely we check that if PVi_0 is
29234    present then at least one of PVi_1 or PVi_2 is present. */
29235             use_projp = 1;
29236             if( axlat != -1 &&
29237                 astKeyFields( this, "PV%d_%d", 2, tubnd, tlbnd ) ){
29238                use_projp = 0;
29239 
29240 /* Are there any PV values for the longitude axis? */
29241                if( tlbnd[ 0 ] <= axlon + 1 && axlon + 1 <= tubnd[ 0 ] ) {
29242 
29243 /* Are either PVi_1 or PVi_2 available? */
29244                   if( HasCard( this, FormatKey( "PV", axlon + 1, 1, ' ', status ),
29245                                method, class, status ) ||
29246                       HasCard( this, FormatKey( "PV", axlon + 1, 2, ' ', status ),
29247                                method, class, status ) ) {
29248 
29249 /* If so use PROJP if PVi_0 is not also available. */
29250                      if( !HasCard( this, FormatKey( "PV", axlon + 1, 0, ' ', status ),
29251                                    method, class, status ) ) use_projp = 1;
29252 
29253 /* If neither PVi_1 or PVi_2 are available, use PROJP if PVi_0 is
29254    available. */
29255                   } else if( HasCard( this, FormatKey( "PV", axlon + 1, 0, ' ', status ),
29256                                       method, class, status ) ) {
29257                      use_projp = 1;
29258                   }
29259                }
29260             }
29261 
29262 /* Translate PROJP to PV if required. */
29263             if( use_projp ) {
29264                for( i = lbnd[ 0 ]; i <= ubnd[ 0 ]; i++ ){
29265                   if( GetValue2( ret, this, FormatKey( "PROJP", i, -1, ' ', status ),
29266                                 AST__FLOAT, (void *) &dval, 0, method, class, status ) &&
29267                       ( encoding == FITSPC_ENCODING ||
29268                         encoding == FITSIRAF_ENCODING ) ){
29269                      SetValue( ret, FormatKey( "PV", axlat + 1, i, ' ', status ),
29270                                (void *) &dval, AST__FLOAT, CardComm( this, status ), status );
29271                   }
29272                }
29273             }
29274          }
29275 
29276 /* CmVALi keywords
29277    --------------- */
29278          if( astKeyFields( this, "C%1dVAL%d", 2, ubnd, lbnd ) ){
29279             ss = 'A';
29280             for( m = lbnd[ 0 ]; m <= ubnd[ 0 ]; m++ ){
29281                for( i = lbnd[ 1 ]; i <= ubnd[ 1 ]; i++ ){
29282                   sprintf( keyname, "C%dVAL%d", m, i );
29283                   if( GetValue2( ret, this, keyname, AST__FLOAT, (void *) &dval, 0,
29284                                 method, class, status ) &&
29285                       ( encoding == FITSPC_ENCODING ||
29286                         encoding == FITSIRAF_ENCODING ) ){
29287                      sprintf( keyname, "CRVAL%d%c", i, ss );
29288                      SetValue( ret, keyname, (void *) &dval, AST__FLOAT,
29289                                CardComm( this, status ), status );
29290                   }
29291                }
29292                ss++;
29293             }
29294          }
29295 
29296 /* CmPIXi keywords
29297    --------------- */
29298          if( astKeyFields( this, "C%1dPIX%d", 2, ubnd, lbnd ) ){
29299             ss = 'A';
29300             for( m = lbnd[ 0 ]; m <= ubnd[ 0 ]; m++ ){
29301                for( i = lbnd[ 1 ]; i <= ubnd[ 1 ]; i++ ){
29302                   sprintf( keyname, "C%dPIX%d", m, i );
29303                   if( GetValue2( ret, this, keyname, AST__FLOAT, (void *) &dval, 0,
29304                                 method, class, status ) &&
29305                       ( encoding == FITSPC_ENCODING ||
29306                         encoding == FITSIRAF_ENCODING ) ){
29307                      sprintf( keyname, "CRPIX%d%c", i, ss );
29308                      SetValue( ret, keyname, (void *) &dval, AST__FLOAT,
29309                                CardComm( this, status ), status );
29310                   }
29311                }
29312                ss++;
29313             }
29314          }
29315 
29316 /* CmYPEi keywords
29317    --------------- */
29318          if( astKeyFields( this, "C%1dYPE%d", 2, ubnd, lbnd ) ){
29319             ss = 'A';
29320             for( m = lbnd[ 0 ]; m <= ubnd[ 0 ]; m++ ){
29321                for( i = lbnd[ 1 ]; i <= ubnd[ 1 ]; i++ ){
29322                   sprintf( keyname, "C%dYPE%d", m, i );
29323                   if( GetValue2( ret, this, keyname, AST__STRING, (void *) &cval, 0,
29324                                 method, class, status ) &&
29325                       ( encoding == FITSPC_ENCODING ||
29326                         encoding == FITSIRAF_ENCODING ) ){
29327                      sprintf( keyname, "CTYPE%d%c", i, ss );
29328                      SetValue( ret, keyname, (void *) &cval, AST__STRING,
29329                                CardComm( this, status ), status );
29330                   }
29331                }
29332                ss++;
29333             }
29334          }
29335 
29336 /* CmNITi keywords
29337    --------------- */
29338          if( astKeyFields( this, "C%1dNIT%d", 2, ubnd, lbnd ) ){
29339             ss = 'A';
29340             for( m = lbnd[ 0 ]; m <= ubnd[ 0 ]; m++ ){
29341                for( i = lbnd[ 1 ]; i <= ubnd[ 1 ]; i++ ){
29342                   sprintf( keyname, "C%dNIT%d", m, i );
29343                   if( GetValue2( ret, this, keyname, AST__STRING, (void *) &cval, 0,
29344                                 method, class, status ) &&
29345                       ( encoding == FITSPC_ENCODING ||
29346                         encoding == FITSIRAF_ENCODING ) ){
29347                      sprintf( keyname, "CUNIT%d%c", i, ss );
29348                      SetValue( ret, keyname, (void *) &cval, AST__STRING,
29349                                CardComm( this, status ), status );
29350                   }
29351                }
29352                ss++;
29353             }
29354          }
29355 
29356 /* CmELTi keywords
29357    --------------- */
29358          if( astKeyFields( this, "C%1dELT%d", 2, ubnd, lbnd ) ){
29359             ss = 'A';
29360             for( m = lbnd[ 0 ]; m <= ubnd[ 0 ]; m++ ){
29361 
29362 /* Create a PCj_is matrix by copying the PCjjjiii values and rename CmELTi as
29363    CDELTis. */
29364 
29365 /* Do each row in the matrix. */
29366                for( j = 0; j < naxis; j++ ){
29367 
29368 /* Get the CDELT value for this row. Report an error if not present. */
29369                   sprintf( keyname, "C%dELT%d", m, j + 1 );
29370                   GetValue2( ret, this, keyname, AST__FLOAT, (void *) &cdeltj, 1,
29371                              method, class, status );
29372 
29373 /* If CDELT is zero, use one hundredth of the corresponding CRVAL value
29374    instead, or 1.0 if CRVAL is zero. Otherwise, the zeros could cause the
29375    matrix to be non-invertable. The Mapping could then not be simplified
29376    or used by a Plot. CDELT values of zero are usually used to indicate
29377    "redundant" axes. For instance, a 2D image may be stored as a 3D cube
29378    with a single plane with the "redundant" 3rd axis used to specify the
29379    wavelength of the filter. The actual value used for CDELT shouldn't
29380    matter since the axis only spans a single pixel anyway. */
29381                   if( cdeltj == 0.0 ){
29382                      GetValue2( ret, this, FormatKey( "CRVAL", j + 1, -1, ss, status ), AST__FLOAT,
29383                                (void *) &dval, 1, method, class, status );
29384                      cdeltj = 0.01*dval;
29385                      if( cdeltj == 0.0 ) cdeltj = 1.0;
29386                   }
29387 
29388 /* Save it as CDELTis */
29389                   sprintf( keyname, "CDELT%d%c", j + 1, ss );
29390                   SetValue( ret, keyname, (void *) &cdeltj, AST__FLOAT,
29391                             CardComm( this, status ), status );
29392 
29393 /* Do each column in the matrix. */
29394                   for( i = 0; i < naxis; i++ ){
29395 
29396 /* Get the PCiiijjj matrix element */
29397                      sprintf( keyname, "PC%.3d%.3d", j + 1, i + 1 );
29398                      if( GetValue2( ret, this, keyname, AST__FLOAT, (void *) &dval, 0,
29399                                    method, class, status ) ){
29400                      } else if( i == j ) {
29401                         dval = 1.0;
29402                      } else {
29403                         dval = 0.0;
29404                      }
29405 
29406 /* Store it as PCi_js. */
29407                      SetValue( ret, FormatKey( "PC", j + 1, i + 1, ss, status ),
29408                                (void *) &dval, AST__FLOAT, NULL, status );
29409                   }
29410                }
29411                ss++;
29412             }
29413          }
29414 
29415 /* EPOCH keywords
29416    ------------ */
29417 
29418 /* Get any EPOCH card, marking it as read. */
29419          if( GetValue2( ret, this, "EPOCH", AST__FLOAT, (void *) &dval, 0, method,
29420                           class, status ) ){
29421 
29422 /* Convert values of zero to B1950. */
29423             if( dval == 0.0 ) dval = 1950.0;
29424 
29425 /* Save a new EQUINOX card in the FitsChan, so long as there is not
29426    already one there. */
29427             if( !GetValue2( ret, this, "EQUINOX", AST__STRING, (void *) &cval, 0,
29428                             method, class, status ) ){
29429                SetValue( ret, "EQUINOX", (void *) &dval, AST__FLOAT,
29430                          "Reference equinox", status );
29431             }
29432          }
29433 
29434 /* String EQUINOX values
29435    ---------------------
29436    If found, EQUINOX will be used in favour of any EPOCH value found
29437    above. */
29438          if( GetValue2( ret, this, "EQUINOX", AST__STRING, (void *) &cval, 0, method,
29439                         class, status ) ){
29440 
29441 /* Note the first character. */
29442             bj = cval[ 0 ];
29443 
29444 /* If it is "B" or "J", read a floating value from the rest */
29445             if( bj == 'B' || bj == 'J' ) {
29446                if( 1 == astSscanf( cval + 1, " %lf ", &dval ) ){
29447 
29448 /* If it is a Besselian epoch, convert to Julian. */
29449                   if( bj == 'B' ) dval = palEpj( palEpb2d( dval ) );
29450 
29451 /* Replace the original EQUINOX card. */
29452                   SetValue( ret, "EQUINOX", (void *) &dval, AST__FLOAT,
29453                             CardComm( this, status ), status );
29454                }
29455             }
29456          }
29457 
29458 /* EQUINOX = 0.0 keywords
29459    ---------------------- */
29460          if( GetValue2( ret, this, "EQUINOX", AST__FLOAT, (void *) &dval, 0, method,
29461                        class, status ) ){
29462             if( dval == 0.0 ){
29463                dval = 1950.0;
29464                SetValue( ret, "EQUINOX", (void *) &dval, AST__FLOAT,
29465                          CardComm( this, status ), status );
29466             }
29467          }
29468       }
29469 
29470 /* DATE-OBS keywords
29471    ---------------- */
29472 
29473 /* Read any DATE-OBS card. This prevents it being written out when the
29474    FitsChan is deleted.  */
29475       if( s == ' ' ) {
29476          strcpy( keyname, "DATE-OBS" );
29477          if( GetValue2( ret, this, keyname, AST__STRING, (void *) &cval, 0, method,
29478                        class, status ) ){
29479 
29480 /* Ignore DATE-OBS values if the header contains an MJD-OBS value */
29481             strcpy( keyname, "MJD-OBS" );
29482             if( !GetValue2( ret, this, keyname, AST__FLOAT, (void *) &dval, 0,
29483                            method, class, status ) ){
29484 
29485 /* Get the corresponding mjd-obs value, checking that DATE-OBS is valid. */
29486                dval = DateObs( cval, status );
29487                if( dval != AST__BAD ){
29488                   SetValue( ret, keyname, (void *) &dval, AST__FLOAT,
29489                             "Date of observation", status );
29490                }
29491             }
29492          }
29493       }
29494 
29495 /* Things specific to the CLASS encoding
29496    ------------------------------------- */
29497       if( encoding == FITSCLASS_ENCODING ) ClassTrans( this, ret, axlat,
29498                                                        axlon, method, class, status );
29499 
29500 /* Convert SAO distorted TAN headers to TPN distorted TAN headers.
29501    -------------------------------------------------------------- */
29502       if( s == ' ' && !Ustrcmp( prj, "-TAN", status ) ){
29503 
29504 /* Translate the COi_m keywords into PV i+m keywords. */
29505          if( SAOTrans( this, ret, method, class, status ) ) {
29506 
29507 /* Change the CTYPE projection form TAN to TPV. */
29508             strcpy( prj, "-TPN" );
29509             strcpy( lontype + 4, "-TPN" );
29510             cval = lontype;
29511             SetValue( ret, FormatKey( "CTYPE", axlon + 1, -1, s, status ),
29512                          (void *) &cval, AST__STRING, NULL, status );
29513             strcpy( lattype + 4, "-TPN" );
29514             cval = lattype;
29515             SetValue( ret, FormatKey( "CTYPE", axlat + 1, -1, s, status ),
29516                       (void *) &cval, AST__STRING, NULL, status );
29517          }
29518       }
29519 
29520 /* AIPS "NCP" projections
29521    --------------------- */
29522 
29523 /* Compare the projection type with "-NCP" */
29524       if( !Ustrcmp( prj, "-NCP", status ) ) {
29525 
29526 /* Get the latitude reference value, and take is cot. */
29527          GetValue2( ret, this, FormatKey( "CRVAL", axlat + 1, -1, s, status ),
29528                    AST__FLOAT, (void *) &dval, 1, method, class, status );
29529          sinval = sin( dval*AST__DD2R );
29530          if( sinval != 0.0 ) {
29531             dval = cos( dval*AST__DD2R )/sinval;
29532 
29533 /* Replace NCP with SIN in the CTYPE values. */
29534             strcpy( lontype + 4, "-SIN" );
29535             cval = lontype;
29536             SetValue( ret, FormatKey( "CTYPE", axlon + 1, -1, s, status ),
29537                       (void *) &cval, AST__STRING, NULL, status );
29538             strcpy( lattype + 4, "-SIN" );
29539             cval = lattype;
29540             SetValue( ret, FormatKey( "CTYPE", axlat + 1, -1, s, status ),
29541                       (void *) &cval, AST__STRING, NULL, status );
29542 
29543 /* Store the new projection parameters using names suitable to FITS_WCS
29544    encoding. */
29545             SetValue( ret, FormatKey( "PV", axlat + 1, 2, s, status ),
29546                       (void *) &dval, AST__FLOAT, NULL, status );
29547             dval = 0.0;
29548             SetValue( ret, FormatKey( "PV", axlat + 1, 1, s, status ),
29549                       (void *) &dval, AST__FLOAT, NULL, status );
29550          }
29551       }
29552 
29553 /* CLASS "ATF" projections
29554    ---------------------- */
29555 
29556 /* Replace ATF with AIT in the CTYPE values. */
29557       if( !Ustrcmp( prj, "-ATF", status ) ) {
29558          strcpy( lontype + 4, "-AIT" );
29559          cval = lontype;
29560          SetValue( ret, FormatKey( "CTYPE", axlon + 1, -1, s, status ),
29561                    (void *) &cval, AST__STRING, NULL, status );
29562          strcpy( lattype + 4, "-AIT" );
29563          cval = lattype;
29564          SetValue( ret, FormatKey( "CTYPE", axlat + 1, -1, s, status ),
29565                    (void *) &cval, AST__STRING, NULL, status );
29566       }
29567 
29568 /* AIPS "GLS" projections
29569    --------------------- */
29570 
29571 /* Compare the projection type with "-GLS" */
29572       if( !Ustrcmp( prj, "-GLS", status ) ) {
29573 
29574 /* Convert to "-SFL" */
29575          strcpy( lontype + 4, "-SFL" );
29576          cval = lontype;
29577          SetValue( ret, FormatKey( "CTYPE", axlon + 1, -1, s, status ),
29578                    (void *) &cval, AST__STRING, NULL, status );
29579          strcpy( lattype + 4, "-SFL" );
29580          cval = lattype;
29581          SetValue( ret, FormatKey( "CTYPE", axlat + 1, -1, s, status ),
29582                    (void *) &cval, AST__STRING, NULL, status );
29583 
29584 /* FITS-WCS paper 2 (sec. 6.1.4) describes how to handle AIPS GLS
29585    projections, but requires that the axes are not rotated. Instead, we
29586    modify the native latitude at the fiducial point, theta_0, as is done
29587    in wcslib function celfix in file wcsfix.c (see also FITS-WCS paper
29588    II sec. 2.5). We only need to change theta_0 if the CRVAL position is
29589    not the celestial origin. */
29590          shifted = 0;
29591          sprintf( keyname, "CRVAL%d", axlon + 1 );
29592          if( GetValue2( ret, this, keyname, AST__FLOAT, (void *) &dval, 0,
29593                         method, class, status ) ){
29594             if( dval != 0.0 ) shifted = 1;
29595          }
29596          sprintf( keyname, "CRVAL%d", axlat + 1 );
29597          if( GetValue2( ret, this, keyname, AST__FLOAT, (void *) &dval, 0,
29598                         method, class, status ) ){
29599             if( dval != 0.0 ) shifted = 1;
29600          }
29601 
29602          if( 0 && shifted ) {
29603             SetValue( ret, FormatKey( "PV", axlon + 1, 2, s, status ),
29604                       (void *) &dval, AST__FLOAT, NULL, status );
29605             dval = 0.0;
29606             SetValue( ret, FormatKey( "PV", axlon + 1, 1, s, status ),
29607                       (void *) &dval, AST__FLOAT, NULL, status );
29608             dval = 1.0;
29609             SetValue( ret, FormatKey( "PV", axlon + 1, 0, s, status ),
29610                       (void *) &dval, AST__FLOAT, NULL, status );
29611          }
29612       }
29613 
29614 /* Rename any "QV" projection parameters to "PV" (such as used by
29615    -TAB to indicate the interpolation method, or by the internal
29616    -TPN projection to indicate distortion coefficients).
29617    ------------------------------------------------------------ */
29618 
29619 /* Rewind the FitsChan. */
29620       astClearCard( this );
29621 
29622 /* Search the FitsChan for QV cards. */
29623       if( s != ' ' ) {
29624          sprintf( template, "QV%%d_%%d%c", s );
29625       } else {
29626          strcpy( template, "QV%d_%d" );
29627       }
29628       while( FindKeyCard( this, template, method, class, status ) && astOK ) {
29629 
29630 /* If the projection name is "TAN", replace TAN with TPN in the CTYPE values. */
29631          if( !Ustrcmp( prj, "-TAN", status ) ){
29632             strcpy( prj, "-TPN" );
29633             strcpy( lontype + 4, "-TPN" );
29634             cval = lontype;
29635             SetValue( ret, FormatKey( "CTYPE", axlon + 1, -1, s, status ),
29636                       (void *) &cval, AST__STRING, NULL, status );
29637             strcpy( lattype + 4, "-TPN" );
29638             cval = lattype;
29639             SetValue( ret, FormatKey( "CTYPE", axlat + 1, -1, s, status ),
29640                       (void *) &cval, AST__STRING, NULL, status );
29641          }
29642 
29643 /* Indicate that the QV card has been consumed. */
29644          MarkCard( this, status );
29645 
29646 /* Get the keyword name and change it from QV to PV. */
29647          strcpy( keyname, CardName( this, status ) );
29648          keyname[ 0 ] ='P';
29649 
29650 /* Store the new PV card so long as there it is not already present in the
29651    FitsChan. */
29652          if( !GetValue2( ret, this, keyname, AST__FLOAT, (void *) &cval, 0,
29653                         method, class, status ) ){
29654             SetValue( ret, keyname, CardData( this, &size, status ), AST__FLOAT,
29655                       CardComm( this, status ), status );
29656          }
29657 
29658 /* Move on to the next card. */
29659          MoveCard( this, 1, method, class, status );
29660       }
29661 
29662 
29663 
29664 /* Change any TAN projection to TPN projection if the PolyTan attribute
29665    is non-zero. Also change any TPV projection to TPN projection.
29666    --------------------------------------------------- */
29667       if( ( !Ustrcmp( prj, "-TAN", status ) &&
29668             GetUsedPolyTan( this, ret, axlat + 1, axlon + 1, s, method, class, status ) ) ||
29669           !Ustrcmp( prj, "-TPV", status ) ){
29670          strcpy( prj, "-TPN" );
29671          strcpy( lontype + 4, "-TPN" );
29672          cval = lontype;
29673          SetValue( ret, FormatKey( "CTYPE", axlon + 1, -1, s, status ),
29674                    (void *) &cval, AST__STRING, NULL, status );
29675          strcpy( lattype + 4, "-TPN" );
29676          cval = lattype;
29677          SetValue( ret, FormatKey( "CTYPE", axlat + 1, -1, s, status ),
29678                    (void *) &cval, AST__STRING, NULL, status );
29679       }
29680 
29681 
29682 
29683 /* IRAF "ZPX" projections
29684    --------------------- */
29685       if( s == ' ' && !Ustrcmp( prj, "-ZPX", status ) ) {
29686 
29687 /* Replace "ZPX" with "ZPN-ZPX" (i.e. ZPN projection with ZPX distortion
29688    code) in the CTYPE values. */
29689          strcpy( lontype + 4, "-ZPN-ZPX" );
29690          cval = lontype;
29691          SetValue( ret, FormatKey( "CTYPE", axlon + 1, -1, ' ', status ),
29692                    (void *) &cval, AST__STRING, NULL, status );
29693          strcpy( lattype + 4, "-ZPN-ZPX" );
29694          cval = lattype;
29695          SetValue( ret, FormatKey( "CTYPE", axlat + 1, -1, ' ', status ),
29696                    (void *) &cval, AST__STRING, NULL, status );
29697 
29698 /* Check latitude then longitude axes */
29699          for( i = 0; i < 2; i++ ){
29700             iaxis = i ? axlat : axlon;
29701 
29702 /* Concatenate all the IRAF "WAT" keywords together for this axis. These
29703    keywords are marked as having been used, so that they are not written
29704    out when the FitsChan is deleted. */
29705             watmem = ConcatWAT( this, iaxis, method, class, status );
29706 
29707 /* Search the total WAT string for any projp terms. */
29708             if( watmem ){
29709                for( iproj = 0; iproj < 10 && astOK; iproj++ ) {
29710                   sprintf( format, "projp%d=", iproj );
29711                   start = strstr( watmem, format );
29712                   if( start ) {
29713                      sprintf( format, "projp%d=%%lf", iproj );
29714                      if( astSscanf( start, format, &projp ) ){
29715                         SetValue( ret, FormatKey( "PV", axlat + 1, iproj, ' ', status ),
29716                                   (void *) &projp, AST__FLOAT,
29717                                   "ZPN projection parameter", status );
29718                      }
29719                   }
29720                }
29721 
29722 /*  Release the memory used to hold the concatenated WAT keywords. */
29723                watmem = (char *) astFree( (void *) watmem );
29724             }
29725          }
29726 
29727 /* IRAF "TNX" projections
29728    --------------------- */
29729       } else if( s == ' ' && !Ustrcmp( prj, "-TNX", status ) ) {
29730 
29731 /* Replace TNX with TPN in the CTYPE values. */
29732          strcpy( lontype + 4, "-TPN" );
29733          cval = lontype;
29734          SetValue( ret, FormatKey( "CTYPE", axlon + 1, -1, ' ', status ),
29735                    (void *) &cval, AST__STRING, NULL, status );
29736          strcpy( lattype + 4, "-TPN" );
29737          cval = lattype;
29738          SetValue( ret, FormatKey( "CTYPE", axlat + 1, -1, ' ', status ),
29739                    (void *) &cval, AST__STRING, NULL, status );
29740 
29741 /* Check latitude then longitude axes */
29742          for( i = 0; i < 2; i++ ){
29743             iaxis = i ? axlat : axlon;
29744 
29745 /* Concatenate all the IRAF "WAT" keywords together for this axis. These
29746    keywords are marked as having been used, so that they are not written
29747    out when the FitsChan is deleted. */
29748             watmem = ConcatWAT( this, iaxis, method, class, status );
29749 
29750 /* Extract the polynomial coefficients from the concatenated WAT string.
29751    These are returned in the form of a list of PVi_m values for a TPN
29752    projection. */
29753             ncoeff = WATCoeffs( watmem, i, &cvals, &mvals, &ok, status );
29754 
29755 /* If we can handle the TNX projection, store the PV values in the FitsChan. */
29756             if( ok ) {
29757                for( icoeff = 0; icoeff < ncoeff; icoeff++ ) {
29758                   SetValue( ret, FormatKey( "PV", iaxis + 1, mvals[ icoeff ],
29759                                             ' ', status ),
29760                            (void *) (cvals + icoeff), AST__FLOAT,
29761                            "TAN projection parameter", status );
29762                }
29763 
29764 /* If the TNX cannot be represented in FITS-WCS (within our restrictions), add
29765    warning keywords to the FitsChan. */
29766             } else {
29767                Warn( this, "tnx", "This FITS header includes, or was "
29768                      "derived from, a TNX projection which requires "
29769                      "unsupported IRAF-specific corrections. The WCS "
29770                      "information may therefore be incorrect.", method, class, status );
29771             }
29772 
29773 /*  Release the memory used to hold the concatenated WAT keywords. */
29774             watmem = (char *) astFree( (void *) watmem );
29775          }
29776       }
29777 
29778 /* MSX CAR projections.
29779    ------------------- */
29780       if( !Ustrcmp( prj, "-CAR", status ) ) {
29781 
29782 /* If the projection is a CAR projection, check that the CRPIX value for
29783    the longitude axis corresponds to a projection plane point which has
29784    valid native longitude. The CAR projection has valid projection plane
29785    points only for native longitudes in the range [-180,+180, so we
29786    modify the CRPIX value if necessary by the number of pixels corresponding
29787    to 360 degres of longitude in order to bring the refernce pixel into
29788    the valid domain of the projection. */
29789          if( GetValue2( ret, this, FormatKey( "CDELT", axlon + 1, -1, s, status ),
29790                         AST__FLOAT, (void *) &cdelti, 1, method, class, status ) &&
29791              GetValue2( ret, this, FormatKey( "CRPIX", axlon + 1, -1, s, status ),
29792                         AST__FLOAT, (void *) &dval, 0, method, class, status ) ) {
29793             if( cdelti != 0.0 ) {
29794                dval = 0.5 + AST__DR2D*palDrange( AST__DD2R*( dval - 0.5 )*cdelti )/cdelti;
29795                SetValue( ret, FormatKey( "CRPIX", axlon + 1, -1, s, status ),
29796                          (void *) &dval, AST__FLOAT, CardComm( this, status ), status );
29797             }
29798          }
29799       }
29800 
29801 /* Replace RESTFREQ by RESTFRQ.
29802    ---------------------------- */
29803 
29804 /* Get any RESTFREQ card, marking it as read. */
29805       if( s != ' ' ) {
29806          sprintf( keyname, "RESTFREQ%c", s );
29807       } else {
29808          strcpy( keyname, "RESTFREQ" );
29809       }
29810       if( GetValue2( ret, this, keyname, AST__FLOAT, (void *) &dval, 0, method,
29811                      class, status ) ){
29812 
29813 /* Look for "MHz" and "GHz" within the comment. If found scale the value
29814    into Hz. */
29815          comm = CardComm( this, status );
29816          if( comm ) {
29817             if( strstr( comm, "GHz" ) ) {
29818                dval *= 1.0E9;
29819                comm = "[Hz] Rest Frequency";
29820             } else if( strstr( comm, "MHz" ) ) {
29821                dval *= 1.0E6;
29822                comm = "[Hz] Rest Frequency";
29823             }
29824          }
29825 
29826 /* Save a new RESTFRQ card in the FitsChan, so long as there is not
29827    already one there. */
29828          if( s != ' ' ) {
29829             sprintf( keyname, "RESTFRQ%c", s );
29830          } else {
29831             strcpy( keyname, "RESTFRQ" );
29832          }
29833          if( !GetValue2( ret, this, keyname, AST__STRING, (void *) &cval, 0,
29834                          method, class, status ) ){
29835             SetValue( ret, keyname, (void *) &dval, AST__FLOAT, comm, status );
29836          }
29837       }
29838 
29839 /* Translate AIPS spectral CTYPE values to FITS-WCS paper III equivalents.
29840    These are of the form AAAA-BBB, where "AAAA" can be "FREQ", "VELO" (=VRAD!)
29841    or "FELO" (=VOPT-F2W), and BBB can be "LSR", "LSD", "HEL" (=*Bary*centric!)
29842    or "GEO". Also convert "LAMBDA" to "WAVE". */
29843       for( j = 0; j < naxis; j++ ) {
29844          if( GetValue2( ret, this, FormatKey( "CTYPE", j + 1, -1, s, status ),
29845                        AST__STRING, (void *) &cval, 0, method,
29846                        class, status ) ){
29847             if( IsAIPSSpectral( cval, &astype, &assys, status ) ) {
29848                SetValue( ret, FormatKey( "CTYPE", j + 1, -1, s, status ),
29849                          (void *) &astype, AST__STRING, NULL, status );
29850                SetValue( ret, "SPECSYS", (void *) &assys, AST__STRING, NULL, status );
29851                break;
29852             } else if( !strcmp( cval, "LAMBDA  " ) ) {
29853                cval = "WAVE";
29854                SetValue( ret, FormatKey( "CTYPE", j + 1, -1, s, status ),
29855                          (void *) &cval, AST__STRING, NULL, status );
29856                break;
29857             }
29858          }
29859       }
29860 
29861 /* Common case insensitive CUNIT values: "Hz", "Angstrom", "km/s", "M/S" */
29862       if( s != ' ' ) {
29863          sprintf( template, "CUNIT%%d%c", s );
29864       } else {
29865          strcpy( template, "CUNIT%d" );
29866       }
29867       if( astKeyFields( this, template, 1, &jhi, &jlo ) ){
29868 
29869 /* Convert keyword indices from 1-based to 0-base, and loop round them all. */
29870          jhi--;
29871          jlo--;
29872          for( j = jlo; j <= jhi; j++ ){
29873             char *keynam;
29874             keynam =  FormatKey( "CUNIT", j + 1, -1, s, status );
29875             if( GetValue2( ret, this, keynam, AST__STRING, (void *) &cval, 0,
29876                            method, class, status ) ){
29877                size_t nc = astChrLen( cval );
29878                if( nc == 0 ) {
29879                   cval = NULL;
29880                } else if( !Ustrcmp( cval, "Hz", status ) ) {
29881                   cval = "Hz";
29882                } else if( !Ustrcmp( cval, "Angstrom", status ) ) {
29883                   cval = "Angstrom";
29884                } else if( !Ustrcmp( cval, "km/s", status ) ) {
29885                   cval = "km/s";
29886                } else if( !Ustrcmp( cval, "m/s", status ) ) {
29887                   cval = "m/s";
29888                } else {
29889                   cval = NULL;
29890                }
29891                if( cval ) SetValue( ret, keynam, (void *) &cval, AST__STRING, NULL, status );
29892             }
29893          }
29894       }
29895 
29896 /* After doing the primary axis descriptions, prepare to do the "A"
29897    description. */
29898       if( s == ' ' ) s = 'A' - 1;
29899    }
29900 
29901 /* IRAF mini-WCS keywords
29902    ---------------------- */
29903 
29904 /* Rewind the FitsChan to search from the first card. */
29905    astClearCard( this );
29906 
29907 /* Search forward through until all cards have been checked. */
29908    while( !astFitsEof( this ) && astOK ){
29909 
29910 /* Check to see if the keyword name from the current card matches
29911    any of the known mini-WCS keywords. If so, mark the card as read. */
29912       if( Match( CardName( this, status ), "WAT%d_%d", 0, NULL, &m, method, class, status ) ||
29913           Match( CardName( this, status ), "LTM%d_%d", 0, NULL, &m, method, class, status ) ||
29914           Match( CardName( this, status ), "LTV%d", 0, NULL, &m, method, class, status ) ||
29915           Match( CardName( this, status ), "WSV%d_LEN", 0, NULL, &m, method, class, status ) ||
29916           Match( CardName( this, status ), "WSV%d_%d", 0, NULL, &m, method, class, status ) ){
29917           MarkCard( this, status );
29918       }
29919 
29920 /* Now move the current card on to the next card. */
29921       MoveCard( this, 1, method, class, status );
29922    }
29923 
29924 /* Delete the returned FitsChan if it is empty. */
29925    if( ret && !astGetNcard( ret ) ) ret = (AstFitsChan *) astDelete( ret );
29926 
29927 /* Return. */
29928    return ret;
29929 }
29930 
Split(const char * card,char ** name,char ** value,char ** comment,const char * method,const char * class,int * status)29931 int Split( const char *card, char **name, char **value,
29932            char **comment, const char *method, const char *class, int *status ){
29933 /*
29934 *  Name:
29935 *     Split
29936 
29937 *  Purpose:
29938 *     Extract the keyword name, value and comment from a FITS header card.
29939 
29940 *  Type:
29941 *     Private function.
29942 
29943 *  Synopsis:
29944 *     #include "fitschan.h"
29945 *     int Split( const char *card, char **name, char **value,
29946 *                char **comment, const char *method, const char *class, int *status  )
29947 
29948 *  Class Membership:
29949 *     FitsChan member function.
29950 
29951 *  Description:
29952 *     The name, value and comment (if present) are extracted from the
29953 *     supplied card text and returned.
29954 
29955 *  Parameters:
29956 *     card
29957 *        Pointer to a string holding the FITS header card.
29958 *     name
29959 *        Pointer to a location at which to return the pointer to a string
29960 *        holding the keyword name.
29961 *     value
29962 *        Pointer to a location at which to return the pointer to a string
29963 *        holding the keyword value.
29964 *     comment
29965 *        Pointer to a location at which to return the pointer to a string
29966 *        holding the keyword comment.
29967 *     method
29968 *        Pointer to a string holding the name of the calling method.
29969 *        This is only for use in constructing error messages.
29970 *     class
29971 *        Pointer to a string holding the name of the supplied object class.
29972 *        This is only for use in constructing error messages.
29973 *     status
29974 *        Pointer to the inherited status variable.
29975 
29976 *  Returned value:
29977 *     -  An integer identifying the data type of the keyword value. This
29978 *     will be one of the values AST__UNDEF, AST__COMMENT, AST__INT,
29979 *     AST__STRING, AST__CONTINUE, AST__FLOAT, AST__COMPLEXI or AST__COMPLEXF
29980 *     defined in fitschan.h.
29981 
29982 *  Notes:
29983 *     -  If the keyword value is a string, then the returned value does not
29984 *     include the delimiting quotes, and pairs of adjacent quotes within the
29985 *     string are replaced by single quotes.
29986 *     -  A maximum of 80 characters are read from the supplied card, so the
29987 *     string does not need to be null terminated unless less than 80
29988 *     characters are to be read.
29989 *     -  The memory holding the three strings "name", "value" and "comment"
29990 *     should be released when no longer needed using astFree.
29991 *     -  NULL pointers and a data type of AST__COMMENT are returned if an
29992 *     error has already occurred, or if this function fails for any reason.
29993 */
29994 
29995 /* Local Variables: */
29996    char *c;                   /* Pointer to returned comment string */
29997    char *dd;                  /* Pointer to intermediate character */
29998    char *slash;               /* Pointer to comment character */
29999    char *v;                   /* Pointer to returned value string */
30000    const char *d;             /* Pointer to first comment character */
30001    const char *v0;            /* Pointer to first non-blank value character */
30002    double fi, fr;             /* Values read from value string */
30003    int blank_name;            /* Is keyword name blank? */
30004    int cont;                  /* Is this a continuation card? */
30005    int i;                     /* Character index */
30006    int ii, ir;                /* Values read from value string */
30007    int iopt;                  /* Index of option within list */
30008    int lq;                    /* Was previous character an escaping quote? */
30009    int len;                   /* Used length of value string */
30010    int nch;                   /* No. of characters used */
30011    int ndig;                  /* No. of digits in the formatted integer */
30012    int type;                  /* Keyword data type */
30013    size_t nc;                 /* Number of character in the supplied card */
30014    size_t ncc;                /* No. of characters in the comment string */
30015    size_t ncv;                /* No. of characters in the value string */
30016 
30017 /* Initialise the returned pointers. */
30018    *name = NULL;
30019    *value = NULL;
30020    *comment = NULL;
30021    type = AST__COMMENT;
30022 
30023 /* Check the global status. */
30024    if( !astOK ) return type;
30025 
30026 /* Store the number of characters to be read from the supplied card. This
30027    is not allowed to be more than the length of a FITS header card. */
30028    nc = 0;
30029    while( nc < AST__FITSCHAN_FITSCARDLEN && card[ nc ] ) nc++;
30030 
30031 /* Reduce the number of characters to read so that any non-printing
30032    characters such as new-lines at the end of the string are ignored. */
30033    while( nc > 0 && !isprint( card[ nc - 1 ] ) ) nc--;
30034 
30035 /* Allocate memory for a copy of the keyword name plus a terminating
30036    null character. */
30037    *name = (char *) astMalloc( ( 1 + FITSNAMLEN )*sizeof(char) );
30038 
30039 /* Check the pointer can be used. */
30040    if( astOK ){
30041 
30042 /* Initialise the name string by filling it with spaces, and terminating it. */
30043       for( i = 0; i < FITSNAMLEN; i++ ) (*name)[ i ] = ' ';
30044       (*name)[ FITSNAMLEN ] = 0;
30045 
30046 /* Copy the the keyword name, ensuring that no more than FITSNAMLEN (8)
30047    characters are copied. */
30048       strncpy( *name, card, ( nc > FITSNAMLEN ) ? FITSNAMLEN : nc );
30049 
30050 /* If there is no keyword name, flag that we have a blank name which will
30051    be treated as a comment card. */
30052       if( strspn( *name, " " ) == strlen( *name ) ){
30053          blank_name = 1;
30054 
30055 /* If the card contains a keyword name, replace any white space with
30056    nulls. */
30057       } else {
30058          blank_name = 0;
30059          dd = *name + strlen( *name ) - 1;
30060          while( isspace( *dd ) ) *(dd--) = 0;
30061       }
30062 
30063 /* Check the keyword name is legal. */
30064       CheckFitsName( *name, method, class, status );
30065 
30066 /* Allocate memory to hold the keyword value and comment strings. */
30067       *value = (char *) astMalloc( sizeof(char)*( 2 + nc ) );
30068       *comment = (char *) astMalloc( sizeof(char)*( 1 + nc ) );
30069 
30070 /* Check the pointers can be used. */
30071       if( astOK ){
30072 
30073 /* Check for CONTINUE cards. These have keyword CONTINUE but have a space
30074    instead of an equals sign in column 9. They must also have a single quote
30075    in column 11. */
30076          cont = ( !Ustrcmp( *name, "CONTINUE", status ) &&
30077                   nc > FITSNAMLEN + 3 &&
30078                   card[ FITSNAMLEN ] == ' ' &&
30079                   card[ FITSNAMLEN + 2 ] == '\'' );
30080 
30081 /* If column 9 does not contain an equals sign (but is not a CONTINUE card), or if
30082    the keyword is "HISTORY", "COMMENT" or blank, then columns 9 to the end are
30083    comment characters, and the value string is null. */
30084          if( ( nc <= FITSNAMLEN || card[ FITSNAMLEN ] != '='
30085                                 || !Ustrcmp( *name, "HISTORY", status )
30086                                 || !Ustrcmp( *name, "COMMENT", status )
30087                                 || blank_name ) && !cont ){
30088             (*value)[ 0 ] = 0;
30089             if( nc > FITSNAMLEN ){
30090                (void) strncpy( *comment, card + FITSNAMLEN,
30091                                nc - FITSNAMLEN );
30092                (*comment)[ nc - FITSNAMLEN ] = 0;
30093             } else {
30094                (*comment)[ 0 ] = 0;
30095             }
30096 
30097 /* Otherwise there is a value field. */
30098          } else {
30099 
30100 /* Find the first non-blank character in the value string. */
30101             v0 = card + FITSNAMLEN + 1;
30102             while( (size_t)(v0 - card) < nc &&
30103                    isspace( (int) *v0 ) ) v0++;
30104 
30105 /* Store pointers to the start of the returned value and comment strings. */
30106             v = *value;
30107             c = *comment;
30108 
30109 /* If the first character in the value string is a single quote, the value is
30110    a string. In this case the value ends at the first non-escaped single
30111    quote. */
30112             if( *v0 == '\''){
30113                type = cont ? AST__CONTINUE : AST__STRING;
30114 
30115 /* We want to copy the string value, without the delimiting quotes, to the
30116    returned value string. Single quotes within the string are represented
30117    by two adjacent quotes, so we also need to check for these and replace
30118    them by one quote in the returned string. First initialise a pointer
30119    to the first character after the opening quote, and set a flag
30120    indicating that (for the purposes of identifying pairs of adjacent
30121    quotes within the string) the previous character was not a quote. */
30122                d = v0 + 1;
30123                lq = 0;
30124 
30125 /* Loop round each remaining character in the supplied card. */
30126                while( (size_t)(d - card) < nc ){
30127 
30128 /* If the current character is a single quote... */
30129                   if( *d == '\'' ){
30130 
30131 /* If the previous character was also a single quote then the quote does
30132    not mark the end of the string, but is a quote to be included literally
30133    in the value. Copy the quote to the returned string and clear the flag
30134    to indicate that the pair of adjacent quotes is now complete. */
30135                     if( lq ){
30136                        *(v++) = '\'';
30137                        lq = 0;
30138 
30139 /* If the last character was not a quote, then set the flag for the next
30140    pass through the loop, but do not copy the quote to the returned string
30141    since it will either be a quote escaping a following adjacent quote, or
30142    a quote to mark the end of the string. */
30143                     } else {
30144                        lq = 1;
30145                     }
30146 
30147 /* If the current character is not a quote... */
30148                   } else {
30149 
30150 /* If the previous character was a quote, then we have found a single
30151    isolated quote which therefore marks the end of the string value.
30152    The pointer "d" is left pointing to the first character
30153    after the terminating quote. */
30154                      if( lq ){
30155                         break;
30156 
30157 /* If the last character was not a quote, copy it to the returned string. */
30158                      } else {
30159                         *(v++) = *d;
30160                      }
30161                   }
30162                   d++;
30163                }
30164 
30165 /* Terminate the returned value string. */
30166                *v = 0;
30167 
30168 /* Now deal with logical and numerical values. */
30169             } else {
30170 
30171 /* The end of the value field is marked by the first "/". Find the number
30172    of characters in the value field. Pointer "d" is left pointing to the
30173    first character in the comment (if any). Only use "/" characters which
30174    occur within the first nc characters. */
30175                d = strchr( card, '/' );
30176                if( !d || ( d - card ) >= nc ){
30177                   ncv = nc - FITSNAMLEN - 1;
30178                   d = NULL;
30179                } else {
30180                   ncv = (size_t)( d - card ) - FITSNAMLEN - 1;
30181                }
30182 
30183 /* Copy the value string to the returned string. */
30184                if( ncv == 0 ){
30185                   *v = 0;
30186                } else {
30187                   strncpy( v, card + FITSNAMLEN + 1, ncv );
30188                   v[ ncv ] = ' ';
30189                   v[ ncv + 1 ] = 0;
30190                }
30191 
30192 /* Find the first non-blank character in the value string. */
30193                v0 = v;
30194                while( *v0 && isspace( (int) *v0 ) ) v0++;
30195 
30196 /* See if the value string is one of the following strings (optionally
30197    abbreviated and case insensitive): YES, NO, TRUE, FALSE. */
30198                iopt = FullForm( "YES NO TRUE FALSE", v0, 1, status );
30199 
30200 /* Return the single character "T" or "F" at the start of the value string
30201    if the value matches one of the above strings. */
30202                if( iopt == 0 || iopt == 2 ) {
30203                   type = AST__LOGICAL;
30204                   strcpy ( v, "T" );
30205                } else if( iopt == 1 || iopt == 3 ) {
30206                   type = AST__LOGICAL;
30207                   strcpy ( v, "F" );
30208 
30209 /* If it does not match, see if the value is numerical. */
30210                } else {
30211 
30212 /* Save the length of the value string excluding trailing blanks. */
30213                   len = ChrLen( v, status );
30214 
30215 /* If the entire string is blank, the value type is UNDEF. */
30216                   if( len == 0 ) {
30217                      type = AST__UNDEF;
30218 
30219 /* If there are no dots (decimal points) or exponents (D or E) in the value... */
30220                   } else if( !strpbrk( v, ".EeDd" ) ){
30221 
30222 /* First attempt to read two integers from the string (separated by white
30223    space). */
30224                      if( nch = 0,
30225                          ( 2 == astSscanf( v, " %d %d%n", &ir, &ii, &nch ) ) &&
30226                          ( nch >= len ) ) {
30227                         type = AST__COMPLEXI;
30228 
30229 /* If that failed, attempt to read a single integer from the string. */
30230                      } else if( nch = 0,
30231                          ( 1 == astSscanf( v, " %d%n", &ir, &nch ) ) &&
30232                          ( nch >= len ) ) {
30233                         type = AST__INT;
30234                      }
30235 
30236 /* If there are dots (decimal points) in the value... */
30237                   } else {
30238 
30239 /* First attempt to read two doubles from the string (separated by white
30240    space). */
30241                      if( nch = 0,
30242                          ( 2 == astSscanf( v, " %lf %lf%n", &fr, &fi, &nch ) ) &&
30243                          ( nch >= len ) ) {
30244                         type = AST__COMPLEXF;
30245 
30246 /* If that failed, attempt to read a single double from the string. */
30247                      } else if( nch = 0,
30248                          ( 1 == astSscanf( v, " %lf%n", &fr, &nch ) ) &&
30249                          ( nch >= len ) ) {
30250                         type = AST__FLOAT;
30251                      }
30252 
30253 /* If both the above failed, it could be because the string contains a
30254    "D" exponent (which is probably valid FITS) instead of an "E" exponent.
30255    Replace any "D" in the string with "e" and try again. */
30256                      if( type == AST__COMMENT && astOK ) {
30257 
30258 /* Replace "d" and "D" by "e" (if this doesn't produce a readable floating
30259    point value then the value string will not be used, so it is safe to
30260    do the replacement in situ). */
30261                         for( i = 0; i < len; i++ ) {
30262                            if( v[ i ] == 'd' || v[ i ] == 'D' ) v[ i ] = 'e';
30263                         }
30264 
30265 /* Attempt to read two doubles from the edited string (separated by white
30266    space). */
30267                         if( nch = 0,
30268                           ( 2 == astSscanf( v, " %lf %lf%n", &fr, &fi, &nch ) ) &&
30269                           ( nch >= len ) ) {
30270                            type = AST__COMPLEXF;
30271 
30272 /* If that failed, attempt to read a single double from the edited string. */
30273                         } else if( nch = 0,
30274                             ( 1 == astSscanf( v, " %lf%n", &fr, &nch ) ) &&
30275                             ( nch >= len ) ) {
30276                            type = AST__FLOAT;
30277                         }
30278                      }
30279                   }
30280                }
30281 
30282 /* If the value type could not be determined report an error. */
30283                if( type == AST__COMMENT && astOK ) {
30284                   astError( AST__BDFTS, "%s(%s): Illegal keyword value "
30285                             "supplied.", status, method, class );
30286                }
30287             }
30288 
30289 /* Find the number of characters in the comment. Pointer "d" should point to
30290    the first character following the value string. */
30291             if( d ){
30292                ncc = nc - (size_t)( d - card );
30293             } else {
30294                ncc = 0;
30295             }
30296 
30297 /* Copy the remainder of the card to the returned comment string. */
30298             if( astOK && ncc > 0 ){
30299                strncpy( c, d, ncc );
30300                c[ ncc ] = 0;
30301 
30302 /* Find the start of the comment (indicated by the first "/" after the
30303    value string). */
30304                slash = strchr( c, '/' );
30305 
30306 /* Temporarily terminate the string at the slash. */
30307                if( slash ) *slash = 0;
30308 
30309 /* Shuffle the characters following the slash down to the
30310    start of the returned string. */
30311                if( slash ){
30312                   ncc -= (size_t)( slash - c ) + 1;
30313                   d = slash + 1;
30314                   for( i = 0; i < 1 + (int) ncc; i++ ) *(c++) = *(d++);
30315                }
30316 
30317 /* If there is no comment string, return a null string. */
30318             } else {
30319                *c = 0;
30320             }
30321          }
30322       }
30323    }
30324 
30325 /* Truncate the returned string to avoid wasting space. */
30326    if( *name ) *name = (char *) astRealloc( (void *) *name, strlen( *name ) + 1 );
30327    if( *comment ) *comment = (char *) astRealloc( (void *) *comment, strlen( *comment ) + 1 );
30328    if( *value ) *value = (char *) astRealloc( (void *) *value, strlen( *value ) + 1 );
30329 
30330 /* If the value is deemed to be integer, check that the number of digits
30331    in the formatted value does not exceed the capacity of an int. This may
30332    be the case if there are too many digits in the integer for an "int" to
30333    hold. In this case, change the data type to float. */
30334    if( *value && type == AST__INT ) {
30335       ndig = 0;
30336       c = *value;
30337       while( *c ) {
30338          if( isdigit( *(c++) ) ) ndig++;
30339       }
30340       if( ndig >= int_dig ) type = AST__FLOAT;
30341    }
30342 
30343 /* If an error occurred, free the returned strings and issue a context
30344    message. */
30345    if( !astOK ){
30346       *name = (char *) astFree( (void *) *name );
30347       *value = (char *) astFree( (void *) *value );
30348       *comment = (char *) astFree( (void *) *comment );
30349       type = AST__COMMENT;
30350       astError( astStatus, "%s(%s): Unable to store the following FITS "
30351                 "header card:\n%s\n", status, method, class, card );
30352    }
30353 
30354 /* Return the data type. */
30355    return type;
30356 }
30357 
SplitMap(AstMapping * map,int invert,int ilon,int ilat,AstMapping ** map1,AstWcsMap ** map2,AstMapping ** map3,int * status)30358 static int SplitMap( AstMapping *map, int invert, int ilon, int ilat,
30359                      AstMapping **map1, AstWcsMap **map2, AstMapping **map3, int *status ){
30360 /*
30361 *  Name:
30362 *     SplitMap
30363 
30364 *  Purpose:
30365 *     Locate a WCS projection within a Mapping.
30366 
30367 *  Type:
30368 *     Private function.
30369 
30370 *  Synopsis:
30371 *     int SplitMap( AstMapping *map, int invert, int ilon, int ilat,
30372 *                   AstMapping **map1, AstWcsMap **map2, AstMapping **map3, int *status )
30373 
30374 *  Class Membership:
30375 *     FitsChan
30376 
30377 *  Description:
30378 *     If possible, the supplied Mapping is decomposed into three component
30379 *     mappings to be compounded in series. To be acceptable, the second of
30380 *     these three Mappings must be an inverted WcsMap with a non-zero
30381 *     FITSProj attribute value, and there must not be such a WcsMap in
30382 *     either of the other two Mappings. If it is not possible to produce
30383 *     such a group of three Mappings, then a zero function value is returned,
30384 *     together with three NULL Mapping pointers. All the mappings before the
30385 *     WcsMap are compounded together and returned as "map1". The inverse of
30386 *     the WcsMap itself is returned as "map2", and any remaining Mappings
30387 *     are compounded together and returned as "map3".
30388 *
30389 *     The search algorithm allows for an arbitrary combination of series and
30390 *     parallel CmpMaps.
30391 
30392 *  Parameters:
30393 *     map
30394 *        A pointer to the Mapping from pixel to physical coordinates.
30395 *     invert
30396 *        The value of the Invert attribute to use with "map" (the value
30397 *        returned by astGetInvert is not used).
30398 *     ilon
30399 *        Index of mapping output which is connected to the longitude axis.
30400 *     ilat
30401 *        Index of mapping output which is connected to the latitude axis.
30402 *     map1
30403 *        A location at which to return a pointer to the Mapping from pixel
30404 *        to intermediate world coordinates.
30405 *     map2
30406 *        A location at which to return a pointer to the Mapping from
30407 *        intermediate world coordinates to native spherical coordinates. This
30408 *        will be an inverted WcsMap with non-zero FITSProj attribute value.
30409 *     map3
30410 *        A location at which to return a pointer to the Mapping from
30411 *        native spherical coordinates to physical coordinates.
30412 *     dep
30413 *        The address of an integer holding the current depth of recursion
30414 *        into this function.
30415 *     status
30416 *        Pointer to the inherited status variable.
30417 
30418 *  Returned Value:
30419 *     One if a suitable WcsMap was found, zero otherwise.
30420 
30421 *  Notes:
30422 *     -  The returned Mappings contain independant copies of the relevant
30423 *     components of the supplied Mapping and can be modified without
30424 *     changing the supplied Mapping.
30425 *     -  NULL pointers will be returned for all Mappings if no WcsMap
30426 *     can be found in the supplied Mapping.
30427 *     -  A pointer to a UnitMap will be returned for map1 if no mappings
30428 *     exist before the WcsMap.
30429 *     -  A pointer to a UnitMap will be returned for map3 if no mappings
30430 *     exist after the WcsMap.
30431 *     -  NULL pointers will be returned for all Mappings and a function
30432 *     value of zero will be returned if an error has occurred, or if this
30433 *     function should fail for any reason.
30434 */
30435 
30436 /* Local Variables */
30437    AstFitsChan *fc;        /* Pointer to temporary FitsChan */
30438    AstFrameSet *tfs;       /* Temporary FrameSet */
30439    AstMapping *mapa;       /* Pre-wcs Mapping */
30440    AstMapping *mapc;       /* Post-wcs Mapping */
30441    AstMapping *tmap1;      /* Temporary Mapping */
30442    AstMapping *tmap2;      /* Temporary Mapping */
30443    AstPointSet *pset1;     /* Pixel positions */
30444    AstPointSet *pset2;     /* WCS positions */
30445    AstWcsMap  *mapb;       /* WcsMap */
30446    char card[ AST__FITSCHAN_FITSCARDLEN + 1 ]; /* Buffer for header card */
30447    double **ptr1;          /* Pointer to pixel axis values */
30448    double **ptr2;          /* Pointer to WCS axis values */
30449    double *iwc_origin;     /* Array holding IWC at pixel origin */
30450    double *pix_origin;     /* Array holding pixel coords at pixel origin */
30451    double *w1;             /* Pointer to work space */
30452    int i;                  /* Loop index */
30453    int npix;               /* Number of pixel axes */
30454    int nwcs;               /* Number of WCS axes */
30455    int ret;                /* Was a non-linear Mapping found? */
30456 
30457 /* Initialise */
30458    *map1 = NULL;
30459    *map2 = NULL;
30460    *map3 = NULL;
30461    ret = 0;
30462 
30463 /* Check the global status. */
30464    if( !astOK ) return ret;
30465 
30466 /* Call SplitMap2 to do the work. SplitMap2 does not check that the
30467    WcsMap is an *inverted* WcsMap, neither does it check that there
30468    are no WcsMaps in either map1 or map3. */
30469    if( SplitMap2( map, invert, map1, map2, map3, status ) ) {
30470 
30471 /* Check that the WcsMap is inverted. */
30472       if( astGetInvert( *map2 ) ) {
30473 
30474 /* Check that map 1 does not contain a WcsMap with non-zero FITSProj
30475    attribute. */
30476          if( !SplitMap2( *map1, astGetInvert( *map1 ), &mapa, &mapb, &mapc,
30477                          status ) ) {
30478 
30479 /* Check that map 3 does not contain a WcsMap with non-zero FITSProj
30480    attribute. */
30481             if( !SplitMap2( *map3, astGetInvert( *map3 ), &mapa, &mapb, &mapc,
30482                             status ) ) {
30483 
30484 /* If so, the three Mappings are OK. */
30485                ret = 1;
30486             } else {
30487                mapa = astAnnul( mapa );
30488                mapb = astAnnul( mapb );
30489                mapc = astAnnul( mapc );
30490             }
30491          } else {
30492             mapa = astAnnul( mapa );
30493             mapb = astAnnul( mapb );
30494             mapc = astAnnul( mapc );
30495          }
30496       }
30497    }
30498 
30499 /* If the above failed to find a suitable WcsMap, we now consider cases
30500    where the pixel->WCS mapping is linear. We can invent a CAR projection
30501    WcsMap for such cases. We use a ShiftMap to move the origin of the
30502    longitude IWC axis to a sensible value (it is left at zero otherwise).
30503    We cannot do this with the latitude axis since pre-FITS-WCS fits
30504    readers could not handle the resulting rotation from native to celestial
30505    coords. */
30506    if( !ret && astGetIsLinear( map ) ) {
30507       nwcs = astGetNout( map );
30508       npix = astGetNin( map );
30509       iwc_origin = astMalloc( sizeof( double )*nwcs );
30510       pix_origin = astMalloc( sizeof( double )*npix );
30511       if( astOK ) {
30512          for( i = 0; i < npix; i++ ) pix_origin[ i ] = 0.0;
30513          astTranN( map, 1, npix, 1, pix_origin, 1, nwcs, 1, iwc_origin );
30514          for( i = 0; i < nwcs; i++ ) {
30515             if( i != ilon ) {
30516                iwc_origin[ i ] = 0.0;
30517             } else {
30518                iwc_origin[ i ] *= -1;
30519             }
30520          }
30521          mapa = (AstMapping *) astShiftMap( nwcs, iwc_origin, "", status );
30522          *map1 = (AstMapping *) astCmpMap( map, mapa, 1, "", status );
30523          *map2 = astWcsMap( nwcs, AST__CAR, ilon + 1, ilat + 1, "Invert=1", status );
30524          astInvert( mapa );
30525          *map3 = astClone( mapa );
30526          mapa = astAnnul( mapa );
30527          ret = 1;
30528       }
30529       iwc_origin = astFree( iwc_origin );
30530       pix_origin = astFree( pix_origin );
30531    }
30532 
30533 /* If the above failed to find a suitable WcsMap, we now consider cases
30534    where the output (long,lat) values are constants supplied by a
30535    final PermMap. We can invent a WcsMap for such cases. */
30536    if( !ret ) {
30537 
30538 /* Transform two arbitrary pixel positions into the WCS Frame. */
30539       npix = astGetNin( map );
30540       nwcs = astGetNout( map );
30541       pset1 = astPointSet( 2, npix, "", status );
30542       pset2 = astPointSet( 2, nwcs, "", status );
30543       ptr1 = astGetPoints( pset1 );
30544       ptr2 = astGetPoints( pset2 );
30545       w1 = astMalloc( sizeof( double )*(size_t) nwcs );
30546       if( astOK ) {
30547          for( i = 0; i < npix; i++ ) {
30548             ptr1[ i ][ 0 ] = 1.0;
30549             ptr1[ i ][ 1 ] = 1000.0;
30550          }
30551          (void) astTransform( map, pset1, 1, pset2 );
30552 
30553 /* If the two wcs positions have equal longitude and latitude values,
30554    assume that the output longitude and latitude axes are assigned
30555    constant values by the Mapping. */
30556          if( ptr2[ ilon ][ 0 ] == ptr2[ ilon ][ 1 ] &&
30557              ptr2[ ilon ][ 0 ] != AST__BAD &&
30558              ptr2[ ilat ][ 0 ] == ptr2[ ilat ][ 1 ] &&
30559              ptr2[ ilat ][ 0 ] != AST__BAD ) {
30560 
30561 /* Create a set of Mappings to return, including a WcsMap, which result in
30562    these constant latitude and longitude values. We do this by creating a
30563    FITS-WCS header and reading the FrameSet from it. Keywords which are not
30564    important to the final mappings are given arbitrary values. */
30565             fc = astFitsChan( NULL, NULL, "", status );
30566             for( i = 0; i < nwcs; i++ ) {
30567                sprintf( card, "CRPIX%d  = 0", i + 1 );
30568                astPutFits( fc, card, 0 );
30569                sprintf( card, "CDELT%d  = 0.0003", i + 1 );
30570                astPutFits( fc, card, 0 );
30571                if( i == ilon ) {
30572                   sprintf( card, "CTYPE%d  = 'RA---TAN'", i + 1 );
30573                } else if( i == ilat ) {
30574                   sprintf( card, "CTYPE%d  = 'DEC--TAN'", i + 1 );
30575                } else {
30576                   sprintf( card, "CTYPE%d  = 'DUMMY'", i + 1 );
30577                }
30578                astPutFits( fc, card, 0 );
30579                if( i == ilon ) {
30580                   sprintf( card, "CRVAL%d  = %.*g", i + 1, DBL_DIG, AST__DR2D*ptr2[ ilon ][ 0 ] );
30581                } else if( i == ilat ) {
30582                   sprintf( card, "CRVAL%d  = %.*g", i + 1, DBL_DIG, AST__DR2D*ptr2[ ilat ][ 0 ] );
30583                } else {
30584                   sprintf( card, "CRVAL%d  = 0.0", i + 1 );
30585                }
30586                astPutFits( fc, card, 0 );
30587             }
30588             astClearCard( fc );
30589             tfs = astRead( fc );
30590             if( tfs ) {
30591 
30592 /* Use SplitMap to get the required Mapings from the FrameSet. */
30593                tmap2 = astGetMapping( tfs, AST__BASE, AST__CURRENT );
30594                SplitMap( tmap2, astGetInvert( tmap2 ), 0, 1, &tmap1, map2,
30595                          map3, status );
30596                tmap1 = astAnnul( tmap1 );
30597                tmap2 = astAnnul( tmap2 );
30598 
30599 /* Create a ShiftMap which subtract the constant longitude and latitude
30600    values off the inputs. */
30601                for( i = 0; i < nwcs; i++ ) w1[ i ] = 0.0;
30602                w1[ ilon ] = -ptr2[ ilon ][ 0 ];
30603                w1[ ilat ] = -ptr2[ ilat ][ 0 ];
30604                tmap1 = (AstMapping *) astShiftMap( nwcs, w1, "", status );
30605 
30606 /* Compose this with the supplied Mapping. This results in the celestial
30607    outputs being zero. This gives the required "map1". */
30608                *map1 = (AstMapping *) astCmpMap( map, tmap1, 1, "", status );
30609 
30610 /* Indicate success.*/
30611                ret = 1;
30612 
30613 /* Free resources. */
30614                tmap1 = astAnnul( tmap1 );
30615                tfs = astAnnul( tfs );
30616             }
30617             fc = astAnnul( fc );
30618          }
30619       }
30620 
30621 /* Free resources */
30622       pset1 = astAnnul( pset1 );
30623       pset2 = astAnnul( pset2 );
30624       w1 = astFree( w1 );
30625    }
30626    if( !ret ) {
30627       if( *map1 ) *map1 = astAnnul( *map1 );
30628       if( *map2 ) *map2 = astAnnul( *map2 );
30629       if( *map3 ) *map3 = astAnnul( *map3 );
30630    }
30631    return ret;
30632 }
30633 
SplitMap2(AstMapping * map,int invert,AstMapping ** map1,AstWcsMap ** map2,AstMapping ** map3,int * status)30634 static int SplitMap2( AstMapping *map, int invert, AstMapping **map1,
30635                       AstWcsMap **map2, AstMapping **map3, int *status ){
30636 /*
30637 *  Name:
30638 *     SplitMap2
30639 
30640 *  Purpose:
30641 *     Locate a WCS projection within a Mapping.
30642 
30643 *  Type:
30644 *     Private function.
30645 
30646 *  Synopsis:
30647 *     int SplitMap2( AstMapping *map, int invert, AstMapping **map1,
30648 *                    AstWcsMap **map2, AstMapping **map3, int *status )
30649 
30650 *  Class Membership:
30651 *     FitsChan
30652 
30653 *  Description:
30654 *     If possible, the supplied Mapping is decomposed into three component
30655 *     mappings to be compounded in series. To be acceptable, the second of
30656 *     these three Mappings must be a WcsMap with a non-zero FITSProj value.
30657 *     If it is not possible to produce such a group of three Mappings, then a
30658 *     zero function value is returned, together with three NULL Mapping
30659 *     pointers. All the mappings before the WcsMap are compounded together
30660 *     and returned as "map1". The WcsMap itself is returned as "map2", and
30661 *     any remaining Mappings are compounded together and returned as "map3".
30662 *
30663 *     The search algorithm allows for an arbitrary combination of series and
30664 *     parallel CmpMaps.
30665 
30666 *  Parameters:
30667 *     map
30668 *        A pointer to the Mapping from pixel to physical coordinates.
30669 *     invert
30670 *        The value of the Invert attribute to use with "map" (the value
30671 *        returned by astGetInvert is not used).
30672 *     map1
30673 *        A location at which to return a pointer to the Mapping from pixel
30674 *        to intermediate world coordinates.
30675 *     map2
30676 *        A location at which to return a pointer to the Mapping from relative
30677 *        physical coordinates to native spherical coordinates. This will
30678 *        be a WcsMap, and it will have a non-zero FITSProj value.
30679 *     map3
30680 *        A location at which to return a pointer to the Mapping from
30681 *        native spherical coordinates to physical coordinates.
30682 *     dep
30683 *        The address of an integer holding the current depth of recursion
30684 *        into this function.
30685 *     status
30686 *        Pointer to the inherited status variable.
30687 
30688 *  Returned Value:
30689 *     One if a suitable WcsMap was found, zero otherwise.
30690 
30691 *  Notes:
30692 *     -  The returned Mappings contain independant copies of the relevant
30693 *     components of the supplied Mapping and can be modified without
30694 *     changing the supplied Mapping.
30695 *     -  NULL pointers will be returned for all Mappings if no WcsMap
30696 *     with anon-zero FITSProj value can be found in the supplied Mapping.
30697 *     -  A pointer to a UnitMap will be returned for map1 if no mappings
30698 *     exist before the WcsMap.
30699 *     -  A pointer to a UnitMap will be returned for map3 if no mappings
30700 *     exist after the WcsMap.
30701 *     -  NULL pointers will be returned for all Mappings and a function
30702 *     value of zero will be returned if an error has occurred, or if this
30703 *     function should fail for any reason.
30704 *     - "*map1" and "*map3" may contain WcsMaps, but they will have zero
30705 *     values for their FITSProj values.
30706 */
30707 
30708 /* Local Variables */
30709    AstMapping **map_list;  /* Mapping array pointer */
30710    AstMapping *mapa;       /* Pre-wcs Mapping */
30711    AstWcsMap *mapb;        /* WcsMap */
30712    AstMapping *mapc;       /* Post-wcs Mapping */
30713    AstMapping *temp;       /* Intermediate Mapping */
30714    const char *class;      /* Pointer to class of supplied Mapping */
30715    double pv;              /* Projection parameter value */
30716    int *invert_list;       /* Invert array pointer */
30717    int axis;               /* No. of axes in whole Mapping */
30718    int axlat;              /* Index of latitude axis */
30719    int axlon;              /* Index of longitude axis */
30720    int haswcs;             /* Was a usable inverted WcsMap found? */
30721    int imap;               /* Index of current Mapping in list */
30722    int i;                  /* axis index */
30723    int m;                  /* Parameter index */
30724    int nax;                /* No. of axes in Mapping */
30725    int nmap;               /* Number of Mappings in the list */
30726    int ret;                /* Was a non-linear Mapping found? */
30727    int wcsaxis;            /* Index of first WcsMap axis */
30728 
30729 /* Initialise */
30730    *map1 = NULL;
30731    *map2 = NULL;
30732    *map3 = NULL;
30733    ret = 0;
30734 
30735 /* Check the global status. */
30736    if( !astOK ) return ret;
30737 
30738 /* Get the class of the Mapping. */
30739    class = astGetClass( map );
30740 
30741 /* If the supplied Mapping is a CmpMap... */
30742    wcsaxis = -1;
30743    if( !strcmp( class, "CmpMap" ) ){
30744 
30745 /* Decompose the Mapping into a sequence of Mappings to be applied in
30746    series and an associated list of Invert flags. */
30747       map_list = NULL;
30748       invert_list = NULL;
30749       nmap = 0;
30750       astMapList( map, 1, invert, &nmap, &map_list, &invert_list );
30751 
30752 /* If there is more than one Mapping, this must be a series CmpMap. */
30753       if( nmap > 1 && astOK ){
30754 
30755 /* Initialise the returned pre-wcs Mapping to be a UnitMap. */
30756          if( invert == astGetInvert( map ) ){
30757             *map1 = (AstMapping *) astUnitMap( astGetNin( map ), "", status );
30758          } else {
30759             *map1 = (AstMapping *) astUnitMap( astGetNout( map ), "", status );
30760          }
30761 
30762 /* Indicate we have not yet found  a WcsMap. */
30763          ret = 0;
30764 
30765 /* Process each series Mapping. */
30766          for( imap = 0; imap < nmap; imap++ ){
30767 
30768 /* If we have not yet found a WcsMap... */
30769             if( !ret ){
30770 
30771 /* Search this Mapping for a WcsMap. */
30772                ret = SplitMap2( map_list[ imap ], invert_list[ imap ], &mapa,
30773                                 map2, map3, status );
30774 
30775 /* If no WcsMap was found, use the whole mapping as part of the
30776    pre-wcs Mapping. */
30777                if( !ret ){
30778                   mapa = astCopy( map_list[ imap ] );
30779                   astSetInvert( mapa, invert_list[ imap ] );
30780                }
30781 
30782 /* Add the pre-wcs mapping to the cumulative pre-wcs CmpMap. */
30783                temp = (AstMapping *) astCmpMap( *map1, mapa, 1, "", status );
30784                *map1 = astAnnul( *map1 );
30785                mapa = astAnnul( mapa );
30786                *map1 = temp;
30787 
30788 /* If we have previously found a WcsMap, use the whole mapping
30789    as part of the post-wcs mapping. */
30790             } else {
30791                mapc = astCopy( map_list[ imap ] );
30792                astSetInvert( mapc, invert_list[ imap ] );
30793                temp = (AstMapping *) astCmpMap( *map3, mapc, 1, "", status );
30794                *map3 = astAnnul( *map3 );
30795                mapc = astAnnul( mapc );
30796                *map3 = temp;
30797             }
30798          }
30799 
30800 /* If there is only one Mapping, this must be a parallel CmpMap. */
30801       } else {
30802 
30803 /* Annul the Mapping pointer in the series list created above, and free the
30804    dynamic arrays. */
30805          map_list[ 0 ] = astAnnul( map_list[ 0 ] );
30806          map_list = astFree( map_list );
30807          invert_list = astFree( invert_list );
30808          nmap = 0;
30809 
30810 /* Decompose the Mapping into a sequence of Mappings to be applied in
30811    parallel and an associated list of Invert flags. */
30812          astMapList( map, 0, invert, &nmap, &map_list, &invert_list );
30813 
30814 /* Process each parallel Mapping. */
30815          axis = 0;
30816          for( imap = 0; imap < nmap && astOK; imap++ ){
30817 
30818 /* See if this Mapping contains a usable WcsMap. Only do the search
30819    if no such WcsMap has already been found, since only the first is usable. */
30820             if( !ret ) {
30821 
30822 /* Search this Mapping for a WcsMap. */
30823                haswcs = SplitMap2( map_list[ imap ], invert_list[ imap ], &mapa,
30824                                   &mapb, &mapc, status );
30825 
30826 /* Note if we have found a usable WcsMap, and its first axis index. */
30827                if( haswcs ){
30828                   ret = 1;
30829                   wcsaxis = axis;
30830                }
30831 
30832 /* If a WcsMap has already been found, the mapping cannot contain a
30833    usable WcsMap. */
30834             } else {
30835                haswcs = 0;
30836             }
30837 
30838 /* If the Mapping did not contain a usable WcsMap, use the whole mapping as
30839    part of the pre-wcs Mapping, and create a UnitMap as part of the post-wcs
30840    mapping. */
30841             if( !haswcs ){
30842                mapa = astCopy( map_list[ imap ] );
30843                astSetInvert( mapa, invert_list[ imap ] );
30844                nax = astGetNout( mapa );
30845                mapc = (AstMapping *) astUnitMap( nax, "", status );
30846             }
30847 
30848 /* Increment the index of the first axis in the next Mapping. */
30849             axis += astGetNout( mapa );
30850 
30851 /* Add the pre-wcs mapping in parallel with the cumulative pre-wcs CmpMap. */
30852             if( *map1 ){
30853                temp = (AstMapping *) astCmpMap( *map1, mapa, 0, "", status );
30854                *map1 = astAnnul( *map1 );
30855                mapa = astAnnul( mapa );
30856                *map1 = temp;
30857             } else {
30858                *map1 = mapa;
30859             }
30860 
30861 /* Add the post-wcs mapping in parallel with the cumulative post-wcs CmpMap. */
30862             if( *map3 ){
30863                temp = (AstMapping *) astCmpMap( *map3, mapc, 0, "", status );
30864                *map3 = astAnnul( *map3 );
30865                mapc = astAnnul( mapc );
30866                *map3 = temp;
30867             } else {
30868                *map3 = mapc;
30869             }
30870          }
30871 
30872 /* If a usable WcsMap was found, create a new one which has all the same
30873    properties, but with enough axes to join the pre and post wcs Mappings
30874    together. Ensure the correct axes are used for longitude and latitude,
30875    and copy the projection parameters. */
30876          if( ret ){
30877             axlat = astGetWcsAxis( mapb, 1 );
30878             axlon = astGetWcsAxis( mapb, 0 );
30879             *map2 = astWcsMap( axis, astGetWcsType( mapb ),
30880                                axlon + wcsaxis + 1,
30881                                axlat + wcsaxis + 1, "", status );
30882             for( i = 0; i < astGetNin( mapb ); i++ ){
30883                for( m = 0; m < WCSLIB_MXPAR; m++ ){
30884                   if( astTestPV( mapb, i, m ) ) {
30885                      pv = astGetPV( mapb, i, m );
30886                      if( pv != AST__BAD ) astSetPV( *map2, i + wcsaxis, m, pv );
30887                   }
30888                }
30889             }
30890             astInvert( *map2 );
30891             mapb = astAnnul( mapb );
30892          }
30893       }
30894 
30895 /* Loop to annul all the Mapping pointers in the list. */
30896       for ( imap = 0; imap < nmap; imap++ ) map_list[ imap ] = astAnnul( map_list[ imap ] );
30897 
30898 /* Free the dynamic arrays. */
30899       map_list = astFree( map_list );
30900       invert_list = astFree( invert_list );
30901 
30902 /* If the supplied Mapping is not a CmpMap, see if it is a WcsMap with a
30903    non-zero FITSProj value. If so, take a copy and set its invert attribute
30904    correctly. Also create UnitMaps for the pre and post wcs mappings. */
30905    } else if( astOK && !strcmp( class, "WcsMap" ) && astGetFITSProj( map ) ){
30906       ret = 1;
30907       nax = astGetNin( map );
30908       *map1 = (AstMapping *) astUnitMap( nax, "", status );
30909       *map2 = astCopy( map );
30910       astSetInvert( *map2, invert );
30911       *map3 = (AstMapping *) astUnitMap( nax, "", status );
30912    }
30913 
30914 /* If an error has occurred, or if no suitable WcsMap was found, annul any
30915    Mappings. */
30916    if( !astOK || !ret ){
30917       ret = 0;
30918       if( *map1 ) *map1 = astAnnul( *map1 );
30919       if( *map2 ) *map2 = astAnnul( *map2 );
30920       if( *map3 ) *map3 = astAnnul( *map3 );
30921    }
30922 
30923 /* Return the answer. */
30924    return ret;
30925 }
30926 
SplitMat(int naxis,double * matrix,double * cdelt,int * status)30927 static int SplitMat( int naxis, double *matrix, double *cdelt, int *status ){
30928 /*
30929 *  Name:
30930 *     SplitMat
30931 
30932 *  Purpose:
30933 *     Factorises a single "CD"-style matrix into a diagonal CDELT matrix
30934 *     and a "PC"-style matrix.
30935 
30936 *  Type:
30937 *     Private function.
30938 
30939 *  Synopsis:
30940 *     int SplitMat( int naxis, double *matrix, double *cdelt, int *status )
30941 
30942 *  Class Membership:
30943 *     FitsChan
30944 
30945 *  Description:
30946 *     This function splits up the supplied CD matrix into separate PC and
30947 *     CDELT matrices. The product of the returned matrices (CDELT.PC)
30948 *     equals the supplied CD matrix. The CDELT values are chosen so that
30949 *     the corresponding row of the PC matrix represents a unit vector.
30950 *     The signs of the CDELT values are chosen so that the diagonal terms
30951 *     of the PC matrix are all positive.
30952 *
30953 
30954 *  Parameters:
30955 *     naxis
30956 *        The number of axes.
30957 *     matrix
30958 *        A pointer to an array of naxis*naxis elements. On entry this holds
30959 *        the "CD" matrix. On exit, it is modified to represent the "PC"
30960 *        matrix.
30961 *     cdelt
30962 *        A pointer to an array of naxis elements. On exit this holds the CDELT
30963 *        values for each axis (i.e. the diagonal terms of the CDELT matrix).
30964 *     status
30965 *        Pointer to the inherited status variable.
30966 
30967 * Returned Value:
30968 *     Zero is returned if any bad values are found in the supplied
30969 *     matrix, or if an error has already occurred. One is returned otherwise.
30970 */
30971 
30972 /* Local Variables: */
30973    int i;
30974    int j;
30975    int ok;
30976    double *a;
30977    int dineg;
30978    double s2;
30979    double cdlt;
30980 
30981 /* Check the inherited status. */
30982    if( !astOK ) return 0;
30983 
30984 /* Assume success. */
30985    ok = 1;
30986 
30987 /* Loop round every row in the matrix. Get a pointer to the first element
30988    in the row. */
30989    for( i = 0; i < naxis; i++ ){
30990       a = matrix + i*naxis;
30991 
30992 /* Note the sign of the diagonal term (i.e. the i'th element) of this row. */
30993       dineg = ( a[ i ] < 0.0 );
30994 
30995 /* Get the magnitude of the vector represented by this row. This is the
30996    CDELT value for the row. BAD values cause the whole function to return. */
30997       s2 = 0.0;
30998       for( j = 0; j < naxis; j++ ){
30999          if( *a == AST__BAD )  {
31000             ok = 0;
31001             break;
31002          }
31003          s2 += (*a)*(*a);
31004          a++;
31005       }
31006       if( !ok ) break;
31007       cdlt = sqrt( MAX( 0.0, s2 ) );
31008 
31009 /* If the diagonal term for this row of the matrix is negative, make
31010    the CDELT value negative instead. This means that the diagonal term in
31011    the final PC matrix will be positive. */
31012       if( dineg ) cdlt = -cdlt;
31013 
31014 /* Store the CDELT value. */
31015       cdelt[ i ] = cdlt;
31016 
31017 /* The row of the PC matrix is obtained by dividing the original row by
31018    the CDELT value. Set to zero any PC values which are less than 1.0E-7
31019    (such values may be produced by rounding errors). */
31020       a = matrix + i*naxis;
31021       for( j = 0; j < naxis; j++ ) {
31022          if( cdlt != 0.0 ){
31023             *a /= cdlt;
31024             if( fabs( *a ) < 1.E-7 ) *a = 0.0;
31025          } else {
31026             *a = 0.0;
31027          }
31028          a++;
31029       }
31030    }
31031    return ok;
31032 }
TableSource(AstFitsChan * this,void (* tabsource)(AstFitsChan *,const char *,int,int,int *),int * status)31033 static void TableSource( AstFitsChan *this,
31034                          void (* tabsource)( AstFitsChan *, const char *,
31035                                              int, int, int * ),
31036                          int *status ){
31037 
31038 /*
31039 *++
31040 *  Name:
31041 c     astTableSource
31042 f     AST_TABLESOURCE
31043 
31044 *  Purpose:
31045 c     Register a source function for accessing tables in FITS files.
31046 f     Register a source routine for accessing tables in FITS files.
31047 
31048 *  Type:
31049 *     Public function.
31050 
31051 *  Synopsis:
31052 c     #include "fitschan.h"
31053 c     void astTableSource( AstFitsChan *this,
31054 c                          void (* tabsource)( AstFitsChan *, const char *,
31055 c                                              int, int, int * ) )
31056 f     CALL AST_TABLESOURCE( THIS, TABSOURCE, STATUS )
31057 
31058 *  Class Membership:
31059 *     FitsChan member function.
31060 
31061 *  Description:
31062 c     This function can be used to register a call-back function
31063 f     This routine can be used to register a call-back routine
31064 *     with a FitsChan. The registered
31065 c     function
31066 f     routine
31067 *     is called when-ever the FitsChan needs to read information from a
31068 *     binary table contained within a FITS file. This occurs if the
31069 c     astRead
31070 f     AST_READ
31071 *     function is invoked to read a FrameSet from a set of FITS headers
31072 *     that use the "-TAB" algorithm to describe one or more axes. Such
31073 *     axes use a FITS binary table to store a look-up table of axis values.
31074 *     The FitsChan will fail to read such axes unless the "TabOK" attribute
31075 *     is set to a non-zero positive integer value. The table containing the
31076 *     axis values must be made available to the FitsChan either by storing
31077 *     the table contents in the FitsChan (using
31078 c     astPutTables or astPutTable) prior to invoking astRead
31079 f     AST_PUTTABLES or AST_PUTTABLE) prior to invoking AST_READ
31080 *     or by registering a call-back
31081 c     function using astTableSource.
31082 f     routine using AST_TABLESOURCE.
31083 *     The first method is possibly simpler, but requires that the name of
31084 *     the extension containing the table be known in advance. Since the
31085 *     table name is embedded in the FITS headers, the name is often not
31086 *     known in advance. If a call-back is registered, the FitsChan will
31087 *     determine the name of the required table and invoke the call-back
31088 c     function
31089 f     routine
31090 *     to supply the table at the point where it is needed (i.e. within
31091 c     the astRead method).
31092 f     the AST_READ method).
31093 
31094 *  Parameters:
31095 c     this
31096 f     THIS = INTEGER (Given)
31097 *        Pointer to the FitsChan.
31098 c     tabsource
31099 f     TABSOURCE = SUBROUTINE (Given)
31100 c        Pointer to the table source function to use.
31101 f        The table source routine to use.
31102 *        It takes five arguments - the first is a pointer to the
31103 *        FitsChan, the second is a string holding the name of the
31104 *        FITS extension containing the required binary table ("EXTNAME"),
31105 *        the third is the integer FITS "EXTVER" header value for the
31106 *        required extension, the fourth is the integer FITS "EXTLEVEL"
31107 *        header value for the required extension, and the fifth is
31108 c        a pointer to
31109 *        the inherited integer status value.
31110 *
31111 *        The call-back should read the entire contents (header and data)
31112 *        of the binary table in the named extension of the external FITS
31113 *        file, storing the contents in a newly created FitsTable object. It
31114 *        should then store this FitsTable in the FitsChan using the
31115 c        astPutTables or astPutTable
31116 f        AST_PUTTABLES or AST_PUTTABLE
31117 *        method, and finally annull its local copy of the FitsTable pointer.
31118 *        If the table cannot be read for any reason, or if any other
31119 *        error occurs, it should return a non-zero integer for the final
31120 *        (third) argument.
31121 *
31122 c        If "tabsource" is NULL,
31123 f        If TABSOURCE is AST_NULL,
31124 *        any registered call-back function will be removed.
31125 f     STATUS = INTEGER (Given and Returned)
31126 f        The global status.
31127 
31128 *  Notes:
31129 c     - Application code can pass arbitrary data (such as file
31130 c     descriptors, etc) to the table source function using the
31131 c     astPutChannelData function. The source function should use
31132 c     the astChannelData macro to retrieve this data.
31133 f     - The name  of the routine supplied for the TABSOURCE
31134 f     argument should appear in an EXTERNAL statement in the Fortran
31135 f     routine which invokes AST_TABLESOURCE. However, this is not generally
31136 f     necessary for the null routine AST_NULL (so long as the AST_PAR
31137 f     include file has been used).
31138 f     - Note that the null routine AST_NULL (one underscore) is
31139 f     different to AST__NULL (two underscores), which is the null Object
31140 f     pointer.
31141 *--
31142 */
31143 
31144 /* Check the global error status. */
31145    if ( !astOK ) return;
31146 
31147 /* Register the supplied source function, using the wrapper function
31148    appropriate for calling C table source functions. */
31149    astSetTableSource( this, (void (*)( void )) tabsource, TabSourceWrap );
31150 }
31151 
TabMapping(AstFitsChan * this,FitsStore * store,char s,int ** tabaxis,const char * method,const char * class,int * status)31152 static AstMapping *TabMapping( AstFitsChan *this, FitsStore *store, char s,
31153                                int **tabaxis, const char *method,
31154                                const char *class, int *status ) {
31155 
31156 /*
31157 *  Name:
31158 *     TabMapping
31159 
31160 *  Purpose:
31161 *     Create a Mapping that performs any -TAB look-ups for all WCS axes.
31162 
31163 *  Type:
31164 *     Private function.
31165 
31166 *  Synopsis:
31167 *     #include "fitschan.h"
31168 *     AstMapping *TabMapping( AstFitsChan *this, FitsStore *store, char s,
31169 *                             int **tabaxis, const char *method,
31170 *                             const char *class, int *status )
31171 
31172 *  Class Membership:
31173 *     FitsChan member function.
31174 
31175 *  Description:
31176 *     This function returns a Mapping that has "nwcs" inputs and outputs,
31177 *     where "nwcs" is the number of FITS WCS axes defined in the supplied
31178 *     FitsStore. The inputs and outputs are in the same order as the
31179 *     CTYPEi keywords in the FitsStore. The forward transformation of the
31180 *     Mapping converts positions from the axes defined by the CRVALi keywords
31181 *     to the WCS axes. This transformation will be a UnitMap except for
31182 *     any axes that are described using the "-TAB" algorithm. For "-TAB"
31183 *     axes, the transformation will implement the relevant coordinate
31184 *     look-up function.
31185 
31186 *  Parameters:
31187 *     this
31188 *        Pointer to the FitsChan.
31189 *     store
31190 *        Pointer to the FitsStore structure holding the values to use for
31191 *        the WCS keywords.
31192 *     s
31193 *        A character identifying the co-ordinate version to use. A space
31194 *        means use primary axis descriptions. Otherwise, it must be an
31195 *        upper-case alphabetical characters ('A' to 'Z').
31196 *     tabaxis
31197 *        Address of a location at which to store a pointer to an array of
31198 *        flags, one for each output of the returned Mapping. A flag will
31199 *        be non-zero if the corresponding output of the returned Mapping
31200 *        corresponds to a -TAB axis. A NULL pointer is returned if the
31201 *        returned Mapping is NULL.
31202 *     method
31203 *        A pointer to a string holding the name of the calling method.
31204 *        This is used only in the construction of error messages.
31205 *     class
31206 *        A pointer to a string holding the class of the object being
31207 *        read. This is used only in the construction of error messages.
31208 *     status
31209 *        Pointer to the inherited status variable.
31210 
31211 *  Returned Value:
31212 *     A pointer to a Mapping. A NULL pointer is returned if the FitsChan does
31213 *     not support the -TAB algorithm (i.e. if the value of the TabOK
31214 *     attribute is zero or negative), or if no axes use the "-TAB" algorithm.
31215 */
31216 
31217 /* Local Variables: */
31218    AstFitsTable *table;
31219    AstKeyMap *used_tables;
31220    AstMapping *tmap1;
31221    AstMapping *tmap2;
31222    AstMapping *indexmap;
31223    AstMapping *tmap0;
31224    AstMapping *ret;
31225    AstPermMap *pm;
31226    char name[21];
31227    const char *indexcol;
31228    const char *extname;
31229    const char *cval;
31230    const char *ctype;
31231    const char *coordscol;
31232    double dval;
31233    int *marray;
31234    int *permin;
31235    int *permout;
31236    int extlevel;
31237    int extver;
31238    int iaxis;
31239    int iiaxis;
31240    int ikey;
31241    int interp;
31242    int ival;
31243    int maxis;
31244    int mdim;
31245    int nkey;
31246    int nperm;
31247    int unit;
31248    int wcsaxes;
31249 
31250 /* Initialise */
31251    ret = NULL;
31252    *tabaxis = NULL;
31253    extname = NULL;
31254    tmap0 = NULL;
31255    tmap2 = NULL;
31256 
31257 /* Check the global status. */
31258    if( !astOK ) return ret;
31259 
31260 /* Obtain the number of physical axes in the header. If the WCSAXES header
31261    was specified, use it. Otherwise assume it is the same as the number
31262    of pixel axes. */
31263    dval = GetItem( &(store->wcsaxes), 0, 0, s, NULL, method, class, status );
31264    if( dval != AST__BAD ) {
31265       wcsaxes = (int) dval + 0.5;
31266    } else {
31267       wcsaxes = store->naxis;
31268    }
31269 
31270 /* If the FitsChan does not support the -TAB algorithm, return a NULL
31271    pointer. */
31272    if( astGetTabOK( this ) > 0 ) {
31273 
31274 /* Create a KeyMap to hold a list of the used extension names. */
31275       used_tables = astKeyMap( " ", status );
31276 
31277 /* Allocate memory to indicate if each WCS axis is described by a -TAB
31278    algorithm or not. Initialiss it to zero. */
31279       *tabaxis = astCalloc( wcsaxes, sizeof( int ) );
31280 
31281 /* Allocate memory to hold the FITS-WCS axis index corresponding to each
31282    input of the "tmap0" Mapping. Indicate that as yet, not values are
31283    stored in this array. Also allocate memory for the inverse of this
31284    permutation array. */
31285       permout = astMalloc( wcsaxes*sizeof( int ) );
31286       permin = astMalloc( wcsaxes*sizeof( int ) );
31287       nperm = 0;
31288       if( astOK ) {
31289 
31290 /* Initialise the permutation arrays. */
31291          for( iaxis = 0; iaxis < wcsaxes; iaxis++ ) {
31292             permout[ iaxis ] = permin[ iaxis ] = -1;
31293          }
31294 
31295 /* Otherwise, loop round all FITS WCS axis indices present in the FitsStore. */
31296          for( iaxis = 0; iaxis < wcsaxes; iaxis++ ) {
31297 
31298 /* If the current FITS WCS axis is already included in the returned
31299    Mapping, skip it. This will be the case if the axis uses the same
31300    coordinate array as an earlier axis since all FITS WCS axes associated
31301    with a coordinate array are processed together. */
31302             if( permin[ iaxis ] == -1 ) {
31303 
31304 /* See if this WCS axis uses the -TAB algorithm. */
31305                ctype = GetItemC( &(store->ctype), iaxis, 0, s, NULL, method,
31306                                  class, status );
31307                if( ctype && !strncmp( ctype + 4, "-TAB", 4 ) ) {
31308 
31309 /* Get the name of the FITS binary table extension holding the coordinate
31310    info. No default, so report an error if not present. */
31311                   sprintf( name, "PS%d_0%c", iaxis + 1, s );
31312                   extname = GetItemC( &(store->ps), iaxis, 0, s, name, method,
31313                                       class, status );
31314 
31315 /* Get the extension version and level. */
31316                   dval = GetItem( &(store->pv), iaxis, 1, s, NULL, method,
31317                                     class, status );
31318                   extver = ( dval != AST__BAD ) ? (int) dval : 1;
31319                   dval = GetItem( &(store->pv), iaxis, 2, s, NULL, method,
31320                                     class, status );
31321                   extlevel = ( dval != AST__BAD ) ? (int) dval : 1;
31322 
31323 /* Get the FITS binary table. This will invoke any supplied table source
31324    function, and put a copy of the table into the FitsChan structure.
31325    Report an error if the table can not be obtained. */
31326                   table = GetNamedTable( this, extname, extver, extlevel, 1,
31327                                          method, status );
31328 
31329 /* Add this extension name to a list of used extensions. */
31330                   astMapPut0I( used_tables, extname, 1, NULL );
31331 
31332 /* Get the name of the table column containing the main coords array. No
31333    default so report error if not present. Report an error if the column
31334    is not present in the table. */
31335                   sprintf( name, "PS%d_1%c", iaxis + 1, s );
31336                   coordscol = GetItemC( &(store->ps), iaxis, 1, s, name, method,
31337                                         class, status );
31338                   if( !astHasColumn( table, coordscol ) && astOK ) {
31339                      astError( AST__BADTAB, "%s(%s): Unable to find the "
31340                                "coordinate array for FITS-WCS axis %d (type %s): "
31341                                "column '%s' cannot be found in table '%s'.", status,
31342                                method, class, iaxis + 1, ctype, coordscol, extname );
31343                   }
31344 
31345 /* Get the number of dimensions spanned by the coordinate array. Report
31346    an error if the coordinate array has only one axis (FITS-WCS paper III
31347    requires it to have at leats two axes). */
31348                   mdim = astGetColumnNdim( table, coordscol );
31349                   if( mdim == 1 && astOK ) {
31350                      astError( AST__BADTAB, "%s(%s): Unable to use the "
31351                                "coordinate array for FITS-WCS axis %d (type %s): "
31352                                "column '%s' in table '%s' has one axis but at "
31353                                "least two are required.", status, method, class,
31354                                 iaxis + 1, ctype, coordscol, extname );
31355                   }
31356 
31357 /* Allocate memory to hold the FITS-WCS axis corresponding to each dimension
31358    of the coordinate array. Initialise it to hold -1 (i.e. "no matching
31359    FITS-WCS axis yet found") at every element. */
31360                   marray = astMalloc( mdim*sizeof( int ) );
31361                   if( astOK ) {
31362                      for( maxis = 0; maxis < mdim; maxis++ ) {
31363                         marray[ maxis ] = -1;
31364                      }
31365 
31366 /* Loop round each dimension of the coordinate array, storing the index
31367    of the corresponding FITS-WCS axis in "marray". We omit the first axis
31368    (axis 0) since FITS-WCS Paper III defines it is a "conventional" axis
31369    used to enumerate the planes of coordinate values. */
31370                      for( maxis = 1; maxis < mdim && astOK ; maxis++ ) {
31371 
31372 /* Each axis of the coordinate array (except axis 0) must have one, and only
31373    one, corresponding FITS-WCS axis. Check each FITS-WCS axis to find one
31374    that uses the same table and column as the "iaxis" axis, and which
31375    corresponds to axis "maxis" of the coordinate array. */
31376                         for( iiaxis = 0; iiaxis < wcsaxes; iiaxis++ ) {
31377                            cval = GetItemC( &(store->ps), iiaxis, 0, s, NULL,
31378                                             method, class, status );
31379                            if( cval && !strcmp( cval, extname ) ) {
31380                               cval= GetItemC( &(store->ps), iiaxis, 1, s, NULL,
31381                                               method, class, status );
31382                               if( cval && !strcmp( cval, coordscol ) ) {
31383                                  dval = GetItem( &(store->pv), iiaxis, 3, s,
31384                                                  NULL, method, class, status );
31385                                  if( dval != AST__BAD ) {
31386                                     ival = (int)( dval + 0.5 );
31387                                  } else {
31388                                     ival = 1;
31389                                  }
31390                                  if( ival == maxis ) {
31391 
31392 /* Arrive here if the "iiaxis" FITS-WCS axis uses the same table and column
31393    as "iaxis", and corresponds to the "maxis" axis in the coordinate
31394    array. If this is the first matching FITS-WCS axis, store its index. */
31395                                     if( marray[ maxis ] == -1 ) {
31396                                        marray[ maxis ] = iiaxis;
31397 
31398 /* If a matching FITS-WCS axis has already been found, report an error. */
31399                                     } else if( astOK ) {
31400                                        astError( AST__BADTAB, "%s(%s): Unable to use "
31401                                                  "the coordinate array for FITS-WCS "
31402                                                  "axis %d (type %s): more than one "
31403                                                  "intermediate WCS axis is mapped onto "
31404                                                  " dimension %d of the coordinate "
31405                                                  "array in column '%s' of table '%s'.",
31406                                                  status, method, class, iaxis + 1,
31407                                                  ctype, maxis, coordscol, extname );
31408                                     }
31409                                  }
31410                               }
31411                            }
31412                         }
31413                      }
31414 
31415 /* Check that every dimension of the coordinate array (except the first) has
31416    a corresponding FITS-WCS axis. */
31417                      for( maxis = 1; maxis < mdim && astOK ; maxis++ ) {
31418                         if( marray[ maxis ] == -1 ) {
31419                            astError( AST__BADTAB, "%s(%s): Unable to use the "
31420                                      "coordinate array for FITS-WCS axis %d (type "
31421                                      "%s): no intermediate WCS axis is mapped onto "
31422                                      " dimension %d of the coordinate array in column "
31423                                      " '%s' of table '%s'.", status, method, class,
31424                                      iaxis + 1, ctype, maxis, coordscol, extname );
31425                         }
31426                      }
31427 
31428 /* Now we know which FITS-WCS axis corresponds to each dimension of the
31429    coordinate array. We now need to form a parallel CmpMap (compound Mapping)
31430    by gathering together the indexing vectors for each dimension of the
31431    coordinates array. Each indexing vector is represented by an inverted
31432    1D LutMap - dimensions that do not have an indexing vector are
31433    represented using a UnitMap. */
31434                      indexmap = NULL;
31435                      unit = 1;
31436                      for( maxis = 1; maxis < mdim && astOK ; maxis++ ) {
31437 
31438 /* Get the name of the column containing the index array. Defaults is to
31439    use a unit index, so do not report an error if not present. */
31440                         indexcol = GetItemC( &(store->ps), marray[ maxis ], 2,
31441                                              s, NULL, method, class, status );
31442 
31443 /* If the table contains an index vector, create a LutMap from it, then
31444    invert it. */
31445                         if( indexcol ) {
31446                            tmap1 = MakeColumnMap( table, indexcol, 1, 0,
31447                                                   method, class, status );
31448                            astInvert( tmap1 );
31449                            unit = 0;
31450 
31451 /* If the table does not contain an index vector, use a UnitMap. */
31452                         } else {
31453                            tmap1 = (AstMapping *) astUnitMap( 1, " ", status );
31454                         }
31455 
31456 /* Combine the index Mapping for this dimension in parallel with the
31457    Mapping for all earlier dimensions. */
31458                         if( indexmap ) {
31459                            tmap2 = (AstMapping *) astCmpMap( indexmap, tmap1,
31460                                                              0, " ", status );
31461                            indexmap = astAnnul( indexmap );
31462                            tmap1 = astAnnul( tmap1 );
31463                            indexmap = tmap2;
31464                         } else {
31465                            indexmap = tmap1;
31466                         }
31467                      }
31468 
31469 /* Get the interpolation method to use for the main coordinate array.
31470    This is an extension to the published -TAB algorithm in which the
31471    QVi_4a keyword is assumed to hold zero for linear interpolation (the
31472    default) and non-zero for nearest neighbour interpolation. The QVi_4a
31473    keyword will be translated to PVi_4a by the SpecTrans function. */
31474                      dval = GetItem( &(store->pv), iaxis, 4, s,
31475                                      NULL, method, class, status );
31476                      if( dval != AST__BAD ) {
31477                         interp = (int)( dval + 0.5 );
31478                      } else {
31479                         interp = 0;
31480                      }
31481 
31482 /* Make a Mapping from the main coordinate array, and then if required
31483    append it in series to the end of the index Mapping created above. */
31484                      tmap1 = MakeColumnMap( table, coordscol, 0, interp,
31485                                             method, class, status );
31486                      if( ! unit ) {
31487                         tmap2 = (AstMapping *) astCmpMap( indexmap, tmap1, 1,
31488                                                           " ", status );
31489                      } else {
31490                         tmap2 = astClone( tmap1 );
31491                      }
31492                      indexmap = astAnnul( indexmap );
31493                      tmap1 = astAnnul( tmap1 );
31494 
31495 /* Extend the array that holds the zero-based FITS-WCS axis index
31496    corresponding to each input of the extended "tmap0" mapping. Also create
31497    the inverse permutation (i.e. zero-based "tmap0" input indexed by
31498    zero-based FITS-WCS axis index). */
31499                      for( maxis = 1; maxis < mdim; maxis++ ) {
31500                         permout[ nperm ] = marray[ maxis ];
31501                         permin[ marray[ maxis ] ] = nperm++;
31502                      }
31503 
31504 /*  Free resources. */
31505                      marray = astFree( marray );
31506                   }
31507 
31508 /* Annul the table pointer. */
31509                   table = astAnnul( table );
31510 
31511 /* Clear the CTYPE algorithm code to indicate that the axis should be
31512    considered to be linear from now on. This means that the following
31513    functions will create a Mapping from pixel to psi (the system in which
31514    the CRVAL values are defined when using -TAB). The psi axes will then
31515    be mapping into the CS axes using the Mappign returned by this function. */
31516                   strncpy( name, ctype, 4 );
31517                   strcpy( name + 4, ctype + 8 );
31518                   SetItemC( &(store->ctype), iaxis, 0, s, name, status );
31519 
31520 /* Set the returned flag for this axis. */
31521                   (*tabaxis)[ iaxis ] = 1;
31522 
31523 /* If the FITS WCS axis "iaxis" does not use a -TAB algorithm, describe
31524    it in the returned Mapping using a 1D UnitMap. */
31525                } else {
31526                   tmap2 = (AstMapping *) astUnitMap( 1, " ", status );
31527 
31528 /* Extend the array that holds the zero-based FITS-WCS axis index
31529    corresponding to each input of the extended "tmap0" mapping. Also create
31530    the inverse permutation (i.e. zero-based "tmap0" input indexed by
31531    zero-based FITS-WCS axis index). */
31532                   permout[ nperm ] = iaxis;
31533                   permin[ iaxis ] = nperm++;
31534                }
31535 
31536 /* Append the Mapping describing the FITS WCS axis "iaxis" in parallel to any
31537    Mappings created for earlier "iaxis" axes. */
31538                if( tmap0 ) {
31539                   tmap1 = (AstMapping *) astCmpMap( tmap0, tmap2, 0, " ", status );
31540                   tmap0 = astAnnul( tmap0 );
31541                   tmap2 = astAnnul( tmap2 );
31542                   tmap0 = tmap1;
31543                } else {
31544                   tmap0 = tmap2;
31545                }
31546             }
31547          }
31548 
31549 /* If no -TAB axes were found, just return a NULL pointer. */
31550          if( extname && astOK ) {
31551 
31552 /* Do a sanity check on the permutation arrays. */
31553             for( iaxis = 0; iaxis < wcsaxes; iaxis++ ) {
31554                if( permin[ iaxis ] < 0 || permin[ iaxis ] >= wcsaxes ||
31555                    permout[ permin[ iaxis ] ] != iaxis ) {
31556                   astError( AST__INTER, "%s(%s): Invalid permutation "
31557                             "arrays in function TabMapping (internal AST "
31558                             "progranmming error).", status, method, class );
31559                   break;
31560                }
31561             }
31562 
31563 /* Sandwich the "tmap0" Mapping in series between two PermMaps to create a
31564    Mapping in which the inputs and outputs correspond to FITS WCS axis
31565    numbering. */
31566             pm = astPermMap( wcsaxes, permin, wcsaxes, permout, NULL, " ",
31567                              status  );
31568             tmap1 = (AstMapping *) astCmpMap( pm, tmap0, 1, " ", status );
31569             astInvert( pm );
31570             tmap2 = (AstMapping *) astCmpMap( tmap1, pm, 1, " ", status );
31571             pm = astAnnul( pm );
31572             tmap1 = astAnnul( tmap1 );
31573 
31574 /* Simplify the returned Mapping. */
31575             ret = astSimplify( tmap2 );
31576             tmap2 = astAnnul( tmap2 );
31577          }
31578 
31579 /* Free remaining resources */
31580          tmap0 = astAnnul( tmap0 );
31581       }
31582       permout = astFree( permout );
31583       permin = astFree( permin );
31584 
31585 /* Remove all used tables from the FitsChan now that they have been used. */
31586       nkey = astMapSize( used_tables );
31587       for( ikey = 0; ikey < nkey; ikey++ ) {
31588          astRemoveTables( this, astMapKey( used_tables, ikey ) );
31589       }
31590 
31591 /* Delete the KeyMap holding the used table names. */
31592       used_tables = astAnnul( used_tables );
31593 
31594 /* If we are not returning a Mapping, ensure we do not return any axis
31595    flags either. */
31596       if( !ret ) *tabaxis = astFree( *tabaxis );
31597    }
31598 
31599 /* Return the result */
31600    return ret;
31601 }
TabSourceWrap(void (* tabsource)(void),AstFitsChan * this,const char * extname,int extver,int extlevel,int * status)31602 static void TabSourceWrap( void (*tabsource)( void  ),
31603                            AstFitsChan *this, const char *extname,
31604                            int extver, int extlevel, int *status ){
31605 
31606 /*
31607 *  Name:
31608 *     TabSourceWrap
31609 
31610 *  Purpose:
31611 *     Wrapper function to invoke the C table source function.
31612 
31613 *  Type:
31614 *     Private function.
31615 
31616 *  Synopsis:
31617 *     #include "fitschan.h"
31618 *     void TabSourceWrap( void (*tabsource)( void ),
31619 *                         AstFitsChan *this, const char *extname,
31620 *                         int extver, int extlevel, int *status )
31621 
31622 *  Class Membership:
31623 *     Channel member function.
31624 
31625 *  Description:
31626 *     This function invokes the table source function whose pointer is
31627 *     supplied in order to read a named FITS binary table from an external
31628 *     FITS file.
31629 
31630 *  Parameters:
31631 *     tabsource
31632 *        Pointer to the C tab source function.
31633 *     this
31634 *        Pointer to the FitsChan. The reference count for the FitsChan is
31635 *        decremented by this function (this behaviour is imposed by
31636 *        restrictions in the equivalent Fortran wrapper function).
31637 *     extname
31638 *        Pointer to the string holding the name of the FITS extension
31639 *        from which a table is to be read.
31640 *     extver
31641 *        The integer "EXTVER" value for the required extension.
31642 *     extlevel
31643 *        The integer "EXTLEVEL" value for the required extension.
31644 *     status
31645 *        Pointer to the inherited status variable.
31646 */
31647 
31648 /* Local Variables: */
31649    AstFitsChan *this_id;
31650    int lstat;
31651 
31652 /* Check the global error status. */
31653    if ( !astOK ) return;
31654 
31655 /* Get an external identifier for the FitsChan. Could use astClone here
31656    to avoid this function anulling the supplied pointer, but the F77 wrapper
31657    cannot use the protected version of astClone, so for consistency we do
31658    not use it here either. */
31659    this_id = astMakeId( this );
31660 
31661 /* Invoke the table source function (casting it to the C API first) to
31662    read the table, and store it in the FitsChan. */
31663    ( *( void (*)( struct AstFitsChan *, const char *, int, int, int * ) )tabsource )( this_id, extname, extver, extlevel, &lstat );
31664 
31665 /* Free the FitsChan identifier (this annuls the supplied "this" pointer). */
31666    this_id = astAnnulId( this_id );
31667 
31668 /* Report an error if the source function failed. */
31669    if( !lstat ) {
31670       astError( AST__NOTAB, "astRead(%s): The table source function failed to read "
31671                 "a binary table from extension %s in an external FITS file.",
31672                 status, astGetClass( this ), extname );
31673    }
31674 }
31675 
TDBConv(double mjd,int timescale,int fromTDB,const char * method,const char * class,int * status)31676 static double TDBConv( double mjd, int timescale, int fromTDB,
31677                        const char *method, const char *class, int *status ){
31678 /*
31679 *  Name:
31680 *     TDBConv
31681 
31682 *  Purpose:
31683 *     Convert an MJD between the TDB time scale and another timescale.
31684 
31685 *  Type:
31686 *     Private function.
31687 
31688 *  Synopsis:
31689 *     double TDBConv( double mjd, int timescale, int fromTDB,
31690 *                     const char *method, const char *class, int *status )
31691 
31692 *  Class Membership:
31693 *     FitsChan
31694 
31695 *  Description:
31696 *     This function converts the supplied mjd value to or from the TDB
31697 *     timescale.
31698 
31699 *  Parameters:
31700 *     mjd
31701 *        The input MJD value.
31702 *     timescale
31703 *        The other timescale.
31704 *     fromTDB
31705 *        Indicates the direction of the required conversion. If non-zero,
31706 *        the supplied "mjd" value should be in the TDB timescale, and the
31707 *        returned value will be in the timescale specified by "timescale".
31708 *        If zero, the supplied "mjd" value should be in the timescale
31709 *        specified by "timescale", and the returned value will be in the
31710 *        TDB timescale.
31711 *     method
31712 *        The calling method. Used only in error messages.
31713 *     class
31714 *        The object class. Used only in error messages.
31715 *     status
31716 *        Pointer to the inherited status variable.
31717 
31718 *  Returned Value:
31719 *     The converted MJD value, or AST__BAD if an error occurs.
31720 */
31721 
31722 /* Local Variables: */
31723    AstFrameSet *fs;    /* Mapping from supplied timescale to TDB */
31724    double ret;         /* The returned value */
31725 
31726 /* Initialise */
31727    ret = AST__BAD;
31728 
31729 /* Check inherited status and supplied TDB value. */
31730    if( !astOK || mjd == AST__BAD ) return ret;
31731 
31732 /* Return the supplied value if no conversion is needed. */
31733    if( timescale == AST__TDB ) {
31734       ret = mjd;
31735 
31736 /* Otherwise, do the conversion. */
31737    } else {
31738 
31739 /* Lock the timeframes for use by the current thread, waiting if they are
31740    currently locked by another thread. */
31741       astManageLock( timeframe, AST__LOCK, 1, NULL );
31742       astManageLock( tdbframe, AST__LOCK, 1, NULL );
31743 
31744 /* Set the required timescale. */
31745       astSetTimeScale( timeframe, timescale );
31746 
31747 /* Get the Mapping between the two timescales, and use it to convert the
31748    suipplied value. */
31749       fs = astConvert( tdbframe, timeframe, "" );
31750       astTran1( fs, 1, &mjd, fromTDB, &ret );
31751       fs = astAnnul( fs );
31752 
31753 /* Unlock the timeframes. */
31754       astManageLock( timeframe, AST__UNLOCK, 1, NULL );
31755       astManageLock( tdbframe, AST__UNLOCK, 1, NULL );
31756    }
31757 
31758 /* Return the result */
31759    return ret;
31760 }
31761 
TestAttrib(AstObject * this_object,const char * attrib,int * status)31762 static int TestAttrib( AstObject *this_object, const char *attrib, int *status ) {
31763 /*
31764 *  Name:
31765 *     TestAttrib
31766 
31767 *  Purpose:
31768 *     Test if a specified attribute value is set for a FitsChan.
31769 
31770 *  Type:
31771 *     Private function.
31772 
31773 *  Synopsis:
31774 *     #include "fitschan.h"
31775 *     int TestAttrib( AstObject *this, const char *attrib, int *status )
31776 
31777 *  Class Membership:
31778 *     FitsChan member function (over-rides the astTestAttrib protected
31779 *     method inherited from the Channel class).
31780 
31781 *  Description:
31782 *     This function returns a boolean result (0 or 1) to indicate whether
31783 *     a value has been set for one of a FitsChan's attributes.
31784 
31785 *  Parameters:
31786 *     this
31787 *        Pointer to the FitsChan.
31788 *     attrib
31789 *        Pointer to a null-terminated string specifying the attribute
31790 *        name.  This should be in lower case with no surrounding white
31791 *        space.
31792 *     status
31793 *        Pointer to the inherited status variable.
31794 
31795 *  Returned Value:
31796 *     One if a value has been set, otherwise zero.
31797 
31798 *  Notes:
31799 *     - A value of zero will be returned if this function is invoked
31800 *     with the global status set, or if it should fail for any reason.
31801 */
31802 
31803 /* Local Variables: */
31804    AstFitsChan *this;            /* Pointer to the FitsChan structure */
31805    int result;                   /* Result value to return */
31806 
31807 /* Initialise. */
31808    result = 0;
31809 
31810 /* Check the global error status. */
31811    if ( !astOK ) return result;
31812 
31813 /* Obtain a pointer to the FitsChan structure. */
31814    this = (AstFitsChan *) this_object;
31815 
31816 /* Card. */
31817 /* ----- */
31818    if ( !strcmp( attrib, "card" ) ) {
31819       result = astTestCard( this );
31820 
31821 /* Encoding. */
31822 /* --------- */
31823    } else if ( !strcmp( attrib, "encoding" ) ) {
31824       result = astTestEncoding( this );
31825 
31826 /* FitsAxisOrder. */
31827 /* -------------- */
31828    } else if ( !strcmp( attrib, "fitsaxisorder" ) ) {
31829       result = astTestFitsAxisOrder( this );
31830 
31831 /* FitsDigits. */
31832 /* ----------- */
31833    } else if ( !strcmp( attrib, "fitsdigits" ) ) {
31834       result = astTestFitsDigits( this );
31835 
31836 /* DefB1950. */
31837 /* --------- */
31838    } else if ( !strcmp( attrib, "defb1950" ) ) {
31839       result = astTestDefB1950( this );
31840 
31841 /* TabOK. */
31842 /* ------ */
31843    } else if ( !strcmp( attrib, "tabok" ) ) {
31844       result = astTestTabOK( this );
31845 
31846 /* CDMatrix. */
31847 /* --------- */
31848    } else if ( !strcmp( attrib, "cdmatrix" ) ) {
31849       result = astTestCDMatrix( this );
31850 
31851 /* CarLin. */
31852 /* --------- */
31853    } else if ( !strcmp( attrib, "carlin" ) ) {
31854       result = astTestCarLin( this );
31855 
31856 /* PolyTan */
31857 /* ------- */
31858    } else if ( !strcmp( attrib, "polytan" ) ) {
31859       result = astTestPolyTan( this );
31860 
31861 /* Iwc. */
31862 /* ---- */
31863    } else if ( !strcmp( attrib, "iwc" ) ) {
31864       result = astTestIwc( this );
31865 
31866 /* Clean. */
31867 /* ------ */
31868    } else if ( !strcmp( attrib, "clean" ) ) {
31869       result = astTestClean( this );
31870 
31871 /* Warnings. */
31872 /* -------- */
31873    } else if ( !strcmp( attrib, "warnings" ) ) {
31874       result = astTestWarnings( this );
31875 
31876 /* If the name is not recognised, test if it matches any of the
31877    read-only attributes of this class. If it does, then return
31878    zero. */
31879    } else if ( !strcmp( attrib, "ncard" ) ||
31880                !strcmp( attrib, "nkey" ) ||
31881                !strcmp( attrib, "cardtype" ) ||
31882                !strcmp( attrib, "cardcomm" ) ||
31883                !strcmp( attrib, "cardname" ) ||
31884                !strcmp( attrib, "allwarnings" ) ){
31885       result = 0;
31886 
31887 /* If the attribute is still not recognised, pass it on to the parent
31888    method for further interpretation. */
31889    } else {
31890       result = (*parent_testattrib)( this_object, attrib, status );
31891    }
31892 
31893 /* Return the result, */
31894    return result;
31895 }
31896 
TestCard(AstFitsChan * this,int * status)31897 static int TestCard( AstFitsChan *this, int *status ){
31898 
31899 /*
31900 *+
31901 *  Name:
31902 *     astTestCard
31903 
31904 *  Purpose:
31905 *     Test the Card attribute.
31906 
31907 *  Type:
31908 *     Protected virtual function.
31909 
31910 *  Synopsis:
31911 *     #include "fitschan.h"
31912 *     int astTestCard( AstFitsChan *this )
31913 
31914 *  Class Membership:
31915 *     FitsChan method.
31916 
31917 *  Description:
31918 *     This function tests the Card attribute for the supplied FitsChan.
31919 
31920 *  Parameters:
31921 *     this
31922 *        Pointer to the FitsChan.
31923 
31924 *  Returned Value:
31925 *     If the Card attribute has its "cleared" value (i.e. if the first card
31926 *     in the FitsChan will be the next one to be read), then zero is returned,
31927 *     otherwise 1 is returned.
31928 *-
31929 */
31930 
31931 /* Local Variables: */
31932    int card;               /* The original value of Card */
31933    int ret;                /* The returned flag */
31934 
31935 /* Ensure the source function has been called */
31936    ReadFromSource( this, status );
31937 
31938 /* Get the current value of Card. */
31939    card = astGetCard( this );
31940 
31941 /* Temporarily clear Card. */
31942    astClearCard( this );
31943 
31944 /* See if the original Card is equal to the cleared card, and set the
31945    returned flag appropriately. Re-instate the original value of card is
31946    required.*/
31947    if( astGetCard( this ) == card ) {
31948       ret = 0;
31949    } else {
31950       astSetCard( this, card );
31951       ret = 1;
31952    }
31953 
31954 /* Return the flag. */
31955    return ret;
31956 }
31957 
TestFits(AstFitsChan * this,const char * name,int * there,int * status)31958 static int TestFits( AstFitsChan *this, const char *name, int *there,
31959                      int *status ){
31960 
31961 /*
31962 *++
31963 *  Name:
31964 c     astTestFits
31965 f     AST_TESTFITS
31966 
31967 *  Purpose:
31968 *     See if a named keyword has a defined value in a FitsChan.
31969 
31970 *  Type:
31971 *     Public virtual function.
31972 
31973 *  Synopsis:
31974 c     #include "fitschan.h"
31975 
31976 c     int astTestFits( AstFitsChan *this, const char *name, int *there )
31977 f     RESULT = AST_TESTFITS( THIS, NAME, THERE, STATUS )
31978 
31979 *  Class Membership:
31980 *     FitsChan method.
31981 
31982 *  Description:
31983 *     This function serches for a named keyword in a FitsChan. If found,
31984 *     and if the keyword has a value associated with it, a
31985 c     non-zero
31986 f     .TRUE.
31987 *     value is returned. If the keyword is not found, or if it does not
31988 *     have an associated value, a
31989 c     zero
31990 f     .FALSE.
31991 *     value is returned.
31992 
31993 *  Parameters:
31994 c     this
31995 f     THIS = INTEGER (Given)
31996 *        Pointer to the FitsChan.
31997 c     name
31998 f     NAME = CHARACTER * ( * ) (Given)
31999 c        Pointer to a null-terminated character string
32000 f        A character string
32001 *        containing the FITS keyword name. This may be a complete FITS
32002 *        header card, in which case the keyword to use is extracted from
32003 *        it. No more than 80 characters are read from this string.
32004 c     there
32005 f     THERE = LOGICAL (Returned)
32006 c        Pointer to an integer which will be returned holding a non-zero
32007 c        value if the keyword was found in the header, and zero otherwise.
32008 f        A value of .TRUE. will be returned if the keyword was found in the
32009 f        header, and .FALSE. otherwise.
32010 *        This parameter allows a distinction to be made between the case
32011 *        where a keyword is not present, and the case where a keyword is
32012 *        present but has no associated value.
32013 c        A NULL pointer may be supplied if this information is not
32014 c        required.
32015 f     STATUS = INTEGER (Given and Returned)
32016 f        The global status.
32017 
32018 *  Returned Value:
32019 c     astTestFits()
32020 f     AST_TESTFITS = LOGICAL
32021 *        A value of zero
32022 f        .FALSE.
32023 *        is returned if the keyword was not found in the FitsChan or has
32024 *        no associated value. Otherwise, a value of
32025 c        one
32026 f        .TRUE.
32027 *        is returned.
32028 
32029 *  Notes:
32030 *     -  The current card is left unchanged by this function.
32031 *     -  The card following the current card is checked first. If this is
32032 *     not the required card, then the rest of the FitsChan is searched,
32033 *     starting with the first card added to the FitsChan. Therefore cards
32034 *     should be accessed in the order they are stored in the FitsChan (if
32035 *     possible) as this will minimise the time spent searching for cards.
32036 *     -  An error will be reported if the keyword name does not conform
32037 *     to FITS requirements.
32038 c     -  Zero
32039 f     -  .FALSE.
32040 *     is returned as the function value if an error has already occurred,
32041 *     or if this function should fail for any reason.
32042 *--
32043 */
32044 
32045 /* Local Variables: */
32046    const char *class;     /* Object class */
32047    const char *method;    /* Calling method */
32048    char *lcom;            /* Supplied keyword comment */
32049    char *lname;           /* Supplied keyword name */
32050    char *lvalue;          /* Supplied keyword value */
32051    int icard;             /* Current card index on entry */
32052    int ret;               /* The returned value */
32053 
32054 /* Initialise */
32055    if( there ) *there = 0;
32056 
32057 /* Check the global error status. */
32058    if ( !astOK ) return 0;
32059 
32060 /* Ensure the source function has been called */
32061    ReadFromSource( this, status );
32062 
32063 /* Store the calling method and object class. */
32064    method = "astTestFits";
32065    class = astGetClass( this );
32066 
32067 /* Initialise the returned value. */
32068    ret = 0;
32069 
32070 /* Extract the keyword name from the supplied string. */
32071    (void) Split( name, &lname, &lvalue, &lcom, method, class, status );
32072 
32073 /* Store the current card index. */
32074    icard = astGetCard( this );
32075 
32076 /* Attempt to find a card in the FitsChan refering to this keyword,
32077    and make it the current card. Only proceed if a card was found. */
32078    if( SearchCard( this, lname, method, class, status ) ){
32079 
32080 /* Indicate the card has been found. */
32081       if( there ) *there = 1;
32082 
32083 /* If the cards data type is no undefined, return 1. */
32084       if( CardType( this, status ) != AST__UNDEF ) ret = 1;
32085    }
32086 
32087 /* Re-instate the original current card index. */
32088    astSetCard( this, icard );
32089 
32090 /* Release the memory used to hold keyword name, value and comment strings. */
32091    lname = (char *) astFree( (void *) lname );
32092    lvalue = (char *) astFree( (void *) lvalue );
32093    lcom = (char *) astFree( (void *) lcom );
32094 
32095 /* Return the answer. */
32096    return ret;
32097 }
32098 
TidyOffsets(AstFrameSet * fset,int * status)32099 static void TidyOffsets( AstFrameSet *fset, int *status ) {
32100 /*
32101 *  Name:
32102 *     TidyOffsets
32103 
32104 *  Purpose:
32105 *     Remove un-needed offset coordinate Frames.
32106 
32107 *  Type:
32108 *     Private function.
32109 
32110 *  Synopsis:
32111 *     void TidyOffsets( AstFrameSet *fset, int *status )
32112 
32113 *  Class Membership:
32114 *     FitsChan
32115 
32116 *  Description:
32117 *     A FITS header stores offset sky coordinates as two alternaive axis
32118 *     descriptions - one giving the offset axes and one giving the absolute
32119 *     axes. But AST can hold both forms in a single SkyFrame. This function
32120 *     removes the FITS Frames describing offset axes from the FrameSet.
32121 *     The remaining absolute Frame is then used to describe both absolute
32122 *     and offset.
32123 
32124 *  Parameters:
32125 *     fset
32126 *        A FrameSet holding the Frames read from a FITS-WCS Header.
32127 *     status
32128 *        Pointer to the inherited status variable.
32129 */
32130 
32131 /* Local Variables: */
32132    AstFrame *frm;
32133    AstFrame *pfrm;
32134    const char *dom;
32135    const char *skyrefis;
32136    int hasabs;
32137    int hasoff;
32138    int iax;
32139    int icurr;
32140    int icurr_is_offset;
32141    int ifrm;
32142    int nax;
32143    int nfrm;
32144    int pax;
32145    int remove;
32146 
32147 /* Check the inherited status. */
32148    if( !astOK ) return;
32149 
32150 /* Note the original current Frame index. */
32151    icurr = astGetCurrent( fset );
32152 
32153 /* Assume the current Frame is not an offset frame until proven
32154    otherwise. */
32155    icurr_is_offset = 0;
32156 
32157 /* Does the FrameSet contain any Frames holding sky offsets? Such Frames
32158    should have been given a Domain of SKY_OFFSETS within function
32159    WcsSkyFrame. Loop round all Frames, checking each one. Also note if
32160    the FrameSet contains any (absolute) SKY frames. Also set the SkyRefIs
32161    attribute for any absolute SkyFrames that were marked with domains
32162    SKY_POLE or SKY_OFFSET in WcsSkyFrame. */
32163    hasabs = 0;
32164    hasoff = 0;
32165    nfrm = astGetNframe( fset );
32166    for( ifrm = 1; ifrm <= nfrm; ifrm++ ){
32167       skyrefis = NULL;
32168       frm = astGetFrame( fset, ifrm );
32169       nax = astGetNaxes( frm );
32170       for( iax = 0; iax < nax; iax++ ) {
32171          astPrimaryFrame( frm, iax, &pfrm, &pax );
32172          if( astIsASkyFrame( pfrm ) ) {
32173             dom = astGetDomain( pfrm );
32174             if( dom ) {
32175                if( !strcmp( dom, "SKY_OFFSETS" ) ){
32176                   hasoff = 1;
32177                   if( ifrm == icurr ) icurr_is_offset = 1;
32178                   iax = nax;
32179                } else if( !strcmp( dom, "SKY" ) ){
32180                   hasabs = 1;
32181                   iax = nax;
32182                } else if( !strcmp( dom, "SKY_POLE" ) ){
32183                   hasabs = 1;
32184                   skyrefis = "POLE";
32185                   iax = nax;
32186                } else if( !strcmp( dom, "SKY_ORIGIN" ) ){
32187                   hasabs = 1;
32188                   skyrefis = "ORIGIN";
32189                   iax = nax;
32190                }
32191             }
32192          }
32193          pfrm = astAnnul( pfrm );
32194       }
32195       frm = astAnnul( frm );
32196 
32197       if( skyrefis ) {
32198          astSetI( fset, "Current", ifrm);
32199          astSetC( fset, "SkyRefIs", skyrefis );
32200          astSetI( fset, "Current", icurr );
32201       }
32202    }
32203 
32204 /* If one or more absolute sky frames were found, then remove any offset
32205    sky frames. Clear the Ident attribute (that holds the FITS-WCS alternate
32206    axis description character) for any absoute Frames. */
32207    if( hasabs && hasoff ) {
32208 
32209       for( ifrm = nfrm; ifrm > 0; ifrm-- ) {
32210          remove = 0;
32211          frm = astGetFrame( fset, ifrm );
32212          nax = astGetNaxes( frm );
32213          for( iax = 0; iax < nax; iax++ ) {
32214             astPrimaryFrame( frm, iax, &pfrm, &pax );
32215             if( astIsASkyFrame( pfrm ) ) {
32216                dom = astGetDomain( pfrm );
32217                if( dom ) {
32218                   if( !strcmp( dom, "SKY_OFFSETS" ) ){
32219                      remove = 1;
32220                      iax = nax;
32221 
32222                   } else if( !strcmp( dom, "SKY_POLE" ) ||
32223                              !strcmp( dom, "SKY_ORIGIN" ) ){
32224                      astClearIdent( frm );
32225                      astClearDomain( pfrm );
32226 
32227 /* If we will be deleting the original current Frame (because it is an
32228    offset Frame), then mark the first absolute Frame as the new current
32229    Frame. */
32230                      if( icurr_is_offset ) {
32231                         astSetCurrent( fset, ifrm );
32232                         icurr_is_offset = 0;
32233                      }
32234                      iax = nax;
32235                   }
32236                }
32237             }
32238             pfrm = astAnnul( pfrm );
32239          }
32240          frm = astAnnul( frm );
32241 
32242          if( remove ) astRemoveFrame( fset, ifrm );
32243       }
32244    }
32245 }
32246 
TimeSysToAst(AstFitsChan * this,const char * timesys,const char * method,const char * class,int * status)32247 static AstTimeScaleType TimeSysToAst( AstFitsChan *this, const char *timesys,
32248                                       const char *method, const char *class, int *status ){
32249 /*
32250 *  Name:
32251 *     TimeSysToAst
32252 
32253 *  Purpose:
32254 *     Convert a FITS TIMESYS value to an AST TimeFrame timescale value.
32255 
32256 *  Type:
32257 *     Private function.
32258 
32259 *  Synopsis:
32260 *     AstTimeScaleType TimeSysToAst( AstFitsChan *this, const char *timesys,
32261 *                                    const char *method, const char *class, int *status )
32262 
32263 *  Class Membership:
32264 *     FitsChan
32265 
32266 *  Description:
32267 *     This function returns the value used by the AST TimeFrame class to
32268 *     represent the timescale specified by the "timesys" parameter, which
32269 *     should hold the value of a FITS TIMESYS keyword. The TIMESYS
32270 *     convention was introduced as part of the Y2K DATE-OBS changes, and
32271 *     is not currently part of the published FITS-WCS conventions.
32272 *
32273 *     If the requested timescale is not supported by AST, then a warning is
32274 *     added to the FitsChan and a value of AST__UTC is returned (but no
32275 *     error is reported).
32276 
32277 *  Parameters:
32278 *     this
32279 *        Pointer to the FitsChan.
32280 *     timesys
32281 *        Pointer to the string holding the TIMESYS value. A NULL pointer
32282 *        returns the default timescale of UTC.
32283 *     method
32284 *        Pointer to a string holding the name of the calling method.
32285 *        This is only for use in constructing error messages.
32286 *     class
32287 *        Pointer to a string holding the name of the supplied object class.
32288 *        This is only for use in constructing error messages.
32289 *     status
32290 *        Pointer to the inherited status variable.
32291 
32292 *  Returned Value:
32293 *     The equivalent AstTimeScaleType value.
32294 */
32295 
32296 /* Local Variables: */
32297    AstTimeScaleType result;  /* The returned timescale */
32298    char buf[ 200 ];          /* Buffer for warning message */
32299 
32300 /* Initialise */
32301    result = AST__UTC;
32302 
32303 /* Check the inherited status. */
32304    if( !astOK ) return result;
32305    if( !timesys ) {
32306       result = AST__UTC;
32307    } else if( !strcmp( timesys, "UTC" ) ) {
32308       result = AST__UTC;
32309    } else if( !strcmp( timesys, "UT" ) ) {
32310       result = AST__UTC;
32311       Warn( this, "badval", "The original FITS header contained a value of UT "
32312             "for keyword TIMESYS which is being interpreted as UTC.", method,
32313             class, status );
32314    } else if( !strcmp( timesys, "TAI" ) ) {
32315       result = AST__TAI;
32316    } else if( !strcmp( timesys, "IAT" ) ) {
32317       result = AST__TAI;
32318    } else if( !strcmp( timesys, "ET" ) ) {
32319       result = AST__TT;
32320       Warn( this, "badval", "The original FITS header contained a value of ET "
32321             "for keyword TIMESYS. TT will be used instead.", method, class, status );
32322    } else if( !strcmp( timesys, "TT" ) ) {
32323       result = AST__TT;
32324    } else if( !strcmp( timesys, "TDT" ) ) {
32325       result = AST__TT;
32326    } else if( !strcmp( timesys, "TDB" ) ) {
32327       result = AST__TDB;
32328    } else if( !strcmp( timesys, "TCG" ) ) {
32329       result = AST__TCG;
32330    } else if( !strcmp( timesys, "TCB" ) ) {
32331       result = AST__TCB;
32332    } else {
32333       result = AST__UTC;
32334       sprintf( buf, "The original FITS header contained a value of %s for "
32335                "keyword TIMESYS. AST does not support this timescale so "
32336                "UTC will be used instead.", timesys );
32337       Warn( this, "badval", buf, method, class, status );
32338    }
32339 
32340 /* Return the result */
32341    return result;
32342 }
32343 
UnPreQuote(const char * string,int * status)32344 static char *UnPreQuote( const char *string, int *status ) {
32345 /*
32346 *  Name:
32347 *     UnPreQuote
32348 
32349 *  Purpose:
32350 *     Reverse the pre-quoting of FITS character data.
32351 
32352 *  Type:
32353 *     Private function.
32354 
32355 *  Synopsis:
32356 *     #include "fitschan.h"
32357 *     char *UnPreQuote( const char *string, int *status )
32358 
32359 *  Class Membership:
32360 *     FitsChan member function.
32361 
32362 *  Description:
32363 *     This function reverses the effect of the PreQuote function on a
32364 *     string (apart from any loss of data due to truncation). It
32365 *     should be used to recover the original character data from the
32366 *     pre-quoted version of a string retrieved from a FITS character
32367 *     value associated with a keyword.
32368 
32369 *  Parameters:
32370 *     string
32371 *        Pointer to a constant null-terminated string containing the
32372 *        pre-quoted character data.
32373 *     status
32374 *        Pointer to the inherited status variable.
32375 
32376 *  Returned Value:
32377 *     Pointer to a dynamically allocated null-terminated string
32378 *     containing the un-quoted character data. The memory holding this
32379 *     string should be freed by the caller (using astFree) when no
32380 *     longer required.
32381 
32382 *  Notes:
32383 *     - A NULL pointer value will be returned if this function is
32384 *     invoked wth the global error status set, or if it should fail
32385 *     for any reason.
32386 */
32387 
32388 /* Local Variables: */
32389    char *result;                 /* Pointer value to return */
32390    int i1;                       /* Offset of first useful character */
32391    int i2;                       /* Offest of last useful character */
32392 
32393 /* Check the global error status. */
32394    if ( !astOK ) return NULL;
32395 
32396 /* Initialise to use the first and last characters in the input
32397    string. */
32398    i1 = 0;
32399    i2 = strlen( string ) - 1;
32400 
32401 /* If the string contains at least 2 characters, check if the first
32402    and last characters are double quotes ("). If so, adjust the
32403    offsets to exclude them. */
32404    if ( ( i2 > i1 ) &&
32405         ( string[ i1 ] == '"' ) && ( string[ i2 ] == '"' ) ) {
32406       i1++;
32407       i2--;
32408    }
32409 
32410 /* Make a dynamically allocated copy of the useful part of the
32411    string. */
32412    result = astString( string + i1, i2 - i1 + 1 );
32413 
32414 /* Return the answer. */
32415    return result;
32416 }
32417 
Use(AstFitsChan * this,int set,int helpful,int * status)32418 static int Use( AstFitsChan *this, int set, int helpful, int *status ) {
32419 
32420 /*
32421 *  Name:
32422 *     Use
32423 
32424 *  Purpose:
32425 *     Decide whether to write a value to a FitsChan.
32426 
32427 *  Type:
32428 *     Private function.
32429 
32430 *  Synopsis:
32431 *     #include "fitschan.h"
32432 
32433 *     int Use( AstFitsChan *this, int set, int helpful, int *status )
32434 
32435 *  Class Membership:
32436 *     FitsChan member function.
32437 
32438 *  Description:
32439 *     This function decides whether a value supplied by a class "Dump"
32440 *     function, via a call to one of the astWrite... protected
32441 *     methods, should actually be written to a FitsChan.
32442 *
32443 *     This decision is based on the settings of the "set" and
32444 *     "helpful" flags supplied to the astWrite... method, plus the
32445 *     attribute settings of the FitsChan.
32446 
32447 *  Parameters:
32448 *     this
32449 *        A pointer to the FitsChan.
32450 *     set
32451 *        The "set" flag supplied.
32452 *     helpful
32453 *        The "helpful" value supplied.
32454 *     status
32455 *        Pointer to the inherited status variable.
32456 
32457 *  Returned Value:
32458 *     One if the value should be written out, otherwise zero.
32459 
32460 *  Notes:
32461 *     - A value of zero will be returned if this function is invoked
32462 *     with the global error status set or if it should fail for any
32463 *     reason.
32464 */
32465 
32466 /* Local Variables: */
32467    int full;                     /* Full attribute value */
32468    int result;                   /* Result value to be returned */
32469 
32470 /* Check the global error status. */
32471    if ( !astOK ) return 0;
32472 
32473 /* If "set" is non-zero, then so is the result ("set" values must
32474    always be written out). */
32475    result = ( set != 0 );
32476 
32477 /* Otherwise, obtain the value of the FitsChan's Full attribute. */
32478    if ( !set ) {
32479       full = astGetFull( this );
32480 
32481 /* If Full is positive, display all values, if zero, display only
32482    "helpful" values, if negative, display no (un-"set") values. */
32483       if ( astOK ) result = ( ( helpful && ( full > -1 ) ) || ( full > 0 ) );
32484    }
32485 
32486 /* Return the result. */
32487    return result;
32488 }
32489 
Ustrcmp(const char * a,const char * b,int * status)32490 static int Ustrcmp( const char *a, const char *b, int *status ){
32491 /*
32492 *  Name:
32493 *     Ustrcmp
32494 
32495 *  Purpose:
32496 *     A case blind version of strcmp.
32497 
32498 *  Type:
32499 *     Private function.
32500 
32501 *  Synopsis:
32502 *     #include "fitschan.h"
32503 *     int Ustrcmp( const char *a, const char *b, int *status )
32504 
32505 *  Class Membership:
32506 *     FitsChan member function.
32507 
32508 *  Description:
32509 *     Returns 0 if there are no differences between the two strings, and 1
32510 *     otherwise. Comparisons are case blind.
32511 
32512 *  Parameters:
32513 *     a
32514 *        Pointer to first string.
32515 *     b
32516 *        Pointer to second string.
32517 *     status
32518 *        Pointer to the inherited status variable.
32519 
32520 *  Returned Value:
32521 *     Zero if the strings match, otherwise one.
32522 
32523 *  Notes:
32524 *     -  This function does not consider the sign of the difference between
32525 *     the two strings, whereas "strcmp" does.
32526 *     -  This function attempts to execute even if an error has occurred.
32527 */
32528 
32529 /* Local Variables: */
32530    const char *aa;         /* Pointer to next "a" character */
32531    const char *bb;         /* Pointer to next "b" character */
32532    int ret;                /* Returned value */
32533 
32534 /* Initialise the returned value to indicate that the strings match. */
32535    ret = 0;
32536 
32537 /* Initialise pointers to the start of each string. */
32538    aa = a;
32539    bb = b;
32540 
32541 /* Loop round each character. */
32542    while( 1 ){
32543 
32544 /* We leave the loop if either of the strings has been exhausted. */
32545       if( !(*aa ) || !(*bb) ){
32546 
32547 /* If one of the strings has not been exhausted, indicate that the
32548    strings are different. */
32549          if( *aa || *bb ) ret = 1;
32550 
32551 /* Break out of the loop. */
32552          break;
32553 
32554 /* If neither string has been exhausted, convert the next characters to
32555    upper case and compare them, incrementing the pointers to the next
32556    characters at the same time. If they are different, break out of the
32557    loop. */
32558       } else {
32559          if( toupper( (int) *(aa++) ) != toupper( (int) *(bb++) ) ){
32560             ret = 1;
32561             break;
32562          }
32563       }
32564    }
32565 
32566 /* Return the result. */
32567    return ret;
32568 }
32569 
Ustrncmp(const char * a,const char * b,size_t n,int * status)32570 static int Ustrncmp( const char *a, const char *b, size_t n, int *status ){
32571 /*
32572 *  Name:
32573 *     Ustrncmp
32574 
32575 *  Purpose:
32576 *     A case blind version of strncmp.
32577 
32578 *  Type:
32579 *     Private function.
32580 
32581 *  Synopsis:
32582 *     #include "fitschan.h"
32583 *     int Ustrncmp( const char *a, const char *b, size_t n, int *status )
32584 
32585 *  Class Membership:
32586 *     FitsChan member function.
32587 
32588 *  Description:
32589 *     Returns 0 if there are no differences between the first "n"
32590 *     characters of the two strings, and 1 otherwise. Comparisons are
32591 *     case blind.
32592 
32593 *  Parameters:
32594 *     a
32595 *        Pointer to first string.
32596 *     b
32597 *        Pointer to second string.
32598 *     n
32599 *        The maximum number of characters to compare.
32600 *     status
32601 *        Pointer to the inherited status variable.
32602 
32603 *  Returned Value:
32604 *     Zero if the strings match, otherwise one.
32605 
32606 *  Notes:
32607 *     -  This function does not consider the sign of the difference between
32608 *     the two strings, whereas "strncmp" does.
32609 *     -  This function attempts to execute even if an error has occurred.
32610 */
32611 
32612 /* Local Variables: */
32613    const char *aa;         /* Pointer to next "a" character */
32614    const char *bb;         /* Pointer to next "b" character */
32615    int i;                  /* Character index */
32616    int ret;                /* Returned value */
32617 
32618 /* Initialise the returned value to indicate that the strings match. */
32619    ret = 0;
32620 
32621 /* Initialise pointers to the start of each string. */
32622    aa = a;
32623    bb = b;
32624 
32625 /* Compare up to "n" characters. */
32626    for( i = 0; i < (int) n; i++ ){
32627 
32628 /* We leave the loop if either of the strings has been exhausted. */
32629       if( !(*aa ) || !(*bb) ){
32630 
32631 /* If one of the strings has not been exhausted, indicate that the
32632    strings are different. */
32633          if( *aa || *bb ) ret = 1;
32634 
32635 /* Break out of the loop. */
32636          break;
32637 
32638 /* If neither string has been exhausted, convert the next characters to
32639    upper case and compare them, incrementing the pointers to the next
32640    characters at the same time. If they are different, break out of the
32641    loop. */
32642       } else {
32643          if( toupper( (int) *(aa++) ) != toupper( (int) *(bb++) ) ){
32644             ret = 1;
32645             break;
32646          }
32647       }
32648    }
32649 
32650 /* Return the result. */
32651    return ret;
32652 }
32653 
Warn(AstFitsChan * this,const char * condition,const char * text,const char * method,const char * class,int * status)32654 static void Warn( AstFitsChan *this, const char *condition, const char *text,
32655                   const char*method, const char *class, int *status ){
32656 
32657 /*
32658 *  Name:
32659 *     Warn
32660 
32661 *  Purpose:
32662 *     Store warning cards in a FitsChan.
32663 
32664 *  Type:
32665 *     Private function.
32666 
32667 *  Synopsis:
32668 *     #include "fitschan.h"
32669 
32670 *     int Warn( AstFitsChan *this, const char *condition, const char *text,
32671 *               const char*method, const char *class, int *status );
32672 
32673 *  Class Membership:
32674 *     FitsChan member function.
32675 
32676 *  Description:
32677 *     If the Warnings attribute indicates that occurences of the specified
32678 *     condition should be reported, the supplied text is split into lines
32679 *     and stored in the FitsChan as a series of ASTWARN cards, in front
32680 *     of the current card. If the specified condition is not being reported,
32681 *     this function returns without action.
32682 
32683 *  Parameters:
32684 *     this
32685 *        The FitsChan. If NULL, this function returns without action.
32686 *     condition
32687 *        Pointer to a string holding a lower case condition name.
32688 *     text
32689 *        Pointer to a string holding the text of the warning.
32690 *     method
32691 *        Pointer to a string holding the name of the calling method.
32692 *        This is only for use in constructing error messages.
32693 *     class
32694 *        Pointer to a string holding the name of the supplied object class.
32695 *        This is only for use in constructing error messages.
32696 *     status
32697 *        Pointer to the inherited status variable.
32698 */
32699 
32700 /* Local Variables: */
32701    char buff[ AST__FITSCHAN_FITSCARDLEN + 1 ]; /* Buffer for new card text */
32702    const char *a;        /* Pointer to 1st character in next card */
32703    const char *b;        /* Pointer to terminating null character */
32704    const char *c;        /* Pointer to last character in next card */
32705    int exists;           /* Has the supplied warning already been issued? */
32706    int icard;            /* Index of original card */
32707    int nc;               /* No. of characters in next card */
32708 
32709 /* Check the inherited status, warning text, FitsChan and Clean attribute. */
32710    if( !astOK || !text || !text[0] || !this || astGetClean( this ) ) return;
32711 
32712 /* Ignore the warning if the supplied condition is not contained within
32713    the list of conditions to be reported in this way (given by the
32714    Warnings attribute). */
32715    if( FullForm( astGetWarnings( this ), condition, 0, status ) >= 0 ){
32716 
32717 /* If found, store the warning in the parent Channel structure. */
32718       astAddWarning( this, 1, "%s", method, status, text );
32719 
32720 /* For historical reasons, warnings are also stored in the FitsChan as a
32721    set of FITS cards... First save the current card index, and rewind the
32722    FitsChan. */
32723       icard = astGetCard( this );
32724       astClearCard( this );
32725 
32726 /* Break the supplied text into lines and check the FitsChan to see if
32727    a block of adjacent ASTWARN cards with these lines already exist
32728    within the FitsChan. Assume they do until proven otherwise. */
32729       exists = 1;
32730       a = text;
32731       b = a + strlen( text );
32732       while( a < b ){
32733 
32734 /* Each card contains about 60 characters of the text. Get a pointer to
32735    the nominal last character in the next card. */
32736          c = a + 60;
32737 
32738 /* If this puts the last character beyond the end of the text, use the
32739    last character before the null as the last character in the card. */
32740          if( c >= b ) {
32741             c = b - 1;
32742 
32743 /* Otherwise, if the last character is not a space, move the last
32744    character backwards to the first space. This avoids breaking words
32745    across cards. */
32746          } else {
32747             while( !isspace( *c ) && c > a ) c--;
32748          }
32749 
32750 /* Copy the text into a null terminated buffer. */
32751          nc = c - a + 1;
32752          strncpy( buff, a, nc );
32753          buff[ nc ] = 0;
32754 
32755 /* If this is the first line, search the entire FitsChan for an ASTWARN card
32756    with this text. If not, indiate that the supplied text needs to be
32757    stored in the FitsChan, and break out of the loop. */
32758          if( a == text ) {
32759             exists = 0;
32760             while( !exists &&
32761                    FindKeyCard( this, "ASTWARN", method, class, status ) ) {
32762                if( !strcmp( (const char *) CardData( this, NULL, status ), buff ) ) {
32763                   exists = 1;
32764                }
32765                MoveCard( this, 1, method, class, status );
32766             }
32767             if( !exists ) break;
32768 
32769 /* If this is not the first line, see if the next card in the FitsChan is
32770    an ASTWARN card with this text. If not, indiate that the supplied text
32771    needs to be stored in the FitsChan, and break out of the loop. */
32772          } else {
32773             if( !strcmp( CardName( this, status ), "ASTWARN" ) &&
32774                 !strcmp( (const char *) CardData( this, NULL, status ), buff ) ) {
32775                MoveCard( this, 1, method, class, status );
32776             } else {
32777                exists = 0;
32778                break;
32779             }
32780          }
32781 
32782 /* Set the start of the next bit of the text. */
32783          a = c + 1;
32784       }
32785 
32786 /* Reinstate the original current card index. */
32787       astSetCard( this, icard );
32788 
32789 /* We only add new cards to the FitsChan if they do not already exist. */
32790       if( !exists ) {
32791 
32792 /* Break the text into lines using the same algorithm as above, and store
32793    each line as a new ASTWARN card. Start with a blank ASTWARN card. */
32794          astSetFitsS( this, "ASTWARN", " ", NULL, 0 );
32795 
32796 /* Loop until the entire text has been written out. */
32797          a = text;
32798          b = a + strlen( text );
32799          while( a < b ){
32800 
32801 /* Each card contains about 60 characters of the text. Get a pointer to
32802    the nominal last character in the next card. */
32803             c = a + 60;
32804 
32805 /* If this puts the last character beyond the end of the text, use the
32806    last character before the null as the last character in the card. */
32807             if( c >= b ) {
32808                c = b - 1;
32809 
32810 /* Otherwise, if the last character is not a space, move the last
32811    character backwards to the first space. This avoids breaking words
32812    across cards. */
32813             } else {
32814                while( !isspace( *c ) && c > a ) c--;
32815             }
32816 
32817 /* Copy the text into a null terminated buffer. */
32818             nc = c - a + 1;
32819             strncpy( buff, a, nc );
32820             buff[ nc ] = 0;
32821 
32822 /* Store the buffer as the next card. */
32823             astSetFitsS( this, "ASTWARN", buff, NULL, 0 );
32824 
32825 /* Set the start of the next bit of the text. */
32826             a = c + 1;
32827          }
32828 
32829 /* Include a final blank card. */
32830          astSetFitsS( this, "ASTWARN", " ", NULL, 0 );
32831       }
32832    }
32833 }
32834 
WATCoeffs(const char * watstr,int iaxis,double ** cvals,int ** mvals,int * ok,int * status)32835 static int WATCoeffs( const char *watstr, int iaxis, double **cvals,
32836                       int **mvals, int *ok, int *status ){
32837 /*
32838 *  Name:
32839 *     WATCoeffs
32840 
32841 *  Purpose:
32842 *     Get the polynomial coefficients from the lngcor or latcor component
32843 *     of an IRAF WAT string.
32844 
32845 *  Type:
32846 *     Private function.
32847 
32848 *  Synopsis:
32849 *     int WATCoeffs( const char *watstr, int iaxis, double **cvals,
32850 *                    int **mvals, int *ok, int *status )
32851 
32852 *  Class Membership:
32853 *     FitsChan
32854 
32855 *  Description:
32856 *     This function extracts the polynomial coefficients from a supplied
32857 *     string containing the concatenated values of a set of IRAF "WAT"
32858 *     keywords, such as used for the IRAF-specific TNX and ZPX projections.
32859 *     The coefficients are returned in the form of a set of PVi_m values
32860 *     for a TPN projection.
32861 
32862 *  Parameters:
32863 *     watstr
32864 *        The concatentated WAT keyword values.
32865 *     iaxis
32866 *        Zero based index of the axis to which the WAT keywords refer (0
32867 *        or 1).
32868 *     cvals
32869 *        Location at which to return a pointer to a dynamically allocated
32870 *        list of coefficient values, or NULL if no lngcor/latcor values
32871 *        were found in the WAT string. Free using astFree.
32872 *     mvals
32873 *        Location at which to return a pointer to a dynamically allocated
32874 *        list of coefficient indices, or NULL if no lngcor/latcor values
32875 *        were found in the WAT string. Free using astFree.
32876 *     ok
32877 *        Pointer to an in which is returned set to zero if the polynomial
32878 *        in the supplied WAT string cannot be represented using TPN form.
32879 *        Non-zero otherwise.
32880 *     status
32881 *        Pointer to the inherited status variable.
32882 
32883 *  Returned Value:
32884 *     The size of the returned cvals and mvals arrays.
32885 
32886 */
32887 
32888 /* Local Variables: */
32889    char **w1;
32890    char **w2;
32891    double *coeff;
32892    double *pc;
32893    int result;
32894    double dval;
32895    double etamax;
32896    double etamin;
32897    double ximax;
32898    double ximin;
32899    int cheb;
32900    int etaorder;
32901    int iword;
32902    int m;
32903    int mn;
32904    int nword;
32905    int order;
32906    int porder;
32907    int xiorder;
32908    int ires;
32909 
32910 /* The number of lngcor/latcor values needed for each order. */
32911    static const int nab[] = {1,3,6,10,15,21,28,36};
32912 
32913 /* Initialise the pointer to the returned Mapping. */
32914    result = 0;
32915    *mvals = NULL;
32916    *cvals = NULL;
32917    *ok = 1;
32918 
32919 /* Other initialisation to avoid compiler warnings. */
32920    etamin = 0.0;
32921    etamax = 0.0;
32922    ximax = 0.0;
32923    ximin = 0.0;
32924    order = 0;
32925 
32926 /* Check the global status. */
32927    if ( !astOK || !watstr ) return result;
32928 
32929 /* Look for cor = "..." and extract the "..." string. */
32930    w1 = astChrSplitRE( watstr, "cor *= *\"(.*)\"", &nword, NULL );
32931    if( w1 ) {
32932 
32933 /* Split the "..." string into words. */
32934       w2 = astChrSplit( w1[ 0 ], &nword );
32935       if( w2 ) {
32936 
32937 /* Initialise flags. */
32938          cheb = 0;
32939          xiorder = 0;
32940          etaorder = 0;
32941          coeff = NULL;
32942          porder = -1;
32943 
32944 /* Loop round each word. Break early if we find that the projection
32945    cannot be represented as a TPN projection. */
32946          for( iword = 0; iword < nword && *ok; iword++ ) {
32947 
32948 /* Convert the word to double. */
32949             dval = astChr2Double( w2[ iword ] );
32950             if( dval == AST__BAD ) {
32951                astError( AST__BDFTS, "astRead(FitsChan): Failed to read a "
32952                          "numerical value from sub-string \"%s\" found in "
32953                          "an IRAF \"WAT...\" keyword.", status,  w2[ iword ] );
32954                break;
32955             }
32956 
32957 /* The first value gives the correction surface type. We can only handle type
32958    1 (chebyshev) or 3 (simple polynomial). */
32959             if( iword == 0 ){
32960                if( dval == 1.0 ) {
32961                   cheb = 1;
32962                } else if( dval == 2.0 ) {
32963                   *ok = 0;
32964                }
32965 
32966 /* The second and third numbers gives the orders of the polynomial in X
32967    and Y. We can only handle cases in which the orders are the same on
32968    both axes, and greater than 0 and less than 8. Store a pointer to the
32969    first TAN projection parameter index to use. */
32970             } else if( iword == 1 ){
32971                order = dval;
32972                porder = order - 1;
32973 
32974             } else if( iword == 2 ){
32975                if( dval - 1 != porder || dval < 0 || dval > 7 ) *ok = 0;
32976 
32977 /* The fourth number defines the type of cross-terms. We can only handle
32978    type 2 (half-cross terms). */
32979             } else if( iword == 3 ){
32980                if( dval != 2.0 ) *ok = 0;
32981 
32982 /* We now know the maximum number of co-efficients that may be needed.
32983    Allocate memory to hold them, and fill it with zeros. They are
32984    stored in this array as if full cross-terms have been supplied (the
32985    unspecified coefficients retain their initialised value of zero).  */
32986                coeff = astCalloc( order*order, sizeof( double ) );
32987                if( !astOK ) break;
32988 
32989 /* The next 4 numbers describe the region of validity of the fits in IRAF's
32990    xi and eta space, e.g. ximin, ximax, etamin, etamax. We only uses
32991    these if we have a chebyshev polynomial. */
32992             } else if( iword == 4 ) {
32993                ximin = dval;
32994 
32995             } else if( iword == 5 ) {
32996                ximax = dval;
32997 
32998             } else if( iword == 6 ) {
32999                etamin = dval;
33000 
33001             } else if( iword == 7 ) {
33002                etamax = dval;
33003 
33004 /* The remaining terms are the coefficients of the polynomial terms. */
33005             } else if( iword > 7 ){
33006 
33007 /* Store the coefficient in the array. They are stored so that power of
33008    xi increases fastest. */
33009                coeff[ xiorder + order*etaorder ] = dval;
33010 
33011 /* Increment the powers of the next coefficient. We know we only have half
33012    cross-terms, so the maximum power of xi decreases from order to zero
33013    as we move through the list of coefficients. */
33014                if( ++xiorder == order - etaorder ) {
33015                   xiorder = 0;
33016                   etaorder++;
33017                }
33018             }
33019          }
33020 
33021 /* Check that all the required co-efficients were found */
33022          if( porder == -1 || nword != 8 + nab[ porder ] ) *ok = 0;
33023 
33024 /* If we can handle the projection, proceed. */
33025          if( *ok && astOK ) {
33026 
33027 /* If the coefficients were supplied in chebyshev form, convert to simple
33028    form. */
33029             if( cheb ) {
33030                double *tcoeff = coeff;
33031                coeff = Cheb2Poly( tcoeff, order, order, ximin,
33032                                   ximax, etamin, etamax, status );
33033                tcoeff = astFree( tcoeff );
33034             }
33035 
33036 /* The polynomials provide a "correction* to be added to the supplied X and
33037    Y values. Therefore increase the linear co-efficients by 1 on the axis
33038    that is being calculated. */
33039             coeff[ iaxis ? order : 1 ] += 1.0;
33040 
33041 /* Loop round all coefficients, keeping track of the power of xi and eta
33042    for the current coefficient. */
33043             pc = coeff;
33044             for( etaorder = 0; etaorder < order; etaorder++ ) {
33045                for( xiorder = 0; xiorder < order; xiorder++,pc++ ) {
33046 
33047 /* Skip coefficients that have their default values (zero, except for the
33048    linear coefficients which default to 1.0). */
33049                   mn = xiorder + etaorder;
33050                   if( *pc != ( mn == 1 ? 1.0 : 0.0 ) ) {
33051 
33052 /* Find the "m" index of the PVi_m FITS keyword for the current
33053    coefficient. */
33054                      m = mn*( 1 + mn )/2 + mn/2;
33055                      m += iaxis ? xiorder : etaorder;
33056 
33057 /* Append the PV and m values to the ends of the returned arrays. */
33058                      ires = result++;
33059                      *cvals = astGrow( *cvals, sizeof( double ), result );
33060                      *mvals = astGrow( *mvals, sizeof( int ), result );
33061                      if( astOK ) {
33062                         (*cvals)[ ires ] = *pc;
33063                         (*mvals)[ ires ] = m;
33064                      }
33065                   }
33066                }
33067             }
33068 
33069 /* Free coefficients arrays */
33070             coeff = astFree( coeff );
33071          }
33072 
33073 /* Free resources */
33074          w2 = astFree( w2 );
33075       }
33076       w1 = astFree( w1 );
33077    }
33078 
33079 /* Return the result. */
33080    return result;
33081 }
33082 
WcsCDeltMatrix(FitsStore * store,char s,int naxes,const char * method,const char * class,int * status)33083 static AstMatrixMap *WcsCDeltMatrix( FitsStore *store, char s, int naxes,
33084                                      const char *method, const char *class, int *status ){
33085 /*
33086 *  Name:
33087 *     WcsCDeltMatrix
33088 
33089 *  Purpose:
33090 *     Create a MatrixMap representing the CDELT scaling.
33091 
33092 *  Type:
33093 *     Private function.
33094 
33095 *  Synopsis:
33096 *     AstMatrixMap *WcsCDeltMatrix( FitsStore *store, char s, int naxes,
33097 *                                   const char *method, const char *class, int *status )
33098 
33099 *  Class Membership:
33100 *     FitsChan
33101 
33102 *  Description:
33103 *     A diagonal MatrixMap representing the FITS "CDELT" keywords is
33104 *     returned.
33105 
33106 *  Parameters:
33107 *     store
33108 *        A structure containing values for FITS keywords relating to
33109 *        the World Coordinate System.
33110 *     s
33111 *        A character s identifying the co-ordinate version to use. A space
33112 *        means use primary axis descriptions. Otherwise, it must be an
33113 *        upper-case alphabetical characters ('A' to 'Z').
33114 *     naxes
33115 *        The number of intermediate world coordinate axes (WCSAXES).
33116 *     method
33117 *        A pointer to a string holding the name of the calling method.
33118 *        This is used only in the construction of error messages.
33119 *     class
33120 *        A pointer to a string holding the class of the object being
33121 *        read. This is used only in the construction of error messages.
33122 *     status
33123 *        Pointer to the inherited status variable.
33124 
33125 *  Returned Value:
33126 *     A pointer to the created MatrixMap or a NULL pointer if an
33127 *     error occurred.
33128 */
33129 
33130 /* Local Variables: */
33131    AstMatrixMap *new;       /* The created MatrixMap */
33132    double *el;              /* Pointer to next matrix element */
33133    double *mat;             /* Pointer to matrix array */
33134    int i;                   /* Pixel axis index */
33135 
33136 /* Initialise/ */
33137    new = NULL;
33138 
33139 /* Check the global status. */
33140    if ( !astOK ) return new;
33141 
33142 /* Allocate memory for the diagonal matrix elements. */
33143    mat = (double *) astMalloc( sizeof(double)*naxes );
33144    if( astOK ){
33145 
33146 /* Fill the matrix diagonal with values from the FitsStore. */
33147       el = mat;
33148       for( i = 0; i < naxes; i++ ){
33149 
33150 /* Get the CDELTi value for this axis. Missing terms can be defaulted so
33151    do not report an error if the required value is not present in the
33152    FitsStore. */
33153          *el = GetItem( &(store->cdelt), i, 0, s, NULL, method, class, status );
33154 
33155 /* Missing terms default to to 1.0. */
33156          if( *el == AST__BAD ) *el = 1.0;
33157 
33158 /* Move on to the next matrix element. */
33159          el++;
33160       }
33161 
33162 /* Create the diagional matrix. */
33163       new = astMatrixMap( naxes, naxes, 1, mat, "", status );
33164 
33165 /* Report an error if the inverse transformation is undefined. */
33166       if( !astGetTranInverse( new ) && astOK ) {
33167         astError( AST__BDFTS, "%s(%s): Unusable CDELT values found "
33168                   "in the FITS-WCS header - one or more values are zero.", status, method, class );
33169       }
33170 
33171 /* Release the memory used to hold the matrix. */
33172       mat = (double *) astFree( (void *) mat );
33173    }
33174 
33175 /* If an error has occurred, attempt to annul the returned MatrixMap. */
33176    if( !astOK ) new = astAnnul( new );
33177 
33178 /* Return the MatrixMap. */
33179    return new;
33180 }
33181 
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)33182 static AstMapping *WcsCelestial( AstFitsChan *this, FitsStore *store, char s,
33183                                  AstFrame **frm, AstFrame *iwcfrm, double *reflon, double *reflat,
33184                                  AstSkyFrame **reffrm, AstMapping **tabmap,
33185                                  int *tabaxis, const char *method,
33186                                  const char *class, int *status ){
33187 /*
33188 *  Name:
33189 *     WcsCelestial
33190 
33191 *  Purpose:
33192 *     Create a Mapping from intermediate world coords to celestial coords
33193 *     as described in a FITS header.
33194 
33195 *  Type:
33196 *     Private function.
33197 
33198 *  Synopsis:
33199 *     AstMapping *WcsCelestial( AstFitsChan *this, FitsStore *store, char s,
33200 *                               AstFrame **frm, AstFrame *iwcfrm, double *reflon, double *reflat,
33201 *                               AstSkyFrame **reffrm, , AstMapping **tabmap,
33202 *                               int *tabaxis, const char *method,
33203 *                               const char *class, int *status )
33204 
33205 *  Class Membership:
33206 *     FitsChan
33207 
33208 *  Description:
33209 *     This function interprets the contents of the supplied FitsStore
33210 *     structure, looking for world coordinate axes which describe positions
33211 *     on the sky. If a pair of such longitude/latitude axes is found, a
33212 *     Mapping is returned which transforms the corresponding intermediate
33213 *     world coordinates to celestial world coordinates (this mapping leaves
33214 *     any other axes unchanged). It also, modifies the supplied Frame to
33215 *     describe the axes (again, other axes are left unchanged). If no
33216 *     pair of celestial axes is found, a UnitMap is returned, and the
33217 *     supplied Frame is left unchanged.
33218 
33219 *  Parameters:
33220 *     this
33221 *        The FitsChan.
33222 *     store
33223 *        A structure containing information about the requested axis
33224 *        descriptions derived from a FITS header.
33225 *     s
33226 *        A character identifying the co-ordinate version to use. A space
33227 *        means use primary axis descriptions. Otherwise, it must be an
33228 *        upper-case alphabetical characters ('A' to 'Z').
33229 *     frm
33230 *        The address of a location at which to store a pointer to the
33231 *        Frame describing the world coordinate axes.
33232 *     iwcfrm
33233 *        A pointer to the Frame describing the intermediate world coordinate
33234 *        axes. The properties of this Frame may be changed on exit.
33235 *     reflon
33236 *        Address of a location at which to return the celestial longitude
33237 *        at the reference point. It is returned as AST__BAD if no
33238 *        celestial coordinate frame is found.
33239 *     reflat
33240 *        Address of a location at which to return the celestial latitude
33241 *        at the reference point. It is returned as AST__BAD if no
33242 *        celestial coordinate frame is found.
33243 *     reffrm
33244 *        Address of a location at which to return a pointer to a SkyFrame
33245 *        which define the reference values returned in reflon and reflat.
33246 *        It is returned as NULL if no celestial coordinate frame is found.
33247 *     tabmap
33248 *        Address of a pointer to a Mapping describing any -TAB
33249 *        transformations to be applied to the results of the Mapping returned
33250 *        by this function. If any celestial axes are found, the supplied
33251 *        Mapping is modified so that the celestial axes produce values in
33252 *        radians rather than degrees. NULL if no axes are described by -TAB.
33253 *     tabaxis
33254 *        Pointer to an array of flags, one for each WCS axis, indicating
33255 *        if the corresponding WCS axis is described by the -TAB algorithm.
33256 *        NULL if no axes are described by -TAB.
33257 *     method
33258 *        A pointer to a string holding the name of the calling method.
33259 *        This is used only in the construction of error messages.
33260 *     class
33261 *        A pointer to a string holding the class of the object being
33262 *        read. This is used only in the construction of error messages.
33263 *     status
33264 *        Pointer to the inherited status variable.
33265 
33266 *  Returned Value:
33267 *     A pointer to the Mapping.
33268 */
33269 
33270 /* Local Variables: */
33271    astDECLARE_GLOBALS        /* Declare the thread specific global data */
33272    AstFrame *ofrm;           /* Pointer to a Frame */
33273    AstMapping *map1;         /* Pointer to a Mapping */
33274    AstMapping *map2;         /* Pointer to a Mapping */
33275    AstMapping *map3;         /* Pointer to a Mapping */
33276    AstMapping *map4;         /* Pointer to a Mapping */
33277    AstMapping *ret;          /* Pointer to the returned Mapping */
33278    AstMapping *newmap;       /* Modified PIXEL->IWC Mapping */
33279    AstMapping *shiftmap;     /* ShiftMap from IWC to PPC */
33280    AstSkyFrame *sfrm;        /* Pointer to a SkyFrame */
33281    char *ctype;              /* Pointer to CTYPE string */
33282    char *keyname;            /* Pointer to keyword name string */
33283    char buf[300];            /* Text buffer */
33284    char latctype[MXCTYPELEN];/* Latitude CTYPE keyword value */
33285    char latkey[10];          /* Latitude CTYPE keyword name */
33286    char lattype[4];          /* Buffer for celestial system */
33287    char lonctype[MXCTYPELEN];/* Longitude CTYPE keyword value */
33288    char lonkey[10];          /* Longitude CTYPE keyword name */
33289    char lontype[4];          /* Buffer for celestial system */
33290    double *shifts;           /* Array holding axis shifts */
33291    double *ina;              /* Pointer to memory holding input position A */
33292    double *inb;              /* Pointer to memory holding input position B */
33293    double *mat;              /* Pointer to data for deg->rad scaling matrix */
33294    double *outa;             /* Pointer to memory holding output position A */
33295    double *outb;             /* Pointer to memory holding output position B */
33296    double latval;            /* CRVAL for latitude axis */
33297    double lonval;            /* CRVAL for longitude axis */
33298    double pv;                /* Projection parameter value */
33299    double x0;                /* IWC X at the projection fiducial point */
33300    double y0;                /* IWC Y at the projection fiducial point */
33301    int *axes;                /* Point to a list of axis indices */
33302    int axlat;                /* Index of latitude physical axis */
33303    int axlon;                /* Index of longitude physical axis */
33304    int carlin;               /* Assume native and WCS axes are the same? */
33305    int ctlen;                /* Length of CTYPE string */
33306    int gotax;                /* Celestial axis found? */
33307    int i;                    /* Loop count */
33308    int j;                    /* Axis index */
33309    int latprj;               /* Latitude projection type identifier */
33310    int lonprj;               /* Longitude projection type identifier */
33311    int m;                    /* Parameter index */
33312    int mxpar_lat;            /* Max. projection parameter index on lat axis */
33313    int mxpar_lon;            /* Max. projection parameter index on lon axis */
33314    int naxes;                /* Number of axes */
33315    int np;                   /* Max parameter index */
33316    int prj;                  /* Projection type identifier */
33317 
33318 /* Initialise the returned values. */
33319    ret = NULL;
33320    *reflon = AST__BAD;
33321    *reflat = AST__BAD;
33322    *reffrm = NULL;
33323 
33324 /* Other initialisation to avoid compiler warnings. */
33325    map1 = NULL;
33326 
33327 /* Check the global status. */
33328    if ( !astOK ) return ret;
33329 
33330 /* Get a pointer to the structure holding thread-specific global data. */
33331    astGET_GLOBALS(this);
33332 
33333 /* Get the number of physical axes. */
33334    naxes = astGetNaxes( *frm );
33335 
33336 /* See if CAR projections should be interpreted in the old fashioned way
33337    (i.e native coords are always the same as WCS coords, so no need for
33338    any rotation). */
33339    carlin = astGetCarLin( this );
33340 
33341 /* The first major section sees if the physical axes include a pair of
33342    longitude/latitude celestial axes.
33343    ================================================================= */
33344 
33345 /* We have not yet found any celestial axes. */
33346    axlon = -1;
33347    axlat = -1;
33348    latprj = AST__WCSBAD;
33349    lonprj = AST__WCSBAD;
33350    prj = AST__WCSBAD;
33351 
33352 /* First, we examine the CTYPE values in the FitsStore to determine
33353    which axes are the longitude and latitude axes, and what the celestial
33354    co-ordinate system and projection are. Loop round the physical axes,
33355    getting each CTYPE value. */
33356    for( i = 0; i < naxes && astOK; i++ ){
33357       keyname =  FormatKey( "CTYPE", i + 1, -1, s, status );
33358       ctype = GetItemC( &(store->ctype), i, 0, s, NULL, method, class, status );
33359 
33360 /* Issue a warning if no CTYPE value was found. */
33361       if( !ctype ) {
33362          sprintf( buf, "Axis type keywords (CTYPE, etc) were not found "
33363                   "for one or more axes in the original FITS header. These "
33364                   "axes will be assumed to be linear." );
33365          Warn( this, "noctype", buf, method, class, status );
33366       } else {
33367 
33368 /* See if this is a longitude axis (e.g. if the first 4 characters of CTYPE
33369    are "RA--" or "xLON" or "yzLN" ). If so, store the value of "x" or "yz"
33370    (or "EQU" for equatorial coordinates) in variable "type" to indicate which
33371    coordinate system is being used. */
33372          gotax = 0;
33373          if( !strcmp( ctype, "RA" ) || !strncmp( ctype, "RA--", 4 ) ){
33374             strcpy( wcscelestial_type, "EQU" );
33375             gotax = 1;
33376          } else if( !strcmp( ctype, "AZ" ) || !strncmp( ctype, "AZ--", 4 ) ){
33377             strcpy( wcscelestial_type, "AZL" );
33378             gotax = 1;
33379          } else if( !strcmp( ctype + 1, "LON" ) || !strncmp( ctype + 1, "LON-", 4 ) ){
33380             wcscelestial_type[ 0 ] = ctype[ 0 ];
33381             wcscelestial_type[ 1 ] = 0;
33382             gotax = 1;
33383          } else if( !strcmp( ctype + 2, "LN" ) || !strncmp( ctype + 2, "LN-", 3 ) ){
33384             wcscelestial_type[ 0 ] = ctype[ 0 ];
33385             wcscelestial_type[ 1 ] = ctype[ 1 ];
33386             wcscelestial_type[ 2 ] = 0;
33387             gotax = 1;
33388          }
33389 
33390 /* If this is a longitude axis... */
33391          if( gotax ){
33392 
33393 /* Check that this is the first longitude axis to be found. */
33394             if( axlon == -1 ){
33395 
33396 /* Find the projection type as specified by the last 4 characters
33397    in the CTYPE keyword value. AST__WCSBAD is stored in "prj" if the
33398    last 4 characters do not specify a known WCS projection, but no error
33399    is reported. Assume simple linear axes if no projection code is
33400    supplied. Note, AST__WCSBAD is used to indicate a TAB header. */
33401                ctlen = strlen( ctype );
33402                if( ctlen > 4 ) {
33403                   prj = astWcsPrjType( ctype + ctlen - 4 );
33404                } else if( tabmap && *tabmap ) {
33405                   prj = AST__WCSBAD;
33406                } else {
33407                   prj = AST__CAR;
33408                   carlin = 1;
33409                }
33410 
33411 /* Report an error if the projection is unknown. */
33412                if( prj == AST__WCSBAD && ctlen > 4 ){
33413                   astError( AST__BDFTS, "%s(%s): FITS keyword '%s' refers to "
33414                         "an unknown projection type '%s'.", status, method, class,
33415                          keyname, ctype + ctlen - 4 );
33416                   break;
33417                }
33418 
33419 /* Store the index of the longitude axis, type of longitude, etc. */
33420                axlon = i;
33421                strcpy( lontype, wcscelestial_type );
33422                strcpy( lonkey, keyname );
33423                strcpy( lonctype, ctype );
33424                lonprj = prj;
33425 
33426 /* If another longitude axis has already been found, report an error. */
33427             } else {
33428                astError( AST__BDFTS, "%s(%s): FITS keywords '%s' (='%s') "
33429                   "and '%s' (='%s') both describe celestial longitude axes.", status,
33430                   method, class, keyname, ctype, lonkey, lonctype );
33431                break;
33432             }
33433          }
33434 
33435 /* Do the same for the latitude axis, checking for "DEC-" and "xLAT" and
33436   "yzLT". */
33437          gotax = 0;
33438          if( !strcmp( ctype, "DEC" ) || !strncmp( ctype, "DEC-", 4 ) ){
33439             strcpy( wcscelestial_type, "EQU" );
33440             gotax = 1;
33441          } else if( !strcmp( ctype, "EL" ) || !strncmp( ctype, "EL--", 4 ) ){
33442             strcpy( wcscelestial_type, "AZL" );
33443             gotax = 1;
33444          } else if( !strcmp( ctype + 1, "LAT" ) || !strncmp( ctype + 1, "LAT-", 4 ) ){
33445             wcscelestial_type[ 0 ] = ctype[ 0 ];
33446             wcscelestial_type[ 1 ] = 0;
33447             gotax = 1;
33448          } else if( !strcmp( ctype + 2, "LT" ) || !strncmp( ctype + 2, "LT-", 3 ) ){
33449             wcscelestial_type[ 0 ] = ctype[ 0 ];
33450             wcscelestial_type[ 1 ] = ctype[ 1 ];
33451             wcscelestial_type[ 2 ] = 0;
33452             gotax = 1;
33453          }
33454          if( gotax ){
33455             if( axlat == -1 ){
33456                ctlen = strlen( ctype );
33457                if( ctlen > 4 ) {
33458                   prj = astWcsPrjType( ctype + ctlen - 4 );
33459                } else if( tabmap && *tabmap ) {
33460                   prj = AST__WCSBAD;
33461                } else {
33462                   prj = AST__CAR;
33463                   carlin = 1;
33464                }
33465 
33466                if( prj == AST__WCSBAD && ctlen > 4 ){
33467                   astError( AST__BDFTS, "%s(%s): FITS keyword '%s' refers to "
33468                         "an unknown projection type '%s'.", status, method, class,
33469                          keyname, ctype + ctlen - 4 );
33470                   break;
33471                }
33472                axlat = i;
33473                strcpy( lattype, wcscelestial_type );
33474                strcpy( latkey, keyname );
33475                strcpy( latctype, ctype );
33476                latprj = prj;
33477             } else {
33478                astError( AST__BDFTS, "%s(%s): FITS keywords '%s' (='%s') "
33479                   "and '%s' (='%s') both describe celestial latitude axes.", status,
33480                   method, class, keyname, ctype, latkey, latctype );
33481                break;
33482             }
33483          }
33484       }
33485    }
33486 
33487 /* Check the above went OK */
33488    if( astOK ){
33489 
33490 /* If both longitude and latitude axes were found... */
33491       if( axlat != -1 && axlon != -1 ){
33492 
33493 /* Report an error if they refer to different celestial coordinate systems. */
33494          if( strcmp( lattype, lontype ) ){
33495             astError( AST__BDFTS, "%s(%s): FITS keywords '%s' and '%s' "
33496                       "indicate different celestial coordinate systems "
33497                       "('%s' and '%s').", status, method, class, latkey, lonkey,
33498                       latctype, lonctype );
33499 
33500 /* Otherwise report an error if longitude and latitude axes use different
33501    projections. */
33502          } else if( lonprj != latprj ){
33503             astError( AST__BDFTS, "%s(%s): FITS keywords '%s' and '%s' "
33504                       "indicate different projections ('%s' and '%s').", status,
33505                       method, class, latkey, lonkey, latctype, lonctype );
33506          }
33507 
33508 /* If only one axis has been provided without the other (e.g. longitude but no
33509    latitude), report an error. */
33510       } else if( axlat != -1 && prj != AST__WCSBAD ){
33511          astError( AST__BDFTS, "%s(%s): A latitude axis ('%s') was found "
33512                    "without a corresponding longitude axis.", status, method, class,
33513                    latctype );
33514       } else if( axlon != -1 && prj != AST__WCSBAD ){
33515          astError( AST__BDFTS, "%s(%s): A longitude axis ('%s') was found "
33516                    "without a corresponding latitude axis.", status, method, class,
33517                    lonctype );
33518       }
33519    }
33520 
33521 /* If a pair of matching celestial axes was not found, return a UnitMap
33522    and leave the Frame unchanged.
33523    ===================================================================== */
33524    if( axlat == -1 || axlon == -1 ) {
33525       ret = (AstMapping *) astUnitMap( naxes, "", status );
33526 
33527 /* The rest of this function deals with creating a Mapping from
33528    intermediate world coords to celestial coords, and modifying the
33529    Frame appropriately.
33530    ===================================================================== */
33531    } else if( astOK ) {
33532 
33533 /* Create a MatrixMap which scales the intermediate world coordinate axes
33534    corresponding to the longitude and latitude axes from degrees to radians.
33535    Only do this if a projection was supplied. */
33536       if( latprj != AST__WCSBAD ) {
33537          mat = (double *) astMalloc( sizeof(double)*naxes );
33538          if( mat ){
33539             for( i = 0; i < naxes; i++ ){
33540                if( i == axlat || i == axlon ){
33541                   mat[ i ] = AST__DD2R;
33542                } else {
33543                   mat[ i ] = 1.0;
33544                }
33545             }
33546             map1 = (AstMapping *) astMatrixMap( naxes, naxes, 1, mat, "", status );
33547             mat = (double *) astFree( (void *) mat );
33548          }
33549       } else {
33550          map1 = (AstMapping *) astUnitMap( naxes, " ", status );
33551       }
33552 
33553 /* If the projection is a CAR projection, but the CarLin attribute is
33554    set, then we consider the CAR projection to be a simple linear mapping
33555    of pixel coords to celestial coords. Do this by using a WcsMap with no
33556    projection. All axes will then be treated as linear and non-celestial.
33557    If no projection was specified (i.e. if prj == AST__WCSBAD, as is the
33558    case when using -TAB for instance) then do the same but use a UnitMap
33559    instead of a WcsMap. */
33560       map3 = NULL;
33561       if( ( latprj == AST__CAR && carlin ) || latprj == AST__WCSBAD ) {
33562          if( latprj == AST__CAR ) {
33563             map2 = (AstMapping *) astWcsMap( naxes, AST__WCSBAD, axlon + 1,
33564                                              axlat + 1, "", status );
33565          } else {
33566             map2 = (AstMapping *) astUnitMap( naxes, "", status );
33567          }
33568 
33569 /* Now create a WinMap which adds on the CRVAL values to each axis. */
33570          ina = astMalloc( sizeof(double)*naxes );
33571          inb = astMalloc( sizeof(double)*naxes );
33572          outa = astMalloc( sizeof(double)*naxes );
33573          outb = astMalloc( sizeof(double)*naxes );
33574          if( astOK ) {
33575             for( i = 0; i < naxes; i++ ) {
33576                ina[ i ] = 0.0;
33577                inb[ i ] = 1.0;
33578                outa[ i ] = 0.0;
33579                outb[ i ] = 1.0;
33580             }
33581             lonval = GetItem( &(store->crval), axlon, 0, s, NULL, method, class, status );
33582             if( lonval != AST__BAD ) {
33583 
33584 /* For recognised projections the CRVAL value is required to be degrees,
33585    so convert to radians. For other algorithms (e.g. -TAB) the CRVAL
33586    values are in unknown units so retain their original scaling. */
33587                *reflon = ( latprj == AST__CAR ) ? lonval*AST__DD2R : lonval;
33588 
33589                outa[ axlon ] += *reflon;
33590                outb[ axlon ] += *reflon;
33591             } else {
33592                outa[ axlon ] = AST__BAD;
33593                outb[ axlon ] = AST__BAD;
33594             }
33595 
33596             latval = GetItem( &(store->crval), axlat, 0, s, NULL, method, class, status );
33597             if( latval != AST__BAD ) {
33598                *reflat = ( latprj == AST__CAR ) ? latval*AST__DD2R : latval;
33599                outa[ axlat ] += *reflat;
33600                outb[ axlat ] += *reflat;
33601             } else {
33602                outa[ axlat ] = AST__BAD;
33603                outb[ axlat ] = AST__BAD;
33604             }
33605 
33606             map3 = (AstMapping *) astWinMap( naxes, ina, inb, outa, outb, "", status );
33607 
33608          }
33609          ina = astFree( ina );
33610          inb = astFree( inb );
33611          outa = astFree( outa );
33612          outb = astFree( outb );
33613 
33614 /* Otherwise, create a WcsMap with the specified projection. The WcsMap
33615    is equivalent to a unit mapping for all axes other than "axlat" and
33616    "axlon". */
33617       } else {
33618 
33619 /* Get the highest index ("m" value) of any supplied PVi_m projection
33620    parameters (on any axes). */
33621          np = GetMaxJM( &(store->pv), s, status );
33622 
33623 /* Create the WcsMap */
33624          map2 = (AstMapping *) astWcsMap( naxes, latprj, axlon + 1,
33625                                           axlat + 1, "", status );
33626 
33627 /* If the FITS header contains any projection parameters, store them in
33628    the WcsMap. */
33629          mxpar_lat = astGetPVMax( map2, axlat );
33630          mxpar_lon = astGetPVMax( map2, axlon );
33631          for( m = 0; m <= np; m++ ){
33632             pv = GetItem( &(store->pv), axlat, m, s, NULL, method, class, status );
33633             if( pv != AST__BAD ) {
33634                if( m <= mxpar_lat ) {
33635                   astSetPV( map2, axlat, m, pv );
33636                } else {
33637                   sprintf( buf, "Projection parameter PV%d_%d found, "
33638                            "but is not used by %s projections.", axlat + 1,
33639                            m, astWcsPrjName( astGetWcsType( map2 ) ) );
33640                   Warn( this, "badpv", buf, method, class, status );
33641                }
33642             }
33643             pv = GetItem( &(store->pv), axlon, m, s, NULL, method, class, status );
33644             if( pv != AST__BAD ) {
33645                if( m <= mxpar_lon ) {
33646                   astSetPV( map2, axlon, m, pv );
33647                } else {
33648                   sprintf( buf, "Projection parameter PV%d_%d found, "
33649                            "but is not used by %s projections.", axlon + 1,
33650                            m, astWcsPrjName( astGetWcsType( map2 ) ) );
33651                   Warn( this, "badpv", buf, method, class, status );
33652                }
33653             }
33654          }
33655 
33656 /* Invert the WcsMap to get a DEprojection. */
33657          astInvert( map2 );
33658 
33659 /* Now produce a Mapping which converts the axes holding "Native Spherical
33660    Coords" into "Celestial Coords", leaving all other axes unchanged. */
33661          map3 = WcsNative( this, store, s, (AstWcsMap *) map2, -1, -1,
33662                            method, class, status );
33663 
33664 /* Retrieve and store the reference longitude and latitude. */
33665          *reflon = GetItem( &(store->crval), axlon, 0, s, NULL, method, class, status );
33666          if( *reflon != AST__BAD ) *reflon *= AST__DD2R;
33667          *reflat = GetItem( &(store->crval), axlat, 0, s, NULL, method, class, status );
33668          if( *reflat != AST__BAD ) *reflat *= AST__DD2R;
33669       }
33670 
33671 /* If projection parameter PVi_0a for the longitude axis "i" is non-zero,
33672    then there is a shift of origin between Intermediate World Coords, IWC,
33673    (the CRPIXi values correspond to the origin of IWC), and Projection Plane
33674    Coords, PPC (these are the cartesian coordinates used by the WcsMap).
33675    This shift of origin results in the fiducial point specified by the
33676    CRVALi values mapping onto the pixel reference point specified by the
33677    CRPIXj values. In this case we need to add a Mapping which implements
33678    the shift of origin. Note, the AST-specific "TPN" projection cannot use
33679    this convention since it uses PVi_0 to hold a polynomial correction term. */
33680       if( latprj != AST__WCSBAD && astGetWcsType( map2 ) != AST__TPN &&
33681           astGetPV( map2, axlon, 0 ) != 0.0 ) {
33682 
33683 /* Find the projection plane coords corresponding to the fiducial point
33684    of the projection. This is done by using the inverse WcsMap to convert
33685    the native spherical coords at the fiducial point into PPC (x,y), which
33686    are returned in units of radians (not degrees). */
33687          GetFiducialPPC( (AstWcsMap *) map2, &x0, &y0, status );
33688          if( x0 != AST__BAD && y0 != AST__BAD ) {
33689 
33690 /* Allocate resources. */
33691             shifts = astMalloc( sizeof( double )*(size_t) naxes );
33692 
33693 /* Check pointers can be used safely. */
33694             if( astOK ) {
33695 
33696 /* Create a Mapping (a ShiftMap) from IWC to PPC. */
33697                for( i = 0; i < naxes; i++ ) shifts[ i ] = 0.0;
33698                shifts[ axlon ] = x0;
33699                shifts[ axlat ] = y0;
33700                shiftmap = (AstMapping *) astShiftMap( naxes, shifts, "", status );
33701 
33702 /* Produce a CmpMap which combines "map1" (which converts degrees to
33703    radians on the celestial axes) with the above ShiftMap. */
33704                newmap = (AstMapping *) astCmpMap( map1, shiftmap, 1, "", status );
33705 
33706 /* Annul the component Mappings and use the new one in place of map1. */
33707                shiftmap = astAnnul( shiftmap );
33708                map1 = astAnnul( map1 );
33709                map1 = newmap;
33710             }
33711 
33712 /* Free resources. */
33713             shifts = astFree( shifts );
33714          }
33715       }
33716 
33717 /* Now concatenate the Mappings to produce the returned Mapping. */
33718       map4 = (AstMapping *) astCmpMap( map1, map2, 1, "", status );
33719       ret = (AstMapping *) astCmpMap( map4, map3, 1, "", status );
33720 
33721 /* Annul the component Mappings. */
33722       map1 = astAnnul( map1 );
33723       map2 = astAnnul( map2 );
33724       map3 = astAnnul( map3 );
33725       map4 = astAnnul( map4 );
33726 
33727 /* We now make changes to the supplied Frame so that the longitude and
33728    latitude axes are described by a SkyFrame. First create an appropriate
33729    SkyFrame. */
33730       sfrm = WcsSkyFrame( this, store, s, prj, wcscelestial_type, axlon,
33731                           axlat, method, class, status );
33732 
33733 /* The values currently stored in *reflat and *reflon are the CRVAL
33734    values. In some circumstances, these may not be the original values in
33735    the supplied header but may have been translated within the SpecTrans
33736    function as part of the process of translating an old unsupported
33737    projection into a new supported projection. Since the returned RefLat
33738    and RefLon values may be used to set the reference position for a
33739    SpecFrame, we should return the original values rather than the
33740    translated values. The original values will have been stored (within
33741    SpecTrans) in the FitsChan as keywords RFVALi. If such keywords can
33742    be found, use their values in preference to the currently stored CRVAL
33743    values.*/
33744       if( GetValue( this, FormatKey( "RFVAL", axlon + 1, -1, s, status ),
33745                     AST__FLOAT, (void *) &lonval, 0, 0, method, class, status ) &&
33746           GetValue( this, FormatKey( "RFVAL", axlat + 1, -1, s, status ),
33747                     AST__FLOAT, (void *) &latval, 0, 0, method, class, status ) ) {
33748          *reflon = lonval*AST__DD2R;
33749          *reflat = latval*AST__DD2R;
33750       }
33751 
33752 /* Store the reflon and reflat values as the SkyRef position in the
33753    SkyFrame, and set SkyRefIs to "ignore" so that the SkyFrame continues
33754    to represent absolute celestial coords. Do not change the SkyFrame if
33755    it already had a set reference posiiton. */
33756       if( ! astTestSkyRef( sfrm, 0 ) ) {
33757          if( *reflon != AST__BAD && *reflat != AST__BAD ) {
33758             astSetSkyRef( sfrm, 0, *reflon );
33759             astSetSkyRef( sfrm, 1, *reflat );
33760             astSet( sfrm, "SkyRefIs=Ignored", status );
33761          }
33762       }
33763 
33764 /* Return a clone of this SkyFrame as the reference Frame. */
33765       *reffrm = astClone( sfrm );
33766 
33767 /* Create a Frame by picking all the other (non-celestial) axes from the
33768    supplied Frame. */
33769       axes = astMalloc( naxes*sizeof( int ) );
33770       if( axes ) {
33771          j = 0;
33772          for( i = 0; i < naxes; i++ ) {
33773             if( i != axlat && i != axlon ) axes[ j++ ] = i;
33774          }
33775 
33776 /* If there were no other axes, replace the supplied Frame with the skyframe. */
33777          if( j == 0 ) {
33778             (void) astAnnul( *frm );
33779             *frm = (AstFrame *) astClone( sfrm );
33780 
33781 /* Otherwise pick the other axes from the supplied Frame */
33782          } else {
33783             ofrm = astPickAxes( *frm, j, axes, NULL );
33784 
33785 /* Replace the supplied Frame with a CmpFrame made up of this Frame and
33786    the SkyFrame. */
33787             (void) astAnnul( *frm );
33788             *frm = (AstFrame *) astCmpFrame( ofrm, sfrm, "", status );
33789             ofrm = astAnnul( ofrm );
33790          }
33791 
33792 /* Permute the axis order to put the longitude and latitude axes back in
33793    their original position. The SkyFrame will have the default axis
33794    ordering (lon=axis 0, lat = axis 1). */
33795          j = 0;
33796          for( i = 0; i < naxes; i++ ) {
33797             if( i == axlat ) {
33798                axes[ i ] = naxes - 1;
33799             } else if( i == axlon ) {
33800                axes[ i ] = naxes - 2;
33801             } else {
33802                axes[ i ] = j++;
33803             }
33804          }
33805          astPermAxes( *frm, axes );
33806 
33807 /* Free the axes array. */
33808          axes= astFree( axes );
33809       }
33810 
33811 /* Set the units in the supplied IWC Frame for the longitude and latitude
33812    axes. Unless using -TAB, these are degrees (the conversion from degs to
33813    rads is part of the Mapping from IWC to WCS). If using -TAB the units
33814    are unknown. */
33815       if( !tabaxis || !tabaxis[ axlon ] ) astSetUnit( iwcfrm, axlon, "deg" );
33816       if( !tabaxis || !tabaxis[ axlat ] ) astSetUnit( iwcfrm, axlat, "deg" );
33817 
33818 /* Modify any supplied tabmap so that the celestial outputs create
33819    radians rather than degrees (but only if the celestial axes are
33820    generated by the -TAB algorithm). */
33821       if( tabaxis && tabaxis[ axlon ] && tabaxis[ axlat ] ) {
33822          mat = (double *) astMalloc( sizeof(double)*naxes );
33823          if( mat ){
33824             for( i = 0; i < naxes; i++ ){
33825                if( i == axlat || i == axlon ){
33826                   mat[ i ] = AST__DD2R;
33827                } else {
33828                   mat[ i ] = 1.0;
33829                }
33830             }
33831             map1 = (AstMapping *) astMatrixMap( naxes, naxes, 1, mat, "", status );
33832             mat = (double *) astFree( (void *) mat );
33833             map2 = (AstMapping *) astCmpMap( *tabmap, map1, 1, " ", status );
33834             map1 = astAnnul( map1 );
33835             (void) astAnnul( *tabmap );
33836             *tabmap = map2;
33837          }
33838 
33839 /* Also modify the returned reflon and reflat values to transform them
33840    using the tabmap. Also transform the reference position in the SkyFrame. */
33841          if( *reflon != AST__BAD && *reflat != AST__BAD ) {
33842             ina = astMalloc( sizeof(double)*naxes );
33843             outa = astMalloc( sizeof(double)*naxes );
33844             if( astOK ) {
33845                for( i = 0; i < naxes; i++ ) ina[ i ] = 0.0;
33846                ina[ axlat ] = *reflat;
33847                ina[ axlon ] = *reflon;
33848                astTranN( *tabmap, 1, naxes, 1, ina, 1, naxes, 1, outa );
33849                *reflon = outa[ axlon ];
33850                *reflat = outa[ axlat ];
33851             }
33852             ina = astFree( ina );
33853             outa = astFree( outa );
33854 
33855 /* Store this transformed reference position in the SkyFrame. */
33856             astSetSkyRef( sfrm, 0, *reflon );
33857             astSetSkyRef( sfrm, 1, *reflat );
33858             astSet( sfrm, "SkyRefIs=Ignored", status );
33859          }
33860       }
33861 
33862 /* If the header contains AXREF values for both lon and lat axes, use
33863    them as the sky reference position in preferences to the values
33864    derived form the CRVAL values. AXREF keywords are created by the
33865    astWrite method for axes described by -TAB algorithm that have no inverse
33866    transformation. */
33867       if( GetValue( this, FormatKey( "AXREF", axlon + 1, -1, s, status ),
33868                     AST__FLOAT, (void *) &lonval, 0, 0, method, class, status ) &&
33869           GetValue( this, FormatKey( "AXREF", axlat + 1, -1, s, status ),
33870                     AST__FLOAT, (void *) &latval, 0, 0, method, class, status ) ) {
33871          *reflon = lonval*AST__DD2R;
33872          *reflat = latval*AST__DD2R;
33873          astSetSkyRef( sfrm, 0, *reflon );
33874          astSetSkyRef( sfrm, 1, *reflat );
33875          astSet( sfrm, "SkyRefIs=Ignored", status );
33876       }
33877 
33878 /* Free resources. */
33879       sfrm = astAnnul( sfrm );
33880    }
33881 
33882 /* Return the result. */
33883    return ret;
33884 }
33885 
WcsFcRead(AstFitsChan * fc,AstFitsChan * fc2,FitsStore * store,const char * method,const char * class,int * status)33886 static void WcsFcRead( AstFitsChan *fc, AstFitsChan *fc2, FitsStore *store,
33887                        const char *method, const char *class, int *status ){
33888 /*
33889 *  Name:
33890 *     WcsFcRead
33891 
33892 *  Purpose:
33893 *     Extract WCS information from a supplied FitsChan using a FITSWCS
33894 *     encoding, and store it in the supplied FitsStore.
33895 
33896 *  Type:
33897 *     Private function.
33898 
33899 *  Synopsis:
33900 *     #include "fitschan.h"
33901 *     void WcsFcRead( AstFitsChan *fc, AstFitsChan *fc2, FitsStore *store,
33902 *                     const char *method, const char *class, int *status )
33903 
33904 *  Class Membership:
33905 *     FitsChan member function.
33906 
33907 *  Description:
33908 *     A FitsStore is a structure containing a generalised represention of
33909 *     a FITS WCS FrameSet. Functions exist to convert a FitsStore to and
33910 *     from a set of FITS header cards (using a specified encoding), or
33911 *     an AST FrameSet. In other words, a FitsStore is an encoding-
33912 *     independant intermediary staging post between a FITS header and
33913 *     an AST FrameSet.
33914 *
33915 *     This function extracts FITSWCS keywords from the supplied FitsChan,
33916 *     and stores the corresponding WCS information in the supplied FitsStore.
33917 
33918 *  Parameters:
33919 *     fc
33920 *        Pointer to the FitsChan containing the cards read from the
33921 *        original FITS header. This should not include any un-used
33922 *        non-standard keywords.
33923 *     fc2
33924 *        Pointer to a second FitsChan. If a card read from "fc" fails to
33925 *        be converted to its correct data type, a warning is only issued
33926 *        if there is no card for this keyword in "fc2". "fc2" may be NULL
33927 *        in which case a warning is always issued.
33928 *     store
33929 *        Pointer to the FitsStore structure.
33930 *     method
33931 *        Pointer to a string holding the name of the calling method.
33932 *        This is only for use in constructing error messages.
33933 *     class
33934 *        Pointer to a string holding the name of the supplied object class.
33935 *        This is only for use in constructing error messages.
33936 *     status
33937 *        Pointer to the inherited status variable.
33938 */
33939 
33940 /* Local Variables: */
33941    char buf[200];     /* Buffer for warning message */
33942    char *cval;        /* String keyword value */
33943    char *keynam;      /* Pointer to current keyword name */
33944    char s;            /* Co-ordinate version character */
33945    double dval;       /* Floating point keyword value */
33946    int fld[2];        /* Integer field values from keyword name */
33947    int jm;            /* Pixel axis or projection parameter index */
33948    int i;             /* Intermediate axis index */
33949    int mark;          /* Non-zero if card should be removed once used */
33950    int nfld;          /* Number of integer fields in test string */
33951    int ok;            /* Was value converted succesfully? */
33952    int type;          /* Keyword data type */
33953    int undef;         /* Is an undefined keyword value acceptable? */
33954    void *item;        /* Pointer to item to get/put */
33955 
33956 /* Check the global error status. */
33957    if ( !astOK ) return;
33958 
33959 /* Ensure the FitsChan is re-wound. */
33960    astClearCard( fc );
33961 
33962 /* Loop round all the cards in the FitsChan obtaining the keyword name for
33963    each card. Note, the single "=" is correct in the following "while"
33964    statement. */
33965    s = 0;
33966    jm = -1;
33967    i = -1;
33968    type = AST__NOTYPE;
33969    while( (keynam = CardName( fc, status )) ){
33970       item = NULL;
33971 
33972 /* Assume the card is to be consumed by the reading process. This means
33973    the card will be marked as used and effectively excluded from the header.
33974    Keywords which supply observation details that do not depend on the
33975    mapping from pixel to WCS axes, or on the nature of the WCS axes,
33976    are not removed as they may be needed for other, non-WCS related,
33977    purposes. */
33978       mark = 1;
33979 
33980 /* For most keywords, if the keyword is present in the header it must
33981    have a definded value. However, some keywords are read from the header
33982    but not actually used for anything. This is done to ensure that the
33983    keyword is stripped from the header. It is acceptable for such
33984    keywords to have an undefined value. Initialise a flag indicating that
33985    the next keyword read is not allowed to have an undefined value. */
33986       undef = 0;
33987 
33988 /* Is this a primary CRVAL keyword? */
33989       if( Match( keynam, "CRVAL%d", 1, fld, &nfld, method, class, status ) ){
33990          item = &(store->crval);
33991          type = AST__FLOAT;
33992          i = fld[ 0 ] - 1;
33993          jm = 0;
33994          s = ' ';
33995 
33996 /* Is this a secondary CRVAL keyword? */
33997       } else if( Match( keynam, "CRVAL%d%1c", 1, fld, &nfld, method, class, status ) ){
33998          item = &(store->crval);
33999          type = AST__FLOAT;
34000          i = fld[ 0 ] - 1;
34001          jm = 0;
34002          s = keynam[ strlen( keynam ) - 1 ];
34003 
34004 /* Is this a primary CRPIX keyword? */
34005       } else if( Match( keynam, "CRPIX%d", 1, fld, &nfld, method, class, status ) ){
34006          item = &(store->crpix);
34007          type = AST__FLOAT;
34008          i = 0;
34009          jm = fld[ 0 ] - 1;
34010          s = ' ';
34011 
34012 /* Is this a secondary CRPIX keyword? */
34013       } else if( Match( keynam, "CRPIX%d%1c", 1, fld, &nfld, method, class, status ) ){
34014          item = &(store->crpix);
34015          type = AST__FLOAT;
34016          i = 0;
34017          jm = fld[ 0 ] - 1;
34018          s = keynam[ strlen( keynam ) - 1 ];
34019 
34020 /* Is this a primary CDELT keyword? */
34021       } else if( Match( keynam, "CDELT%d", 1, fld, &nfld, method, class, status ) ){
34022          item = &(store->cdelt);
34023          type = AST__FLOAT;
34024          i = fld[ 0 ] - 1;
34025          jm = 0;
34026          s = ' ';
34027 
34028 /* Is this a secondary CDELT keyword? */
34029       } else if( Match( keynam, "CDELT%d%1c", 1, fld, &nfld, method, class, status ) ){
34030          item = &(store->cdelt);
34031          type = AST__FLOAT;
34032          i = fld[ 0 ] - 1;
34033          jm = 0;
34034          s = keynam[ strlen( keynam ) - 1 ];
34035 
34036 /* Is this a primary CTYPE keyword? If so, store the associated comment. */
34037       } else if( Match( keynam, "CTYPE%d", 1, fld, &nfld, method, class, status ) ){
34038          item = &(store->ctype);
34039          type = AST__STRING;
34040          i = fld[ 0 ] - 1;
34041          jm = 0;
34042          s = ' ';
34043          SetItemC( &(store->ctype_com), i, 0, ' ', CardComm( fc, status ), status );
34044 
34045 /* Is this a secondary CTYPE keyword? If so, store the associated comment. */
34046       } else if( Match( keynam, "CTYPE%d%1c", 1, fld, &nfld, method, class, status ) ){
34047          item = &(store->ctype);
34048          type = AST__STRING;
34049          i = fld[ 0 ] - 1;
34050          jm = 0;
34051          s = keynam[ strlen( keynam ) - 1 ];
34052          SetItemC( &(store->ctype_com), i, 0, s, CardComm( fc, status ), status );
34053 
34054 /* Is this a primary CNAME keyword? */
34055       } else if( Match( keynam, "CNAME%d", 1, fld, &nfld, method, class, status ) ){
34056          item = &(store->cname);
34057          type = AST__STRING;
34058          i = fld[ 0 ] - 1;
34059          jm = 0;
34060          s = ' ';
34061 
34062 /* Is this a secondary CNAME keyword? */
34063       } else if( Match( keynam, "CNAME%d%1c", 1, fld, &nfld, method, class, status ) ){
34064          item = &(store->cname);
34065          type = AST__STRING;
34066          i = fld[ 0 ] - 1;
34067          jm = 0;
34068          s = keynam[ strlen( keynam ) - 1 ];
34069 
34070 /* Is this a primary CUNIT keyword? */
34071       } else if( Match( keynam, "CUNIT%d", 1, fld, &nfld, method, class, status ) ){
34072          item = &(store->cunit);
34073          type = AST__STRING;
34074          i = fld[ 0 ] - 1;
34075          jm = 0;
34076          s = ' ';
34077 
34078 /* Is this a secondary CUNIT keyword? */
34079       } else if( Match( keynam, "CUNIT%d%1c", 1, fld, &nfld, method, class, status ) ){
34080          item = &(store->cunit);
34081          type = AST__STRING;
34082          i = fld[ 0 ] - 1;
34083          jm = 0;
34084          s = keynam[ strlen( keynam ) - 1 ];
34085 
34086 /* Is this a primary PC keyword? */
34087       } else if( Match( keynam, "PC%d_%d", 2, fld, &nfld, method, class, status ) ){
34088          item = &(store->pc);
34089          type = AST__FLOAT;
34090          i = fld[ 0 ] - 1;
34091          jm = fld[ 1 ] - 1;
34092          s = ' ';
34093 
34094 /* Is this a secondary PC keyword? */
34095       } else if( Match( keynam, "PC%d_%d%1c", 2, fld, &nfld, method, class, status ) ){
34096          item = &(store->pc);
34097          type = AST__FLOAT;
34098          i = fld[ 0 ] - 1;
34099          jm = fld[ 1 ] - 1;
34100          s = keynam[ strlen( keynam ) - 1 ];
34101 
34102 /* Is this a primary PV keyword? */
34103       } else if( Match( keynam, "PV%d_%d", 2, fld, &nfld, method, class, status ) ){
34104          item = &(store->pv);
34105          type = AST__FLOAT;
34106          i = fld[ 0 ] - 1;
34107          jm = fld[ 1 ];
34108          s = ' ';
34109 
34110 /* Is this a secondary PV keyword? */
34111       } else if( Match( keynam, "PV%d_%d%1c", 2, fld, &nfld, method, class, status ) ){
34112          item = &(store->pv);
34113          type = AST__FLOAT;
34114          i = fld[ 0 ] - 1;
34115          jm = fld[ 1 ];
34116          s = keynam[ strlen( keynam ) - 1 ];
34117 
34118 /* Is this a primary PS keyword? */
34119       } else if( Match( keynam, "PS%d_%d", 2, fld, &nfld, method, class, status ) ){
34120          item = &(store->ps);
34121          type = AST__STRING;
34122          i = fld[ 0 ] - 1;
34123          jm = fld[ 1 ];
34124          s = ' ';
34125 
34126 /* Is this a secondary PS keyword? */
34127       } else if( Match( keynam, "PS%d_%d%1c", 2, fld, &nfld, method, class, status ) ){
34128          item = &(store->ps);
34129          type = AST__STRING;
34130          i = fld[ 0 ] - 1;
34131          jm = fld[ 1 ];
34132          s = keynam[ strlen( keynam ) - 1 ];
34133 
34134 /* Is this a primary RADESYS keyword? */
34135       } else if( Match( keynam, "RADESYS", 0, fld, &nfld, method, class, status ) ){
34136          item = &(store->radesys);
34137          type = AST__STRING;
34138          i = 0;
34139          jm = 0;
34140          s = ' ';
34141 
34142 /* Is this a secondary RADESYS keyword? */
34143       } else if( Match( keynam, "RADESYS%1c", 0, fld, &nfld, method, class, status ) ){
34144          item = &(store->radesys);
34145          type = AST__STRING;
34146          i = 0;
34147          jm = 0;
34148          s = keynam[ strlen( keynam ) - 1 ];
34149 
34150 /* Is this a primary EQUINOX keyword? */
34151       } else if( Match( keynam, "EQUINOX", 0, fld, &nfld, method, class, status ) ){
34152          item = &(store->equinox);
34153          type = AST__FLOAT;
34154          i = 0;
34155          jm = 0;
34156          s = ' ';
34157 
34158 /* Is this a secondary EQUINOX keyword? */
34159       } else if( Match( keynam, "EQUINOX%1c", 0, fld, &nfld, method, class, status ) ){
34160          item = &(store->equinox);
34161          type = AST__FLOAT;
34162          i = 0;
34163          jm = 0;
34164          s = keynam[ strlen( keynam ) - 1 ];
34165 
34166 /* Is this a primary LATPOLE keyword? */
34167       } else if( Match( keynam, "LATPOLE", 0, fld, &nfld, method, class, status ) ){
34168          item = &(store->latpole);
34169          type = AST__FLOAT;
34170          i = 0;
34171          jm = 0;
34172          s = ' ';
34173 
34174 /* Is this a secondary LATPOLE keyword? */
34175       } else if( Match( keynam, "LATPOLE%1c", 0, fld, &nfld, method, class, status ) ){
34176          item = &(store->latpole);
34177          type = AST__FLOAT;
34178          i = 0;
34179          jm = 0;
34180          s = keynam[ strlen( keynam ) - 1 ];
34181 
34182 /* Is this a primary LONPOLE keyword? */
34183       } else if( Match( keynam, "LONPOLE", 0, fld, &nfld, method, class, status ) ){
34184          item = &(store->lonpole);
34185          type = AST__FLOAT;
34186          i = 0;
34187          jm = 0;
34188          s = ' ';
34189 
34190 /* Is this a secondary LONPOLE keyword? */
34191       } else if( Match( keynam, "LONPOLE%1c", 0, fld, &nfld, method, class, status ) ){
34192          item = &(store->lonpole);
34193          type = AST__FLOAT;
34194          i = 0;
34195          jm = 0;
34196          s = keynam[ strlen( keynam ) - 1 ];
34197 
34198 /* Is this a primary WXSAXES keyword? */
34199       } else if( Match( keynam, "WCSAXES", 0, fld, &nfld, method, class, status ) ){
34200          item = &(store->wcsaxes);
34201          type = AST__FLOAT;
34202          i = 0;
34203          jm = 0;
34204          s = ' ';
34205 
34206 /* Is this a secondary WCSAXES keyword? */
34207       } else if( Match( keynam, "WCSAXES%1c", 0, fld, &nfld, method, class, status ) ){
34208          item = &(store->wcsaxes);
34209          type = AST__FLOAT;
34210          i = 0;
34211          jm = 0;
34212          s = keynam[ strlen( keynam ) - 1 ];
34213 
34214 /* Is this a primary DUT1 keyword? */
34215       } else if( Match( keynam, "DUT1", 0, fld, &nfld, method, class, status ) ){
34216          mark = 0;
34217          item = &(store->dut1);
34218          type = AST__FLOAT;
34219          i = 0;
34220          jm = 0;
34221          s = ' ';
34222 
34223 /* Is this a primary MJD-OBS keyword? */
34224       } else if( Match( keynam, "MJD-OBS", 0, fld, &nfld, method, class, status ) ){
34225          mark = 0;
34226          item = &(store->mjdobs);
34227          type = AST__FLOAT;
34228          i = 0;
34229          jm = 0;
34230          s = ' ';
34231 
34232 /* Is this a primary WCSNAME keyword? */
34233       } else if( Match( keynam, "WCSNAME", 0, fld, &nfld, method, class, status ) ){
34234          item = &(store->wcsname);
34235          type = AST__STRING;
34236          i = 0;
34237          jm = 0;
34238          s = ' ';
34239 
34240 /* Is this a secondary WCSNAME keyword? */
34241       } else if( Match( keynam, "WCSNAME%1c", 0, fld, &nfld, method, class, status ) ){
34242          item = &(store->wcsname);
34243          type = AST__STRING;
34244          i = 0;
34245          jm = 0;
34246          s = keynam[ strlen( keynam ) - 1 ];
34247 
34248 /* Is this a primary SPECSYS keyword? */
34249       } else if( Match( keynam, "SPECSYS", 0, fld, &nfld, method, class, status ) ){
34250          item = &(store->specsys);
34251          type = AST__STRING;
34252          i = 0;
34253          jm = 0;
34254          s = ' ';
34255 
34256 /* Is this a secondary SPECSYS keyword? */
34257       } else if( Match( keynam, "SPECSYS%1c", 0, fld, &nfld, method, class, status ) ){
34258          item = &(store->specsys);
34259          type = AST__STRING;
34260          i = 0;
34261          jm = 0;
34262          s = keynam[ strlen( keynam ) - 1 ];
34263 
34264 /* Is this a primary SSYSSRC keyword? */
34265       } else if( Match( keynam, "SSYSSRC", 0, fld, &nfld, method, class, status ) ){
34266          item = &(store->ssyssrc);
34267          type = AST__STRING;
34268          i = 0;
34269          jm = 0;
34270          s = ' ';
34271 
34272 /* Is this a secondary SSYSSRC keyword? */
34273       } else if( Match( keynam, "SSYSSRC%1c", 0, fld, &nfld, method, class, status ) ){
34274          item = &(store->ssyssrc);
34275          type = AST__STRING;
34276          i = 0;
34277          jm = 0;
34278          s = keynam[ strlen( keynam ) - 1 ];
34279 
34280 /* Is this a primary ZSOURCE keyword? */
34281       } else if( Match( keynam, "ZSOURCE", 0, fld, &nfld, method, class, status ) ){
34282          item = &(store->zsource);
34283          type = AST__FLOAT;
34284          i = 0;
34285          jm = 0;
34286          s = ' ';
34287 
34288 /* Is this a secondary ZSOURCE keyword? */
34289       } else if( Match( keynam, "ZSOURCE%1c", 0, fld, &nfld, method, class, status ) ){
34290          item = &(store->zsource);
34291          type = AST__FLOAT;
34292          i = 0;
34293          jm = 0;
34294          s = keynam[ strlen( keynam ) - 1 ];
34295 
34296 /* Is this a primary VELOSYS keyword? */
34297       } else if( Match( keynam, "VELOSYS", 0, fld, &nfld, method, class, status ) ){
34298          item = &(store->velosys);
34299          type = AST__FLOAT;
34300          undef = 1;
34301          i = 0;
34302          jm = 0;
34303          s = ' ';
34304 
34305 /* Is this a secondary VELOSYS keyword? */
34306       } else if( Match( keynam, "VELOSYS%1c", 0, fld, &nfld, method, class, status ) ){
34307          item = &(store->velosys);
34308          type = AST__FLOAT;
34309          undef = 1;
34310          i = 0;
34311          jm = 0;
34312          s = keynam[ strlen( keynam ) - 1 ];
34313 
34314 /* Is this a primary RESTFRQ keyword? */
34315       } else if( Match( keynam, "RESTFRQ", 0, fld, &nfld, method, class, status ) ){
34316          item = &(store->restfrq);
34317          type = AST__FLOAT;
34318          i = 0;
34319          jm = 0;
34320          s = ' ';
34321 
34322 /* Is this a secondary RESTFRQ keyword? */
34323       } else if( Match( keynam, "RESTFRQ%1c", 0, fld, &nfld, method, class, status ) ){
34324          item = &(store->restfrq);
34325          type = AST__FLOAT;
34326          i = 0;
34327          jm = 0;
34328          s = keynam[ strlen( keynam ) - 1 ];
34329 
34330 /* Is this a primary RESTWAV keyword? */
34331       } else if( Match( keynam, "RESTWAV", 0, fld, &nfld, method, class, status ) ){
34332          item = &(store->restwav);
34333          type = AST__FLOAT;
34334          i = 0;
34335          jm = 0;
34336          s = ' ';
34337 
34338 /* Is this a secondary RESTWAV keyword? */
34339       } else if( Match( keynam, "RESTWAV%1c", 0, fld, &nfld, method, class, status ) ){
34340          item = &(store->restwav);
34341          type = AST__FLOAT;
34342          i = 0;
34343          jm = 0;
34344          s = keynam[ strlen( keynam ) - 1 ];
34345 
34346 /* Is this a primary IMAGFREQ keyword? */
34347       } else if( Match( keynam, "IMAGFREQ", 0, fld, &nfld, method, class, status ) ){
34348          item = &(store->imagfreq);
34349          type = AST__FLOAT;
34350          i = 0;
34351          jm = 0;
34352          s = ' ';
34353 
34354 /* Is this a primary SKYREF keyword? */
34355       } else if( Match( keynam, "SREF%d", 1, fld, &nfld, method, class, status ) ){
34356          item = &(store->skyref);
34357          type = AST__FLOAT;
34358          i = fld[ 0 ] - 1;
34359          jm = 0;
34360          s = ' ';
34361 
34362 /* Is this a secondary SKYREF keyword? */
34363       } else if( Match( keynam, "SREF%d%1c", 1, fld, &nfld, method, class, status ) ){
34364          item = &(store->skyref);
34365          type = AST__FLOAT;
34366          i = fld[ 0 ] - 1;
34367          jm = 0;
34368          s = keynam[ strlen( keynam ) - 1 ];
34369 
34370 /* Is this a primary SKYREFP keyword? */
34371       } else if( Match( keynam, "SREFP%d", 1, fld, &nfld, method, class, status ) ){
34372          item = &(store->skyrefp);
34373          type = AST__FLOAT;
34374          i = fld[ 0 ] - 1;
34375          jm = 0;
34376          s = ' ';
34377 
34378 /* Is this a secondary SKYREFP keyword? */
34379       } else if( Match( keynam, "SREFP%d%1c", 1, fld, &nfld, method, class, status ) ){
34380          item = &(store->skyrefp);
34381          type = AST__FLOAT;
34382          i = fld[ 0 ] - 1;
34383          jm = 0;
34384          s = keynam[ strlen( keynam ) - 1 ];
34385 
34386 /* Is this a primary SKYREFIS keyword? */
34387       } else if( Match( keynam, "SREFIS", 0, fld, &nfld, method, class, status ) ){
34388          item = &(store->skyrefis);
34389          type = AST__STRING;
34390          i = 0;
34391          jm = 0;
34392          s = ' ';
34393 
34394 /* Is this a secondary SKYREFIS keyword? */
34395       } else if( Match( keynam, "SREFIS%1c", 0, fld, &nfld, method, class, status ) ){
34396          item = &(store->skyrefis);
34397          type = AST__STRING;
34398          i = 0;
34399          jm = 0;
34400          s = keynam[ strlen( keynam ) - 1 ];
34401 
34402 /* Is this a primary AXREF keyword? */
34403       } else if( Match( keynam, "AXREF%d", 1, fld, &nfld, method, class, status ) ){
34404          item = &(store->axref);
34405          type = AST__FLOAT;
34406          i = fld[ 0 ] - 1;
34407          jm = 0;
34408          s = ' ';
34409 
34410 /* Is this a secondary AXREF keyword? */
34411       } else if( Match( keynam, "AXREF%d%1c", 1, fld, &nfld, method, class, status ) ){
34412          item = &(store->axref);
34413          type = AST__FLOAT;
34414          i = fld[ 0 ] - 1;
34415          jm = 0;
34416          s = keynam[ strlen( keynam ) - 1 ];
34417 
34418 /* Is this a MJD-AVG keyword? */
34419       } else if( Match( keynam, "MJD-AVG", 0, fld, &nfld, method, class, status ) ){
34420          mark = 0;
34421          item = &(store->mjdavg);
34422          type = AST__FLOAT;
34423          i = 0;
34424          jm = 0;
34425          s = ' ';
34426 
34427 /* Is this a OBSGEO-X keyword? */
34428       } else if( Match( keynam, "OBSGEO-X", 0, fld, &nfld, method, class, status ) ){
34429          mark = 0;
34430          item = &(store->obsgeox);
34431          type = AST__FLOAT;
34432          i = 0;
34433          jm = 0;
34434          s = ' ';
34435 
34436 /* Is this a OBSGEO-Y keyword? */
34437       } else if( Match( keynam, "OBSGEO-Y", 0, fld, &nfld, method, class, status ) ){
34438          mark = 0;
34439          item = &(store->obsgeoy);
34440          type = AST__FLOAT;
34441          i = 0;
34442          jm = 0;
34443          s = ' ';
34444 
34445 /* Is this a OBSGEO-Z keyword? */
34446       } else if( Match( keynam, "OBSGEO-Z", 0, fld, &nfld, method, class, status ) ){
34447          mark = 0;
34448          item = &(store->obsgeoz);
34449          type = AST__FLOAT;
34450          i = 0;
34451          jm = 0;
34452          s = ' ';
34453 
34454 /* Is this a TIMESYS keyword? */
34455       } else if( Match( keynam, "TIMESYS", 0, fld, &nfld, method, class, status ) ){
34456          item = &(store->timesys);
34457          type = AST__STRING;
34458          i = 0;
34459          jm = 0;
34460          s = ' ';
34461 
34462 /* Following keywords are used to describe "-SIP" distortion as used by
34463    the Spitzer project... */
34464 
34465 /* Is this a primary A keyword? */
34466       } else if( Match( keynam, "A_%d_%d", 2, fld, &nfld, method, class, status ) ){
34467          item = &(store->asip);
34468          type = AST__FLOAT;
34469          i = fld[ 0 ];
34470          jm = fld[ 1 ];
34471          s = ' ';
34472 
34473 /* Is this a secondary A keyword? */
34474       } else if( Match( keynam, "A_%d_%d%1c", 2, fld, &nfld, method, class, status ) ){
34475          item = &(store->asip);
34476          type = AST__FLOAT;
34477          i = fld[ 0 ];
34478          jm = fld[ 1 ];
34479          s = keynam[ strlen( keynam ) - 1 ];
34480 
34481 /* Is this a primary B keyword? */
34482       } else if( Match( keynam, "B_%d_%d", 2, fld, &nfld, method, class, status ) ){
34483          item = &(store->bsip);
34484          type = AST__FLOAT;
34485          i = fld[ 0 ];
34486          jm = fld[ 1 ];
34487          s = ' ';
34488 
34489 /* Is this a secondary B keyword? */
34490       } else if( Match( keynam, "B_%d_%d%1c", 2, fld, &nfld, method, class, status ) ){
34491          item = &(store->bsip);
34492          type = AST__FLOAT;
34493          i = fld[ 0 ];
34494          jm = fld[ 1 ];
34495          s = keynam[ strlen( keynam ) - 1 ];
34496 
34497 /* Is this a primary AP keyword? */
34498       } else if( Match( keynam, "AP_%d_%d", 2, fld, &nfld, method, class, status ) ){
34499          item = &(store->apsip);
34500          type = AST__FLOAT;
34501          i = fld[ 0 ];
34502          jm = fld[ 1 ];
34503          s = ' ';
34504 
34505 /* Is this a secondary AP keyword? */
34506       } else if( Match( keynam, "AP_%d_%d%1c", 2, fld, &nfld, method, class, status ) ){
34507          item = &(store->apsip);
34508          type = AST__FLOAT;
34509          i = fld[ 0 ];
34510          jm = fld[ 1 ];
34511          s = keynam[ strlen( keynam ) - 1 ];
34512 
34513 /* Is this a primary BP keyword? */
34514       } else if( Match( keynam, "BP_%d_%d", 2, fld, &nfld, method, class, status ) ){
34515          item = &(store->bpsip);
34516          type = AST__FLOAT;
34517          i = fld[ 0 ];
34518          jm = fld[ 1 ];
34519          s = ' ';
34520 
34521 /* Is this a secondary BP keyword? */
34522       } else if( Match( keynam, "BP_%d_%d%1c", 2, fld, &nfld, method, class, status ) ){
34523          item = &(store->bpsip);
34524          type = AST__FLOAT;
34525          i = fld[ 0 ];
34526          jm = fld[ 1 ];
34527          s = keynam[ strlen( keynam ) - 1 ];
34528       }
34529 
34530 /* If this keyword was recognized, store it in the FitsStore, and mark it
34531    as having been read. */
34532       if( item ){
34533          ok = 1;
34534          if( type == AST__FLOAT ){
34535             if( CnvValue( fc, AST__FLOAT, undef, &dval, method, status ) ) {
34536                SetItem( (double ****) item, i, jm, s, dval, status );
34537                if( mark ) MarkCard( fc, status );
34538             } else {
34539                ok = 0;
34540             }
34541          } else {
34542             if( CnvValue( fc, AST__STRING, undef, &cval, method, status ) ) {
34543                cval[ astChrLen( cval ) ] = 0;  /* Exclude trailing spaces */
34544                SetItemC( (char *****) item, i, jm, s, cval, status );
34545                if( mark ) MarkCard( fc, status );
34546             } else {
34547               ok = 0;
34548             }
34549          }
34550 
34551 /* Issue a warning if the value could not be converted to the expected
34552    type. */
34553          if( !ok ) {
34554 
34555 /* First check that the keyword is not included in "fc2". */
34556             if( !HasCard( fc2, keynam, method, class, status ) ) {
34557                sprintf( buf, "The original FITS header contained a value for "
34558                         "keyword %s which could not be converted to a %s.",
34559                         keynam, ( type==AST__FLOAT ? "floating point number":
34560                         "character string" ) );
34561                Warn( fc, "badval", buf, "astRead", "FitsChan", status );
34562             }
34563          }
34564       }
34565 
34566 /* Move on to the next card. */
34567       MoveCard( fc, 1, method, class, status );
34568    }
34569 }
34570 
WcsFromStore(AstFitsChan * this,FitsStore * store,const char * method,const char * class,int * status)34571 static int WcsFromStore( AstFitsChan *this, FitsStore *store,
34572                          const char *method, const char *class, int *status ){
34573 
34574 /*
34575 *  Name:
34576 *     WcsFromStore
34577 
34578 *  Purpose:
34579 *     Store WCS keywords in a FitsChan using FITS-WCS encoding.
34580 
34581 *  Type:
34582 *     Private function.
34583 
34584 *  Synopsis:
34585 *     int WcsFromStore( AstFitsChan *this, FitsStore *store,
34586 *                       const char *method, const char *class, int *status )
34587 
34588 *  Class Membership:
34589 *     FitsChan
34590 
34591 *  Description:
34592 *     A FitsStore is a structure containing a generalised represention of
34593 *     a FITS WCS FrameSet. Functions exist to convert a FitsStore to and
34594 *     from a set of FITS header cards (using a specified encoding), or
34595 *     an AST FrameSet. In other words, a FitsStore is an encoding-
34596 *     independant intermediary staging post between a FITS header and
34597 *     an AST FrameSet.
34598 *
34599 *     This function copies the WCS information stored in the supplied
34600 *     FitsStore into the supplied FitsChan, using FITS-WCS encoding.
34601 
34602 *  Parameters:
34603 *     this
34604 *        Pointer to the FitsChan.
34605 *     store
34606 *        Pointer to the FitsStore.
34607 *     method
34608 *        Pointer to a string holding the name of the calling method.
34609 *        This is only for use in constructing error messages.
34610 *     class
34611 *        Pointer to a string holding the name of the supplied object class.
34612 *        This is only for use in constructing error messages.
34613 *     status
34614 *        Pointer to the inherited status variable.
34615 
34616 *  Returned Value:
34617 *     A value of 1 is returned if succesfull, and zero is returned
34618 *     otherwise.
34619 */
34620 
34621 /* Local Variables: */
34622    char *comm;         /* Pointer to comment string */
34623    char *cval;         /* Pointer to string keyword value */
34624    char parprefix[3];  /* Prefix for projection parameter keywords */
34625    char combuf[80];    /* Buffer for FITS card comment */
34626    char s;             /* Co-ordinate version character */
34627    char sign[2];       /* Fraction's sign character */
34628    char sup;           /* Upper limit on s */
34629    char type[MXCTYPELEN];/* Buffer for CTYPE value */
34630    double cdl;         /* CDELT value */
34631    double fd;          /* Fraction of a day */
34632    double mjd99;       /* MJD at start of 1999 */
34633    double val;         /* General purpose value */
34634    int *tabaxis;       /* Flags WCS axes that use -TAB algorithm */
34635    int i;              /* Axis index */
34636    int ihmsf[ 4 ];     /* Hour, minute, second, fractional second */
34637    int iymdf[ 4 ];     /* Year, month, date, fractional day */
34638    int j;              /* Axis index */
34639    int jj;             /* SlaLib status */
34640    int m;              /* Parameter index */
34641    int maxm;           /* Upper limit on m */
34642    int naxis;          /* Value of NAXIS keyword */
34643    int nc;             /* Length of STYPE string */
34644    int nwcs;           /* No. of WCS axes */
34645    int ok;             /* Frame created succesfully? */
34646    int prj;            /* Projection type */
34647    int ret;            /* Returned value */
34648 
34649 /* Initialise */
34650    ret = 0;
34651 
34652 /* Other initialisation to avoid compiler warnings. */
34653    tabaxis = NULL;
34654 
34655 /* Check the inherited status. */
34656    if( !astOK ) return ret;
34657 
34658 /* If the FitsChan contains a value for the NAXIS keyword, note it.
34659    Otherwise store -1. */
34660    if( !astGetFitsI( this, "NAXIS", &naxis ) ) naxis = -1;
34661 
34662 /* Find the last WCS related card. */
34663    FindWcs( this, 1, 1, 0, method, class, status );
34664 
34665 /* Loop round all co-ordinate versions */
34666    sup = GetMaxS( &(store->crval), status );
34667    for( s = ' '; s <= sup && astOK; s++ ){
34668 
34669 /* For alternate axes, skip this axis description if there is no CRPIX1 or
34670    CRVAL1 value. This avoids partial axis descriptions being written out. */
34671       if( s != ' ' ) {
34672          if( GetItem( &(store->crpix), 0, 0, s, NULL, method, class, status ) ==
34673              AST__BAD ||
34674              GetItem( &(store->crval), 0, 0, s, NULL, method, class, status ) ==
34675              AST__BAD ) {
34676             ok = 0;
34677             goto next;
34678          }
34679       }
34680 
34681 /* Assume the Frame can be created succesfully. */
34682       ok = 1;
34683 
34684 /* Save the number of wcs axes. If a value for WCSAXES has been set, or
34685    if the number of axes is not the same as specified in the NAXIS keyword,
34686    store a WCSAXES keyword. */
34687       val = GetItem( &(store->wcsaxes), 0, 0, s, NULL, method, class, status );
34688       if( val != AST__BAD ) {
34689          nwcs = (int) ( val + 0.5 );
34690       } else {
34691          nwcs = GetMaxJM( &(store->crpix), s, status ) + 1;
34692          if( nwcs != 0 && nwcs != naxis ) val = (double) nwcs;
34693       }
34694       if( val != AST__BAD ) {
34695          SetValue( this, FormatKey( "WCSAXES", -1, -1, s, status ),
34696                    &nwcs, AST__INT, "Number of WCS axes", status );
34697       }
34698 
34699 /* Get and save WCSNAME. This is NOT required, so do not return if it is
34700    not available. If the WCS is 1-d, only store WCSNAME if its value is
34701    different to the CTYPE1 value. */
34702       cval = GetItemC( &(store->wcsname), 0, 0, s, NULL, method, class, status );
34703       if( cval && nwcs == 1 ) {
34704          comm = GetItemC( &(store->ctype), 0, 0, s, NULL, method, class, status );
34705          if( comm && Similar( comm, cval, status ) ) cval = NULL;
34706       }
34707       if( cval ) SetValue( this, FormatKey( "WCSNAME", -1, -1, s, status ), &cval,
34708                            AST__STRING, "Reference name for the coord. frame", status );
34709 
34710 /* The prefix for numerical projection parameters is usually "PV". */
34711       strcpy( parprefix, "PV" );
34712 
34713 /* Keywords common to all axis types... */
34714 
34715 /* Get and save CRPIX for all pixel axes. These are required, so pass on
34716    if they are not available. */
34717       for( i = 0; i < nwcs; i++ ) {
34718          val = GetItem( &(store->crpix), 0, i, s, NULL, method, class, status );
34719          if( val == AST__BAD ) {
34720             ok = 0;
34721             goto next;
34722          }
34723          sprintf( combuf, "Reference pixel on axis %d", i + 1 );
34724          SetValue( this, FormatKey( "CRPIX", i + 1, -1, s, status ), &val, AST__FLOAT,
34725                    combuf, status );
34726        }
34727 
34728 /* Get and save CRVAL for all WCS axes. These are required, so
34729    pass on if they are not available. */
34730       for( i = 0; i < nwcs; i++ ) {
34731          val = GetItem( &(store->crval), i, 0, s, NULL, method, class, status );
34732          if( val == AST__BAD ) {
34733             ok = 0;
34734             goto next;
34735          }
34736          sprintf( combuf, "Value at ref. pixel on axis %d", i + 1 );
34737          SetValue( this, FormatKey( "CRVAL", i + 1, -1, s, status ), &val, AST__FLOAT,
34738                    combuf, status );
34739       }
34740 
34741 /* Allocate memory to indicate if each WCS axis is described by a -TAB
34742    algorithm or not. Initialiss it to zero. */
34743       tabaxis = astCalloc( nwcs, sizeof( int ) );
34744 
34745 /* Get and save CTYPE for all WCS axes. These are required, so
34746    pass on if they are not available. */
34747       for( i = 0; i < nwcs; i++ ) {
34748          cval = GetItemC( &(store->ctype), i, 0, s, NULL, method, class, status );
34749          if( !cval ) {
34750             ok = 0;
34751             goto next;
34752          }
34753          comm = GetItemC( &(store->ctype_com), i, 0, s, NULL, method, class, status );
34754          if( !comm ) {
34755             sprintf( combuf, "Type of co-ordinate on axis %d", i + 1 );
34756             comm = combuf;
34757          }
34758 
34759 /* Extract the projection type as specified by the last 4 characters
34760    in the CTYPE keyword value. This will be AST__WCSBAD for non-celestial
34761    axes. Note, CTYPE can be more than 8 characters long. */
34762          nc = strlen( cval );
34763          prj = ( nc > 4 ) ? astWcsPrjType( cval + nc - 4 ) : AST__WCSBAD;
34764 
34765 /* If the projection type is "TPN" (an AST-specific code) convert it to
34766    standard FITS-WCS code "TAN" and change the prefix for projection
34767    parameters from "PV" to "QV". AST will do the inverse conversions when
34768    reading such a header. Non-AST software will simply ignore the QV
34769    terms and interpret the header as a simple TAN projection. */
34770          if( prj == AST__TPN ) {
34771             strcpy( parprefix, "QV" );
34772             strcpy( type, cval );
34773             (void) strcpy( type + nc - 4, "-TAN" );
34774             cval = type;
34775          }
34776 
34777 /* Note if the axis is described by the -TAB algorithm. */
34778          tabaxis[ i ] = ( prj == AST__WCSBAD && strlen( cval ) >= 8 &&
34779                           !strncmp( cval + 4, "-TAB", 4 ) );
34780 
34781 /* Store the (potentially modified) CTYPE value. */
34782          SetValue( this, FormatKey( "CTYPE", i + 1, -1, s, status ), &cval, AST__STRING,
34783                    comm, status );
34784       }
34785 
34786 /* Get and save CNAME for all WCS axes. These are NOT required, so
34787    do not pass on if they are not available. Do not include a CNAME
34788    keyword if its value equals the commen or value of the corresponding
34789    CTYPE keyword. */
34790       for( i = 0; i < nwcs; i++ ) {
34791          cval = GetItemC( &(store->cname), i, 0, s, NULL, method, class, status );
34792          if( cval ) {
34793             comm = GetItemC( &(store->ctype), i, 0, s, NULL, method, class, status );
34794             if( !comm || strcmp( comm, cval ) ) {
34795                comm = GetItemC( &(store->ctype_com), i, 0, s, NULL, method, class, status );
34796                if( !comm || strcmp( comm, cval ) ) {
34797                   sprintf( combuf, "Description of axis %d", i + 1 );
34798                   SetValue( this, FormatKey( "CNAME", i + 1, -1, s, status ), &cval,
34799                             AST__STRING, combuf, status );
34800                }
34801             }
34802          }
34803       }
34804 
34805 /* Now choose whether to produce CDi_j or CDELT/PCi_j keywords. */
34806       if( astGetCDMatrix( this ) ) {
34807 
34808 /* CD matrix. Multiply the row of the PC matrix by the CDELT value. */
34809          for( i = 0; i < nwcs; i++ ) {
34810             cdl = GetItem( &(store->cdelt), i, 0, s, NULL, method, class, status );
34811             if( cdl == AST__BAD ) cdl = 1.0;
34812             for( j = 0; j < nwcs; j++ ){
34813                val = GetItem( &(store->pc), i, j, s, NULL, method, class, status );
34814                if( val == AST__BAD ) val = ( i == j ) ? 1.0 : 0.0;
34815                val *= cdl;
34816                if( val != 0.0 ) {
34817                    SetValue( this, FormatKey( "CD", i + 1, j + 1, s, status ), &val,
34818                              AST__FLOAT, "Transformation matrix element", status );
34819                }
34820             }
34821          }
34822 
34823 /* If producing PC/CDELT keywords... */
34824       } else {
34825 
34826 /* CDELT keywords. */
34827          for( i = 0; i < nwcs; i++ ) {
34828             val = GetItem( &(store->cdelt), i, 0, s, NULL, method, class, status );
34829             if( val == AST__BAD ) {
34830                ok = 0;
34831                goto next;
34832             }
34833             sprintf( combuf, "Pixel size on axis %d", i + 1 );
34834             SetValue( this, FormatKey( "CDELT", i + 1, -1, s, status ), &val, AST__FLOAT,
34835                       combuf, status );
34836          }
34837 
34838 /* PC matrix. */
34839          for( i = 0; i < nwcs; i++ ) {
34840             for( j = 0; j < nwcs; j++ ){
34841                val = GetItem( &(store->pc), i, j, s, NULL, method, class, status );
34842                if( val != AST__BAD ) {
34843                   if( i == j ) {
34844                      if( EQUAL( val, 1.0 ) ) val = AST__BAD;
34845                   } else {
34846                      if( EQUAL( val, 0.0 ) ) val = AST__BAD;
34847                   }
34848                }
34849                if( val != AST__BAD ) {
34850                   SetValue( this, FormatKey( "PC", i + 1, j + 1, s, status ), &val,
34851                             AST__FLOAT, "Transformation matrix element", status );
34852                }
34853             }
34854          }
34855       }
34856 
34857 /* Get and save CUNIT for all WCS axes. These are NOT required, so
34858    do not pass on if they are not available. */
34859       for( i = 0; i < nwcs; i++ ) {
34860          cval = GetItemC( &(store->cunit), i, 0, s, NULL, method, class, status );
34861          if( cval ) {
34862             sprintf( combuf, "Units for axis %d", i + 1 );
34863             SetValue( this, FormatKey( "CUNIT", i + 1, -1, s, status ), &cval, AST__STRING,
34864                       combuf, status );
34865          }
34866       }
34867 
34868 /* Get and save AXREF for all WCS axes. These are NOT required, so do not
34869    pass on if they are not available. Note, AXREF is a non-standard keyword
34870    used by AST to communicate the reference position on any axes described
34871    by the -TAB algorithm and which has no inverse transformation. For all
34872    other cases, the reference position corresponds to the values of CRVAL. */
34873       for( i = 0; i < nwcs; i++ ) {
34874          val = GetItem( &(store->axref), i, 0, s, NULL, method, class, status );
34875          if( val != AST__BAD ) {
34876             sprintf( combuf, "Reference WCS value on axis %d", i + 1 );
34877             SetValue( this, FormatKey( "AXREF", i + 1, -1, s, status ), &val, AST__FLOAT,
34878                       combuf, status );
34879          }
34880       }
34881 
34882 /* Get and save SREFIS. This is NOT required, so do not return if it is
34883    not available. Note, SREFIS is a non-standard keyword used by AST to
34884    communicate the SkyRefIs attribute in the original SkyFrame. */
34885       cval = GetItemC( &(store->skyrefis), 0, 0, s, NULL, method, class, status );
34886       if( cval ) SetValue( this, FormatKey( "SREFIS", -1, -1, s, status ), &cval,
34887                            AST__STRING, "Is SkyRef used as pole or origin?", status );
34888 
34889 /* Get and save SREF for all WCS axes. These are NOT required, so do not
34890    pass on if they are not available. Note, SREF is a non-standard keyword
34891    used by AST to communicate the SkyRef position on any axes described
34892    by a offset SkyFrame. */
34893       for( i = 0; i < nwcs; i++ ) {
34894          val = GetItem( &(store->skyref), i, 0, s, NULL, method, class, status );
34895          if( val != AST__BAD ) {
34896             sprintf( combuf, "Sky reference position on axis %d", i + 1 );
34897             SetValue( this, FormatKey( "SREF", i + 1, -1, s, status ), &val, AST__FLOAT,
34898                       combuf, status );
34899          }
34900       }
34901 
34902 /* Get and save SREFP for all WCS axes. These are NOT required, so do not
34903    pass on if they are not available. Note, SREFP is a non-standard keyword
34904    used by AST to communicate the SkyRefP position on any axes described
34905    by a offset SkyFrame. */
34906       for( i = 0; i < nwcs; i++ ) {
34907          val = GetItem( &(store->skyrefp), i, 0, s, NULL, method, class, status );
34908          if( val != AST__BAD ) {
34909             sprintf( combuf, "Sky primary meridian position on axis %d", i + 1 );
34910             SetValue( this, FormatKey( "SREFP", i + 1, -1, s, status ), &val, AST__FLOAT,
34911                       combuf, status );
34912          }
34913       }
34914 
34915 /* Date of observation (only allowed for primary axis descriptions). */
34916       if( s == ' ' ) {
34917          val = GetItem( &(store->mjdobs), 0, 0, s, NULL, method, class, status );
34918          if( val != AST__BAD ) {
34919             SetValue( this, FormatKey( "MJD-OBS", -1, -1, s, status ),
34920                       &val, AST__FLOAT, "Modified Julian Date of observation", status );
34921 
34922 /* The format used for the DATE-OBS keyword depends on the value of the
34923    keyword. For DATE-OBS < 1999.0, use the old "dd/mm/yy" format.
34924    Otherwise, use the new "ccyy-mm-ddThh:mm:ss[.ssss]" format. */
34925             palCaldj( 99, 1, 1, &mjd99, &jj );
34926             if( val < mjd99 ) {
34927                palDjcal( 0, val, iymdf, &jj );
34928                sprintf( combuf, "%2.2d/%2.2d/%2.2d", iymdf[ 2 ], iymdf[ 1 ],
34929                         iymdf[ 0 ] - ( ( iymdf[ 0 ] > 1999 ) ? 2000 : 1900 ) );
34930             } else {
34931                palDjcl( val, iymdf, iymdf+1, iymdf+2, &fd, &jj );
34932                palDd2tf( 3, fd, sign, ihmsf );
34933                sprintf( combuf, "%4.4d-%2.2d-%2.2dT%2.2d:%2.2d:%2.2d.%3.3d",
34934                         iymdf[0], iymdf[1], iymdf[2], ihmsf[0], ihmsf[1],
34935                         ihmsf[2], ihmsf[3] );
34936             }
34937 
34938 /* Now store the formatted string in the FitsChan. */
34939             cval = combuf;
34940             SetValue( this, "DATE-OBS", (void *) &cval, AST__STRING,
34941                       "Date of observation", status );
34942          }
34943          val = GetItem( &(store->mjdavg), 0, 0, ' ', NULL, method, class, status );
34944          if( val != AST__BAD ) SetValue( this, "MJD-AVG", &val, AST__FLOAT,
34945                                          "Average Modified Julian Date of observation", status );
34946 
34947 /* Store the timescale in TIMESYS. */
34948          cval = GetItemC( &(store->timesys), 0, 0, s, NULL, method, class, status );
34949          if( cval ) SetValue( this, "TIMESYS", &cval, AST__STRING,
34950                               "Timescale for MJD-OBS/MJD-AVG values", status );
34951       }
34952 
34953 /* Numerical projection parameters */
34954       maxm = GetMaxJM( &(store->pv), s, status );
34955       for( i = 0; i < nwcs; i++ ){
34956          for( m = 0; m <= maxm; m++ ){
34957             val = GetItem( &(store->pv), i, m, s, NULL, method, class, status );
34958             if( val != AST__BAD ) {
34959 
34960 /* If the axis uses the "TAB" algorithm, there may be a PVi_4a parameter
34961    in the FitsStore. This is an AST extension to the published -TAB
34962    algorithm, and is used to hold the interpolation method. To avoid
34963    clashing with any standard use of PV1_4a, rename it to QVi_4a. The
34964    default is zero (linear interpolation) so do not write the QV value
34965    if it zero. */
34966                if( m == 4 && tabaxis[ i ] ) {
34967                   if( val != 0.0 ) {
34968                      SetValue( this, FormatKey( "QV", i + 1, m, s, status ),
34969                                &val, AST__FLOAT, "Use nearest neighbour "
34970                                "interpolation", status );
34971                   }
34972 
34973 /* Just store the parameters for other type of axes. */
34974                } else {
34975                   SetValue( this, FormatKey( parprefix, i + 1, m, s, status ), &val,
34976                             AST__FLOAT, "Projection parameter", status );
34977                }
34978             }
34979          }
34980       }
34981 
34982 /* String projection parameters */
34983       maxm = GetMaxJMC( &(store->ps), s, status );
34984       for( i = 0; i < nwcs; i++ ){
34985          for( m = 0; m <= maxm; m++ ){
34986             cval = GetItemC( &(store->ps), i, m, s, NULL, method, class, status );
34987             if( cval ) {
34988                SetValue( this, FormatKey( "PS", i + 1, m, s, status ), &cval,
34989                          AST__STRING, "Projection parameter", status );
34990             }
34991          }
34992       }
34993 
34994 /* Keywords specific to celestial axes... */
34995 
34996 /* Get and save RADESYS. This is NOT required, so do not return if it is
34997    not available. */
34998       cval = GetItemC( &(store->radesys), 0, 0, s, NULL, method, class, status );
34999       if( cval ) SetValue( this, FormatKey( "RADESYS", -1, -1, s, status ), &cval,
35000                            AST__STRING, "Reference frame for RA/DEC values", status );
35001 
35002 /* Reference equinox */
35003       val = GetItem( &(store->equinox), 0, 0, s, NULL, method, class, status );
35004       if( val != AST__BAD ) SetValue( this, FormatKey( "EQUINOX", -1, -1, s, status ),
35005                                       &val, AST__FLOAT,
35006                                       "[yr] Epoch of reference equinox", status );
35007 
35008 /* Latitude of native north pole */
35009       val = GetItem( &(store->latpole), 0, 0, s, NULL, method, class, status );
35010       if( val != AST__BAD ) SetValue( this, FormatKey( "LATPOLE", -1, -1, s, status ),
35011                                       &val, AST__FLOAT,
35012                                       "[deg] Latitude of native north pole", status );
35013 
35014 /* Longitude of native north pole */
35015       val = GetItem( &(store->lonpole), 0, 0, s, NULL, method, class, status );
35016       if( val != AST__BAD ) SetValue( this, FormatKey( "LONPOLE", -1, -1, s, status ),
35017                                       &val, AST__FLOAT,
35018                                       "[deg] Longitude of native north pole", status );
35019 
35020 /* Keywords specific to spectral axes... */
35021 
35022 /* SPECSYS - the standard of rest for the spectral axis */
35023       cval = GetItemC( &(store->specsys), 0, 0, s, NULL, method, class, status );
35024       if( cval ) SetValue( this, FormatKey( "SPECSYS", -1, -1, s, status ), &cval,
35025                            AST__STRING, "Standard of rest for spectral axis", status );
35026 
35027 /* SSYSSRC - the standard of rest in which ZSOURCE is stored. */
35028       cval = GetItemC( &(store->ssyssrc), 0, 0, s, NULL, method, class, status );
35029       if( cval ) SetValue( this, FormatKey( "SSYSSRC", -1, -1, s, status ), &cval,
35030                            AST__STRING, "Standard of rest for source redshift", status );
35031 
35032 /* ZSOURCE - topocentric optical velocity of source */
35033       val = GetItem( &(store->zsource), 0, 0, s, NULL, method, class, status );
35034       if( val != AST__BAD ) SetValue( this, FormatKey( "ZSOURCE", -1, -1, s, status ),
35035                                       &val, AST__FLOAT, "[] Redshift of source", status );
35036 
35037 /* VELOSYS - topocentric apparent radial velocity of the standard of rest. */
35038       val = GetItem( &(store->velosys), 0, 0, s, NULL, method, class, status );
35039       if( val != AST__BAD ) SetValue( this, FormatKey( "VELOSYS", -1, -1, s, status ),
35040                                       &val, AST__FLOAT, "[m/s] Topo. apparent velocity of rest frame", status );
35041 
35042 /* RESTFRQ - rest frequency */
35043       val = GetItem( &(store->restfrq), 0, 0, s, NULL, method, class, status );
35044       if( val != AST__BAD ) SetValue( this, FormatKey( "RESTFRQ", -1, -1, s, status ),
35045                                       &val, AST__FLOAT, "[Hz] Rest frequency", status );
35046 
35047 /* RESTWAV - rest wavelength */
35048       val = GetItem( &(store->restwav), 0, 0, s, NULL, method, class, status );
35049       if( val != AST__BAD ) SetValue( this, FormatKey( "RESTWAV", -1, -1, s, status ),
35050                                       &val, AST__FLOAT, "[m] Rest wavelength", status );
35051 
35052 /* The image frequency corresponding to the rest frequency (only used for
35053    double sideband data). This is not part of the FITS-WCS standard but
35054    is added for the benefit of JACH. */
35055       val = GetItem( &(store->imagfreq), 0, 0, s, NULL, method, class, status );
35056       if( val != AST__BAD ) {
35057          SetValue( this, "IMAGFREQ", &val, AST__FLOAT, "[Hz] Image frequency", status );
35058       }
35059 
35060 /* OBSGEO-X/Y/Z - observer's geocentric coords. Note, these always refer
35061    to the primary axes. */
35062       if( s == ' ' ) {
35063          val = GetItem( &(store->obsgeox), 0, 0, s, NULL, method, class, status );
35064          if( val != AST__BAD ) SetValue( this, "OBSGEO-X", &val, AST__FLOAT, "[m] Observatory geocentric X", status );
35065          val = GetItem( &(store->obsgeoy), 0, 0, s, NULL, method, class, status );
35066          if( val != AST__BAD ) SetValue( this, "OBSGEO-Y", &val, AST__FLOAT, "[m] Observatory geocentric Y", status );
35067          val = GetItem( &(store->obsgeoz), 0, 0, s, NULL, method, class, status );
35068          if( val != AST__BAD ) SetValue( this, "OBSGEO-Z", &val, AST__FLOAT, "[m] Observatory geocentric Z", status );
35069       }
35070 
35071 /* See if a Frame was sucessfully written to the FitsChan. */
35072 next:
35073       ok = ok && astOK;
35074 
35075 /* If so, indicate we have something to return. */
35076       if( ok ) ret = 1;
35077 
35078 /* If we are producing secondary axes, clear any error status so we can
35079    continue to produce the next Frame. Retain the error if the primary axes
35080    could not be produced. After the primary axes, do the A axes. */
35081       if( s != ' ' ) {
35082          astClearStatus;
35083       } else {
35084          s = 'A' - 1;
35085       }
35086 
35087 /* Remove the secondary "new" flags from the FitsChan. This flag is
35088    associated with cards which have been added to the FitsChan during
35089    this pass through the main loop in this function. If the Frame was
35090    written out succesfully, just clear the flags. If anything went wrong
35091    with this Frame, remove the flagged cards from the FitsChan. */
35092       FixNew( this, NEW2, !ok, method, class, status );
35093 
35094 /* Set the current card so that it points to the last WCS-related keyword
35095    in the FitsChan (whether previously read or not). */
35096       FindWcs( this, 1, 1, 0, method, class, status );
35097 
35098 /* Free resources. */
35099       tabaxis = astFree( tabaxis );
35100    }
35101 
35102 /* Return zero or ret depending on whether an error has occurred. */
35103    return astOK ? ret : 0;
35104 }
35105 
WcsIntWorld(AstFitsChan * this,FitsStore * store,char s,int naxes,const char * method,const char * class,int * status)35106 static AstMapping *WcsIntWorld( AstFitsChan *this, FitsStore *store, char s,
35107                                 int naxes, const char *method, const char *class, int *status ){
35108 
35109 /*
35110 *  Name:
35111 *     WcsIntWorld
35112 
35113 *  Purpose:
35114 *     Create a Mapping from pixel coords to intermediate world coords.
35115 
35116 *  Type:
35117 *     Private function.
35118 
35119 *  Synopsis:
35120 
35121 *     AstMapping *WcsIntWorld( AstFitsChan *this, FitsStore *store, char s,
35122 *                              int naxes, const char *method, const char *class, int *status )
35123 
35124 *  Class Membership:
35125 *     FitsChan
35126 
35127 *  Description:
35128 *     This function interprets the contents of the supplied FitsStore
35129 *     structure, and creates a Mapping which describes the transformation
35130 *     from pixel coordinates to intermediate world coordinates, using the
35131 *     FITS World Coordinate System conventions. This is a general linear
35132 *     transformation described by the CRPIXj, PCi_j and CDELTi keywords.
35133 
35134 *  Parameters:
35135 *     this
35136 *        The FitsChan. ASTWARN cards may be added to this FitsChan if any
35137 *        anomalies are found in the keyword values in the FitsStore.
35138 *     store
35139 *        A structure containing information about the requested axis
35140 *        descriptions derived from a FITS header.
35141 *     s
35142 *        A character identifying the co-ordinate version to use. A space
35143 *        means use primary axis descriptions. Otherwise, it must be an
35144 *        upper-case alphabetical characters ('A' to 'Z').
35145 *     naxes
35146 *        The number of intermediate world coordinate axes (WCSAXES).
35147 *     method
35148 *        A pointer to a string holding the name of the calling method.
35149 *        This is used only in the construction of error messages.
35150 *     class
35151 *        A pointer to a string holding the class of the object being
35152 *        read. This is used only in the construction of error messages.
35153 *     status
35154 *        Pointer to the inherited status variable.
35155 
35156 *  Returned Value:
35157 *     A pointer to the Mapping.
35158 */
35159 
35160 /* Local Variables: */
35161    AstMapping   *mapd1;      /* Pointer to first distortion Mapping */
35162    AstMapping   *mapd2;      /* Pointer to second distortion Mapping */
35163    AstMapping   *mapd3;      /* Pointer to third distortion Mapping */
35164    AstMapping   *mapd4;      /* Pointer to fourth distortion Mapping */
35165    AstMapping   *map0;       /* Pointer to a Mapping */
35166    AstMapping   *map1;       /* Pointer to a Mapping */
35167    AstMapping   *ret;        /* Pointer to the returned Mapping */
35168 
35169 /* Initialise the pointer to the returned Mapping. */
35170    ret = NULL;
35171 
35172 /* Check the global status. */
35173    if ( !astOK ) return ret;
35174 
35175 /* First of all, check the CTYPE keywords to see if they contain any known
35176    distortion codes (following the syntax described in FITS-WCS paper IV).
35177    If so, Mappings are returned which represents the distortions to be
35178    applied at each point in the chain of Mappings produced by this function.
35179    Any distortion codes are removed from the CTYPE values in the FitsStore. */
35180    DistortMaps( this, store, s, naxes, &mapd1, &mapd2, &mapd3, &mapd4, method,
35181                 class, status );
35182 
35183 /* If distortion is to be applied now, initialise the returned Mapping to
35184    be the distortion. */
35185    if( mapd1 ) ret = mapd1;
35186 
35187 /* Try to create a WinMap which translates the pixel coordinates so
35188    that they are refered to an origin at the reference pixel. This
35189    subtracts the value of CRPIXi from axis i. */
35190    map1 = (AstMapping *) WcsShift( store, s, naxes, method, class, status );
35191 
35192 /* Combine this with any previous Mapping. */
35193    if( ret ) {
35194       map0 = (AstMapping *) astCmpMap( ret, map1, 1, "", status );
35195       ret = astAnnul( ret );
35196       map1 = astAnnul( map1 );
35197       ret = map0;
35198    } else {
35199       ret = map1;
35200    }
35201 
35202 /* If distortion is to be applied now, combine the two Mappings. */
35203    if( mapd2 ) {
35204       map0 = (AstMapping *) astCmpMap( ret, mapd2, 1, "", status );
35205       ret = astAnnul( ret );
35206       mapd2 = astAnnul( mapd2 );
35207       ret = map0;
35208    }
35209 
35210 /* Now try to create a MatrixMap to implement the PC matrix. Combine it
35211    with the above Mapping. Add a Warning if this mapping cannot be inverted. */
35212    map1 = (AstMapping *) WcsPCMatrix( store, s, naxes, method, class, status );
35213    if( !astGetTranInverse( map1 ) ) {
35214       Warn( this, "badmat", "The pixel rotation matrix in the original FITS "
35215             "header (specified by CD or PC keywords) could not be inverted. "
35216             "This may be because the matrix contains rows or columns which "
35217             "are entirely zero.", method, class, status );
35218    }
35219    map0 = (AstMapping *) astCmpMap( ret, map1, 1, "", status );
35220    ret = astAnnul( ret );
35221    map1 = astAnnul( map1 );
35222    ret = map0;
35223 
35224 /* If distortion is to be applied now, combine the two Mappings. */
35225    if( mapd3 ) {
35226       map0 = (AstMapping *) astCmpMap( ret, mapd3, 1, "", status );
35227       ret = astAnnul( ret );
35228       mapd3 = astAnnul( mapd3 );
35229       ret = map0;
35230    }
35231 
35232 /* Now try to create a diagonal MatrixMap to implement the CDELT scaling.
35233    Combine it with the above Mapping.  */
35234    map1 = (AstMapping *) WcsCDeltMatrix( store, s, naxes, method, class, status );
35235    map0 = (AstMapping *) astCmpMap( ret, map1, 1, "", status );
35236    ret = astAnnul( ret );
35237    map1 = astAnnul( map1 );
35238    ret = map0;
35239 
35240 /* If distortion is to be applied now, combine the two Mappings. */
35241    if( mapd4 ) {
35242       map0 = (AstMapping *) astCmpMap( ret, mapd4, 1, "", status );
35243       ret = astAnnul( ret );
35244       mapd4 = astAnnul( mapd4 );
35245       ret = map0;
35246    }
35247 
35248 /* Return the result. */
35249    return ret;
35250 }
35251 
WcsMapFrm(AstFitsChan * this,FitsStore * store,char s,AstFrame ** frm,const char * method,const char * class,int * status)35252 static AstMapping *WcsMapFrm( AstFitsChan *this, FitsStore *store, char s,
35253                               AstFrame **frm, const char *method,
35254                               const char *class, int *status ){
35255 
35256 /*
35257 *  Name:
35258 *     WcsMapFrm
35259 
35260 *  Purpose:
35261 *     Create a Mapping and Frame for the WCS transformations described in a
35262 *     FITS header.
35263 
35264 *  Type:
35265 *     Private function.
35266 
35267 *  Synopsis:
35268 
35269 *     AstMapping *WcsMapFrm( AstFitsChan *this, FitsStore *store, char s,
35270 *                            AstFrame **frm, const char *method,
35271 *                            const char *class, int *status )
35272 
35273 *  Class Membership:
35274 *     FitsChan
35275 
35276 *  Description:
35277 *     This function interprets the contents of the supplied FitsStore
35278 *     structure, and creates a Mapping which describes the transformation
35279 *     from pixel coordinates to world coordinates, using the FITS World
35280 *     Coordinate System conventions. It also creates a Frame describing
35281 *     the world coordinate axes.
35282 
35283 *  Parameters:
35284 *     this
35285 *        The FitsChan.
35286 *     store
35287 *        A structure containing information about the requested axis
35288 *        descriptions derived from a FITS header.
35289 *     s
35290 *        A character identifying the co-ordinate version to use. A space
35291 *        means use primary axis descriptions. Otherwise, it must be an
35292 *        upper-case alphabetical characters ('A' to 'Z').
35293 *     frm
35294 *        The address of a location at which to store a pointer to the
35295 *        Frame describing the world coordinate axes. If the Iwc attribute
35296 *        is non-zero, then this is actually a FrameSet in which the current
35297 *        Frame is the required WCS system. The FrameSet also contains one
35298 *        other Frame which defines the FITS IWC system.
35299 *     method
35300 *        A pointer to a string holding the name of the calling method.
35301 *        This is used only in the construction of error messages.
35302 *     class
35303 *        A pointer to a string holding the class of the object being
35304 *        read. This is used only in the construction of error messages.
35305 *     status
35306 *        Pointer to the inherited status variable.
35307 
35308 *  Returned Value:
35309 *     A pointer to the Mapping.
35310 */
35311 
35312 /* Local Variables: */
35313    AstFrame *iwcfrm;         /* Frame defining IWC system */
35314    AstFrameSet *fs;          /* Pointer to returned FrameSet */
35315    AstMapping *map10;        /* Pointer to a Mapping */
35316    AstMapping *map1;         /* Pointer to a Mapping */
35317    AstMapping *map2;         /* Pointer to a Mapping */
35318    AstMapping *map3;         /* Pointer to a Mapping */
35319    AstMapping *map4;         /* Pointer to a Mapping */
35320    AstMapping *map5;         /* Pointer to a Mapping */
35321    AstMapping *map6;         /* Pointer to a Mapping */
35322    AstMapping *map7;         /* Pointer to a Mapping */
35323    AstMapping *map8;         /* Pointer to a Mapping */
35324    AstMapping *map9;         /* Pointer to a Mapping */
35325    AstMapping *ret;          /* Pointer to the returned Mapping */
35326    AstMapping *tabmap;       /* Mapping from psi to WCS (paper III - 6.1.2) */
35327    AstSkyFrame *reffrm;      /* SkyFrame defining reflon and reflat */
35328    char id[2];               /* ID string for returned Frame */
35329    char iwc[5];              /* Domain name for IWC Frame */
35330    const char *cc;           /* Pointer to Domain */
35331    double dut1;              /* UT1-UTC correction in days */
35332    double dval;              /* Temporary double value */
35333    double reflat;            /* Reference celestial latitude */
35334    double reflon;            /* Reference celestial longitude */
35335    int *tabaxis;             /* Flags indicating -TAB axes */
35336    int wcsaxes;              /* Number of physical axes */
35337 
35338 /* Initialise the pointer to the returned Mapping. */
35339    ret = NULL;
35340 
35341 /* Check the global status. */
35342    if ( !astOK ) return ret;
35343 
35344 /* Identify any axes that use the -TAB algoritm code described in FITS-WCS
35345    paper III, and convert their CTYPE values to describe linear axes
35346    (i.e. just remove "-TAB" from the CTYPE value). This also returns a
35347    Mapping (which includes one or more LutMaps) that should be applied to
35348    the resulting linear axis values in order to generate the final WCS
35349    axis values. A NULL pointer is returned if no axes use -TAB. */
35350    tabmap = TabMapping( this, store, s, &tabaxis, method, class, status );
35351 
35352 /* Obtain the number of physical axes in the header. If the WCSAXES header
35353    was specified, use it. Otherwise assume it is the same as the number
35354    of pixel axes. */
35355    dval = GetItem( &(store->wcsaxes), 0, 0, s, NULL, method, class, status );
35356    if( dval != AST__BAD ) {
35357       wcsaxes = (int) dval + 0.5;
35358    } else {
35359       wcsaxes = store->naxis;
35360    }
35361 
35362 /* Create a simple Frame to represent IWC coords. */
35363    iwcfrm = astFrame( wcsaxes, "Title=FITS Intermediate World Coordinates", status );
35364    strcpy( iwc, "IWC" );
35365    iwc[ 3 ]= s;
35366    iwc[ 4 ]= 0;
35367    astSetDomain( iwcfrm, iwc );
35368 
35369 /* Create a simple Frame which will be used as the initial representation
35370    for the physical axes. This Frame will be changed later (or possibly
35371    replaced by a Frame of another class) when we know what type of
35372    physical axes we are dealing with. Set its Domain to "AST_FITSCHAN"
35373    This value is used to identify axes which have not been changed,
35374    and will be replaced before returning the final FrameSet. */
35375    *frm = astFrame( wcsaxes, "Domain=AST_FITSCHAN", status );
35376 
35377 /* Store the coordinate version character as the Ident attribute for the
35378    returned Frame. */
35379    id[ 0 ] = s;
35380    id[ 1 ] = 0;
35381    astSetIdent( *frm, id );
35382 
35383 /* Create a Mapping which goes from pixel coordinates to what FITS-WCS
35384    paper I calls "intermediate world coordinates". This stage is the same
35385    for all axes. It uses the CRPIXj, PCi_j and CDELTi headers (and
35386    distortion codes from the CTYPE keywords). */
35387    map1 = WcsIntWorld( this, store, s, wcsaxes, method, class, status );
35388 
35389 /* The conversion from intermediate world coordinates to the final world
35390    coordinates depends on the type of axis being converted (as specified
35391    by its CTYPE keyword). Check for each type of axis for which known
35392    conventions exist... */
35393 
35394 /* Celestial coordinate axes. The following call returns a Mapping which
35395    transforms any celestial coordinate axes from intermediate world
35396    coordinates to the final celestial coordinates. Other axes are left
35397    unchanged by the Mapping. It also modifies the Frame so that a
35398    SkyFrame is used to describe the celestial axes. */
35399    map2 = WcsCelestial( this, store, s, frm, iwcfrm, &reflon, &reflat,
35400                         &reffrm, &tabmap, tabaxis, method, class, status );
35401 
35402 /* Spectral coordinate axes. The following call returns a Mapping which
35403    transforms any spectral coordinate axes from intermediate world
35404    coordinates to the final spectral coordinates. Other axes are left
35405    unchanged by the Mapping. It also modifies the Frame so that a
35406    SpecFrame is used to describe the spectral axes. */
35407    map3 = WcsSpectral( this, store, s, frm, iwcfrm, reflon, reflat, reffrm,
35408                        method, class, status );
35409 
35410 /* Any axes which were not recognized by the above calls are assumed to
35411    be linear. Create a Mapping which adds on the reference value for such
35412    axes, and modify the Frame to desribe the axes. */
35413    map4 = WcsOthers( this, store, s, frm, iwcfrm, method, class, status );
35414 
35415 /* If the Frame still has the Domain "AST_FITSCHAN", clear it. */
35416    cc = astGetDomain( *frm );
35417    if( cc && !strcmp( cc, "AST_FITSCHAN" ) ) astClearDomain( *frm );
35418 
35419 /* Concatenate the Mappings and simplify the result. */
35420    map5 = (AstMapping *) astCmpMap( map1, map2, 1, "", status );
35421    map6 = (AstMapping *) astCmpMap( map5, map3, 1, "", status );
35422    map7 = (AstMapping *) astCmpMap( map6, map4, 1, "", status );
35423    if( tabmap ) {
35424       map8 = (AstMapping *) astCmpMap( map7, tabmap, 1, "", status );
35425    } else {
35426       map8 = astClone( map7 );
35427    }
35428 
35429    ret = astSimplify( map8 );
35430 
35431 /* Ensure that the coordinate version character is stored as the Ident
35432    attribute for the returned Frame (the above calls may have changed it). */
35433    astSetIdent( *frm, id );
35434 
35435 /* Set the DUT1 value. Note, the JACH store DUT1 in units of days in their
35436    FITS headers, so convert from days to seconds. May need to do somthing
35437    about this if the forthcoming FITS-WCS paper 5 (time axes) defines DUT1
35438    to be in seconds. */
35439    dut1 = GetItem( &(store->dut1), 0, 0, s, NULL, method, class, status );
35440    if( dut1 != AST__BAD ) astSetDut1( *frm, dut1*SPD );
35441 
35442 /* The returned Frame is actually a FrameSet in which the current Frame
35443    is the required WCS Frame. The FrameSet contains one other Frame,
35444    which is the Frame representing IWC. Create a FrameSet containing these
35445    two Frames. */
35446    if( astGetIwc( this ) ) {
35447       fs = astFrameSet( iwcfrm, "", status );
35448       astInvert( map1 );
35449       map9 = (AstMapping *) astCmpMap( map1, ret, 1, "", status );
35450       astInvert( map1 );
35451       map10 = astSimplify( map9 );
35452       astAddFrame( fs, AST__BASE, map10, *frm );
35453 
35454 /* Return this FrameSet instead of the Frame. */
35455       *frm = astAnnul( *frm );
35456       *frm = (AstFrame *) fs;
35457 
35458 /* Free resources */
35459       map9 = astAnnul( map9 );
35460       map10 = astAnnul( map10 );
35461    }
35462 
35463 /* Annull temporary resources. */
35464    if( reffrm ) reffrm = astAnnul( reffrm );
35465    if( tabmap ) tabmap = astAnnul( tabmap );
35466    tabaxis = astFree( tabaxis );
35467    iwcfrm = astAnnul( iwcfrm );
35468    map1 = astAnnul( map1 );
35469    map2 = astAnnul( map2 );
35470    map3 = astAnnul( map3 );
35471    map4 = astAnnul( map4 );
35472    map5 = astAnnul( map5 );
35473    map6 = astAnnul( map6 );
35474    map7 = astAnnul( map7 );
35475    map8 = astAnnul( map8 );
35476 
35477 /* Annul thre returned objects if an error has occurred. */
35478    if( !astOK ) {
35479       ret = astAnnul( ret );
35480       *frm = astAnnul( *frm );
35481    }
35482 
35483 /* Return the result. */
35484    return ret;
35485 }
35486 
WcsPCMatrix(FitsStore * store,char s,int naxes,const char * method,const char * class,int * status)35487 static AstMatrixMap *WcsPCMatrix( FitsStore *store, char s, int naxes,
35488                                   const char *method, const char *class, int *status ){
35489 /*
35490 *  Name:
35491 *     WcsPCMatrix
35492 
35493 *  Purpose:
35494 *     Create a MatrixMap representing the PC matrix.
35495 
35496 *  Type:
35497 *     Private function.
35498 
35499 *  Synopsis:
35500 *     AstMatrixMap *WcsPCMatrix( FitsStore *store, char s, int naxes,
35501 *                                const char *method, const char *class, int *status )
35502 
35503 *  Class Membership:
35504 *     FitsChan
35505 
35506 *  Description:
35507 *     A MatrixMap representing the FITS "PC" matrix is returned.
35508 
35509 *  Parameters:
35510 *     store
35511 *        A structure containing values for FITS keywords relating to
35512 *        the World Coordinate System.
35513 *     s
35514 *        A character s identifying the co-ordinate version to use. A space
35515 *        means use primary axis descriptions. Otherwise, it must be an
35516 *        upper-case alphabetical characters ('A' to 'Z').
35517 *     naxes
35518 *        The number of intermediate world coordinate axes (WCSAXES).
35519 *     method
35520 *        A pointer to a string holding the name of the calling method.
35521 *        This is used only in the construction of error messages.
35522 *     class
35523 *        A pointer to a string holding the class of the object being
35524 *        read. This is used only in the construction of error messages.
35525 *     status
35526 *        Pointer to the inherited status variable.
35527 
35528 *  Returned Value:
35529 *     A pointer to the created MatrixMap or a NULL pointer if an
35530 *     error occurred.
35531 */
35532 
35533 /* Local Variables: */
35534    AstMatrixMap *new;       /* The created MatrixMap */
35535    double *el;              /* Pointer to next matrix element */
35536    double *mat;             /* Pointer to matrix array */
35537    int i;                   /* Pixel axis index */
35538    int j;                   /* Intermediate axis index. */
35539 
35540 /* Initialise/ */
35541    new = NULL;
35542 
35543 /* Check the global status. */
35544    if ( !astOK ) return new;
35545 
35546 /* Allocate memory for the matrix. */
35547    mat = (double *) astMalloc( sizeof(double)*naxes*naxes );
35548    if( astOK ){
35549 
35550 /* Fill the matrix with values from the FitsStore. */
35551       el = mat;
35552       for( i = 0; i < naxes; i++ ){
35553          for( j = 0; j < naxes; j++ ){
35554 
35555 /* Get the PCj_i value for this axis. Missing terms can be defaulted so
35556    do not report an error if the required value is not present in the
35557    FitsStore. */
35558             *el = GetItem( &(store->pc), i, j, s, NULL, method, class, status );
35559 
35560 /* Diagonal terms default to to 1.0, off-diagonal to zero. */
35561             if( *el == AST__BAD ) *el = ( i == j ) ? 1.0: 0.0;
35562 
35563 /* Move on to the next matrix element. */
35564             el++;
35565          }
35566       }
35567 
35568 /* Create the matrix. */
35569       new = astMatrixMap( naxes, naxes, 0, mat, "", status );
35570 
35571 /* Report an error if the inverse transformation is undefined. */
35572       if( !astGetTranInverse( new ) && astOK ) {
35573         astError( AST__BDFTS, "%s(%s): Unusable rotation matrix (PC or CD) found "
35574                   "in the FITS-WCS header - the matrix cannot be inverted.", status, method, class );
35575       }
35576 
35577 /* Release the memory used to hold the matrix. */
35578       mat = (double *) astFree( (void *) mat );
35579    }
35580 
35581 /* If an error has occurred, attempt to annul the returned MatrixMap. */
35582    if( !astOK ) new = astAnnul( new );
35583 
35584 /* Return the MatrixMap. */
35585    return new;
35586 }
35587 
WcsNative(AstFitsChan * this,FitsStore * store,char s,AstWcsMap * wcsmap,int fits_ilon,int fits_ilat,const char * method,const char * class,int * status)35588 static AstMapping *WcsNative( AstFitsChan *this, FitsStore *store, char s,
35589                               AstWcsMap *wcsmap, int fits_ilon, int fits_ilat,
35590                               const char *method, const char *class, int *status ){
35591 
35592 /*
35593 *  Name:
35594 *     WcsNative
35595 
35596 *  Purpose:
35597 *     Create a CmpMap which transforms Native Spherical Coords to
35598 *     Celestial Coords.
35599 
35600 *  Type:
35601 *     Private function.
35602 
35603 *  Synopsis:
35604 
35605 *     AstMapping *WcsNative( AstFitsChan *this, FitsStore *store, char s,
35606 *                            AstWcsMap *wcsmap, int fits_ilon, int fits_ilat,
35607 *                            const char *method, const char *class, int *status )
35608 
35609 *  Class Membership:
35610 *     FitsChan
35611 
35612 *  Description:
35613 *     A CmpMap is created which rotates the supplied Native Spherical Coords
35614 *     into Celestial Coords in the standard system specified by the CTYPE
35615 *     keywords. Any non-celestial axes are left unchanged.
35616 *
35617 *     At the highest level, the returned CmpMap is made up of the following
35618 
35619 *     Mappings in series (if celestial long/lat axes are present):
35620 *        1 - A PermMap which rearranges the axes so that the longitude axis is
35621 *            axis 0, the latitude axis is axis 1, and all other axes are
35622 *            stored at higher indices, starting at axis 2.
35623 *        2 - A CmpMap which converts the values on axes 0 and 1 from Native
35624 *            Spherical to Celestial coordinates, leaving all other axes
35625 *            unchanged.
35626 *        3 - A PermMap which rearranges the axes to put the longitude and
35627 *            latitude axes back in their original places. This is just the
35628 *            inverse of the PermMap used at stage 1 above.
35629 *
35630 *     The CmpMap used at stage 2 above, is made up of two Mappings in
35631 
35632 *     parallel:
35633 *         4 - A CmpMap which maps axes 0 and 1 from Native Spherical to
35634 *             Celestial coordinates.
35635 *         5 - A UnitMap which passes on the values to axes 2, 3, etc,
35636 *             without change.
35637 *
35638 *     The CmpMap used at stage 4 above, is made up of the following Mappings
35639 
35640 *     in series:
35641 *         6 - A SphMap which converts the supplied spherical coordinates into
35642 *             Cartesian Coordinates.
35643 *         7 - A MatrixMap which rotates the Cartesian coordinates from the
35644 *             Native to the Celestial system.
35645 *         8 - A SphMap which converts the resulting Cartesian coordinates back
35646 *             to spherical coordinates.
35647 
35648 *  Parameters:
35649 *     this
35650 *        The FitsChan in which to store any warning cards. If NULL, no
35651 *        warnings are stored.
35652 *     store
35653 *        A structure containing values for FITS keywords relating to
35654 *        the World Coordinate System.
35655 *     s
35656 *        Co-ordinate version character to use (space means primary axes).
35657 *     wcsmap
35658 *        A mapping describing the deprojection which is being used. This is
35659 *        needed in order to be able to locate the fiducial point within the
35660 *        Native Speherical Coordinate system, since it varies from projection
35661 *        to projection.
35662 *     fits_ilon
35663 *        The zero-based FITS WCS axis index corresponding to celestial
35664 *        longitude (i.e. one less than the value of "i" in the keyword
35665 *        names "CTYPEi", "CRVALi", etc). If -1 is supplied, the index of
35666 *        the longitude axis in the supplied WcsMap is used.
35667 *     fits_ilat
35668 *        The zero-based FITS WCS axis index corresponding to celestial
35669 *        latitude (i.e. one less than the value of "i" in the keyword
35670 *        names "CTYPEi", "CRVALi", etc). If -1 is supplied, the index of
35671 *        the latitude axis in the supplied WcsMap is used.
35672 *     method
35673 *        A pointer to a string holding the name of the calling method.
35674 *        This is used only in the construction of error messages.
35675 *     class
35676 *        A pointer to a string holding the class of the object being
35677 *        read. This is used only in the construction of error messages.
35678 *     status
35679 *        Pointer to the inherited status variable.
35680 
35681 *  Returned Value:
35682 *     A pointer to the created CmpMap or a NULL pointer if an error occurred.
35683 
35684 *  Notes:
35685 *     -  The local variable names correspond to the notation in the papers
35686 *     by Greisen & Calabretta describing the FITS WCS system.
35687 */
35688 
35689 /* Local Variables: */
35690    AstCmpMap *cmpmap;         /* A CmpMap */
35691    AstMapping *new;           /* The returned CmpMap */
35692    AstMatrixMap *matmap2;     /* Another MatrixMap */
35693    AstMatrixMap *matmap;      /* A MatrixMap */
35694    AstPermMap *permmap;       /* A PermMap */
35695    AstSphMap *sphmap;         /* A SphMap */
35696    AstUnitMap *unitmap;       /* A UnitMap */
35697    char buf[150];             /* Message buffer */
35698    double alpha0;             /* Long. of fiduaicl point in standard system */
35699    double alphap;             /* Long. of native nth pole in standard system */
35700    double axis[3];            /* Vector giving the axis of rotation */
35701    double delta0;             /* Lat. of fiducial point in standard system */
35702    double deltap;             /* Lat. of native nth pole in standard system */
35703    double latpole;            /* Lat. of native nth pole in standard system if deltap undefined */
35704    double phip;               /* Long. of standard nth pole in native system */
35705    double phi0;               /* Native longitude at fiducial point */
35706    double theta0;             /* Native latitude at fiducial point */
35707    int *inperm;               /* Pointer to array of output axis indices */
35708    int *outperm;              /* Pointer to array of input axis indices */
35709    int axlat;                 /* Index of latitude physical axis */
35710    int axlon;                 /* Index of longitude physical axis */
35711    int i;                     /* Loop count */
35712    int nax_rem;               /* No. of non-astrometric axes */
35713    int naxis;                 /* No. of axes. */
35714    int new_axlat;             /* Index of lat. physical axis after perming */
35715    int tpn;                   /* Is this a TPN projection? */
35716 
35717 /* Check the global status. */
35718    if ( !astOK ) return NULL;
35719 
35720 /* Initialise the returned CmpMap pointer. */
35721    new = NULL;
35722 
35723 /* Store the number of axes in a local variable. */
35724    naxis = astGetNin( wcsmap );
35725 
35726 /* Get the indices of the celestial axes. */
35727    axlon = astGetWcsAxis( wcsmap, 0 );
35728    axlat = astGetWcsAxis( wcsmap, 1 );
35729 
35730 /* If the corresponding FITS axis indices were not supplied, use the
35731    WcsMap axes found above. */
35732    if( fits_ilon == -1 ) fits_ilon = axlon;
35733    if( fits_ilat == -1 ) fits_ilat = axlat;
35734 
35735 /* If there is no longitude or latitude axis, or if we have a
35736    non-celestial projection, just return a UnitMap. */
35737    if( axlon == axlat || astGetWcsType( wcsmap ) == AST__WCSBAD ){
35738       new = (AstMapping *) astUnitMap( naxis, "", status );
35739 
35740 /* If there is a lon/lat axis pair, create the inperm and outperm arrays
35741    which will be needed later to create the PermMap which reorganises
35742    the axes so that axis zero is the longitude axis and axis 1 is the
35743    latitude axis. */
35744    } else {
35745 
35746 /* Get storage for the two arrays. */
35747       inperm = (int *) astMalloc( sizeof( int )*(size_t)naxis );
35748       outperm = (int *) astMalloc( sizeof( int )*(size_t)naxis );
35749       if( astOK ){
35750 
35751 /* Initialise an array holding the indices of the input axes which are copied
35752    to each output axis. Initially assume that there is no re-arranging of
35753    the axes. */
35754          for( i = 0; i < naxis; i++ ) outperm[ i ] = i;
35755 
35756 /* Swap the longitude axis and axis 0. */
35757          i = outperm[ axlon ];
35758          outperm[ axlon ] = outperm[ 0 ];
35759          outperm[ 0 ] = i;
35760 
35761 /* If axis 0 was originally the latitude axis, the latitude axis will now
35762    be where the longitude axis was originally (because of the above axis
35763    swap). */
35764          if( axlat == 0 ) {
35765             new_axlat = axlon;
35766          } else {
35767             new_axlat = axlat;
35768          }
35769 
35770 /* Swap the latitude axis and axis 1. */
35771          i = outperm[ new_axlat ];
35772          outperm[ new_axlat ] = outperm[ 1 ];
35773          outperm[ 1 ] = i;
35774 
35775 /* Create the array holding the output axis index corresponding to
35776    each input axis. */
35777          for( i = 0; i < naxis; i++ ) inperm[ outperm[ i ] ] = i;
35778       }
35779 
35780 /* Store the latitude and longitude (in the standard system) of the fiducial
35781    point, in radians. */
35782       delta0 = GetItem( &(store->crval), fits_ilat, 0, s, NULL, method, class, status );
35783       if( delta0 == AST__BAD ) delta0 = 0.0;
35784       delta0 *= AST__DD2R;
35785       alpha0 = GetItem( &(store->crval), fits_ilon, 0, s, NULL, method, class, status );
35786       if( alpha0 == AST__BAD ) alpha0 = 0.0;
35787       alpha0 *= AST__DD2R;
35788 
35789 /* Limit the latitude to the range +/- PI/2, issuing a warning if the
35790    supplied CRVAL value is outside this range. The "alphap" variable is used
35791    as workspace here. */
35792       alphap = palDrange( delta0 );
35793       delta0 = alphap;
35794       if ( delta0 > AST__DPIBY2 ){
35795          delta0 = AST__DPIBY2;
35796       } else if ( delta0 < -AST__DPIBY2 ){
35797          delta0 = -AST__DPIBY2;
35798       }
35799       if( alphap != delta0 ) {
35800          sprintf( buf, "The original FITS header specified a fiducial "
35801                   "point with latitude %.*g. A value of %.*g is being used "
35802                   "instead. ", DBL_DIG, alphap*AST__DR2D, DBL_DIG,
35803                   delta0*AST__DR2D );
35804          Warn( this, "badlat", buf, method, class, status );
35805       }
35806 
35807 /* Set a flag indicating if we have a TPN projection. The handling or
35808    projection parameters  is different for TPN projections.  */
35809       tpn = ( astGetWcsType( wcsmap ) == AST__TPN );
35810 
35811 /* Store the radian values of the FITS keywords LONPOLE and LATPOLE. Defaults
35812    will be used if either of these items was not supplied. These keyword
35813    values may be stored in projection parameters PVi_3a and PVi_4a for
35814    longitude axis "i" - in which case the "PV" values take precedence over
35815    the "LONPOLE" and "LATPOLE" values. Do not do this for TPN projections
35816    since they use these projection parameters to specify correcton terms. */
35817       if( astTestPV( wcsmap, axlon, 3 ) && !tpn ) {
35818          phip = astGetPV( wcsmap, axlon, 3 );
35819       } else {
35820          phip = GetItem( &(store->lonpole), 0, 0, s, NULL, method, class, status );
35821          if( phip != AST__BAD && !tpn ) astSetPV( wcsmap, axlon, 3, phip );
35822       }
35823       if( phip != AST__BAD ) phip *= AST__DD2R;
35824       if( astTestPV( wcsmap, axlon, 4 ) && !tpn ) {
35825          latpole = astGetPV( wcsmap, axlon, 4 );
35826       } else {
35827          latpole = GetItem( &(store->latpole), 0, 0, s, NULL, method, class, status );
35828          if( latpole != AST__BAD && !tpn ) astSetPV( wcsmap, axlon, 4, latpole );
35829       }
35830       if( latpole != AST__BAD ) latpole *= AST__DD2R;
35831 
35832 /* Find the standard Celestial Coordinates of the north pole of the Native
35833    Spherical Coordinate system. Report an error if the position was not
35834    defined. */
35835       if( !WcsNatPole( this, wcsmap, alpha0, delta0, latpole, &phip, &alphap,
35836                        &deltap, status ) && astOK ){
35837          astError( AST__BDFTS, "%s(%s): Conversion from FITS WCS native "
35838                    "coordinates to celestial coordinates is ill-conditioned.", status,
35839                    method, class );
35840       }
35841 
35842 /* Create the SphMap which converts spherical coordinates to Cartesian
35843    coordinates (stage 6 in the prologue). This asumes that axis 0 is the
35844    longitude axis, and axis 1 is the latitude axis. This will be ensured
35845    by a PermMap created later. Indicate that the SphMap will only be used
35846    to transform points on a unit sphere. This enables a forward SphMap
35847    to be combined with an inverse SphMap into a UnitMap, and thus aids
35848    simplification. */
35849       sphmap = astSphMap( "UnitRadius=1", status );
35850       astInvert( sphmap );
35851 
35852 /* Set the PolarLong attribute of the SphMap so that a longitude of phi0 (the
35853    native longitude of the fiducial point) is returned by the inverse
35854    transformation (cartesian->spherical) at either pole. */
35855       GetFiducialNSC( wcsmap, &phi0, &theta0, status );
35856       astSetPolarLong( sphmap, phi0 );
35857 
35858 /* Create a unit MatrixMap to be the basis of the MatrixMap which rotates
35859    Native Spherical Coords to Celestial Coords (stage 7 in the prologue). */
35860       matmap = astMatrixMap( 3, 3, 2, NULL, "", status );
35861 
35862 /* Modify the above MatrixMap so that it rotates the Cartesian position vectors
35863    by -phip (i.e. LONPOLE) about the Z axis. This puts the north pole of the
35864    standard system at zero longitude in the rotated system. Then annul the
35865    original MatrixMap and use the new one instead. */
35866       axis[ 0 ] = 0;
35867       axis[ 1 ] = 0;
35868       axis[ 2 ] = 1;
35869       matmap2 = astMtrRot( matmap, -phip, axis );
35870       matmap = astAnnul( matmap );
35871       matmap = matmap2;
35872 
35873 /* Now modify the above MatrixMap so that it rotates the Cartesian position
35874    vectors by -(PI/2-deltap) about the Y axis. This puts the north pole of
35875    the standard system as 90 degrees latitude in the rotated system. Then annul
35876    the original MatrixMap and use the new one instead. */
35877       axis[ 0 ] = 0;
35878       axis[ 1 ] = 1;
35879       axis[ 2 ] = 0;
35880       matmap2 = astMtrRot( matmap, deltap - AST__DPIBY2, axis );
35881       matmap = astAnnul( matmap );
35882       matmap = matmap2;
35883 
35884 /* Finally modify the above MatrixMap so that it rotates the Cartesian position
35885    vectors (PI+alphap) about the Z axis. This puts the primary meridian of the
35886    standard system at zero longitude in the rotated system. This results in the
35887    rotated system being coincident with the standard system. */
35888       axis[ 0 ] = 0;
35889       axis[ 1 ] = 0;
35890       axis[ 2 ] = 1;
35891       matmap2 = astMtrRot( matmap, AST__DPI + alphap, axis );
35892       matmap = astAnnul( matmap );
35893       matmap = matmap2;
35894 
35895 /* Combine the SphMap (stage 6) and MatrixMap (stage 7) in series. */
35896       cmpmap = astCmpMap( sphmap, matmap, 1, "", status );
35897       sphmap = astAnnul( sphmap );
35898       matmap = astAnnul( matmap );
35899 
35900 /* Create a new SphMap which converts Cartesian coordinates to spherical
35901    coordinates (stage 8 in the prologue). Indicate that the SphMap will
35902    only be used to transform points on a unit sphere. */
35903       sphmap = astSphMap( "UnitRadius=1", status );
35904 
35905 /* Set the PolarLong attribute of the SphMap so that a longitude of alpha0
35906    (the celestial longitude of the fiducial point) is returned by the
35907    forward transformation (cartesian->spherical) at either pole. */
35908       astSetPolarLong( sphmap, alpha0 );
35909 
35910 /* Add it to the compound mapping. The CmpMap then corresponds to stage 4
35911    in the prologue. Annul the constituent mappings. */
35912       new = (AstMapping *) astCmpMap( cmpmap, sphmap, 1, "", status );
35913       cmpmap = astAnnul( cmpmap );
35914       sphmap = astAnnul( sphmap );
35915 
35916 /* If there are any remaining axes (i.e. axes which do not describe a
35917    Celestial coordinate system), create a UnitMap which passes on their
35918    values unchanged (stage 5 in the prologue), and add it the CmpMap,
35919    putting it in parallel with the earlier mappings. The resulting CmpMap
35920    then corresponds to stage 2 in the prologue. Note, the axis numbering
35921    used by this UnitMap needs to take account of the fact that it is only
35922    applied to the non-celestial axes. The axes are re-ordered by the
35923    PermMap described at stage 1 in the prologue. */
35924       nax_rem = naxis - 2;
35925       if( nax_rem > 0 ){
35926          unitmap = astUnitMap( nax_rem, "", status );
35927          cmpmap = astCmpMap( new, unitmap, 0, "", status );
35928          new = astAnnul( new );
35929          unitmap = astAnnul( unitmap );
35930          new = (AstMapping *) cmpmap;
35931       }
35932 
35933 /* Now we need to ensure that axes 0 and 1 correspond to longitude and
35934    latitude. If this is already the case, then the CmpMap can be returned
35935    as it is. Otherwise, a PermMap needs to be created to rearrange the
35936    axes. */
35937       if( axlon != 0 || axlat != 1 ){
35938 
35939 /* Create the PermMap using the inperm and outperm arrays created earlier.
35940    This is the mapping described as stage 1 in the prologue. */
35941          permmap = astPermMap( naxis, inperm, naxis, outperm, NULL, "", status );
35942 
35943 /* Compound this PermMap and the CmpMap corresponding to stage 2 (created
35944    earlier) in series. */
35945          cmpmap = astCmpMap( permmap, new, 1, "", status );
35946          new = astAnnul( new );
35947          new = (AstMapping *) cmpmap;
35948 
35949 /* Now invert the PermMap, so that it re-arranges the axes back into
35950    their original order. This is the mapping described as stage 3 in
35951    the prologue. */
35952          astInvert( permmap );
35953 
35954 /* And finally.... add this inverted PermMap onto the end of the CmpMap. */
35955          cmpmap = astCmpMap( new, permmap, 1, "", status );
35956          permmap = astAnnul( permmap );
35957          new = astAnnul( new );
35958          new = (AstMapping *) cmpmap;
35959       }
35960 
35961 /* Free the temporary arrays. */
35962       inperm = (int *) astFree( (void *) inperm );
35963       outperm = (int *) astFree( (void *) outperm );
35964    }
35965 
35966 /* If an error has occurred, attempt to annul the new CmpMap. */
35967    if( !astOK ) new = astAnnul( new );
35968 
35969 /* Return the CmpMap. */
35970    return new;
35971 }
35972 
WcsNatPole(AstFitsChan * this,AstWcsMap * wcsmap,double alpha0,double delta0,double latpole,double * phip,double * alphap,double * deltap,int * status)35973 static int WcsNatPole( AstFitsChan *this, AstWcsMap *wcsmap, double alpha0,
35974                        double delta0, double latpole, double *phip,
35975                        double *alphap, double *deltap, int *status ){
35976 
35977 /*
35978 *  Name:
35979 *     WcsNatPole
35980 
35981 *  Purpose:
35982 *     Find the celestial coordinates of the north pole of the Native Spherical
35983 *     Coordinate system.
35984 
35985 *  Type:
35986 *     Private function.
35987 
35988 *  Synopsis:
35989 
35990 *     int WcsNatPole( AstFitsChan *this, AstWcsMap *wcsmap, double alpha0,
35991 *                     double delta0, double latpole, double *phip,
35992 *                     double *alphap, double *deltap, int *status )
35993 
35994 *  Class Membership:
35995 *     FitsChan
35996 
35997 *  Description:
35998 *     The supplied WcsMap converts projected positions given in
35999 *     "Projection Plane Coords" to positions in the "Native Spherical
36000 *     Coordinate" system. This function finds the pole of this spherical
36001 *     coordinate system in terms of the standard celestial coordinate
36002 *     system to which the CRVALi, LONPOLE and LATPOLE keywords refer (this
36003 *     system should be identified by characters 5-8 of the CTYPEi
36004 *     keywords). It also supplies a default value for LONPOLE if no value
36005 *     has been supplied explicitly in the FITS header.
36006 *
36007 *     This function implements equations 8, 9 and 10 from the FITS-WCS paper
36008 *     II by Calabretta & Greisen (plus the associated treatment of special
36009 *     cases). The paper provides more detailed documentation for the
36010 *     mathematics implemented by this function.
36011 
36012 *  Parameters:
36013 *     this
36014 *        The FitsChan in which to store any warning cards. If NULL, no
36015 *        warnings are stored.
36016 *     wcsmap
36017 *        A mapping describing the deprojection being used (i.e. the
36018 *        mapping from Projection Plane Coords to Native Spherical Coords).
36019 *     alpha0
36020 *        The longitude of the fiducial point in the standard celestial
36021 *        coordinate frame (in radians). Note, this fiducial point does
36022 *        not necessarily correspond to the point given by keywords CRPIXj.
36023 *     delta0
36024 *        The celestial latitude (radians) of the fiducial point.
36025 *     latpole
36026 *        The value of FITS keyword LATPOLE, converted to radians, or the
36027 *        symbolic constant AST__BAD if the keyword was not supplied.
36028 *     phip
36029 *        Pointer to a location at which is stored the longitude of the north
36030 *        pole of the standard Celestial coordinate system, as measured in
36031 *        the Native Spherical Coordinate system, in radians. This should be
36032 *        supplied holding the radian equivalent of the value of the FITS
36033 *        keyword LONPOLE, or the symbolic constant AST__BAD if the keyword was
36034 *        not supplied (in which case a default value will be returned at the
36035 *        given location).
36036 *     alphap
36037 *        Pointer to a location at which to store the calculated longitude
36038 *        of the Native North Pole, in radians.
36039 *     deltap
36040 *        Pointer to a location at which to store the calculated latitude
36041 *        of the Native North Pole, in radians.
36042 *     class
36043 *        A pointer to a string holding the class of the object being
36044 *        read. This is used only in the construction of error messages.
36045 *     status
36046 *        Pointer to the inherited status variable.
36047 
36048 *  Returned Value:
36049 *     A status: non-zero for success, zero if the position of the native
36050 *     north pole is undefined.
36051 
36052 *  Notes:
36053 *     -  Certain combinations of keyword values result in the latitude of
36054 *     the Native North Pole being undefined. In these cases, a value of
36055 *     0 is returned for the function value, but no error is reported.
36056 *     -  All angular values used by this function are in radians.
36057 *     -  A value of 0 is returned if an error has already occurred.
36058 */
36059 
36060 /* Local Variables: */
36061    char buf[150];                  /* Buffer for warning message */
36062    double cos_theta0;              /* Cosine of theta0 */
36063    double cos_phip;                /* Cosine of (phip - phi0) */
36064    double cos_delta0;              /* Cosine of delta0 */
36065    double cos_deltap;              /* Cosine of deltap */
36066    double deltap_1;                /* First possible value for deltap */
36067    double deltap_2;                /* Second possible value for deltap */
36068    double sin_theta0;              /* Sine of theta0 */
36069    double sin_phip;                /* Sine of (phip - phi0) */
36070    double sin_delta0;              /* Sine of delta0 */
36071    double sin_deltap;              /* Sine of deltap */
36072    double t0, t1, t2, t3, t4;      /* Intermediate values */
36073    double phi0, theta0;            /* Native coords of fiducial point */
36074 
36075 /* Check the global status. */
36076    if ( !astOK ) return 0;
36077 
36078 /* Get the longitude and latitude of the fiducial point in the native
36079    spherical coordinate frame (in radians). */
36080    GetFiducialNSC( wcsmap, &phi0, &theta0, status );
36081 
36082 /* If no value was supplied for the FITS keyword LONPOLE, set up a default
36083    value such that the celestial latitude increases in the same direction
36084    as the native latitude at the fiducial; point. */
36085    if( *phip == AST__BAD ){
36086       if( delta0 >= theta0 ){
36087          *phip = 0.0;
36088       } else {
36089          *phip = AST__DPI;
36090       }
36091 
36092 /* Issue a warning that a default lonpole value has been adopted. */
36093       sprintf( buf, "The original FITS header did not specify the "
36094                "longitude of the native north pole. A default value "
36095                "of %.8g degrees was assumed.", (*phip)*AST__DR2D );
36096       Warn( this, "nolonpole", buf, "astRead", "FitsChan", status );
36097    }
36098 
36099 /* If the fiducial point is coincident with the Native North Pole, then the
36100    Native North Pole must have the same coordinates as the fiducial
36101    point. Tests for equality include some tolerance to allow for rounding
36102    errors. */
36103    sin_theta0 = sin( theta0 );
36104    if( EQUAL( sin_theta0, 1.0 ) ){
36105       *alphap = alpha0;
36106       *deltap = delta0;
36107 
36108 /* If the fiducial point is concident with the Native South Pole, then the
36109    Native North Pole must have the coordinates of the point diametrically
36110    opposite the fiducial point. */
36111    } else if( EQUAL( sin_theta0, -1.0 ) ){
36112       *alphap = alpha0 + AST__DPI;
36113       *deltap = -delta0;
36114 
36115 /* For all other cases, go through the procedure described in the WCS paper
36116    by Greisen & Calabretta, to find the position of the Native North Pole.
36117    First store some useful values. */
36118    } else {
36119       cos_theta0 = cos( theta0 );
36120       cos_delta0 = cos( delta0 );
36121       cos_phip = cos( *phip - phi0 );
36122       sin_delta0 = sin( delta0 );
36123       sin_phip = sin( *phip - phi0 );
36124 
36125 /* Next, find the two possible values for the latitude of the Native
36126    North Pole (deltap). If any stage of this transformation is
36127    indeterminate, return zero (except for the single special case noted
36128    in item 6 para. 2 of the WCS paper, for which LATPOLE specifies the
36129    values to be used). */
36130       t0 = cos_theta0*cos_phip;
36131       if( fabs( t0 ) < TOL2 && fabs( sin_theta0 ) < TOL2 ){
36132          if( latpole != AST__BAD ) {
36133             *deltap = latpole;
36134          } else {
36135             return 0;
36136          }
36137       } else {
36138          t1 = atan2( sin_theta0, t0 );
36139          t2 = cos_theta0*cos_phip;
36140          t2 *= t2;
36141          t2 += sin_theta0*sin_theta0;
36142          if( t2 <= DBL_MIN ){
36143             return 0;
36144          } else {
36145             t3 = sin_delta0/sqrt( t2 );
36146             if( fabs( t3 ) > 1.0 + TOL1 ){
36147                return 0;
36148             } else {
36149                if( t3 < -1.0 ){
36150                   t4 = AST__DPI;
36151                } else if( t3 > 1.0 ){
36152                   t4 = 0.0;
36153                } else {
36154                   t4 = acos( t3 );
36155                }
36156                deltap_1 = palDrange( t1 + t4 );
36157                deltap_2 = palDrange( t1 - t4 );
36158 
36159 /* Select which of these two values of deltap to use. Values outside the
36160    range +/- PI/2 cannot be used. If both values are within this range
36161    use the value which is closest to the supplied value of latpole (or
36162    use the northern most value if the LATPOLE keyword was not supplied. */
36163                if( fabs( deltap_1 ) > AST__DPIBY2 + TOL2 ){
36164                   *deltap = deltap_2;
36165                } else if( fabs( deltap_2 ) > AST__DPIBY2 + TOL2 ){
36166                   *deltap = deltap_1;
36167                } else {
36168                   if( latpole != AST__BAD ){
36169                      if( fabs( deltap_1 - latpole ) <
36170                          fabs( deltap_2 - latpole ) ){
36171                         *deltap = deltap_1;
36172                      } else {
36173                         *deltap = deltap_2;
36174                      }
36175                   } else {
36176                      if( deltap_1 > deltap_2 ){
36177                         *deltap = deltap_1;
36178                      } else {
36179                         *deltap = deltap_2;
36180                      }
36181 
36182 /* Issue a warning that a default latpole value has been adopted. */
36183                      sprintf( buf, "The original FITS header did not specify "
36184                               "the latitude of the native north pole. A "
36185                               "default value of %.8g degrees was assumed.",
36186                               (*deltap)*AST__DR2D );
36187                      Warn( this, "nolatpole", buf, "astRead", "FitsChan", status );
36188                   }
36189                }
36190                if( fabs( *deltap  ) > AST__DPIBY2 + TOL2 ) {
36191                   return 0;
36192                } else if( *deltap < -AST__DPIBY2 ){
36193                   *deltap = -AST__DPIBY2;
36194                } else if( *deltap > AST__DPIBY2 ){
36195                   *deltap = AST__DPIBY2;
36196                }
36197             }
36198          }
36199       }
36200 
36201 /* If a valid value for the latitude (deltap) has been found, find the
36202    longitude of the Native North Pole. */
36203       if( *deltap != AST__BAD ) {
36204          if( fabs( cos_delta0) > TOL2 ){
36205             cos_deltap = cos( *deltap );
36206             sin_deltap = sin( *deltap );
36207             if( fabs( cos_deltap ) > TOL2 ){
36208                t1 = sin_phip*cos_theta0/cos_delta0;
36209                t2 = ( sin_theta0 - sin_deltap*sin_delta0 )
36210                     /( cos_delta0*cos_deltap );
36211                if( ( fabs( t1 ) > TOL2 ) || ( fabs( t2 ) > TOL2 ) ){
36212                   *alphap = alpha0 - atan2( t1, t2 );
36213                } else {
36214                   *alphap = alpha0;
36215                }
36216             } else if( sin_deltap > 0.0 ){
36217                *alphap = alpha0 + (*phip - phi0) - AST__DPI;
36218             } else {
36219                *alphap = alpha0 - (*phip - phi0);
36220             }
36221          } else {
36222             *alphap = alpha0;
36223          }
36224       } else {
36225          *alphap = AST__BAD;
36226       }
36227    }
36228 
36229 /* Return a success status if valid latitude and longitude values were
36230    found. */
36231    return (*deltap) != AST__BAD && (*alphap) != AST__BAD ;
36232 }
36233 
WcsOthers(AstFitsChan * this,FitsStore * store,char s,AstFrame ** frm,AstFrame * iwcfrm,const char * method,const char * class,int * status)36234 static AstMapping *WcsOthers( AstFitsChan *this, FitsStore *store, char s,
36235                               AstFrame **frm,  AstFrame *iwcfrm, const char *method,
36236                               const char *class, int *status ){
36237 
36238 /*
36239 *  Name:
36240 *     WcsOthers
36241 
36242 *  Purpose:
36243 *     Create a Mapping from intermediate world coords to any axes
36244 *     which are not covered by specialised conventions.
36245 
36246 *  Type:
36247 *     Private function.
36248 
36249 *  Synopsis:
36250 
36251 *     AstMapping *WcsOthers( AstFitsChan *this, FitsStore *store, char s,
36252 *                            AstFrame **frm,  AstFrame *iwcfrm, const char *method,
36253 *                            const char *class, int *status )
36254 
36255 *  Class Membership:
36256 *     FitsChan
36257 
36258 *  Description:
36259 *     This function interprets the contents of the supplied FitsStore
36260 *     structure, looking for world coordinate axes for which no
36261 *     description has yet been added to the supplied Frame . It is
36262 *     assumed that any such axes are simple linear axes. It returns a
36263 *     Mapping which simply adds on the CRVAL values to such axes.
36264 *     It also modifies the supplied Frame to describe the axes.
36265 
36266 *  Parameters:
36267 *     this
36268 *        The FitsChan.
36269 *     store
36270 *        A structure containing information about the requested axis
36271 *        descriptions derived from a FITS header.
36272 *     s
36273 *        A character identifying the co-ordinate version to use. A space
36274 *        means use primary axis descriptions. Otherwise, it must be an
36275 *        upper-case alphabetical characters ('A' to 'Z').
36276 *     frm
36277 *        The address of a location at which to store a pointer to the
36278 *        Frame describing the world coordinate axes.
36279 *     iwcfrm
36280 *        A pointer to the Frame describing the intermediate world coordinate
36281 *        axes. The properties of this Frame may be changed on exit.
36282 *     method
36283 *        A pointer to a string holding the name of the calling method.
36284 *        This is used only in the construction of error messages.
36285 *     class
36286 *        A pointer to a string holding the class of the object being
36287 *        read. This is used only in the construction of error messages.
36288 *     status
36289 *        Pointer to the inherited status variable.
36290 
36291 *  Returned Value:
36292 *     A pointer to the Mapping.
36293 */
36294 
36295 /* Local Variables: */
36296    AstFrame *pfrm;           /* Pointer to primary Frame */
36297    AstFrame *pfrm2;          /* Pointer to primary Frame */
36298    AstMapping *map1;         /* Pointer to a Mapping */
36299    AstMapping *map2;         /* Pointer to a Mapping */
36300    AstMapping *ret;          /* The returned Mapping */
36301    char **comms;             /* Pointer to array of CTYPE commments */
36302    char buf[ 100 ];          /* Buffer for textual attribute value */
36303    char buf2[ 100 ];         /* Buffer for textual attribute value */
36304    char buf3[ 20 ];          /* Buffer for default CTYPE value */
36305    char *newdom;             /* Pointer to new Domain value */
36306    const char *ckeyval;      /* Pointer to character keyword value */
36307    int i;                    /* Axis index */
36308    int j;                    /* Axis index */
36309    int len;                  /* Used length of string */
36310    int naxes;                /* no. of axes in Frame */
36311    int nother;               /* The number of "other" axes */
36312    int paxis;                /* Primary axis index */
36313    int usecom;               /* Use CTYPE comments as axis Labels? */
36314 
36315 /* Initialise the pointer to the returned Mapping. */
36316    ret = NULL;
36317 
36318 /* Check the global status. */
36319    if ( !astOK ) return ret;
36320 
36321 /* Get the number of physical axes. */
36322    naxes = astGetNaxes( *frm );
36323 
36324 /* Assume we will use CTYPE comments as the axis labels. */
36325    usecom = 1;
36326 
36327 /* Initialise the count of "other" axes. */
36328    nother = 0;
36329 
36330 /* Get the comments associated with the CTYPE keywords for all "other"
36331    axes. */
36332    comms = astMalloc( naxes*sizeof( char * ) );
36333    if( comms ) {
36334 
36335 /* Loop round all axes in the Frame, and initialise the pointer to its
36336    comment. */
36337       for( i = 0; i < naxes; i++ ){
36338          comms[ i ] = NULL;
36339 
36340 /* Get the Domain for the primary frame containing the axis. This will be
36341    "AST_FITSCHAN" if the axis has not yet been recognised (this Domain is
36342    set up by WcsMapFrm). Only consider the axis further if the Domain has
36343    not been changed. */
36344          astPrimaryFrame( *frm, i, &pfrm, &paxis );
36345          if( !strcmp( astGetDomain( pfrm ), "AST_FITSCHAN" ) ) {
36346 
36347 /* Increment the count of "other" axes. */
36348             nother++;
36349 
36350 /* Get the comment associated with the CTYPE header. */
36351             ckeyval = GetItemC( &(store->ctype_com), i, 0, s, NULL, method, class, status );
36352 
36353 /* If this axis has no CTYPE comment, we will use CTYPE values as axis
36354    labels (if given, the CNAME keyword take precedence). */
36355             if( !ckeyval || astChrLen( ckeyval ) == 0  ) {
36356                usecom = 0;
36357 
36358 /* If the CTYPE comment for this axis is the same as any other comment, we
36359    will use CTYPE values as axis labels. */
36360             } else {
36361                for( j = 0; j < nother - 1; j++ ) {
36362                   if( comms[ j ]  && !strcmp( ckeyval, comms[ j ] ) ) {
36363                      usecom = 0;
36364                      break;
36365                   }
36366                }
36367             }
36368 
36369 /* If we are still using comments as axis labels, store a copy of it in the
36370    workspace. */
36371             if( usecom ) comms[ i ] = astStore( NULL, ckeyval,
36372                                                 strlen( ckeyval ) + 1 );
36373          }
36374          pfrm = astAnnul( pfrm );
36375       }
36376 
36377 /* Free the workspace holding comments. */
36378       for( i = 0; i < naxes; i++ ) comms[ i ] = astFree( comms[ i ] );
36379       comms = astFree( comms );
36380    }
36381 
36382 /* If there are no "other" axes, just return a UnitMap. */
36383    if( nother == 0 ) {
36384       ret = (AstMapping *) astUnitMap( naxes, "", status );
36385 
36386 /* Otherwise... */
36387    } else {
36388 
36389 /* If we have only a single other axis, use CTYPE value instead of
36390    comment. */
36391       if( nother == 1 ) usecom = 0;
36392 
36393 /* Not yet started a new Domain value to replace "AST_FITSCHAN". */
36394       newdom = NULL;
36395       pfrm2 = NULL;
36396 
36397 /* Check each axis of the Frame looking for axes which have not yet been
36398    recognised. */
36399       for( i = 0; i < naxes; i++ ) {
36400 
36401 /* Get the Domain for the primary frame containing the axis. This will be
36402    "AST_FITSCHAN" if the axis has not yet been recognised (this Domain is
36403    set up by WcsMapFrm). Only consider the axis further if the Domain has
36404    not been changed. */
36405          astPrimaryFrame( *frm, i, &pfrm, &paxis );
36406          if( !strcmp( astGetDomain( pfrm ), "AST_FITSCHAN" ) ) {
36407 
36408 /* Save a pointer to the primary Frame which we will use to set the
36409    Domain of the primary Frame. */
36410             if( !pfrm2 ) pfrm2 = astClone( pfrm );
36411 
36412 /* Get the CTYPE value. Use a default of "AXISn". */
36413             ckeyval = GetItemC( &(store->ctype), i, 0, s, NULL, method, class, status );
36414             if( !ckeyval ) {
36415                sprintf( buf3, "AXIS%d", i + 1 );
36416                ckeyval = buf3;
36417             }
36418 
36419 /* If the CTYPE value ends with "-LOG", assume it is a logarithmically spaced
36420    axis. Get the Mapping from IWC to WCS. Reduce the used length of the
36421    CTYPE string to exlude any trailing "-LOG" string. */
36422             len = strlen( ckeyval );
36423             if( len > 3 && !strcmp( ckeyval + len - 4, "-LOG" ) ){
36424                map1 = LogWcs( store, i, s, method, class, status );
36425                sprintf( buf2, "%.*s", len - 4, ckeyval );
36426 
36427 /* Otherwise, assume the axis is linearly spaced. */
36428             } else {
36429                map1 = LinearWcs( store, i, s, method, class, status );
36430                sprintf( buf2, "%.*s", len, ckeyval );
36431             }
36432 
36433 /* Append the CTYPE value to the final Domain value for the primary Frame. */
36434             if( ckeyval && astChrLen( ckeyval ) > 0 ) {
36435                if( newdom ) {
36436                   sprintf( buf, "%s-%s", newdom, buf2 );
36437                } else {
36438                   sprintf( buf, "%s", buf2 );
36439                   newdom = buf;
36440                }
36441             }
36442 
36443 /* Now modify the axis in the Frame to have appropriate values for the
36444    Unit, Label and Symbol attributes. Also set the Unit attribute for
36445    the corresponding axis in the IWC Frame. */
36446             if( ckeyval ) astSetSymbol( *frm, i, buf2 );
36447             ckeyval = GetItemC( &(store->cname), i, 0, s, NULL, method, class, status );
36448             if( !ckeyval && usecom ) ckeyval = GetItemC( &(store->ctype_com),
36449                                                    i, 0, s, NULL, method, class, status );
36450             if( !ckeyval ) ckeyval = buf2;
36451             if( ckeyval ) astSetLabel( *frm, i, ckeyval );
36452             ckeyval = GetItemC( &(store->cunit), i, 0, s, NULL, method, class, status );
36453             if( ckeyval ) {
36454                astSetUnit( *frm, i, ckeyval );
36455                astSetUnit( iwcfrm, i, ckeyval );
36456             }
36457 
36458 /* If this axis has been described by an earlier function (because it
36459    uses specialised conventions such as those described in FITS-WCS papers
36460    II or III), then create a UnitMap for this axis. */
36461          } else {
36462             map1 = (AstMapping *) astUnitMap( 1, "", status );
36463          }
36464 
36465 /* Annul the pointer to the primary Frame containing the current axis. */
36466          pfrm = astAnnul( pfrm );
36467 
36468 /* Add the Mapping for this axis in parallel with the current "running sum"
36469    Mapping (if any). */
36470          if( ret ) {
36471             map2 = (AstMapping *) astCmpMap( ret, map1, 0, "", status );
36472             ret = astAnnul( ret );
36473             map1 = astAnnul( map1 );
36474             ret = map2;
36475          } else {
36476             ret = map1;
36477          }
36478       }
36479 
36480 /* Set the Domain name for the primary Frame. It is currently set to
36481    AST_FITSCHAN. We replace it with a value formed by concatenating the
36482    CTYPE values of its axes. */
36483       if( pfrm2 ) {
36484          if( newdom && astChrLen( newdom ) > 0 ) {
36485             astSetDomain( pfrm2, newdom );
36486          } else {
36487             astClearDomain( pfrm2 );
36488          }
36489          pfrm2 = astAnnul( pfrm2 );
36490       }
36491 
36492 /* If the header contained a WCSNAME keyword, use it as the Domain name for
36493    the Frame. Also use it to create a title. */
36494       ckeyval = GetItemC( &(store->wcsname), 0, 0, s, NULL, method, class, status );
36495       if( ckeyval ){
36496          astSetDomain( *frm, ckeyval );
36497          sprintf( buf, "%s coordinates", ckeyval );
36498          astSetTitle( *frm, buf );
36499       }
36500    }
36501 
36502 /* Return the result. */
36503    return ret;
36504 }
36505 
WcsShift(FitsStore * store,char s,int naxes,const char * method,const char * class,int * status)36506 static AstWinMap *WcsShift( FitsStore *store, char s, int naxes,
36507                             const char *method, const char *class, int *status ){
36508 /*
36509 *  Name:
36510 *     WcsShift
36511 
36512 *  Purpose:
36513 *     Create a WinMap which shifts pixels coordinates so that their origin
36514 *     is at the reference pixel.
36515 
36516 *  Type:
36517 *     Private function.
36518 
36519 *  Synopsis:
36520 *     AstWinMap *WcsShift( FitsStore *store, char s, int naxes,
36521 *                          const char *method, const char *class, int *status )
36522 
36523 *  Class Membership:
36524 *     FitsChan
36525 
36526 *  Description:
36527 *     A WinMap is created which implements a shift of origin by subtracting
36528 *     the reference pixel coordinates (CRPIXi) from the input pixel
36529 *     coordinates.
36530 
36531 *  Parameters:
36532 *     store
36533 *        A structure containing values for FITS keywords relating to
36534 *        the World Coordinate System.
36535 *     s
36536 *        A character identifying the co-ordinate version to use. A space
36537 *        means use primary axis descriptions. Otherwise, it must be an
36538 *        upper-case alphabetical characters ('A' to 'Z').
36539 *     naxes
36540 *        The number of intermediate world coordinate axes (WCSAXES).
36541 *     method
36542 *        A pointer to a string holding the name of the calling method.
36543 *        This is used only in the construction of error messages.
36544 *     class
36545 *        A pointer to a string holding the class of the object being
36546 *        read. This is used only in the construction of error messages.
36547 *     status
36548 *        Pointer to the inherited status variable.
36549 
36550 *  Returned Value:
36551 *     A pointer to the created WinMap or a NULL pointer if an
36552 *     error occurred.
36553 
36554 *  Notes:
36555 *     -  If an error occurs, a NULL pointer is returned.
36556 */
36557 
36558 /* Local Variables: */
36559    AstWinMap *new;                 /* The created WinMap */
36560    int j;                          /* Pixel axis index */
36561    double crpix;                   /* CRPIX keyword value */
36562    double *c1_in;                  /* Input corner 1 */
36563    double *c2_in;                  /* Input corner 2 */
36564    double *c1_out;                 /* Output corner 1 */
36565    double *c2_out;                 /* Output corner 2 */
36566 
36567 /* Check the global status. */
36568    if ( !astOK ) return NULL;
36569 
36570 /* Initialise the returned WinMap pointer. */
36571    new = NULL;
36572 
36573 /* Allocate memory to hold the two corners, in both input and output
36574    coordinates. */
36575    c1_in = (double *) astMalloc( sizeof( double )*(size_t) naxes );
36576    c1_out = (double *) astMalloc( sizeof( double )*(size_t) naxes );
36577    c2_in = (double *) astMalloc( sizeof( double )*(size_t) naxes );
36578    c2_out = (double *) astMalloc( sizeof( double )*(size_t) naxes );
36579 
36580 /* Check these pointers can be used. */
36581    if( astOK ){
36582 
36583 /* Set up two arbitrary corners in the input coordinate system, and the
36584    corresponding values with the CRPIX values subtracted off. */
36585       for( j = 0; j < naxes; j++ ){
36586 
36587 /* Get the CRPIX value for this axis. */
36588          crpix = GetItem( &(store->crpix), 0, j, s, NULL, method, class, status );
36589          if( crpix == AST__BAD ) crpix = 0.0;
36590 
36591 /* Store the corner co-ordinates. */
36592          c1_in[ j ] = 0.0;
36593          c2_in[ j ] = 1.0;
36594          c1_out[ j ] = -crpix;
36595          c2_out[ j ] = 1.0 - crpix;
36596       }
36597 
36598 /* Create the WinMap. */
36599       new = astWinMap( naxes, c1_in, c2_in, c1_out, c2_out, "", status );
36600 
36601 /* If an error has occurred, attempt to annul the new WinMap. */
36602       if( !astOK ) new = astAnnul( new );
36603    }
36604 
36605 /* Free the memory holding the corners. */
36606    c1_in = (double *) astFree( (void *) c1_in );
36607    c1_out = (double *) astFree( (void *) c1_out );
36608    c2_in = (double *) astFree( (void *) c2_in );
36609    c2_out = (double *) astFree( (void *) c2_out );
36610 
36611 /* Return the WinMap. */
36612    return new;
36613 }
36614 
WcsSkyFrame(AstFitsChan * this,FitsStore * store,char s,int prj,char * sys,int axlon,int axlat,const char * method,const char * class,int * status)36615 static AstSkyFrame *WcsSkyFrame( AstFitsChan *this, FitsStore *store, char s,
36616                                  int prj, char *sys, int axlon, int axlat,
36617                                  const char *method, const char *class, int *status ){
36618 
36619 /*
36620 *  Name:
36621 *     WcsSkyFrame
36622 
36623 *  Purpose:
36624 *     Create a SkyFrame to describe a WCS celestial coordinate system.
36625 
36626 *  Type:
36627 *     Private function.
36628 
36629 *  Synopsis:
36630 *     AstSkyFrame *WcsSkyFrame( AstFitsChan this, FitsStore *store, char s, int prj,
36631 *                               char *sys, int axlon, int axlat, const char *method,
36632 *                               const char *class, int *status )
36633 
36634 *  Class Membership:
36635 *     FitsChan member function.
36636 
36637 *  Description:
36638 *     A SkyFrame is returned describing the celestial coordinate system
36639 *     described by a FITS header. The axes are *not* permuted in the
36640 *     returned Frame (that is, axis 0 is longitude and axis 1 is latitude
36641 *     in the returned SkyFrame, no matter what values are supplied for
36642 *     "axlat" and "axlon").
36643 
36644 *  Parameters:
36645 *     this
36646 *        The FitsChan from which the keywords were read. Warning messages
36647 *        may be added to this FitsChan.
36648 *     store
36649 *        A structure containing values for FITS keywords relating to
36650 *        the World Coordinate System.
36651 *     s
36652 *        A character identifying the co-ordinate version to use. A space
36653 *        means use primary axis descriptions. Otherwise, it must be an
36654 *        upper-case alphabetical characters ('A' to 'Z').
36655 *     prj
36656 *        An integer code for the WCS projection being used.
36657 *     sys
36658 *        A pointer to a string identifying the celestial co-ordinate system
36659 *        implied by the CTYPE values in the FitsStore. This will be "EQU" (for
36660 *        equatorial), or a one or two character code extracted from the
36661 *        CTYPE values.
36662 *     axlon
36663 *        Zero based index of the longitude axis in the FITS header.
36664 *     axlat
36665 *        Zero based index of the latitude axis in the FITS header.
36666 *     method
36667 *        The calling method. Used only in error messages.
36668 *     class
36669 *        The object class. Used only in error messages.
36670 *     status
36671 *        Pointer to the inherited status variable.
36672 
36673 *  Returned Value:
36674 *     A pointer to the SkyFrame.
36675 
36676 *  Notes:
36677 *     -  A NULL pointer is returned if an error has already occurred, or
36678 *     if this function should fail for any reason.
36679 */
36680 
36681 /* Local Variables: */
36682    AstSkyFrame *ret;              /* Returned Frame */
36683    char *ckeyval;                 /* Pointer to string item value */
36684    char *lattype;                 /* Pointer to latitude CTYPE value */
36685    char *lontype;                 /* Pointer to longitude CTYPE value */
36686    char bj;                       /* Besselian/Julian selector */
36687    char buf[300];                 /* Text buffer */
36688    char sym[10];                  /* Axis symbol */
36689    double dval;                   /* Floating point attribute value */
36690    double eqmjd;                  /* MJD equivalent of equinox */
36691    double equinox;                /* EQUINOX value */
36692    double geolat;                 /* Observer's geodetic latitude */
36693    double geolon;                 /* Observer's geodetic longitude */
36694    double h;                      /* Observer's geodetic height */
36695    double mjdobs;                 /* MJD-OBS value */
36696    double obsgeo[ 3 ];            /* Observer's Cartesian position */
36697    int radesys;                   /* RADESYS value */
36698    int report;                    /* Report unknown lon/lat system? */
36699 
36700 /* Initialise. */
36701    ret = NULL;
36702 
36703 /* Check the global error status. */
36704    if ( !astOK ) return ret;
36705 
36706 /* Get the RADESYS keyword from the header, and identify the value.
36707    Store a integer value identifying the system. Report an error if an
36708    unrecognised system is supplied. Store NORADEC if the keyword was
36709    not supplied. */
36710    ckeyval = GetItemC( &(store->radesys), 0, 0, s, NULL, method, class, status );
36711    radesys = NORADEC;
36712    if( ckeyval ){
36713       if( !strncmp( ckeyval, "FK4 ", 4 ) ||
36714           !strcmp( ckeyval, "FK4" ) ){
36715          radesys = FK4;
36716       } else if( !strncmp( ckeyval, "FK4-NO-E", 8 ) ){
36717          radesys = FK4NOE;
36718       } else if( !strncmp( ckeyval, "FK5 ", 4 ) ||
36719                  !strcmp( ckeyval, "FK5" ) ){
36720          radesys = FK5;
36721       } else if( !strncmp( ckeyval, "ICRS ", 5 ) ||
36722                  !strcmp( ckeyval, "ICRS" ) ){
36723          radesys = ICRS;
36724       } else if( !strncmp( ckeyval, "GAPPT ", 6 ) ||
36725                  !strcmp( ckeyval, "GAPPT" ) ){
36726          radesys = GAPPT;
36727       } else if( astOK ){
36728          astError( AST__BDFTS, "%s(%s): FITS keyword '%s' has the "
36729                    "unrecognised value '%s'.", status, method, class,
36730                    FormatKey( "RADESYS", -1, -1, s, status ), ckeyval );
36731       }
36732    } else {
36733       radesys = NORADEC;
36734    }
36735 
36736 /* Get the value of the EQUINOX keyword. */
36737    equinox = GetItem( &(store->equinox), 0, 0, s, NULL, method, class, status );
36738 
36739 /* For FK4 and FK4-NO-E any supplied equinox value is Besselian. For all
36740    other systems, the equinox value is Julian. */
36741    bj = 0;
36742    if( equinox != AST__BAD ){
36743       if( radesys == FK4 || radesys == FK4NOE ){
36744          bj = 'B';
36745       } else if( radesys != NORADEC ) {
36746          bj = 'J';
36747 
36748 /* If no RADESYS was suppied, but an equinox was, use the IAU 1984 rule
36749    to determine the default RADESYS and equinox type. */
36750       } else {
36751          if( equinox < 1984.0 ){
36752             radesys = FK4;
36753             bj = 'B';
36754          } else {
36755             radesys = FK5;
36756             bj = 'J';
36757          }
36758 
36759 /* If an equatorial system is being used, give a warning that a default RADESYS
36760    value is being used. */
36761          if( !strcmp( sys, "EQU" ) ){
36762             sprintf( buf, "The original FITS header did not specify the "
36763                      "RA/DEC reference frame. A default value of %s was "
36764                      "assumed.", ( radesys == FK4 ) ? "FK4" : "FK5" );
36765             Warn( this, "noradesys", buf, method, class, status );
36766          }
36767       }
36768 
36769 /* If no equinox was supplied, use a default equinox value depending
36770    on the frame of reference. For FK4-based systems, use B1950. */
36771    } else {
36772       if( radesys == FK4 || radesys == FK4NOE ){
36773          equinox = 1950.0;
36774          bj = 'B';
36775 
36776 /* For FK5-based systems, use J2000. */
36777       } else if( radesys == FK5 ){
36778          equinox = 2000.0;
36779          bj = 'J';
36780 
36781 /* If no RADESYS or EQUINOX was supplied, assume either FK4 B1950 or ICRS -
36782    as decided by attribute DefB1950 (GAPPT and ICRS do not use EQUINOX). */
36783       } else if( radesys == NORADEC ) {
36784          if( astGetDefB1950( this ) ) {
36785             equinox = 1950.0;
36786             bj = 'B';
36787             radesys = FK4;
36788          } else {
36789             radesys = ICRS;
36790          }
36791          if( !strcmp( sys, "EQU" ) ){
36792             sprintf( buf, "The original FITS header did not specify the "
36793                      "RA/DEC reference frame. A default value of %s was "
36794                      "assumed.", ( radesys == FK4 ) ? "FK4" : "ICRS" );
36795             Warn( this, "noradesys", buf, method, class, status );
36796          }
36797       }
36798 
36799 /* If we have an equatorial or ecliptic system, issue a warning that a default
36800    equinox has been adopted. */
36801       if( ( !strcmp( sys, "EQU" ) && radesys != ICRS && radesys != GAPPT ) ||
36802           !strcmp( sys, "ECL" ) ){
36803          sprintf( buf, "The original FITS header did not specify the "
36804                   "reference equinox. A default value of %c%.8g was "
36805                   "assumed.", bj, equinox );
36806          Warn( this, "noequinox", buf, method, class, status );
36807       }
36808    }
36809 
36810 /* Convert the equinox to a Modified Julian Date. */
36811    if( equinox != AST__BAD ) {
36812       if( bj == 'B' ) {
36813          eqmjd = palEpb2d( equinox );
36814       } else {
36815          eqmjd = palEpj2d( equinox );
36816       }
36817    } else {
36818       eqmjd = AST__BAD;
36819    }
36820 
36821 /* Get a value for the Epoch attribute. If no value is available, use
36822    EQUINOX and issue a warning. */
36823    mjdobs = ChooseEpoch( this, store, s, method, class, status );
36824    if( mjdobs == AST__BAD ) {
36825       mjdobs = eqmjd;
36826       if( mjdobs != AST__BAD ) {
36827          sprintf( buf, "The original FITS header did not specify the "
36828                   "date of observation. A default value of %c%.8g was "
36829                   "assumed.", bj, equinox );
36830          Warn( this, "nomjd-obs", buf, method, class, status );
36831       }
36832    }
36833 
36834 /* Create a SkyFrame for the specified system. */
36835    if( !strcmp( sys, "E" ) ){
36836       ret = astSkyFrame( "System=Ecliptic", status );
36837    } else if( !strcmp( sys, "H" ) ){
36838       ret = astSkyFrame( "System=Helioecliptic", status );
36839    } else if( !(strcmp( sys, "G" ) ) ){
36840       ret = astSkyFrame( "System=Galactic", status );
36841    } else if( !(strcmp( sys, "S" ) ) ){
36842       ret = astSkyFrame( "System=Supergalactic", status );
36843    } else if( !(strcmp( sys, "AZL" ) ) ){
36844       ret = astSkyFrame( "System=AzEl", status );
36845    } else if( !(strcmp( sys, "EQU" ) ) ){
36846 
36847 /* For equatorial systems, the specific system is given by the RADESYS
36848    value. */
36849       if( radesys == FK4 ){
36850          ret = astSkyFrame( "System=FK4", status );
36851       } else if( radesys == FK4NOE ){
36852          ret = astSkyFrame( "System=FK4-NO-E", status );
36853       } else if( radesys == FK5 ){
36854          ret = astSkyFrame( "System=FK5", status );
36855       } else if( radesys == ICRS ){
36856          ret = astSkyFrame( "System=ICRS", status );
36857       } else if( radesys == GAPPT ){
36858          ret = astSkyFrame( "System=GAPPT", status );
36859       } else if( astOK ){
36860          astError( AST__INTER, "%s(%s): Internal AST programming "
36861                    "error - FITS equatorial coordinate system type %d "
36862                    "not yet supported in WcsSkyFrame.", status, method, class, radesys );
36863       }
36864 
36865 /* If an unknown celestial co-ordinate system was specified by the CTYPE
36866    keywords, add warning messages to the FitsChan and treat the axes as
36867    a general spherical coordinate system. */
36868    } else if( astOK ){
36869       report = 1;
36870       ret = astSkyFrame( "System=UNKNOWN", status );
36871       strcpy( sym, sys );
36872       if( strlen( sys ) == 1 ) {
36873          strcpy( sym + 1, "LON" );
36874          astSetSymbol( ret, 0, sym );
36875          strcpy( sym + 1, "LAT" );
36876          astSetSymbol( ret, 1, sym );
36877       } else {
36878          strcpy( sym + 2, "LN" );
36879          astSetSymbol( ret, 0, sym );
36880          strcpy( sym + 2, "LT" );
36881          astSetSymbol( ret, 1, sym );
36882 
36883 /* The code "OF" is used by AST to describe offset sky coordinates. Set
36884    the Domain to SKY_OFFSETS in these cases, so that we can identify
36885    these Frames later. */
36886          if( !strcmp( sys, "OF" ) ) {
36887             astSetDomain( ret, "SKY_OFFSETS" );
36888             report = 0;
36889          }
36890       }
36891 
36892       if( report ) {
36893          lontype = GetItemC( &(store->ctype), axlon, 0, s, NULL, method, class, status );
36894          lattype = GetItemC( &(store->ctype), axlat, 0, s, NULL, method, class, status );
36895          if( lontype && lattype ){
36896             sprintf( buf, "This FITS header contains references to an unknown "
36897                      "spherical co-ordinate system specified in the values "
36898                      "%s and %s. It may not be possible to convert to "
36899                      "other standard co-ordinate systems.", lontype, lattype );
36900             Warn( this, "badcel", buf, method, class, status );
36901          }
36902       }
36903    }
36904 
36905 /* If a skyFrame was created... */
36906    if( ret ){
36907 
36908 /* Store the projection description. */
36909       if( prj != AST__WCSBAD ) astSetProjection( ret, astWcsPrjDesc( prj )  );
36910 
36911 /* Store the epoch of the observation in the SkyFrame. */
36912       if( mjdobs != AST__BAD ) astSetEpoch( ret, mjdobs );
36913 
36914 /* For equatorial and ecliptic systems, store the epoch of the reference
36915    equinox in the SkyFrame. */
36916       if( ( !strcmp( sys, "EQU" ) || !strcmp( sys, "ECL" ) ) &&
36917           equinox != AST__BAD ) astSetEquinox( ret, eqmjd );
36918 
36919 /* If either of the CNAME keywords is set, use it as the axis label. */
36920       ckeyval = GetItemC( &(store->cname), axlon, 0, s, NULL, method, class, status );
36921       if( ckeyval ) astSetLabel( ret, 0, ckeyval );
36922       ckeyval = GetItemC( &(store->cname), axlat, 0, s, NULL, method, class, status );
36923       if( ckeyval ) astSetLabel( ret, 1, ckeyval );
36924 
36925 /* Observer's position (from primary axis descriptions). Get the OBSGEO-X/Y/Z
36926    keywords, convert to geodetic longitude and latitude and store as the
36927    SpecFrame's ObsLat, ObsLon and ObsAlt attributes. */
36928       obsgeo[ 0 ] = GetItem( &(store->obsgeox), 0, 0, ' ', NULL, method, class, status );
36929       obsgeo[ 1 ] = GetItem( &(store->obsgeoy), 0, 0, ' ', NULL, method, class, status );
36930       obsgeo[ 2 ] = GetItem( &(store->obsgeoz), 0, 0, ' ', NULL, method, class, status );
36931       if( obsgeo[ 0 ] != AST__BAD &&
36932           obsgeo[ 1 ] != AST__BAD &&
36933           obsgeo[ 2 ] != AST__BAD ) {
36934          eraGc2gd( 1, obsgeo, &geolon, &geolat, &h );
36935          astSetObsLat( ret, geolat );
36936          astSetObsLon( ret, geolon );
36937          astSetObsAlt( ret, h );
36938       }
36939 
36940 /* Store values for the reference point in the SkyFrame. */
36941       dval = GetItem( &(store->skyref), axlon, 0, s, NULL, method, class, status );
36942       if( dval != AST__BAD ) astSetSkyRef( ret, 0, dval );
36943       dval = GetItem( &(store->skyref), axlat, 0, s, NULL, method, class, status );
36944       if( dval != AST__BAD ) astSetSkyRef( ret, 1, dval );
36945 
36946       dval = GetItem( &(store->skyrefp), axlon, 0, s, NULL, method, class, status );
36947       if( dval != AST__BAD ) astSetSkyRefP( ret, 0, dval );
36948       dval = GetItem( &(store->skyrefp), axlat, 0, s, NULL, method, class, status );
36949       if( dval != AST__BAD ) astSetSkyRefP( ret, 1, dval );
36950 
36951 /* We cannot store the SkyRefIs value yet since this needs to be done
36952    after the SkyFrame has been added into the FrameSet, so that the Frame
36953    will be remapped to represent the intended offsets. SO instance, mark
36954    the Frame by setting the domain to "SKY_POLE" or "SKY_ORIGIN". This
36955    odd Domain value will be cleared later in TidyOffsets. */
36956       ckeyval = GetItemC( &(store->skyrefis), 0, 0, s, NULL, method, class, status );
36957       if( ckeyval ) {
36958          if( !Ustrcmp( "POLE", ckeyval, status ) ) {
36959             astSetDomain( ret, "SKY_POLE" );
36960          } else if( !Ustrcmp( "ORIGIN", ckeyval, status ) ) {
36961             astSetDomain( ret, "SKY_ORIGIN" );
36962          }
36963       }
36964    }
36965 
36966 /* If an error has occurred, annul the Frame. */
36967    if( !astOK ) ret = astAnnul( ret );
36968 
36969 /* Return the Frame. */
36970    return ret;
36971 }
36972 
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)36973 static AstMapping *WcsSpectral( AstFitsChan *this, FitsStore *store, char s,
36974                                 AstFrame **frm, AstFrame *iwcfrm, double reflon, double reflat,
36975                                 AstSkyFrame *reffrm, const char *method,
36976                                 const char *class, int *status ){
36977 
36978 /*
36979 *  Name:
36980 *     WcsSpectral
36981 
36982 *  Purpose:
36983 *     Create a Mapping from intermediate world coords to spectral coords
36984 *     as described in a FITS header.
36985 
36986 *  Type:
36987 *     Private function.
36988 
36989 *  Synopsis:
36990 
36991 *     AstMapping *WcsSpectral( AstFitsChan *this, FitsStore *store, char s,
36992 *                              AstFrame **frm, AstFrame *iwcfrm, double reflon,
36993 *                              double reflat, AstSkyFrame *reffrm,
36994 *                              const char *method, const char *class, int *status )
36995 
36996 *  Class Membership:
36997 *     FitsChan
36998 
36999 *  Description:
37000 *     This function interprets the contents of the supplied FitsStore
37001 *     structure, looking for world coordinate axes which describe positions
37002 *     in a spectrum. If such an axis is found, a Mapping is returned which
37003 *     transforms the corresponding intermediate world coordinates to
37004 *     spectral world coordinates (this mapping leaves any other axes
37005 *     unchanged). It also, modifies the supplied Frame to describe the
37006 *     axis (again, other axes are left unchanged). If no spectral axis
37007 *     is found, a UnitMap is returned, and the supplied Frame is left
37008 *     unchanged.
37009 
37010 *  Parameters:
37011 *     this
37012 *        The FitsChan.
37013 *     store
37014 *        A structure containing information about the requested axis
37015 *        descriptions derived from a FITS header.
37016 *     s
37017 *        A character identifying the co-ordinate version to use. A space
37018 *        means use primary axis descriptions. Otherwise, it must be an
37019 *        upper-case alphabetical characters ('A' to 'Z').
37020 *     frm
37021 *        The address of a location at which to store a pointer to the
37022 *        Frame describing the world coordinate axes.
37023 *     iwcfrm
37024 *        A pointer to the Frame describing the intermediate world coordinate
37025 *        axes. The properties of this Frame may be changed on exit.
37026 *     reflon
37027 *        The reference celestial longitude, in the frame given by reffrm.
37028 *     reflat
37029 *        The reference celestial latitude, in the frame given by reffrm.
37030 *     reffrm
37031 *        The SkyFrame defining reflon and reflat.
37032 *     method
37033 *        A pointer to a string holding the name of the calling method.
37034 *        This is used only in the construction of error messages.
37035 *     class
37036 *        A pointer to a string holding the class of the object being
37037 *        read. This is used only in the construction of error messages.
37038 *     status
37039 *        Pointer to the inherited status variable.
37040 
37041 *  Returned Value:
37042 *     A pointer to the Mapping.
37043 */
37044 
37045 /* Local Variables: */
37046    AstFrame *ofrm;        /* Pointer to a Frame */
37047    AstMapping *map1;      /* Pointer to Mapping */
37048    AstMapping *map2;      /* Pointer to Mapping */
37049    AstMapping *ret;       /* Pointer to the returned Mapping */
37050    AstSpecFrame *specfrm; /* Pointer to a SpecFrame */
37051    char algcode[ 5 ];     /* Displayed spectral type string */
37052    char stype[ 5 ];       /* Displayed spectral type string */
37053    const char *cname;     /* Pointer to CNAME value */
37054    const char *ctype;     /* Pointer to CTYPE value */
37055    const char *cunit;     /* Pointer to CUNIT value */
37056    const char *defunit;   /* Default unit string */
37057    const char *specsys;   /* Pointer to SPECSYS value */
37058    const char *ssyssrc;   /* Pointer to SSYSSRC value */
37059    double geolat;         /* Observer's geodetic latitude */
37060    double geolon;         /* Observer's geodetic longitude */
37061    double h;              /* Observer's geodetic height */
37062    double mjd;            /* Modified Julian Date */
37063    double obscentre;      /* Spectral value at observation centre */
37064    double obsgeo[ 3 ];    /* Observer's Cartesian position */
37065    double restfrq;        /* RESTFRQ keyword value */
37066    double vsource;        /* Source velocity */
37067    int *axes;             /* Pointer to axis permutation array */
37068    int i;                 /* Axis index */
37069    int j;                 /* Loop count */
37070    int k;                 /* Loop count */
37071    int kk;                /* Loop count */
37072    int naxes;             /* No. of axes in Frame */
37073 
37074 /* Initialise the pointer to the returned Mapping. */
37075    ret = NULL;
37076 
37077 /* Check the global status. */
37078    if ( !astOK ) return ret;
37079 
37080 /* Get the number of physical axes. */
37081    naxes = astGetNaxes( *frm );
37082 
37083 /* An array to hold a list of axis selections. */
37084    axes = astMalloc( naxes*sizeof( int ) );
37085 
37086 /* Loop round checking each axis. */
37087    defunit = NULL;
37088    map1 = NULL;
37089    for( i = 0; i < naxes && astOK; i++ ) {
37090 
37091 /* Get the CTYPE value. Pass on to the next axis if no CTYPE is available. */
37092       ctype = GetItemC( &(store->ctype), i, 0, s, NULL, method, class, status );
37093       if( ctype ) {
37094 
37095 /* See if this CTYPE describes a spectral axis, and if so, extract the
37096    system code, the algorithm code and get the default units. */
37097          defunit = IsSpectral( ctype, stype, algcode, status );
37098 
37099 /* Skip to the next axis if the system type was not a spectral system
37100    type. */
37101          if( defunit ) {
37102 
37103 /* Create a SpecFrame or DSBSpecFrame with this system (the FITS type codes
37104    are also legal SpecFrame System values). We use astSetC rather than
37105    astSetSystem because astSetC translates string values into the
37106    corresponding integer system identifiers. */
37107             if( GetItem( &(store->imagfreq), 0, 0, s, NULL, method,
37108                          class, status ) == AST__BAD ) {
37109                specfrm = astSpecFrame( "", status );
37110             } else {
37111                specfrm = (AstSpecFrame *) astDSBSpecFrame( "", status );
37112             }
37113             astSetC( specfrm, "System", stype );
37114 
37115 /* Set the reference position (attributes RefRA and RefDec), if known. */
37116             if( reffrm ) astSetRefPos( specfrm, reffrm, reflon, reflat );
37117 
37118 /* Set the SpecFrame units. Use the value of the CUNIT FITS keyword for this
37119    axis if available, otherwise use the default units for the system, noted
37120    above. */
37121             cunit = GetItemC( &(store->cunit), i, 0, s, NULL, method, class, status );
37122             if( !cunit ) cunit = defunit;
37123             astSetUnit( specfrm, 0, cunit );
37124 
37125 /* Set the axis unit in the IWC Frame. */
37126             astSetUnit( iwcfrm, i, cunit );
37127 
37128 /* Get a value for the Epoch attribute (the date of observation). */
37129             mjd = ChooseEpoch( this, store, s, method, class, status );
37130             if( mjd != AST__BAD ) astSetEpoch( specfrm, mjd );
37131 
37132 /* Set the rest frequency. Use the RESTFRQ keyword (assumed to be in Hz),
37133    or (if RESTFRQ is not available), RESTWAV (assumes to be in m). */
37134             restfrq = GetItem( &(store->restfrq), 0, 0, s, NULL, method, class, status );
37135             if( restfrq == AST__BAD ) {
37136                restfrq = GetItem( &(store->restwav), 0, 0, s, NULL, method, class, status );
37137                if( restfrq != AST__BAD ) restfrq = AST__C/restfrq;
37138             }
37139             astSetRestFreq( specfrm, restfrq );
37140 
37141 /* Observer's position (from primary axis descriptions). Get the OBSGEO-X/Y/Z
37142    keywords, convert to geodetic longitude and latitude and store as the
37143    SpecFrame's ObsLat, ObsLon and ObsAlt attributes. */
37144             obsgeo[ 0 ] = GetItem( &(store->obsgeox), 0, 0, ' ', NULL, method, class, status );
37145             obsgeo[ 1 ] = GetItem( &(store->obsgeoy), 0, 0, ' ', NULL, method, class, status );
37146             obsgeo[ 2 ] = GetItem( &(store->obsgeoz), 0, 0, ' ', NULL, method, class, status );
37147             if( obsgeo[ 0 ] != AST__BAD &&
37148                 obsgeo[ 1 ] != AST__BAD &&
37149                 obsgeo[ 2 ] != AST__BAD ) {
37150                eraGc2gd( 1, obsgeo, &geolon, &geolat, &h );
37151                astSetObsLat( specfrm, geolat );
37152                astSetObsLon( specfrm, geolon );
37153                astSetObsAlt( specfrm, h );
37154             }
37155 
37156 /* Source velocity rest frame */
37157             ssyssrc = GetItemC( &(store->ssyssrc), 0, 0, s, NULL, method, class, status );
37158             if( ssyssrc ) astSetC( specfrm, "SourceVRF", ssyssrc );
37159 
37160 /* Source velocity. Use the ZSOURCE keyword and convert from redshift to
37161    velocity. */
37162             vsource = GetItem( &(store->zsource), 0, 0, s, NULL, method, class, status );
37163             if( vsource != AST__BAD ) {
37164                vsource += 1.0;
37165                vsource *= vsource;
37166                vsource = AST__C*( vsource - 1.0  )/( vsource + 1.0 );
37167                astSetSourceVel( specfrm, vsource );
37168             }
37169 
37170 /* Reference frame. If the SPECSYS keyword is set, use it (the FITS codes
37171    are also legal SpecFrame StdOfRest values). We use astSetC rather than
37172    astSetSystem because astSetC translates string values into the
37173    corresponding integer system identifiers. */
37174             specsys = GetItemC( &(store->specsys), 0, 0, s, NULL, method, class, status );
37175             if( specsys ) astSetC( specfrm, "StdOfRest", specsys );
37176 
37177 /* Axis label. If the CNAME keyword is set, use it as the axis label. */
37178             cname = GetItemC( &(store->cname), i, 0, s, NULL, method, class, status );
37179             if( cname ) astSetLabel( specfrm, 0, cname );
37180 
37181 /* If the header contains an AXREF value for the spectral axis, use it as the
37182    observation centre in preferences to the CRVAL value. AXREF keywords are
37183    created by the astWrite method for axes described by -TAB algorithm that
37184    have no inverse transformation. */
37185             obscentre = GetItem( &(store->axref), i, 0, s, NULL, method,
37186                                   class, status );
37187             if( obscentre == AST__BAD ) {
37188                obscentre = GetItem( &(store->crval), i, 0, s, NULL, method,
37189                                     class, status );
37190             }
37191 
37192 /* Now do the extra stuff needed if we are creating a dual sideband
37193    SpecFrame. */
37194             if( astIsADSBSpecFrame( specfrm ) ) {
37195                DSBSetUp( this, store, (AstDSBSpecFrame *) specfrm, s,
37196                          obscentre, method, class, status );
37197             }
37198 
37199 /* Now branch for each type of algorithm code. Each case returns a 1D
37200    Mapping which converts IWC value into the specified Spectral system. */
37201 
37202 /* Linear */
37203             if( strlen( algcode ) == 0 ) {
37204                map1 = LinearWcs( store, i, s, method, class, status );
37205 
37206 /* Log-Linear */
37207             } else if( !strcmp( "-LOG", algcode ) ) {
37208                map1 = LogWcs( store, i, s, method, class, status );
37209 
37210 /* Non-Linear */
37211             } else if( algcode[ 0 ] == '-' && algcode[ 2 ] == '2' ) {
37212                map1 = NonLinSpecWcs( this, algcode, store, i, s, specfrm, method, class, status );
37213 
37214 /* Grism */
37215             } else if( !strcmp( "-GRI", algcode ) ||
37216                        !strcmp( "-GRA", algcode ) ) {
37217                map1 = GrismSpecWcs( algcode, store, i, s, specfrm, method, class, status );
37218             } else {
37219                map1 = NULL;
37220             }
37221             if( map1 == NULL && astOK ) {
37222                specfrm = astAnnul( specfrm );
37223                astError( AST__BDFTS, "%s(%s): Cannot implement spectral "
37224                          "algorithm code '%s' specified in FITS keyword '%s'.", status,
37225                          method, class, ctype + 4, FormatKey( "CTYPE", i + 1, -1, s, status ) );
37226                astError( AST__BDFTS, "%s(%s): Unknown algorithm code or "
37227                          "unusable parameter values.", status, method, class );
37228                break;
37229             }
37230 
37231 /* Create a Frame by picking all the other (non-spectral) axes from the
37232    supplied Frame. */
37233             j = 0;
37234             for( k = 0; k < naxes; k++ ) {
37235                if( k != i ) axes[ j++ ] = k;
37236             }
37237 
37238 /* If there were no other axes, replace the supplied Frame with the
37239    specframe. */
37240             if( j == 0 ) {
37241                (void) astAnnul( *frm );
37242                *frm = (AstFrame *) specfrm;
37243 
37244 /* Otherwise pick the other axes from the supplied Frame */
37245             } else {
37246                ofrm = astPickAxes( *frm, j, axes, NULL );
37247 
37248 /* Replace the supplied Frame with a CmpFrame made up of this Frame and
37249    the SpecFrame. */
37250                (void) astAnnul( *frm );
37251                *frm = (AstFrame *) astCmpFrame( ofrm, specfrm, "", status );
37252                ofrm = astAnnul( ofrm );
37253                specfrm = astAnnul( specfrm );
37254             }
37255 
37256 /* Permute the axis order to put the spectral axis back in its original
37257    position. */
37258             j = 0;
37259             for( kk = 0; kk < naxes; kk++ ) {
37260                if( kk == i ) {
37261                   axes[ kk ] = naxes - 1;
37262                } else {
37263                   axes[ kk ] = j++;
37264                }
37265             }
37266             astPermAxes( *frm, axes );
37267          }
37268       }
37269 
37270 /* If this axis is not a spectral axis, create a UnitMap (the Frame is left
37271    unchanged). */
37272       if( !map1 && astOK ) map1 = (AstMapping *) astUnitMap( 1, "", status );
37273 
37274 /* Add the Mapping for this axis in parallel with the Mappings for
37275    previous axes. */
37276       if( ret ) {
37277          map2 = (AstMapping *) astCmpMap( ret, map1, 0, "", status );
37278          ret = astAnnul( ret );
37279          map1 = astAnnul( map1 );
37280          ret = map2;
37281       } else {
37282          ret = map1;
37283          map1 = NULL;
37284       }
37285    }
37286 
37287 /* Free the axes array. */
37288    axes= astFree( axes );
37289 
37290 /* Return the result. */
37291    return ret;
37292 }
37293 
WcsToStore(AstFitsChan * this,AstFitsChan * trans,FitsStore * store,const char * method,const char * class,int * status)37294 static void WcsToStore( AstFitsChan *this, AstFitsChan *trans,
37295                         FitsStore *store, const char *method,
37296                         const char *class, int *status ){
37297 
37298 /*
37299 *  Name:
37300 *     WcsToStore
37301 
37302 *  Purpose:
37303 *     Extract WCS information from the supplied FitsChan using a FITSWCS
37304 *     encoding, and store it in the supplied FitsStore.
37305 
37306 *  Type:
37307 *     Private function.
37308 
37309 *  Synopsis:
37310 *     #include "fitschan.h"
37311 
37312 *     void WcsToStore( AstFitsChan *this, AstFitsChan *trans,
37313 *                      FitsStore *store, const char *method,
37314 *                      const char *class, int *status )
37315 
37316 *  Class Membership:
37317 *     FitsChan member function.
37318 
37319 *  Description:
37320 *     A FitsStore is a structure containing a generalised represention of
37321 *     a FITS WCS FrameSet. Functions exist to convert a FitsStore to and
37322 *     from a set of FITS header cards (using a specified encoding), or
37323 *     an AST FrameSet. In other words, a FitsStore is an encoding-
37324 *     independant intermediary staging post between a FITS header and
37325 *     an AST FrameSet.
37326 *
37327 *     This function extracts FITSWCS keywords from the supplied FitsChan(s),
37328 *     and stores the corresponding WCS information in the supplied FitsStore.
37329 *     Keywords will be searched for first in "trans", and then, if they
37330 *     are not found in "trans", they will be searched for in "this".
37331 
37332 *  Parameters:
37333 *     this
37334 *        Pointer to the FitsChan containing the cards read from the
37335 *        original FITS header. This may include non-standard keywords.
37336 *     trans
37337 *        Pointer to a FitsChan containing cards representing standard
37338 *        translations of any non-standard keywords in "this". A NULL
37339 *        pointer indicates that "this" contains no non-standard keywords.
37340 *     store
37341 *        Pointer to the FitsStore structure.
37342 *     method
37343 *        Pointer to a string holding the name of the calling method.
37344 *        This is only for use in constructing error messages.
37345 *     class
37346 *        Pointer to a string holding the name of the supplied object class.
37347 *        This is only for use in constructing error messages.
37348 *     status
37349 *        Pointer to the inherited status variable.
37350 */
37351 
37352 /* Check the global error status. */
37353    if ( !astOK ) return;
37354 
37355 /* Read all usable cards out of the main FitsChan, into the FitsStore. */
37356    WcsFcRead( this, trans, store, method, class, status );
37357 
37358 /* If a FitsChan containing standard translations was supplied, read all
37359    cards out of it, into the FitsStore, potentially over-writing the
37360    non-standard values stored in the previous call to WcsFcRead. */
37361    if( trans ) WcsFcRead( trans, NULL, store, method, class, status );
37362 }
37363 
WorldAxes(AstFitsChan * this,AstMapping * cmap,double * dim,int * perm,int * status)37364 static int WorldAxes( AstFitsChan *this, AstMapping *cmap, double *dim, int *perm,
37365                       int *status ){
37366 
37367 /*
37368 *  Name:
37369 *     WorldAxes
37370 
37371 *  Purpose:
37372 *     Associate final world axes with pixel axes.
37373 
37374 *  Type:
37375 *     Private function.
37376 
37377 *  Synopsis:
37378 *     #include "fitschan.h"
37379 
37380 *     int WorldAxes( AstFitsChan *this, AstMapping *cmap, double *dim, int *perm,
37381 *                    int *status )
37382 
37383 *  Class Membership:
37384 *     FitsChan
37385 
37386 *  Description:
37387 *     This function finds the association between the axes of the final
37388 *     world coordinate system, and those of the pixel coordinate
37389 *     system. This may not simply be a 1-to-1 association because the
37390 *     Mapping may include a PermMap. Each output axis is associated with
37391 *     the input axis which is most nearly aligned with it.
37392 
37393 *  Parameters:
37394 *     this
37395 *        Pointer to the FitsChan.
37396 *     cmap
37397 *        Pointer to the Mapping from pixel coordinates to final world
37398 *        coordinates.
37399 *     dim
37400 *        Pointer to an array with one element for each input of "map",
37401 *        supplied holding the no. of pixels in the data cube along the axis, or
37402 *        AST__BAD If unknown.
37403 *     perm
37404 *        Pointer to an array with one element for each output of "map".
37405 *        On exit, each element of this array holds the zero-based index of the
37406 *        "corresponding" (i.e. most nearly parallel) pixel axis.
37407 *     status
37408 *        Pointer to the inherited status variable.
37409 
37410 *  Returned Value:
37411 *     Non-zero for success - zero for failure.
37412 */
37413 
37414 /* Local Variables: */
37415    AstMapping *smap;
37416    AstMapping *map;
37417    AstPointSet *pset1;
37418    AstPointSet *pset2;
37419    double **ptr2;
37420    double **ptr1;
37421    double *dw;
37422    double *g0;
37423    double *nwt;
37424    double *ntn;
37425    double *tn;
37426    double *wt;
37427    double *w0;
37428    double dg;
37429    double s;
37430    double sj;
37431    double tnmin;
37432    double wtmax;
37433    int *outs;
37434    int i2;
37435    int i;
37436    int imin;
37437    int j2;
37438    int j;
37439    int jmin;
37440    int nin;
37441    int nout;
37442    int nouts;
37443    int nused;
37444    int ret;
37445    int retain;
37446    int used;
37447 
37448 /* Initialise returned value */
37449    ret = 0;
37450 
37451 /* Other initialisation to avoid compiler warnings. */
37452    retain = 0;
37453 
37454 /* Check the status */
37455    if( !astOK ) return ret;
37456 
37457 /* Simplfy the Mapping. */
37458    map = astSimplify( cmap );
37459 
37460 /* Get the number of inputs and outputs for the Mapping. */
37461    nin = astGetNin( map );
37462    nout = astGetNout( map );
37463 
37464 /* Initialise "perm". */
37465    for( i = 0; i < nout; i++ ) perm[ i ] = i;
37466 
37467 /* First deal with Mappings that are defined in both directions. */
37468    if( astGetTranForward( map ) && astGetTranInverse( map ) ) {
37469 
37470 /* Use FindBasisVectors to find an input position which coresponds to a
37471    good output position. Store it in a dynamic array pointed to by "g0". */
37472       pset1 = astPointSet( nin+1, nin, "", status );
37473       pset2 = astPointSet( nin+1, nout, "", status );
37474       if( FindBasisVectors( map, nin, nout, dim, pset1, pset2, status ) ) {
37475          g0 = astMalloc( sizeof(double)*nin );
37476          ptr1 = astGetPoints( pset1 );
37477          if( astOK ) {
37478             for( j = 0; j < nin; j++ ) g0[ j ] = ptr1[ j ][ 0 ];
37479          }
37480          pset1 = astAnnul( pset1 );
37481          pset2 = astAnnul( pset2 );
37482 
37483 /* If no basis vectors found, return. */
37484       } else {
37485          pset1 = astAnnul( pset1 );
37486          pset2 = astAnnul( pset2 );
37487          return ret;
37488       }
37489 
37490 /* Create Pointset to hold two input (pixel) points. */
37491       pset1 = astPointSet( 2, nin, "", status );
37492       ptr1 = astGetPoints( pset1 );
37493 
37494 /* Create a Pointset to hold the same number of output (world) points. */
37495       pset2 = astPointSet( 2, nout, "", status );
37496       ptr2 = astGetPoints( pset2 );
37497 
37498 /* Allocate memory to use as work space */
37499       w0 = astMalloc( sizeof(double)*nout );
37500       dw = astMalloc( sizeof(double)*nout );
37501       tn = astMalloc( sizeof(double)*nout*nin );
37502       wt = astMalloc( sizeof(double)*nout*nin );
37503 
37504 /* Check that the pointers can be used. */
37505       if( astOK ) {
37506 
37507 /* Transform the grid position found above, plus a position 1 pixel away
37508    along all pixel axes, into world coords. Also set up "dw" to hold
37509    "a small increment" along each world axis. */
37510          for( j = 0; j < nin; j++ ) {
37511             ptr1[ j ] [ 0 ] = g0[ j ];
37512             ptr1[ j ] [ 1 ] = g0[ j ] + 1.0;
37513          }
37514          (void) astTransform( map, pset1, 1, pset2 );
37515          for( i = 0; i < nout; i++ ) {
37516             w0[ i ] = ptr2[ i ] [ 0 ];
37517             if( w0[ i ] != AST__BAD && ptr2[ i ] [ 1 ] != AST__BAD ) {
37518                dw[ i ] = fabs( 0.1*( ptr2[ i ] [ 1 ] - w0[ i ] ) );
37519                if( dw[ i ] <= fabs( 0.001*w0[ i ] ) ) {
37520                   if( w0[ i ] != 0.0 ) {
37521                      dw[ i ] = fabs( 0.001*w0[ i ] );
37522                   } else {
37523                      dw[ i ] = 1.0;
37524                   }
37525                }
37526             } else {
37527                dw[ i ] = AST__BAD;
37528             }
37529          }
37530 
37531 /* Any PermMap in the mapping may result in the the "inverse transformation"
37532    not being a true inverse of the forward transformation (for instance,
37533    constant values fed in for degenerate axis would have this effect). To
37534    ensure that "g0" and "w0" are corresponding positions, transform the
37535    "w0" position back into grid coords and use the resulting grid position
37536    as "g0". */
37537          (void) astTransform( map, pset2, 0, pset1 );
37538          for( j = 0; j < nin; j++ ) {
37539             g0[ j ] = ptr1[ j ] [ 0 ];
37540          }
37541 
37542 /* In the next loop we find the tan of the angle between each WCS axis and
37543    each of the pixel axes. Loop round each WCS axis. */
37544          for( i = 0; i < nout; i++ ) {
37545 
37546 /* Initialise the tan values for this WCS axis to AST__BAD. */
37547             ntn = tn + i*nin;
37548             nwt = wt + i*nin;
37549             for( j = 0; j < nin; j++ ) ntn[ j ] = AST__BAD;
37550 
37551 /* As a side issue, initialise the pixel axis assigned to each WCS axis
37552    to -1, to indicate that no grid axis has yet been associated with this
37553    WCS axis. */
37554             perm[ i ] = -1;
37555 
37556 /* Skip over this axis if the increment is bad. */
37557             if( dw[ i ] != AST__BAD ) {
37558 
37559 /* Store a WCS position which is offset from the "w0" position by a small
37560    amount along the current WCS axis. The first position in "ptr2" is
37561    currently "w0". */
37562                ptr2[ i ][ 0 ] += dw[ i ];
37563 
37564 /* Transform this position into grid coords. */
37565                (void) astTransform( map, pset2, 0, pset1 );
37566 
37567 /* Re-instate the original "w0" values within "ptr2", ready for the next
37568    WCS axis. */
37569                ptr2[ i ][ 0 ] = w0[ i ];
37570 
37571 /* Consider each pixel axis in turn as a candidate for being assigned to
37572    the current WCS axis. */
37573                for( j = 0; j < nin; j++ ) {
37574 
37575 /* Find the tan of the angle between the current ("i"th) WCS axis and the
37576    current ("j"th) pixel axis. This gets stored in tn[j+nin*i]. A
37577    corresponding weight for each angle is stored in nwt[j+nin*i]. This
37578    is the length of the projection of the vector onto the "j"th pixel
37579    axis.  */
37580                   s = 0.0;
37581                   sj = 0.0;
37582                   for( j2 = 0; j2 < nin; j2++ ) {
37583                      if( ptr1[ j2 ][ 0 ] != AST__BAD ) {
37584                          dg = ptr1[ j2 ][ 0 ] - g0[ j2 ];
37585                          if( j2 != j ) {
37586                             s += dg*dg;
37587                          } else {
37588                             sj = fabs( dg );
37589                          }
37590                      } else {
37591                          s = AST__BAD;
37592                          break;
37593                      }
37594                   }
37595                   if( s != AST__BAD && sj != 0.0 ) {
37596                      ntn[ j ] = sqrt( s )/sj;
37597                      nwt[ j ] = sj;
37598                   }
37599                }
37600             }
37601          }
37602 
37603 /* Loop until every grid axes has been assigned to a WCS axis. */
37604          while( 1 ) {
37605 
37606 /* Pass through the array of tan values, finding the smallest. Note the
37607    pixel and WCS axis for which the smallest tan value occurs. If the tan
37608    values are equal, favour the one with highest weight. */
37609             ntn = tn;
37610             nwt = wt;
37611             tnmin = AST__BAD;
37612             wtmax = AST__BAD;
37613             imin = 0;
37614             jmin = 0;
37615             for( i = 0; i < nout; i++ ) {
37616                for( j = 0; j < nin; j++ ) {
37617                   if( *ntn != AST__BAD ) {
37618                      if( tnmin == AST__BAD || *ntn < tnmin ) {
37619                         tnmin = *ntn;
37620                         wtmax = *nwt;
37621                         imin = i;
37622                         jmin = j;
37623                      } else if( EQUAL( *ntn, tnmin ) && *nwt > wtmax ) {
37624                         wtmax = *nwt;
37625                         imin = i;
37626                         jmin = j;
37627                      }
37628                   }
37629                   ntn++;
37630                   nwt++;
37631                }
37632             }
37633 
37634 /* Check we found a usable minimum tan value */
37635             if( tnmin != AST__BAD ) {
37636 
37637 /* Assign the pixel axis to the WCS axis. */
37638                perm[ imin ] = jmin;
37639 
37640 /* Set bad all the tan values for this pixel and WCS axis pair. This ensures
37641    that the pixel axis will not be assigned to another WCS axis, and that
37642    the WCS will not have another pixel axis assigned to it. */
37643                ntn = tn;
37644                for( i = 0; i < nout; i++ ) {
37645                   for( j = 0; j < nin; j++ ) {
37646                      if( i == imin || j == jmin ) *ntn = AST__BAD;
37647                      ntn++;
37648                   }
37649                }
37650 
37651 /* Leave the loop if no more good tan values were found. */
37652             } else {
37653                break;
37654             }
37655          }
37656 
37657 /* The above process may have left some WCS axes with out any assigned
37658    pixel axis. We assign the remaining pixel arbitrarily to such axes,
37659    starting with the first remaining pixel axis. Find the lowest unused
37660    pixel axis. */
37661          for( j = 0; j < nin; j++ ) {
37662             used = 0;
37663             for( i = 0; i < nout; i++ ) {
37664                if( perm[ i ] == j ) {
37665                   used = 1;
37666                   break;
37667                }
37668             }
37669             if( !used ) break;
37670          }
37671 
37672 /* Now check each WCS axis looking for outputs which were not assigned a
37673    pixel axis in the above process. */
37674          for( i = 0; i < nout; i++ ) {
37675             if( perm[ i ] == -1 ) {
37676 
37677 /* Use the next unused axis value. */
37678                perm[ i ] = j++;
37679 
37680 /* Find the next unused axis value. */
37681                for( ; j < nin; j++ ) {
37682                   used = 0;
37683                   for( i2 = 0; i2 < nout; i2++ ) {
37684                      if( perm[ i2 ] == j ) {
37685                         used = 1;
37686                         break;
37687                      }
37688                   }
37689                   if( !used ) break;
37690                }
37691             }
37692          }
37693 
37694 /* Indicate success. */
37695          if( astOK ) ret = 1;
37696       }
37697 
37698 /* Free resources. */
37699       pset1 = astAnnul( pset1 );
37700       pset2 = astAnnul( pset2 );
37701       g0 = astFree( g0 );
37702       w0 = astFree( w0 );
37703       tn = astFree( tn );
37704       wt = astFree( wt );
37705       dw = astFree( dw );
37706 
37707 /* Now, if we can use the TAB algorithm, deal with Mappings that are defined only in the forward direction. */
37708    } else if( astGetTranForward( map ) && astGetTabOK( this ) > 0 ) {
37709 
37710 /* Assume success. */
37711       ret = 1;
37712 
37713 /* Initialise to indicate no outputs have yet been assigned. */
37714       for( i = 0; i < nout; i++ ) perm[ i ] = -1;
37715 
37716 /* Find the output associated with each input. */
37717       for( j = 0; j < nin; j++ ) {
37718 
37719 /* Attempt to split off the current input. */
37720          outs = astMapSplit( map, 1, &j, &smap );
37721 
37722 /* If successfull, store the index of the corresponding input for each
37723    output. */
37724          if( outs && smap ) {
37725             nouts = astGetNout( smap );
37726             for( i = 0; i < nouts; i++ ) {
37727                if( perm[ outs[ i ] ] == -1 ) {
37728                   perm[ outs[ i ] ] = j;
37729                } else {
37730                   ret = 0;
37731                }
37732             }
37733          }
37734 
37735 /* Free resources. */
37736          outs = astFree( outs );
37737          if( smap ) smap = astAnnul( smap );
37738       }
37739 
37740 /* Check all outputs were assigned . */
37741       for( i = 0; i < nout && ret; i++ ) {
37742          if( perm[ i ] == -1 ) ret = 0;
37743       }
37744 
37745 /* If succesful, attempt to remove any duplicates from the "perm" array
37746    (i.e. inputs that supply more than one output). First get a list of
37747    the inputs that are currently unused (i.e. do not appear in "perm"). */
37748       if( ret ) {
37749 
37750 /* Check each input. */
37751          for( j = 0; j < nin; j++ ) {
37752 
37753 /* See how many outputs are fed by this input. */
37754             nused = 0;
37755             for( i = 0; i < nout; i++ ) {
37756                if( perm[ i ] ==  j ) nused++;
37757             }
37758 
37759 /* If it used more than once, we need to remove all but one of the
37760    occurrences. */
37761             if( nused > 1 ) {
37762 
37763 /* Choose the occurrence to retain. If the output with the same index as
37764    the input is one of them, use it. Otherwise, use the first occurrence. */
37765                if( perm[ j ] == j ) {
37766                   retain = j;
37767                } else {
37768                   for( i = 0; i < nout; i++ ) {
37769                      if( perm[ i ] ==  j ) {
37770                         retain = i;
37771                         break;
37772                      }
37773                   }
37774                }
37775 
37776 /* Loop round all occurrences of this input again. */
37777                for( i = 0; i < nout && ret; i++ ) {
37778                   if( perm[ i ] ==  j ) {
37779 
37780 /* Replace all occurrences, except for the one being retained. */
37781                      if( i != retain ) {
37782 
37783 /* Replace it with the next unused input. */
37784                         for( j2 = 0; j2 < nin; j2++ ) {
37785                            used = 0;
37786                            for( i2 = 0; i2 < nout; i2++ ) {
37787                               if( perm[ i2 ] == j2 ) {
37788                                  used = 1;
37789                                  break;
37790                               }
37791                            }
37792                            if( ! used ) {
37793                               perm[ i ] = j2;
37794                               break;
37795                            }
37796                         }
37797 
37798 /* If there were no unused inputs, we cannot do it. */
37799                         if( used ) ret = 0;
37800                      }
37801                   }
37802                }
37803             }
37804          }
37805       }
37806    }
37807 
37808 /* Free resources. */
37809    map = astAnnul( map );
37810 
37811 /* Return the result. */
37812    return ret;
37813 }
37814 
Write(AstChannel * this_channel,AstObject * object,int * status)37815 static int Write( AstChannel *this_channel, AstObject *object, int *status ) {
37816 /*
37817 *  Name:
37818 *     Write
37819 
37820 *  Purpose:
37821 *     Write an Object to a FitsChan.
37822 
37823 *  Type:
37824 *     Private function.
37825 
37826 *  Synopsis:
37827 *     #include "fitschan.h"
37828 *     int Write( AstChannel *this, AstObject *object, int *status )
37829 
37830 *  Class Membership:
37831 *     FitsChan member function (over-rides the astWrite method
37832 *     inherited from the Channel class).
37833 
37834 *  Description:
37835 *     This function writes an Object to a FitsChan.
37836 
37837 *  Parameters:
37838 *     this
37839 *        Pointer to the FitsChan.
37840 *     object
37841 *        Pointer to the Object which is to be written.
37842 *     status
37843 *        Pointer to the inherited status variable.
37844 
37845 *  Returned Value:
37846 *     The number of Objects written to the FitsChan by this invocation of
37847 *     astWrite.
37848 
37849 *  Notes:
37850 *     - A value of zero will be returned if this function is invoked
37851 *     with the AST error status set, or if it should fail for any
37852 *     reason.
37853 *     - The Base Frame in the FrameSet is used as the pixel Frame, and
37854 *     the Current Frame is used to create the primary axis descriptions.
37855 *     Attempts are made to create secondary axis descriptions for any
37856 *     other Frames in the FrameSet (up to a total of 26).
37857 */
37858 
37859 /* Local Variables: */
37860    astDECLARE_GLOBALS            /* Declare the thread specific global data */
37861    AstFitsChan *this;            /* Pointer to the FitsChan structure */
37862    FitsStore *store;             /* Intermediate storage for WCS information */
37863    char banner[ AST__FITSCHAN_FITSCARDLEN - FITSNAMLEN + 1 ]; /* Buffer for begin/end banner */
37864    const char *class;            /* Pointer to string holding object class */
37865    const char *method;           /* Pointer to string holding calling method */
37866    double *dim;                  /* Pointer to array of axis dimensions */
37867    int card0;                    /* Index of original current card */
37868    int comm;                     /* Value of Comm attribute */
37869    int encoding;                 /* FITS encoding scheme to use */
37870    int i;                        /* Axis index */
37871    int naxis;                    /* No. of pixel axes */
37872    int ret;                      /* Number of objects read */
37873 
37874 /* Initialise. */
37875    ret = 0;
37876 
37877 /* Check the global error status. */
37878    if ( !astOK ) return ret;
37879 
37880 /* Get a pointer to the structure holding thread-specific global data. */
37881    astGET_GLOBALS(this_channel);
37882 
37883 /* Obtain a pointer to the FitsChan structure. */
37884    this = (AstFitsChan *) this_channel;
37885 
37886 /* Ensure the source function has been called */
37887    ReadFromSource( this, status );
37888 
37889 /* Store the calling method, and object class. */
37890    method = "astWrite";
37891    class = astGetClass( this );
37892 
37893 /* The original current card is re-instated at the end if no object
37894    is written. Save its index. */
37895    card0 = astGetCard( this );
37896 
37897 /* Indicate that all cards added to the FitsCHan by this call should be
37898    marked as "new". */
37899    mark_new = 1;
37900 
37901 /* Get the encoding scheme used by the FitsChan. */
37902    encoding = astGetEncoding( this );
37903 
37904 /* First deal with cases where we are writing to a FitsChan in which AST
37905    objects are encoded using native AST-specific keywords... */
37906    if( encoding == NATIVE_ENCODING ){
37907 
37908 /* Increment the nesting level which keeps track of recursive
37909    invocations of this function. */
37910       write_nest++;
37911 
37912 /* Initialise the current indentation level for top-level objects. */
37913       if ( !write_nest ) current_indent = 0;
37914 
37915 /* Obtain the value of the Comm attribute. */
37916       comm = astGetComment( this );
37917 
37918 /* If this is the top-level invocation (i.e. we are about to write out
37919    a new top-level Object), then prefix it with a blank FITS line and
37920    an appropriate banner of FITS comments, unless comments have been
37921    suppressed. */
37922       if ( !write_nest && comm ) {
37923          astSetFitsCom( this, "        ", "", 0 );
37924          MakeBanner(
37925 "++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++",
37926                      "", "", banner, status );
37927          astSetFitsCom( this, "COMMENT", banner, 0 );
37928          if( astIsAFrameSet( object ) ) {
37929             MakeBanner( "WCS information in AST format", "", "", banner, status );
37930             astSetFitsCom( this, "COMMENT", banner, 0 );
37931             MakeBanner( "See http://www.starlink.ac.uk/ast/", "", "", banner, status );
37932             astSetFitsCom( this, "COMMENT", banner, 0 );
37933          }
37934          MakeBanner( HEADER_TEXT, astGetClass( object ), " object", banner, status );
37935          astSetFitsCom( this, "COMMENT", banner, 0 );
37936          MakeBanner(
37937 "................................................................",
37938                      "", "", banner, status );
37939          astSetFitsCom( this, "COMMENT", banner, 0 );
37940       }
37941 
37942 /* Invoke the parent astWrite method to write out the Object data. */
37943       (*parent_write)( this_channel, object, status );
37944 
37945 /* Append a banner of FITS comments to the object data, as above, if
37946    necessary. */
37947       if ( !write_nest && comm ) {
37948          MakeBanner(
37949 "................................................................",
37950                      "", "", banner, status );
37951          astSetFitsCom( this, "COMMENT", banner, 0 );
37952          MakeBanner( FOOTER_TEXT, astGetClass( object ), " object", banner, status );
37953          astSetFitsCom( this, "COMMENT", banner, 0 );
37954          MakeBanner(
37955 "----------------------------------------------------------------",
37956                      "", "", banner, status );
37957          astSetFitsCom( this, "COMMENT", banner, 0 );
37958       }
37959 
37960 /* Return the nesting level to its previous value. */
37961       write_nest--;
37962 
37963 /* Indicate that an object has been written. */
37964       ret = 1;
37965 
37966 /* Now deal with cases where we are writing to a FitsChan in which AST
37967    objects are encoded using any of the supported foreign encodings... */
37968    } else {
37969 
37970 /* Only proceed if the supplied object is a FrameSet. */
37971       if( astIsAFrameSet( object ) ){
37972 
37973 /* Note the number of pixel (i.e. Base Frame) axes, and allocate memory to
37974    hold the image dimensions. */
37975          naxis = astGetNin( (AstFrameSet *) object );
37976          dim = (double *) astMalloc( sizeof(double)*naxis );
37977          if( dim ){
37978 
37979 /* Note the image dimensions, if known. If not, store AST__BAD values. */
37980             for( i = 0; i < naxis; i++ ){
37981                if( !astGetFitsF( this, FormatKey( "NAXIS", i + 1, -1, ' ', status ),
37982                                  dim + i ) ) dim[ i ] = AST__BAD;
37983             }
37984 
37985 /* Extract the required information from the FrameSet into a standard
37986    intermediary structure called a FitsStore. The indices of any
37987    celestial axes are returned. */
37988             store = FsetToStore( this, (AstFrameSet *) object, naxis, dim,
37989                                  encoding, method, class, status );
37990 
37991 /* If the FrameSet cannot be described in terms of any of the supported
37992    FITS encodings, a null pointer will have been returned. */
37993             if( store ){
37994 
37995 /* Now put header cards describing the contents of the FitsStore into the
37996    supplied FitsChan, using the requested encoding. Zero or one is
37997    returned depending on whether the information could be encoded. */
37998                ret = FitsFromStore( this, store, encoding, dim,
37999                                     (AstFrameSet *) object, method, class, status );
38000 
38001 /* Release the resources used by the FitsStore. */
38002                store = FreeStore( store, status );
38003 
38004 /* If the Object was written to the FitsChan, set the current card to
38005    end-of-file. */
38006                if( ret ) astSetCard( this, INT_MAX );
38007             }
38008 
38009 /* Free workspace holding image dimensions */
38010             dim = (double *) astFree( (void *) dim );
38011          }
38012       }
38013    }
38014 
38015 /* If an error has occurred, return zero and remove any new cards added
38016    to the FitsCHan by this call. */
38017    if( !astOK ) ret = 0;
38018 
38019 /* Clear the new flag associated with cards which have been added to the
38020    FitsChan as a result of this function. If the object was not added
38021    succesfully to the FitsChan, remove any cards which were added before
38022    the error was discovered. */
38023    FixNew( this, NEW1, !ret, method, class, status );
38024    FixNew( this, NEW2, !ret, method, class, status );
38025 
38026 /* Indicate that all cards added to the FitsChan from now on should not be
38027    marked as "new". */
38028    mark_new = 0;
38029 
38030 /* If no object was written, re-instate the original current card. */
38031    if( !ret ) astSetCard( this, card0 );
38032 
38033 /* Return the answer. */
38034    return ret;
38035 }
38036 
WriteBegin(AstChannel * this_channel,const char * class,const char * comment,int * status)38037 static void WriteBegin( AstChannel *this_channel, const char *class,
38038                         const char *comment, int *status ) {
38039 /*
38040 *  Name:
38041 *     WriteBegin
38042 
38043 *  Purpose:
38044 *     Write a "Begin" data item to a data sink.
38045 
38046 *  Type:
38047 *     Private function.
38048 
38049 *  Synopsis:
38050 *     #include "fitschan.h"
38051 *     void WriteBegin( AstChannel *this, const char *class,
38052 *                      const char *comment )
38053 
38054 *  Class Membership:
38055 *     FitsChan member function (over-rides the protected astWriteBegin
38056 *     method inherited from the Channel class).
38057 
38058 *  Description:
38059 *     This function writes a "Begin" data item to the data sink
38060 *     associated with a FitsChan, so as to begin the output of a new
38061 *     Object definition.
38062 
38063 *  Parameters:
38064 *     this
38065 *        Pointer to the FitsChan.
38066 *     class
38067 *        Pointer to a constant null-terminated string containing the
38068 *        name of the class to which the Object belongs.
38069 *     comment
38070 *        Pointer to a constant null-terminated string containing a
38071 *        textual comment to be associated with the "Begin"
38072 *        item. Normally, this will describe the purpose of the Object.
38073 
38074 *  Notes:
38075 *     - The comment supplied may not actually be used, depending on
38076 *     the nature of the FitsChan supplied.
38077 */
38078 
38079 /* Local Variables: */
38080    astDECLARE_GLOBALS            /* Declare the thread specific global data */
38081    AstFitsChan *this;            /* Pointer to the FitsChan structure. */
38082    char buff[ AST__FITSCHAN_FITSCARDLEN - FITSNAMLEN + 1 ];
38083                                  /* Character buffer */
38084    char keyword[ FITSNAMLEN + 1 ]; /* Buffer for FITS keyword */
38085 
38086 /* Check the global error status. */
38087    if ( !astOK ) return;
38088 
38089 /* Get a pointer to the structure holding thread-specific global data. */
38090    astGET_GLOBALS(this_channel);
38091 
38092 /* Obtain a pointer to the FitsChan structure. */
38093    this = (AstFitsChan *) this_channel;
38094 
38095 /* Increment the indentation level for comments. */
38096    current_indent += INDENT_INC;
38097 
38098 /* If we are not beginning a top-level Object definition, and helpful
38099    information has not been suppressed, generate an indented comment
38100    to mark the "Begin" item and write it to the FitsChan as a comment
38101    card with a blank keyword. */
38102    if ( write_nest && ( astGetFull( this ) >= 0 ) ) {
38103       MakeIndentedComment( current_indent, '+', "Beginning of ", class, buff, status );
38104       astSetFitsCom( this, "        ", buff, 0 );
38105    }
38106 
38107 /* Create a unique FITS keyword for this "Begin" item, basing it on
38108    "BEGAST". */
38109    CreateKeyword( this, "BEGAST", keyword, status );
38110 
38111 /* Generate a pre-quoted version of the class name. */
38112    PreQuote( class, buff, status );
38113 
38114 /* Write the "Begin" item to the FitsChan as a keyword and string
38115    value. */
38116    astSetFitsS( this, keyword, buff,
38117                      astGetComment( this ) ? comment : NULL, 0 );
38118 
38119 /* Clear the count of items written. */
38120    items_written = 0;
38121 }
38122 
WriteDouble(AstChannel * this_channel,const char * name,int set,int helpful,double value,const char * comment,int * status)38123 static void WriteDouble( AstChannel *this_channel, const char *name,
38124                          int set, int helpful,
38125                          double value, const char *comment, int *status ) {
38126 /*
38127 *  Name:
38128 *     WriteDouble
38129 
38130 *  Purpose:
38131 *     Write a double value to a data sink.
38132 
38133 *  Type:
38134 *     Private function.
38135 
38136 *  Synopsis:
38137 *     #include "fitschan.h"
38138 *     void WriteDouble( AstChannel *this, const char *name,
38139 *                       int set, int helpful,
38140 *                       double value, const char *comment )
38141 
38142 *  Class Membership:
38143 *     FitsChan member function (over-rides the protected
38144 *     astWriteDouble method inherited from the Channel class).
38145 
38146 *  Description:
38147 *     This function writes a named double value, representing the
38148 *     value of a class instance variable, to the data sink associated
38149 *     with a FitsChan. It is intended for use by class "Dump"
38150 *     functions when writing out class information which will
38151 *     subsequently be re-read.
38152 
38153 *  Parameters:
38154 *     this
38155 *        Pointer to the FitsChan.
38156 *     name
38157 *        Pointer to a constant null-terminated string containing the
38158 *        name to be used to identify the value in the external
38159 *        representation. This will form the key for identifying it
38160 *        again when it is re-read. The name supplied should be unique
38161 *        within its class.
38162 *
38163 *        Mixed case may be used and will be preserved in the external
38164 *        representation (where possible) for cosmetic effect. However,
38165 *        case is not significant when re-reading values.
38166 *
38167 *        It is recommended that a maximum of 6 alphanumeric characters
38168 *        (starting with an alphabetic character) be used. This permits
38169 *        maximum flexibility in adapting to standard external data
38170 *        representations (e.g. FITS).
38171 *     set
38172 *        If this is zero, it indicates that the value being written is
38173 *        a default value (or can be re-generated from other values) so
38174 *        need not necessarily be written out. Such values will
38175 *        typically be included in the external representation with
38176 *        (e.g.) a comment character so that they are available to
38177 *        human readers but will be ignored when re-read. They may also
38178 *        be completely omitted in some circumstances.
38179 *
38180 *        If "set" is non-zero, the value will always be explicitly
38181 *        included in the external representation so that it can be
38182 *        re-read.
38183 *     helpful
38184 *        This flag provides a hint about whether a value whose "set"
38185 *        flag is zero (above) should actually appear at all in the
38186 *        external representaton.
38187 *
38188 *        If the external representation allows values to be "commented
38189 *        out" then, by default, values will be included in this form
38190 *        only if their "helpful" flag is non-zero. Otherwise, they
38191 *        will be omitted entirely. When possible, omitting the more
38192 *        obscure values associated with a class is recommended in
38193 *        order to improve readability.
38194 *
38195 *        This default behaviour may be further modified if the
38196 *        FitsChan's Full attribute is set - either to permit all
38197 *        values to be shown, or to suppress non-essential information
38198 *        entirely.
38199 *     value
38200 *        The value to be written.
38201 *     comment
38202 *        Pointer to a constant null-terminated string containing a
38203 *        textual comment to be associated with the value.
38204 *
38205 *        Note that this comment may not actually be used, depending on
38206 *        the nature of the FitsChan supplied and the setting of its
38207 *        Comm attribute.
38208 */
38209 
38210 /* Local Variables: */
38211    astDECLARE_GLOBALS            /* Declare the thread specific global data */
38212    AstFitsChan *this;            /* Pointer to the FitsChan structure. */
38213    char keyword[ FITSNAMLEN + 1 ]; /* Buffer for FITS keyword */
38214 
38215 /* Check the global error status. */
38216    if ( !astOK ) return;
38217 
38218 /* Get a pointer to the structure holding thread-specific global data. */
38219    astGET_GLOBALS(this_channel);
38220 
38221 /* Obtain a pointer to the FitsChan structure. */
38222    this = (AstFitsChan *) this_channel;
38223 
38224 /* Use the "set" and "helpful" flags, along with the FitsChan's
38225    attributes to decide whether this value should actually be
38226    written. */
38227    if ( Use( this, set, helpful, status ) ) {
38228 
38229 /* Create a unique FITS keyword from the name supplied. */
38230       CreateKeyword( this, name, keyword, status );
38231 
38232 /* Write the value to the FitsChan as a keyword and value */
38233       astSetFitsF( this, keyword, value,
38234                        astGetComment( this ) ? comment : NULL, 0 );
38235 
38236 /* If the value is not "set", replace the card just written by a COMMENT
38237    card containing the text of the card as the comment. */
38238       if( !set ) MakeIntoComment( this, "astWrite", astGetClass( this ), status );
38239 
38240 /* Increment the count of items written. */
38241       items_written++;
38242    }
38243 }
38244 
WriteEnd(AstChannel * this_channel,const char * class,int * status)38245 static void WriteEnd( AstChannel *this_channel, const char *class, int *status ) {
38246 /*
38247 *  Name:
38248 *     WriteEnd
38249 
38250 *  Purpose:
38251 *     Write an "End" data item to a data sink.
38252 
38253 *  Type:
38254 *     Private function.
38255 
38256 *  Synopsis:
38257 *     #include "fitschan.h"
38258 *     void WriteEnd( AstChannel *this, const char *class )
38259 
38260 *  Class Membership:
38261 *     FitsChan member function (over-rides the protected astWriteEnd
38262 *     method inherited from the Channel class).
38263 
38264 *  Description:
38265 *     This function writes an "End" data item to the data sink
38266 *     associated with a FitsChan. This item delimits the end of an
38267 *     Object definition.
38268 
38269 *  Parameters:
38270 *     this
38271 *        Pointer to the FitsChan.
38272 *     class
38273 *        Pointer to a constant null-terminated string containing the
38274 *        class name of the Object whose definition is being terminated
38275 *        by the "End" item.
38276 */
38277 
38278 /* Local Variables: */
38279    astDECLARE_GLOBALS            /* Declare the thread specific global data */
38280    AstFitsChan *this;            /* Pointer to the FitsChan structure. */
38281    char buff[ AST__FITSCHAN_FITSCARDLEN - FITSNAMLEN + 1 ];
38282                                  /* Character buffer */
38283    char keyword[ FITSNAMLEN + 1 ]; /* Buffer for FITS keyword */
38284 
38285 /* Check the global error status. */
38286    if ( !astOK ) return;
38287 
38288 /* Get a pointer to the structure holding thread-specific global data. */
38289    astGET_GLOBALS(this_channel);
38290 
38291 /* Obtain a pointer to the FitsChan structure. */
38292    this = (AstFitsChan *) this_channel;
38293 
38294 /* Create a unique FITS keyword for this "End" item, basing it on
38295    "ENDAST". */
38296    CreateKeyword( this, "ENDAST", keyword, status );
38297 
38298 /* Generate a pre-quoted version of the class name. */
38299    PreQuote( class, buff, status );
38300 
38301 /* Write the "End" item to the FitsChan as a keyword and string
38302    value. */
38303    astSetFitsS( this, keyword, buff,
38304                      astGetComment( this ) ? "End of object definition" : NULL,
38305                      0 );
38306 
38307 /* If we are not ending a top-level Object definition, and helpful
38308    information has not been suppressed, generate an indented comment
38309    to mark the "End" item and write it to the FitsChan as a comment
38310    card with a blank keyword. */
38311    if ( write_nest && ( astGetFull( this ) >= 0 ) ) {
38312       MakeIndentedComment( current_indent, '-', "End of ", class, buff, status );
38313       astSetFitsCom( this, "        ", buff, 0 );
38314    }
38315 
38316 /* Decrement the indentation level for comments. */
38317    current_indent -= INDENT_INC;
38318 }
38319 
WriteFits(AstFitsChan * this,int * status)38320 static void WriteFits( AstFitsChan *this, int *status ){
38321 
38322 /*
38323 *++
38324 *  Name:
38325 c     astWriteFits
38326 f     AST_WRITEFITS
38327 
38328 *  Purpose:
38329 *     Write out all cards in a FitsChan to the sink function.
38330 
38331 *  Type:
38332 *     Public virtual function.
38333 
38334 *  Synopsis:
38335 c     #include "fitschan.h"
38336 c     void astWriteFits( AstFitsChan *this )
38337 f     CALL AST_WRITEFITS( THIS, STATUS )
38338 
38339 *  Class Membership:
38340 *     FitsChan method.
38341 
38342 *  Description:
38343 c     This function
38344 f     This routine
38345 *     writes out all cards currently in the FitsChan. If the SinkFile
38346 *     attribute is set, they will be written out to the specified sink file.
38347 *     Otherwise, they will be written out using the sink function specified
38348 *     when the FitsChan was created. All cards are then deleted from the
38349 *     FitsChan.
38350 
38351 *  Parameters:
38352 c     this
38353 f     THIS = INTEGER (Given)
38354 *        Pointer to the FitsChan.
38355 f     STATUS = INTEGER (Given and Returned)
38356 f        The global status.
38357 
38358 *  Notes:
38359 *     - If the SinkFile is unset, and no sink function is available, this
38360 *     method simply empties the FitsChan, and is then equivalent to
38361 c     astEmptyFits.
38362 f     AST_EMPTYFITS.
38363 *     - This method attempt to execute even if an error has occurred
38364 *     previously.
38365 *--
38366 */
38367 
38368 /* Ensure a FitsChan was supplied. */
38369    if( this ) {
38370 
38371 /* Ensure the source function has been called */
38372       ReadFromSource( this, status );
38373 
38374 /* We can usefully use the local destructor function to do the work,
38375    since it only frees resources used within teh FitsChan, rather than
38376    freeing the FitsChan itself. */
38377       Delete( (AstObject *) this, status );
38378    }
38379 }
38380 
WriteInt(AstChannel * this_channel,const char * name,int set,int helpful,int value,const char * comment,int * status)38381 static void WriteInt( AstChannel *this_channel, const char *name,
38382                       int set, int helpful,
38383                       int value, const char *comment, int *status ) {
38384 /*
38385 *  Name:
38386 *     WriteInt
38387 
38388 *  Purpose:
38389 *     Write an int value to a data sink.
38390 
38391 *  Type:
38392 *     Private function.
38393 
38394 *  Synopsis:
38395 *     #include "fitschan.h"
38396 *     void WriteInt( AstChannel *this, const char *name,
38397 *                    int set, int helpful,
38398 *                    int value, const char *comment )
38399 
38400 *  Class Membership:
38401 *     FitsChan member function (over-rides the protected
38402 *     astWriteInt method inherited from the Channel class).
38403 
38404 *  Description:
38405 *     This function writes a named int value, representing the
38406 *     value of a class instance variable, to the data sink associated
38407 *     with a FitsChan. It is intended for use by class "Dump"
38408 *     functions when writing out class information which will
38409 *     subsequently be re-read.
38410 
38411 *  Parameters:
38412 *     this
38413 *        Pointer to the FitsChan.
38414 *     name
38415 *        Pointer to a constant null-terminated string containing the
38416 *        name to be used to identify the value in the external
38417 *        representation. This will form the key for identifying it
38418 *        again when it is re-read. The name supplied should be unique
38419 *        within its class.
38420 *
38421 *        Mixed case may be used and will be preserved in the external
38422 *        representation (where possible) for cosmetic effect. However,
38423 *        case is not significant when re-reading values.
38424 *
38425 *        It is recommended that a maximum of 6 alphanumeric characters
38426 *        (starting with an alphabetic character) be used. This permits
38427 *        maximum flexibility in adapting to standard external data
38428 *        representations (e.g. FITS).
38429 *     set
38430 *        If this is zero, it indicates that the value being written is
38431 *        a default value (or can be re-generated from other values) so
38432 *        need not necessarily be written out. Such values will
38433 *        typically be included in the external representation with
38434 *        (e.g.) a comment character so that they are available to
38435 *        human readers but will be ignored when re-read. They may also
38436 *        be completely omitted in some circumstances.
38437 *
38438 *        If "set" is non-zero, the value will always be explicitly
38439 *        included in the external representation so that it can be
38440 *        re-read.
38441 *     helpful
38442 *        This flag provides a hint about whether a value whose "set"
38443 *        flag is zero (above) should actually appear at all in the
38444 *        external representaton.
38445 *
38446 *        If the external representation allows values to be "commented
38447 *        out" then, by default, values will be included in this form
38448 *        only if their "helpful" flag is non-zero. Otherwise, they
38449 *        will be omitted entirely. When possible, omitting the more
38450 *        obscure values associated with a class is recommended in
38451 *        order to improve readability.
38452 *
38453 *        This default behaviour may be further modified if the
38454 *        FitsChan's Full attribute is set - either to permit all
38455 *        values to be shown, or to suppress non-essential information
38456 *        entirely.
38457 *     value
38458 *        The value to be written.
38459 *     comment
38460 *        Pointer to a constant null-terminated string containing a
38461 *        textual comment to be associated with the value.
38462 *
38463 *        Note that this comment may not actually be used, depending on
38464 *        the nature of the FitsChan supplied and the setting of its
38465 *        Comm attribute.
38466 */
38467 
38468 /* Local Variables: */
38469    astDECLARE_GLOBALS            /* Declare the thread specific global data */
38470    AstFitsChan *this;            /* Pointer to the FitsChan structure. */
38471    char keyword[ FITSNAMLEN + 1 ]; /* Buffer for FITS keyword */
38472 
38473 /* Check the global error status. */
38474    if ( !astOK ) return;
38475 
38476 /* Get a pointer to the structure holding thread-specific global data. */
38477    astGET_GLOBALS(this_channel);
38478 
38479 /* Obtain a pointer to the FitsChan structure. */
38480    this = (AstFitsChan *) this_channel;
38481 
38482 /* Use the "set" and "helpful" flags, along with the FitsChan's
38483    attributes to decide whether this value should actually be
38484    written. */
38485    if ( Use( this, set, helpful, status ) ) {
38486 
38487 /* Create a unique FITS keyword from the name supplied. */
38488       CreateKeyword( this, name, keyword, status );
38489 
38490 /* Write the value to the FitsChan as a keyword and value */
38491       astSetFitsI( this, keyword, value,
38492                    astGetComment( this ) ? comment : NULL, 0 );
38493 
38494 /* If the value is not "set", replace the card just written by a COMMENT
38495    card containing the text of the card as the comment. */
38496       if( !set ) MakeIntoComment( this, "astWrite", astGetClass( this ), status );
38497 
38498 /* Increment the count of items written. */
38499       items_written++;
38500    }
38501 }
38502 
WriteIsA(AstChannel * this_channel,const char * class,const char * comment,int * status)38503 static void WriteIsA( AstChannel *this_channel, const char *class,
38504                       const char *comment, int *status ) {
38505 /*
38506 *  Name:
38507 *     WriteIsA
38508 
38509 *  Purpose:
38510 *     Write an "IsA" data item to a data sink.
38511 
38512 *  Type:
38513 *     Private function.
38514 
38515 *  Synopsis:
38516 *     #include "fitschan.h"
38517 *     void WriteIsA( AstChannel *this, const char *class,
38518 *                    const char *comment )
38519 
38520 *  Class Membership:
38521 *     FitsChan member function (over-rides the protected astWriteIsA
38522 *     method inherited from the Channel class).
38523 
38524 *  Description:
38525 *     This function writes an "IsA" data item to the data sink
38526 *     associated with a FitsChan. This item delimits the end of the
38527 *     data associated with the instance variables of a class, as part
38528 *     of an overall Object definition.
38529 
38530 *  Parameters:
38531 *     this
38532 *        Pointer to the FitsChan.
38533 *     class
38534 *        Pointer to a constant null-terminated string containing the
38535 *        name of the class whose data are terminated by the "IsA"
38536 *        item.
38537 *     comment
38538 *        Pointer to a constant null-terminated string containing a
38539 *        textual comment to be associated with the "IsA"
38540 *        item. Normally, this will describe the purpose of the class
38541 *        whose data are being terminated.
38542 
38543 *  Notes:
38544 *     - The comment supplied may not actually be used, depending on
38545 *     the nature of the FitsChan supplied.
38546 */
38547 
38548 /* Local Variables: */
38549    astDECLARE_GLOBALS            /* Declare the thread specific global data */
38550    AstFitsChan *this;            /* Pointer to the FitsChan structure. */
38551    char buff[ AST__FITSCHAN_FITSCARDLEN - FITSNAMLEN + 1 ];
38552                                  /* Character buffer */
38553    char keyword[ FITSNAMLEN + 1 ]; /* Buffer for FITS keyword */
38554 
38555 /* Check the global error status. */
38556    if ( !astOK ) return;
38557 
38558 /* Get a pointer to the structure holding thread-specific global data. */
38559    astGET_GLOBALS(this_channel);
38560 
38561 /* Obtain a pointer to the FitsChan structure. */
38562    this = (AstFitsChan *) this_channel;
38563 
38564 /* Output an "IsA" item only if there has been at least one item
38565    written since the last "Begin" or "IsA" item, or if the Full
38566    attribute for the Channel is greater than zero (requesting maximum
38567    information). */
38568    if ( items_written || astGetFull( this ) > 0 ) {
38569 
38570 /* Create a unique FITS keyword for this "IsA" item, basing it on
38571    "ISA". */
38572       CreateKeyword( this, "ISA", keyword, status );
38573 
38574 /* Generate a pre-quoted version of the class name. */
38575       PreQuote( class, buff, status );
38576 
38577 /* Write the "IsA" item to the FitsChan as a keyword and string
38578    value. */
38579       astSetFitsS( this, keyword, buff,
38580                         astGetComment( this ) ? comment : NULL, 0 );
38581 
38582 /* If helpful information has not been suppressed, generate an
38583    indented comment to mark the "IsA" item and write it to the
38584    FitsChan as a comment card with a blank keyword. */
38585       if ( astGetFull( this ) >= 0 ) {
38586          MakeIndentedComment( current_indent, '.', "Class boundary", "",
38587                               buff, status );
38588          astSetFitsCom( this, "        ", buff, 0 );
38589       }
38590    }
38591 
38592 /* Clear the count of items written. */
38593    items_written = 0;
38594 }
38595 
WriteObject(AstChannel * this_channel,const char * name,int set,int helpful,AstObject * value,const char * comment,int * status)38596 static void WriteObject( AstChannel *this_channel, const char *name,
38597                          int set, int helpful,
38598                          AstObject *value, const char *comment, int *status ) {
38599 /*
38600 *  Name:
38601 *     WriteObject
38602 
38603 *  Purpose:
38604 *     Write an Object value to a data sink.
38605 
38606 *  Type:
38607 *     Private function.
38608 
38609 *  Synopsis:
38610 *     #include "fitschan.h"
38611 *     void WriteObject( AstChannel *this, const char *name,
38612 *                       int set, int helpful,
38613 *                       AstObject *value, const char *comment )
38614 
38615 *  Class Membership:
38616 *     FitsChan member function (over-rides the protected
38617 *     astWriteObject method inherited from the Channel class).
38618 
38619 *  Description:
38620 *     This function writes a named Object value, representing the
38621 *     value of a class instance variable, to the data sink associated
38622 *     with a FitsChan. It is intended for use by class "Dump"
38623 *     functions when writing out class information which will
38624 *     subsequently be re-read.
38625 
38626 *  Parameters:
38627 *     this
38628 *        Pointer to the FitsChan.
38629 *     name
38630 *        Pointer to a constant null-terminated string containing the
38631 *        name to be used to identify the value in the external
38632 *        representation. This will form the key for identifying it
38633 *        again when it is re-read. The name supplied should be unique
38634 *        within its class.
38635 *
38636 *        Mixed case may be used and will be preserved in the external
38637 *        representation (where possible) for cosmetic effect. However,
38638 *        case is not significant when re-reading values.
38639 *
38640 *        It is recommended that a maximum of 6 alphanumeric characters
38641 *        (starting with an alphabetic character) be used. This permits
38642 *        maximum flexibility in adapting to standard external data
38643 *        representations (e.g. FITS).
38644 *     set
38645 *        If this is zero, it indicates that the value being written is
38646 *        a default value (or can be re-generated from other values) so
38647 *        need not necessarily be written out. Such values will
38648 *        typically be included in the external representation with
38649 *        (e.g.) a comment character so that they are available to
38650 *        human readers but will be ignored when re-read. They may also
38651 *        be completely omitted in some circumstances.
38652 *
38653 *        If "set" is non-zero, the value will always be explicitly
38654 *        included in the external representation so that it can be
38655 *        re-read.
38656 *     helpful
38657 *        This flag provides a hint about whether a value whose "set"
38658 *        flag is zero (above) should actually appear at all in the
38659 *        external representaton.
38660 *
38661 *        If the external representation allows values to be "commented
38662 *        out" then, by default, values will be included in this form
38663 *        only if their "helpful" flag is non-zero. Otherwise, they
38664 *        will be omitted entirely. When possible, omitting the more
38665 *        obscure values associated with a class is recommended in
38666 *        order to improve readability.
38667 *
38668 *        This default behaviour may be further modified if the
38669 *        FitsChan's Full attribute is set - either to permit all
38670 *        values to be shown, or to suppress non-essential information
38671 *        entirely.
38672 *     value
38673 *        A pointer to the Object to be written.
38674 *     comment
38675 *        Pointer to a constant null-terminated string containing a
38676 *        textual comment to be associated with the value.
38677 *
38678 *        Note that this comment may not actually be used, depending on
38679 *        the nature of the FitsChan supplied and the setting of its
38680 *        Comm attribute.
38681 */
38682 
38683 /* Local Variables: */
38684    astDECLARE_GLOBALS            /* Declare the thread specific global data */
38685    AstFitsChan *this;            /* Pointer to the FitsChan structure. */
38686    char keyword[ FITSNAMLEN + 1 ]; /* Buffer for FITS keyword */
38687 
38688 /* Check the global error status. */
38689    if ( !astOK ) return;
38690 
38691 /* Get a pointer to the structure holding thread-specific global data. */
38692    astGET_GLOBALS(this_channel);
38693 
38694 /* Obtain a pointer to the FitsChan structure. */
38695    this = (AstFitsChan *) this_channel;
38696 
38697 /* Use the "set" and "helpful" flags, along with the FitsChan's
38698    attributes to decide whether this value should actually be
38699    written. */
38700    if ( Use( this, set, helpful, status ) ) {
38701 
38702 /* Create a unique FITS keyword from the name supplied. */
38703       CreateKeyword( this, name, keyword, status );
38704 
38705 /* Write the value to the FitsChan as a keyword and a blank string value,
38706    not pre-quoted (this "null" value indicates that an Object description
38707    follows). */
38708       astSetFitsS( this, keyword, "",
38709                         astGetComment( this ) ? comment : NULL, 0 );
38710 
38711 /* If the value is "set", write out the Object description. */
38712       if ( set ) {
38713          astWrite( this, value );
38714 
38715 /* If the value is not set, replace the card just written to the FitsChan
38716    by COMENT card containing the keyword and blank string value (do not
38717    write out the Object description). */
38718       } else {
38719          MakeIntoComment( this, "astWrite", astGetClass( this ), status );
38720       }
38721 
38722 /* Increment the count of items written. */
38723       items_written++;
38724    }
38725 }
38726 
WriteToSink(AstFitsChan * this,int * status)38727 static void WriteToSink( AstFitsChan *this, int *status ){
38728 /*
38729 *  Name:
38730 *     WriteToSink
38731 
38732 *  Purpose:
38733 *     Write the contents of the FitsChan out to the sink file or function.
38734 
38735 *  Type:
38736 *     Private function.
38737 
38738 *  Synopsis:
38739 *     #include "fitschan.h"
38740 *     void WriteToSink( AstFitsChan *this, int *status )
38741 
38742 *  Class Membership:
38743 *     FitsChan member function.
38744 
38745 *  Description:
38746 *     If the SinkFile attribute is set, each card in the FitsChan is
38747 *     written out to the sink file. Otherwise, the cards are passed in
38748 *     turn to the sink function specified when the FitsChan was created.
38749 *     If no sink function was provided, the cards are not written out.
38750 *     Cards marked as having been read into an AST object are not written
38751 *     out.
38752 
38753 *  Parameters:
38754 *     this
38755 *        Pointer to the FitsChan.
38756 *     status
38757 *        Pointer to the inherited status variable.
38758 
38759 *  Notes:
38760 *     -  The current card is left unchanged.
38761 */
38762 
38763 /* Local Constants: */
38764 #define ERRBUF_LEN 80
38765 
38766 /* Local Variables: */
38767    FILE *fd;                    /* File descriptor for sink file */
38768    astDECLARE_GLOBALS           /* Declare the thread specific global data */
38769    char *errstat;               /* Pointer for system error message */
38770    char card[ AST__FITSCHAN_FITSCARDLEN + 1]; /* Buffer for header card */
38771    char errbuf[ ERRBUF_LEN ];   /* Buffer for system error message */
38772    const char *sink_file;       /* Path to output sink file */
38773    int icard;                   /* Current card index on entry */
38774    int old_ignore_used;         /* Original value of external variable ignore_used */
38775 
38776 /* Check the global status. */
38777    if( !astOK ) return;
38778 
38779 /* Get a pointer to the structure holding thread-specific global data. */
38780    astGET_GLOBALS(this);
38781 
38782 /* If the SinkFile attribute is set, open the file. */
38783    fd = NULL;
38784    if( astTestSinkFile( this ) ) {
38785       sink_file = astGetSinkFile( this );
38786       fd = fopen( sink_file, "w" );
38787       if( !fd ) {
38788          if ( errno ) {
38789 #if HAVE_STRERROR_R
38790             strerror_r( errno, errbuf, ERRBUF_LEN );
38791             errstat = errbuf;
38792 #else
38793             errstat = strerror( errno );
38794 #endif
38795             astError( AST__WRERR, "astDelete(%s): Failed to open output "
38796                       "SinkFile '%s' - %s.", status, astGetClass( this ),
38797                       sink_file, errstat );
38798          } else {
38799             astError( AST__WRERR, "astDelete(%s): Failed to open output "
38800                       "SinkFile '%s'.", status, astGetClass( this ),
38801                       sink_file );
38802          }
38803       }
38804    }
38805 
38806 /* Only proceed if a file was opened, or sink function and wrapper were supplied. */
38807    if( fd || ( this->sink && this->sink_wrap ) ){
38808 
38809 /* Store the current card index. */
38810       icard = astGetCard( this );
38811 
38812 /* Indicate that cards which have been read into an AST object should skipped
38813    over by the functions which navigate the linked list of cards. */
38814       old_ignore_used = ignore_used;
38815       ignore_used = 1;
38816 
38817 /* Ensure that the first card in the FitsChan will be the next one to be
38818    read. */
38819       astSetCard( this, 1 );
38820 
38821 /* Loop round obtaining and writing out each card, until all cards have been
38822    processed. */
38823       while( !astFitsEof( this ) && astOK ){
38824 
38825 /* Get the current card, and write it out through the sink function.
38826    The call to astFindFits increments the current card. */
38827          if( astFindFits( this, "%f", card, 1 ) ) {
38828 
38829 /* If s sink file was opened, write the card out to it. */
38830             if( fd ) {
38831                fprintf( fd, "%s\n", card );
38832 
38833 /* Otherwise, use the isnk function. The sink function is an externally
38834    supplied function which may not be thread-safe, so lock a mutex first.
38835    Also store the channel data pointer in a global variable so that it can
38836    be accessed in the sink function using macro astChannelData. */
38837             } else {
38838                astStoreChannelData( this );
38839                LOCK_MUTEX3;
38840                ( *this->sink_wrap )( *this->sink, card, status );
38841                UNLOCK_MUTEX3;
38842             }
38843          }
38844       }
38845 
38846 /* Re-instate the original flag indicating if cards marked as having been
38847    read should be skipped over. */
38848       ignore_used = old_ignore_used;
38849 
38850 /* Set the current card index back to what it was on entry. */
38851       astSetCard( this, icard );
38852    }
38853 
38854 /* Close the sink file. */
38855    if( fd ) fclose( fd );
38856 }
38857 
WriteString(AstChannel * this_channel,const char * name,int set,int helpful,const char * value,const char * comment,int * status)38858 static void WriteString( AstChannel *this_channel, const char *name,
38859                          int set, int helpful,
38860                          const char *value, const char *comment, int *status ) {
38861 /*
38862 *  Name:
38863 *     WriteString
38864 
38865 *  Purpose:
38866 *     Write a string value to a data sink.
38867 
38868 *  Type:
38869 *     Private function.
38870 
38871 *  Synopsis:
38872 *     #include "fitschan.h"
38873 *     void WriteString( AstChannel *this, const char *name,
38874 *                       int set, int helpful,
38875 *                       const char *value, const char *comment )
38876 
38877 *  Class Membership:
38878 *     FitsChan member function (over-rides the protected
38879 *     astWriteString method inherited from the Channel class).
38880 
38881 *  Description:
38882 *     This function writes a named string value, representing the
38883 *     value of a class instance variable, to the data sink associated
38884 *     with a FitsChan. It is intended for use by class "Dump"
38885 *     functions when writing out class information which will
38886 *     subsequently be re-read.
38887 
38888 *  Parameters:
38889 *     this
38890 *        Pointer to the FitsChan.
38891 *     name
38892 *        Pointer to a constant null-terminated string containing the
38893 *        name to be used to identify the value in the external
38894 *        representation. This will form the key for identifying it
38895 *        again when it is re-read. The name supplied should be unique
38896 *        within its class.
38897 *
38898 *        Mixed case may be used and will be preserved in the external
38899 *        representation (where possible) for cosmetic effect. However,
38900 *        case is not significant when re-reading values.
38901 *
38902 *        It is recommended that a maximum of 6 alphanumeric characters
38903 *        (starting with an alphabetic character) be used. This permits
38904 *        maximum flexibility in adapting to standard external data
38905 *        representations (e.g. FITS).
38906 *     set
38907 *        If this is zero, it indicates that the value being written is
38908 *        a default value (or can be re-generated from other values) so
38909 *        need not necessarily be written out. Such values will
38910 *        typically be included in the external representation with
38911 *        (e.g.) a comment character so that they are available to
38912 *        human readers but will be ignored when re-read. They may also
38913 *        be completely omitted in some circumstances.
38914 *
38915 *        If "set" is non-zero, the value will always be explicitly
38916 *        included in the external representation so that it can be
38917 *        re-read.
38918 *     helpful
38919 *        This flag provides a hint about whether a value whose "set"
38920 *        flag is zero (above) should actually appear at all in the
38921 *        external representaton.
38922 *
38923 *        If the external representation allows values to be "commented
38924 *        out" then, by default, values will be included in this form
38925 *        only if their "helpful" flag is non-zero. Otherwise, they
38926 *        will be omitted entirely. When possible, omitting the more
38927 *        obscure values associated with a class is recommended in
38928 *        order to improve readability.
38929 *
38930 *        This default behaviour may be further modified if the
38931 *        FitsChan's Full attribute is set - either to permit all
38932 *        values to be shown, or to suppress non-essential information
38933 *        entirely.
38934 *     value
38935 *        Pointer to a constant null-terminated string containing the
38936 *        value to be written.
38937 *     comment
38938 *        Pointer to a constant null-terminated string containing a
38939 *        textual comment to be associated with the value.
38940 *
38941 *        Note that this comment may not actually be used, depending on
38942 *        the nature of the FitsChan supplied and the setting of its
38943 *        Comm attribute.
38944 */
38945 
38946 /* Local Variables: */
38947    astDECLARE_GLOBALS            /* Declare the thread specific global data */
38948    AstFitsChan *this;            /* Pointer to the FitsChan structure. */
38949    char *c;                      /* Pointer to next buffer character */
38950    char buff1[ AST__FITSCHAN_FITSCARDLEN - FITSNAMLEN - 3 ]; /* Buffer for a single substring */
38951    char buff2[ AST__FITSCHAN_FITSCARDLEN - FITSNAMLEN - 3 ]; /* Buffer for pre-quoted string */
38952    char cc;                      /* Next character */
38953    char keyword[ FITSNAMLEN + 1 ]; /* Buffer for FITS keyword */
38954    const char *start;            /* Pointer to start of substring */
38955    int first;                    /* Is this the first sub-string? */
38956    int nc;                       /* No. of available columns remaining */
38957 
38958 /* Check the global error status. */
38959    if ( !astOK ) return;
38960 
38961 /* Get a pointer to the structure holding thread-specific global data. */
38962    astGET_GLOBALS(this_channel);
38963 
38964 /* Obtain a pointer to the FitsChan structure. */
38965    this = (AstFitsChan *) this_channel;
38966 
38967 /* Use the "set" and "helpful" flags, along with the FitsChan's
38968    attributes to decide whether this value should actually be
38969    written. */
38970    if ( Use( this, set, helpful, status ) ) {
38971 
38972 /* Create a unique FITS keyword from the name supplied. */
38973       CreateKeyword( this, name, keyword, status );
38974 
38975 /* Store a pointer to the start of the next sub-string (i.e. the
38976    beggining of the string), and then loop round until the end of the
38977    string is reached. */
38978       start = value;
38979       first = 1;
38980       while( *start && astOK ){
38981 
38982 /* Store the number of characters available in the 80 column header card
38983    for the next substring, leaving room for the "= " string at the start,
38984    and the delimiting quotes. Also reserve 2 characters to allow for the
38985    possibility of double quotes being needed to protect trailing white space
38986    (see function PreQuote). */
38987          nc = AST__FITSCHAN_FITSCARDLEN - FITSNAMLEN - 6;
38988 
38989 /* If this is the first sub-string reserve room for any comment. */
38990          if( first ){
38991             if( comment && comment[0] ) nc -= ChrLen( comment, status ) + 3;
38992 
38993 /* If the first card will be turned into a comment card, we need to leave room
38994    for the keyword name and equals sign, etc, within the 80 columns. */
38995             if( !set ) nc -= FITSNAMLEN + 5;
38996          }
38997 
38998 /* We need to check the sub-string for single quotes since these will
38999    take up 2 characters each instead of 1 when encoded since single quotes
39000    within a string are doubled. Search through from the starting
39001    character, copying the sub-string into a buffer, and reducing the number
39002    of available characters remaining in the card for each character. */
39003          c = buff1;
39004          while( *start && nc > 0 ){
39005             cc = *(start++);
39006             *(c++) = cc;
39007             if( cc == '\'' ) {
39008                nc -= 2;
39009             } else {
39010                nc -= 1;
39011             }
39012          }
39013 
39014 /* If the last character in the substring was a single quote, there may
39015    not have been room for the extra quote which is added when the
39016    sub-string is encoded. In this case we need to back up a character in
39017    order to remove the single quote frin this substring and move it into
39018    the next sub-string. */
39019          if( nc < 0 ){
39020             start--;
39021             c--;
39022          }
39023 
39024 /* If the supplied value has not been exhausted, append an ampersand to
39025    the string. In this case we need to move the last character in the
39026    substring into the next substring to make room for the ampersand. */
39027          if( *start ) {
39028             start--;
39029             c--;
39030             *(c++) = '&';
39031          }
39032 
39033 /* Terminate the buffer. */
39034          *c = 0;
39035 
39036 /* The FITS standard considers trailing white space is be insignificant,
39037    and so we need to guard against external applications throwing away
39038    significant trailing white space. This is done by encosing the string,
39039    including trailing white space, in double quotes. */
39040          PreQuote( buff1, buff2, status );
39041 
39042 /* On the first pass through this loop, write the value to the FitsChan as
39043    a keyword and value */
39044          if( first ){
39045             astSetFitsS( this, keyword, buff2,
39046                          astGetComment( this ) ? comment : NULL, 0 );
39047 
39048 /* If the value is not "set", replace the card just written by a COMMENT
39049    card containing the text of the card as the comment. */
39050             if( !set ) MakeIntoComment( this, "astWrite", astGetClass( this ), status );
39051 
39052 /* On subsequent passes through the loop, store the string using a CONTINUE
39053    keyword, with type AST__CONTINUE (this type is like AST__STRING but is
39054    formatted without an equals sign). */
39055          } else {
39056             astSetFitsCN( this, "CONTINUE", buff2, NULL, 0 );
39057          }
39058          first = 0;
39059       }
39060 
39061 /* Increment the count of items written. */
39062       items_written++;
39063    }
39064 }
39065 
ZPXMapping(AstFitsChan * this,FitsStore * store,char s,int naxes,int zpxaxes[2],const char * method,const char * class,int * status)39066 static AstMapping *ZPXMapping( AstFitsChan *this, FitsStore *store, char s,
39067                                int naxes, int zpxaxes[2], const char *method,
39068                                const char *class, int *status ){
39069 /*
39070 *  Name:
39071 *     ZPXMapping
39072 
39073 *  Purpose:
39074 *     Create a Mapping descriping "-ZPX" (IRAF) distortion.
39075 
39076 *  Type:
39077 *     Private function.
39078 
39079 *  Synopsis:
39080 *     AstMapping *ZPXMapping( AstFitsChan *this, FitsStore *store, char s,
39081 *                             int naxes, int zpxaxes[2], const char *method,
39082 *                             const char *class, int *status )
39083 
39084 *  Class Membership:
39085 *     FitsChan
39086 
39087 *  Description:
39088 *     This function uses the values in the supplied FitsStore to create a
39089 *     Mapping which implements the "-ZPX" distortion code, produced by
39090 *     the IRAF project. See:
39091 *
39092 *     http://iraf.noao.edu/projects/ccdmosaic/zpx.html
39093 *
39094 *     Note, the Mapping created by this function implements the "lngcor"
39095 *     and "latcor" corrections described in the WAT... keywords. The
39096 *     basic ZPN projection code is handled in the normal way, as any
39097 *     other projection is handled.
39098 
39099 *  Parameters:
39100 *     store
39101 *        A structure containing information about the requested axis
39102 *        descriptions derived from a FITS header.
39103 *     s
39104 *        A character identifying the co-ordinate version to use. A space
39105 *        means use primary axis descriptions. Otherwise, it must be an
39106 *        upper-case alphabetical characters ('A' to 'Z').
39107 *     naxes
39108 *        The number of intermediate world coordinate axes (WCSAXES).
39109 *     zpxaxes
39110 *        The zero-based indices of the two IWC axes that use the ZPX projection.
39111 *     method
39112 *        A pointer to a string holding the name of the calling method.
39113 *        This is used only in the construction of error messages.
39114 *     class
39115 *        A pointer to a string holding the class of the object being
39116 *        read. This is used only in the construction of error messages.
39117 *     status
39118 *        Pointer to the inherited status variable.
39119 
39120 *  Returned Value:
39121 *     A pointer to the Mapping.
39122 */
39123 
39124 /* Local Variables: */
39125    AstMapping   *ret;
39126    char *watstr;
39127    double *cvals[ 2 ];
39128    int *mvals[ 2 ];
39129    int ncoeff[ 2 ];
39130    int i;
39131    int icoeff;
39132    int ok;
39133 
39134 /* Initialise the pointer to the returned Mapping. */
39135    ret = NULL;
39136 
39137 /* Check the global status. */
39138    if ( !astOK ) return ret;
39139 
39140 /* Check both axes */
39141    for( i = 0; i < 2; i++ ){
39142       mvals[ i ] = NULL;
39143       cvals[ i ] = NULL;
39144       ncoeff[ i ] = 0;
39145 
39146 /* Concatenate all the IRAF "WAT" keywords together for this axis. These
39147    keywords are marked as having been used, so that they are not written
39148    out when the FitsChan is deleted. */
39149       watstr = ConcatWAT( this, zpxaxes[ i ], method, class, status );
39150 
39151 /* Extract the polynomial coefficients from the concatenated WAT string.
39152    These are returned in the form of a list of PVi_m values for a TPN
39153    projection. */
39154       ncoeff[ i ] = WATCoeffs( watstr, i, cvals + i, mvals + i, &ok, status );
39155 
39156 /* If the current axis of the ZPX projection uses features not supported
39157    by AST, do not do any more axes. */
39158       if( !ok ) break;
39159 
39160 /* Free the WAT string. */
39161       watstr = astFree( watstr );
39162    }
39163 
39164 /* If we can handle the ZPX projection, store the polynomial coefficients in
39165    a new inverted TPN WcsMap. This WcsMap is used as a correction to the ZPN
39166    WcsMap to be created later, therefore set its FITSProj value to zero so
39167    that it is not used as the FITS projection when written out via
39168    astWrite. Also set TPNTan to zero to indicate that the TAN part of the
39169    TPN projection should not be used (i.e. just use the polynomial part). */
39170    if( ok && astOK ) {
39171 
39172       if( ncoeff[ 0 ] || ncoeff[ 1 ] ) {
39173          ret = (AstMapping *) astWcsMap( naxes, AST__TPN, zpxaxes[ 0 ] + 1,
39174                                          zpxaxes[ 1 ] + 1, "Invert=1",
39175                                          status );
39176          astSetFITSProj( ret, 0 );
39177          astSetTPNTan( ret, 0 );
39178          for( i = 0; i < 2; i++ ){
39179             for( icoeff = 0; icoeff < ncoeff[ i ]; icoeff++ ) {
39180                astSetPV( ret, zpxaxes[ i ], (mvals[ i ])[ icoeff ],
39181                          (cvals[ i ])[ icoeff ] );
39182             }
39183          }
39184 
39185       } else {
39186          ret = (AstMapping *) astUnitMap( naxes, " ", status );
39187       }
39188 
39189 /* If the TNX cannot be represented in FITS-WCS (within our restrictions), add
39190    warning keywords to the FitsChan. */
39191    } else {
39192       Warn( this, "zpx", "This FITS header includes, or was "
39193             "derived from, a ZPX projection which requires "
39194             "unsupported IRAF-specific corrections. The WCS "
39195             "information may therefore be incorrect.", method, class,
39196             status );
39197    }
39198 
39199 /* Return the result. */
39200    return ret;
39201 }
39202 
39203 /* Functions which access class attributes. */
39204 /* ---------------------------------------- */
39205 
39206 /* Implement member functions to access the attributes associated with
39207    this class using the macros defined for this purpose in the
39208    "object.h" file. For a description of each attribute, see the class
39209    interface (in the associated .h file). */
39210 
39211 /* Card. */
39212 /* ===== */
39213 
39214 /*
39215 *att++
39216 *  Name:
39217 *     Card
39218 
39219 *  Purpose:
39220 *     Index of current FITS card in a FitsChan.
39221 
39222 *  Type:
39223 *     Public attribute.
39224 
39225 *  Synopsis:
39226 *     Integer.
39227 
39228 *  Description:
39229 *     This attribute gives the index of the "current" FITS header card
39230 *     within a FitsChan, the first card having an index of 1. The
39231 c     choice of current card affects the behaviour of functions that
39232 f     choice of current card affects the behaviour of routines that
39233 c     access the contents of the FitsChan, such as astDelFits,
39234 c     astFindFits and astPutFits.
39235 f     access the contents of the FitsChan, such as AST_DELFITS,
39236 f     AST_FINDFITS and AST_PUTFITS.
39237 *
39238 *     A value assigned to Card will position the FitsChan at any
39239 *     desired point, so that a particular card within it can be
39240 *     accessed. Alternatively, the value of Card may be enquired in
39241 *     order to determine the current position of a FitsChan.
39242 *
39243 *     The default value of Card is 1. This means that clearing
39244 c     this attribute (using astClear) effectively "rewinds" the
39245 f     this attribute (using AST_CLEAR) effectively "rewinds" the
39246 *     FitsChan, so that the first card is accessed next.  If Card is
39247 *     set to a value which exceeds the total number of cards in the
39248 *     FitsChan (as given by its Ncard attribute), it is regarded as
39249 *     pointing at the "end-of-file". In this case, the value returned
39250 *     in response to an enquiry is always one more than the number of
39251 *     cards in the FitsChan.
39252 
39253 *  Applicability:
39254 *     FitsChan
39255 *        All FitsChans have this attribute.
39256 *att--
39257 */
39258 
39259 /* Encoding. */
39260 /* ========= */
39261 
39262 /*
39263 *att++
39264 *  Name:
39265 *     Encoding
39266 
39267 *  Purpose:
39268 *     System for encoding Objects as FITS headers.
39269 
39270 *  Type:
39271 *     Public attribute.
39272 
39273 *  Synopsis:
39274 *     String.
39275 
39276 *  Description:
39277 *     This attribute specifies the encoding system to use when AST
39278 *     Objects are stored as FITS header cards in a FitsChan. It
39279 c     affects the behaviour of the astWrite and astRead functions when
39280 f     affects the behaviour of the AST_WRITE and AST_READ routines when
39281 *     they are used to transfer any AST Object to or from an external
39282 *     representation consisting of FITS header cards (i.e. whenever a
39283 *     write or read operation is performed using a FitsChan as the I/O
39284 *     Channel).
39285 *
39286 *     There are several ways (conventions) by which coordinate system
39287 *     information may be represented in the form of FITS headers and
39288 *     the Encoding attribute is used to specify which of these should
39289 *     be used. The encoding options available are outlined in the
39290 *     "Encodings Available" section below, and in more detail in the
39291 *     sections which follow.
39292 *
39293 *     Encoding systems differ in the range of possible Objects
39294 *     (e.g. classes) they can represent, in the restrictions they
39295 *     place on these Objects (e.g. compatibility with some
39296 *     externally-defined coordinate system model) and in the number of
39297 *     Objects that can be stored together in any particular set of
39298 *     FITS header cards (e.g. multiple Objects, or only a single
39299 *     Object). The choice of encoding also affects the range of
39300 *     external applications which can potentially read and interpret
39301 *     the FITS header cards produced.
39302 *
39303 *     The encoding options available are not necessarily mutually
39304 *     exclusive, and it may sometimes be possible to store multiple
39305 *     Objects (or the same Object several times) using different
39306 *     encodings within the same set of FITS header cards. This
39307 *     possibility increases the likelihood of other applications being
39308 *     able to read and interpret the information.
39309 *
39310 *     By default, a FitsChan will attempt to determine which encoding
39311 *     system is already in use, and will set the default Encoding
39312 *     value accordingly (so that subsequent I/O operations adopt the
39313 *     same conventions). It does this by looking for certain critical
39314 *     FITS keywords which only occur in particular encodings. For
39315 *     details of how this works, see the "Choice of Default Encoding"
39316 *     section below. If you wish to ensure that a particular encoding
39317 *     system is used, independently of any FITS cards already present,
39318 *     you should set an explicit Encoding value yourself.
39319 
39320 *  Encodings Available:
39321 *     The Encoding attribute can take any of the following (case
39322 *     insensitive) string values to select the corresponding encoding
39323 
39324 *     system:
39325 *
39326 *     - "DSS": Encodes coordinate system information in FITS header
39327 *     cards using the convention developed at the Space Telescope
39328 *     Science Institute (STScI) for the Digitised Sky Survey (DSS)
39329 *     astrometric plate calibrations. The main advantages of this
39330 *     encoding are that FITS images which use it are widely available
39331 *     and it is understood by a number of important and
39332 *     well-established astronomy applications. For further details,
39333 *     see the section "The DSS Encoding" below.
39334 *
39335 *     - "FITS-WCS": Encodes coordinate system information in FITS
39336 *     header cards using the conventions described in the FITS
39337 *     world coordinate system (FITS-WCS) papers by E.W. Greisen,
39338 *     M. Calabretta, et al. The main advantages of this encoding are that
39339 *     it should be understood by any FITS-WCS compliant application and
39340 *     is likely to be adopted widely for FITS data in future. For further
39341 *     details, see the section "The FITS-WCS Encoding" below.
39342 *
39343 *     - "FITS-PC": Encodes coordinate system information in FITS
39344 *     header cards using the conventions described in an earlier draft
39345 *     of the FITS world coordinate system papers by E.W. Greisen and
39346 *     M. Calabretta. This encoding uses a combination of CDELTi and
39347 *     PCiiijjj keywords to describe the scale and rotation of the pixel
39348 *     axes. This encoding is included to support existing data and
39349 *     software which uses these now superceded conventions. In general,
39350 *     the "FITS-WCS" encoding (which uses CDi_j or PCi_j keywords to
39351 *     describe the scale and rotation) should be used in preference to
39352 *     "FITS-PC".
39353 *
39354 *     - "FITS-IRAF": Encodes coordinate system information in FITS
39355 *     header cards using the conventions described in the document
39356 *     "World Coordinate Systems Representations Within the FITS
39357 *     Format" by R.J. Hanisch and D.G. Wells, 1988.  This encoding is
39358 *     currently employed by the IRAF data analysis facility, so its
39359 *     use will facilitate data exchange with IRAF. Its main advantages
39360 *     are that it is a stable convention which approximates to a
39361 *     subset of the propsed FITS-WCS encoding (above). This makes it
39362 *     suitable as an interim method for storing coordinate system
39363 *     information in FITS headers until the FITS-WCS encoding becomes
39364 *     stable. Since many datasets currently use the FITS-IRAF
39365 *     encoding, conversion of data from FITS-IRAF to the final form of
39366 *     FITS-WCS is likely to be well supported.
39367 *
39368 *     - "FITS-AIPS": Encodes coordinate system information in FITS
39369 *     header cards using the conventions originally introduced by the
39370 *     AIPS data analysis facility. This is base on the use of CDELTi and
39371 *     CROTAi keuwords to desribe the scale and rotation of each axis.
39372 *     These conventions have been superceded but are still widely used.
39373 *
39374 *     - "FITS-AIPS++": Encodes coordinate system information in FITS
39375 *     header cards using the conventions used by the AIPS++ project.
39376 *     This is an extension of FITS-AIPS which includes some of the
39377 *     features of FITS-IRAF and FITS-PC.
39378 *
39379 *     - "FITS-CLASS": Encodes coordinate system information in FITS
39380 *     header cards using the conventions used by the CLASS project.
39381 *     CLASS is a software package for reducing single-dish radio and
39382 *     sub-mm spectroscopic data. See the section "CLASS FITS format" at
39383 *     http://www.iram.fr/IRAMFR/GILDAS/doc/html/class-html/.
39384 *
39385 *     - "NATIVE": Encodes AST Objects in FITS header cards using a
39386 *     convention which is private to the AST library (but adheres to
39387 *     the general FITS standard) and which uses FITS keywords that
39388 *     will not clash with other encoding systems. The main advantages
39389 *     of this are that any class of AST Object may be encoded, and any
39390 *     (reasonable) number of Objects may be stored sequentially in the
39391 *     same FITS header. This makes FITS headers an almost loss-less
39392 *     communication path for passing AST Objects between applications
39393 *     (although all such applications must, of course, make use of the
39394 *     AST library to interpret the information). For further details,
39395 *     see the section "The NATIVE Encoding" below.
39396 
39397 *  Choice of Default Encoding:
39398 *     If the Encoding attribute of a FitsChan is not set, the default
39399 *     value it takes is determined by the presence of certain critical
39400 *     FITS keywords within the FitsChan. The sequence of decisions
39401 
39402 *     used to arrive at the default value is as follows:
39403 *
39404 *     - If the FitsChan contains any keywords beginning with the
39405 *     string "BEGAST", then NATIVE encoding is used,
39406 *     - Otherwise, FITS-CLASS is used if the FitsChan contains a DELTAV
39407 *     keyword and a keyword of the form VELO-xxx, where xxx indicates one
39408 *     of the rest frames used by class (e.g. "VELO-LSR"), or "VLSR".
39409 *     - Otherwise, if the FitsChan contains a CTYPE keyword which
39410 *     represents a spectral axis using the conventions of the AIPS and
39411 *     AIPS++ projects (e.g. "FELO-LSR", etc), then one of FITS-AIPS or
39412 *     FITS-AIPS++ encoding is used. FITS-AIPS++ is used if any of the
39413 *     keywords CDi_j, PROJP, LONPOLE or LATPOLE are
39414 *     found in the FitsChan. Otherwise FITS-AIPS is used.
39415 *     - Otherwise, if the FitsChan contains a keyword of the form
39416 *     "PCiiijjj", where "i" and "j" are single digits, then
39417 *     FITS-PC encoding is used,
39418 *     - Otherwise, if the FitsChan contains a keyword of the form
39419 *     "CDiiijjj", where "i" and "j" are single digits, then
39420 *     FITS-IRAF encoding is used,
39421 *     - Otherwise, if the FitsChan contains a keyword of the form
39422 *     "CDi_j", and at least one of RADECSYS, PROJPi, or CjVALi
39423 *     where "i" and "j" are single digits, then FITS-IRAF encoding is
39424 *     used.
39425 *     - Otherwise, if the FitsChan contains any keywords of the form
39426 *     PROJPi, CjVALi or RADECSYS, where "i" and "j" are single digits,
39427 *     then FITS-PC encoding is used.
39428 *     - Otherwise, if the FitsChan contains a keyword of the form
39429 *     CROTAi, where "i" is a single digit, then FITS-AIPS encoding is
39430 *     used.
39431 *     - Otherwise, if the FitsChan contains a keyword of the form
39432 *     CRVALi, where "i" is a single digit, then FITS-WCS encoding is
39433 *     used.
39434 *     - Otherwise, if the FitsChan contains the "PLTRAH" keyword, then
39435 *     DSS encoding is used,
39436 *     - Otherwise, if none of these conditions is met (as would be the
39437 *     case when using an empty FitsChan), then NATIVE encoding is
39438 *     used.
39439 *
39440 *     Except for the NATIVE and DSS encodings, all the above checks
39441 *     also require that the header contains at least one CTYPE, CRPIX and
39442 *     CRVAL keyword (otherwise the checking process continues to the next
39443 *     case).
39444 *
39445 *     Setting an explicit value for the Encoding attribute always
39446 *     over-rides this default behaviour.
39447 *
39448 *     Note that when writing information to a FitsChan, the choice of
39449 *     encoding will depend greatly on the type of application you
39450 *     expect to be reading the information in future. If you do not
39451 *     know this, there may sometimes be an advantage in writing the
39452 *     information several times, using a different encoding on each
39453 *     occasion.
39454 
39455 *  The DSS Encoding:
39456 *     The DSS encoding uses FITS header cards to store a multi-term
39457 *     polynomial which relates pixel positions on a digitised
39458 *     photographic plate to celestial coordinates (right ascension and
39459 *     declination). This encoding may only be used to store a single
39460 *     AST Object in any set of FITS header cards, and that Object must
39461 *     be a FrameSet which conforms to the STScI/DSS coordinate system
39462 *     model (this means the Mapping which relates its base and current
39463 *     Frames must include either a DssMap or a WcsMap with type
39464 *     AST__TAN or AST__TPN).
39465 *
39466 c     When reading a DSS encoded Object (using astRead), the FitsChan
39467 f     When reading a DSS encoded Object (using AST_READ), the FitsChan
39468 *     concerned must initially be positioned at the first card (its
39469 *     Card attribute must equal 1) and the result of the read, if
39470 *     successful, will always be a pointer to a FrameSet. The base
39471 *     Frame of this FrameSet represents DSS pixel coordinates, and the
39472 *     current Frame represents DSS celestial coordinates. Such a read
39473 *     is always destructive and causes the FITS header cards required
39474 *     for the construction of the FrameSet to be removed from the
39475 *     FitsChan, which is then left positioned at the "end-of-file". A
39476 *     subsequent read using the same encoding will therefore not
39477 *     return another FrameSet, even if the FitsChan is rewound.
39478 *
39479 c     When astWrite is used to store a FrameSet using DSS encoding,
39480 f     When AST_WRITE is used to store a FrameSet using DSS encoding,
39481 *     an attempt is first made to simplify the FrameSet to see if it
39482 *     conforms to the DSS model.  Specifically, the current Frame must
39483 *     be a FK5 SkyFrame; the projection must be a tangent plane
39484 *     (gnomonic) projection with polynomial corrections conforming to
39485 *     DSS requirements, and north must be parallel to the second base
39486 *     Frame axis.
39487 *
39488 *     If the simplification process succeeds, a description of the
39489 *     FrameSet is written to the FitsChan using appropriate DSS FITS
39490 *     header cards. The base Frame of the FrameSet is used to form the
39491 *     DSS pixel coordinate system and the current Frame gives the DSS
39492 *     celestial coordinate system.  A successful write operation will
39493 *     over-write any existing DSS encoded data in the FitsChan, but
39494 *     will not affect other (non-DSS) header cards. If a destructive
39495 *     read of a DSS encoded Object has previously occurred, then an
39496 *     attempt will be made to store the FITS header cards back in
39497 *     their original locations.
39498 *
39499 *     If an attempt to simplify a FrameSet to conform to the DSS model
39500 *     fails (or if the Object supplied is not a FrameSet), then no
39501 c     data will be written to the FitsChan and astWrite will return
39502 f     data will be written to the FitsChan and AST_WRITE will return
39503 *     zero. No error will result.
39504 
39505 *  The FITS-WCS Encoding:
39506 *     The FITS-WCS convention uses FITS header cards to describe the
39507 *     relationship between pixels in an image (not necessarily
39508 *     2-dimensional) and one or more related "world coordinate systems".
39509 *     The FITS-WCS encoding may only be used to store a single AST Object
39510 *     in any set of FITS header cards, and that Object must be a FrameSet
39511 *     which conforms to the FITS-WCS model (the FrameSet may, however,
39512 *     contain multiple Frames which will be result in multiple FITS
39513 *     "alternate axis descriptions"). Details of the use made by this
39514 *     Encoding of the conventions described in the FITS-WCS papers are
39515 *     given in the appendix "FITS-WCS Coverage" of this document. A few
39516 *     main points are  described below.
39517 *
39518 *     The rotation and scaling of the intermediate world coordinate system
39519 *     can be specified using either "CDi_j" keywords, or "PCi_j" together
39520 *     with "CDELTi" keywords. When writing a FrameSet to a FitsChan, the
39521 *     the value of the CDMatrix attribute of the FitsChan determines
39522 *     which system is used.
39523 *
39524 *     In addition, this encoding supports the "TAN with polynomial correction
39525 *     terms" projection which was included in a draft of the FITS-WCS paper,
39526 *     but was not present in the final version. A "TAN with polynomial
39527 *     correction terms" projection is represented using a WcsMap with type
39528 *     AST__TPN (rather than AST__TAN which is used to represent simple
39529 *     TAN projections). When reading a FITS header, a CTYPE keyword value
39530 *     including a "-TAN" code results in an AST__TPN projection if there are
39531 *     any projection parameters (given by the PVi_m keywords) associated with
39532 *     the latitude axis, or if there are projection parameters associated
39533 *     with the longitude axis for m greater than 4. When writing a
39534 *     FrameSet to a FITS header, an AST__TPN projection gives rise to a
39535 *     CTYPE value including the normal "-TAN" code, but the projection
39536 *     parameters are stored in keywords with names "QVi_m", instead of the
39537 *     usual "PVi_m". Since these QV parameters are not part of the
39538 *     FITS-WCS standard they will be ignored by other non-AST software,
39539 *     resulting in the WCS being interpreted as a simple TAN projection
39540 *     without any corrections. This should be seen as an interim solution
39541 *     until such time as an agreed method for describing projection
39542 *     distortions within FITS-WCS has been published.
39543 *
39544 *     AST extends the range of celestial coordinate systems which may be
39545 *     described using this encoding by allowing the inclusion of
39546 *     "AZ--" and "EL--" as the coordinate specification within CTYPE
39547 *     values. These form a longitude/latitude pair of axes which describe
39548 *     azimuth and elevation. The geographic position of the observer
39549 *     should be supplied using the OBSGEO-X/Y/Z keywords described in FITS-WCS
39550 *     paper III. Currently, a simple model is used which includes diurnal
39551 *     aberration, but ignores atmospheric refraction, polar motion, etc.
39552 *     These may be added in a later release.
39553 *
39554 *     If an AST SkyFrame that represents offset rather than absolute
39555 *     coordinates (see attribute SkyRefIs) is written to a FitsChan using
39556 *     FITS-WCS encoding, two alternate axis descriptions will be created.
39557 *     One will describe the offset coordinates, and will use "OFLN" and
39558 *     "OFLT" as the axis codes in the CTYPE keywords. The other will
39559 *     describe absolute coordinates as specified by the System attribute
39560 *     of the SkyFrame, using the usual CTYPE codes ("RA--"/"DEC-", etc).
39561 *     In addition, the absolute coordinates description will contain
39562 *     AST-specific keywords (SREF1/2, SREFP1/2 and SREFIS) that allow the
39563 *     header to be read back into AST in order to reconstruct the original
39564 *     SkyFrame.
39565 *
39566 c     When reading a FITS-WCS encoded Object (using astRead), the FitsChan
39567 f     When reading a FITS-WCS encoded Object (using AST_READ), the FitsChan
39568 *     concerned must initially be positioned at the first card (its
39569 *     Card attribute must equal 1) and the result of the read, if
39570 *     successful, will always be a pointer to a FrameSet. The base
39571 *     Frame of this FrameSet represents FITS-WCS pixel coordinates,
39572 *     and the current Frame represents the physical coordinate system
39573 *     described by the FITS-WCS primary axis descriptions. If
39574 *     secondary axis descriptions are also present, then the FrameSet
39575 *     may contain additional (non-current) Frames which represent
39576 *     these.  Such a read is always destructive and causes the FITS
39577 *     header cards required for the construction of the FrameSet to be
39578 *     removed from the FitsChan, which is then left positioned at the
39579 *     "end-of-file". A subsequent read using the same encoding will
39580 *     therefore not return another FrameSet, even if the FitsChan is
39581 *     rewound.
39582 *
39583 c     When astWrite is used to store a FrameSet using FITS-WCS
39584 f     When AST_WRITE is used to store a FrameSet using FITS-WCS
39585 *     encoding, an attempt is first made to simplify the FrameSet to
39586 *     see if it conforms to the FITS-WCS model. If this simplification
39587 *     process succeeds (as it often should, as the model is reasonably
39588 *     flexible), a description of the FrameSet is written to the
39589 *     FitsChan using appropriate FITS header cards. The base Frame of
39590 *     the FrameSet is used to form the FITS-WCS pixel coordinate
39591 *     system and the current Frame gives the physical coordinate
39592 *     system to be described by the FITS-WCS primary axis
39593 *     descriptions.  Any additional Frames in the FrameSet may be used
39594 *     to construct secondary axis descriptions, where appropriate.
39595 *
39596 *     A successful write operation will over-write any existing
39597 *     FITS-WCS encoded data in the FitsChan, but will not affect other
39598 *     (non-FITS-WCS) header cards. If a destructive read of a FITS-WCS
39599 *     encoded Object has previously occurred, then an attempt will be
39600 *     made to store the FITS header cards back in their original
39601 *     locations. Otherwise, the new cards will be inserted following
39602 *     any other FITS-WCS related header cards present or, failing
39603 *     that, in front of the current card (as given by the Card
39604 *     attribute).
39605 *
39606 *     If an attempt to simplify a FrameSet to conform to the FITS-WCS
39607 *     model fails (or if the Object supplied is not a FrameSet), then
39608 c     no data will be written to the FitsChan and astWrite will
39609 f     no data will be written to the FitsChan and AST_WRITE will
39610 *     return zero. No error will result.
39611 
39612 *  The FITS-IRAF Encoding:
39613 *     The FITS-IRAF encoding can, for most purposes, be considered as
39614 *     a subset of the FITS-WCS encoding (above), although it differs
39615 *     in the details of the FITS keywords used. It is used in exactly
39616 *     the same way and has the same restrictions, but with the
39617 
39618 *     addition of the following:
39619 *
39620 *     - The only celestial coordinate systems that may be represented
39621 *     are equatorial, galactic and ecliptic,
39622 *     - Sky projections can be represented only if any associated
39623 *     projection parameters are set to their default values.
39624 *     - Secondary axis descriptions are not supported, so when writing
39625 *     a FrameSet to a FitsChan, only information from the base and
39626 *     current Frames will be stored.
39627 *
39628 *     Note that this encoding is provided mainly as an interim measure to
39629 *     provide a more stable alternative to the FITS-WCS encoding until the
39630 *     FITS standard for encoding WCS information is finalised.  The name
39631 *     "FITS-IRAF" indicates the general keyword conventions used and does
39632 *     not imply that this encoding will necessarily support all features of
39633 *     the WCS scheme used by IRAF software. Nevertheless, an attempt has
39634 *     been made to support a few such features where they are known to be
39635 *     used by important sources of data.
39636 *
39637 *     When writing a FrameSet using the FITS-IRAF encoding, axis rotations
39638 *     are specified by a matrix of FITS keywords of the form "CDi_j", where
39639 *     "i" and "j" are single digits. The alternative form "CDiiijjj", which
39640 *     is also in use, is recognised when reading an Object, but is never
39641 *     written.
39642 *
39643 *     In addition, the experimental IRAF "ZPX" and "TNX" sky projections will
39644 *     be accepted when reading, but will never be written (the corresponding
39645 *     FITS "ZPN" or "distorted TAN" projection being used instead). However,
39646 *     there are restrictions on the use of these experimental projections.
39647 *     For "ZPX", longitude and latitude correction surfaces (appearing as
39648 *     "lngcor" or "latcor" terms in the IRAF-specific "WAT" keywords) are
39649 *     not supported. For "TNX" projections, only cubic surfaces encoded as
39650 *     simple polynomials with "half cross-terms" are supported. If an
39651 *     un-usable "TNX" or "ZPX" projection is encountered while reading
39652 *     from a FitsChan, a simpler form of TAN or ZPN projection is used
39653 *     which ignores the unsupported features and may therefore be
39654 *     inaccurate. If this happens, a warning message is added to the
39655 *     contents of the FitsChan as a set of cards using the keyword "ASTWARN".
39656 *
39657 *     You should not normally attempt to mix the foreign FITS encodings within
39658 *     the same FitsChan, since there is a risk that keyword clashes may occur.
39659 
39660 *  The FITS-PC Encoding:
39661 *     The FITS-PC encoding can, for most purposes, be considered as
39662 *     equivalent to the FITS-WCS encoding (above), although it differs
39663 *     in the details of the FITS keywords used. It is used in exactly
39664 *     the same way and has the same restrictions.
39665 
39666 *  The FITS-AIPS Encoding:
39667 *     The FITS-AIPS encoding can, for most purposes, be considered as
39668 *     equivalent to the FITS-WCS encoding (above), although it differs
39669 *     in the details of the FITS keywords used. It is used in exactly
39670 *     the same way and has the same restrictions, but with the
39671 
39672 *     addition of the following:
39673 *
39674 *     - The only celestial coordinate systems that may be represented
39675 *     are equatorial, galactic and ecliptic,
39676 *     - Spectral axes can only be represented if they represent
39677 *     frequency, radio velocity or optical velocity, and are linearly
39678 *     sampled in frequency. In addition, the standard of rest
39679 *     must be LSRK, LSRD, barycentric or geocentric.
39680 *     - Sky projections can be represented only if any associated
39681 *     projection parameters are set to their default values.
39682 *     - The AIT, SFL and MER projections can only be written if the CRVAL
39683 *     keywords are zero for both longitude and latitude axes.
39684 *     - Secondary axis descriptions are not supported, so when writing
39685 *     a FrameSet to a FitsChan, only information from the base and
39686 *     current Frames will be stored.
39687 *     - If there are more than 2 axes in the base and current Frames, any
39688 *     rotation must be restricted to the celestial plane, and must involve
39689 *     no shear.
39690 
39691 *  The FITS-AIPS++ Encoding:
39692 *     The FITS-AIPS++ encoding is based on the FITS-AIPS encoding, but
39693 *     includes some features of the FITS-IRAF and FITS-PC encodings.
39694 *     Specifically, any celestial projections supported by FITS-PC may be
39695 *     used, including those which require parameterisation, and the axis
39696 *     rotation and scaling may be specified using CDi_j keywords. When
39697 *     writing a FITS header, rotation will be specified using CROTA/CDELT
39698 *     keywords if possible, otherwise CDi_j keywords will be used instead.
39699 
39700 *  The FITS-CLASS Encoding:
39701 *     The FITS-CLASS encoding uses the conventions of the CLASS project.
39702 *     These are described in the section "Developer Manual"/"CLASS FITS
39703 
39704 *     Format" contained in the CLASS documentation at:
39705 *
39706 *     http://www.iram.fr/IRAMFR/GILDAS/doc/html/class-html/class.html.
39707 *
39708 
39709 *     This encoding is similar to FITS-AIPS with the following restrictions:
39710 *
39711 *     - When a SpecFrame is created by reading a FITS-CLASS header, the
39712 *       attributes describing the observer's position (ObsLat, ObsLon and
39713 *       ObsAlt) are left unset because the CLASS encoding does not specify
39714 *       these values. Conversions to or from the topocentric standard of rest
39715 *       will therefore be inaccurate (typically by up to about 0.5 km/s)
39716 *       unless suitable values are assigned to these attributes after the
39717 *       FrameSet has been created.
39718 *     - When writing a FrameSet to a FITS-CLASS header, the current Frame
39719 *       in the FrameSet must have at least 3 WCS axes, of which one must be
39720 *       a linear spectral axis. The spectral axis in the created header will
39721 *       always describe frequency. If the spectral axis in the supplied
39722 *       FrameSet refers to some other system (e.g. radio velocity, etc),
39723 *       then it will be converted to frequency.
39724 *     - There must be a pair of celestial axes - either (RA,Dec) or
39725 *       (GLON,GLAT). RA and Dec must be either FK4/B1950 or FK5/J2000.
39726 *     - A limited range of projection codes (TAN, ARC, STG, AIT, SFL, SIN)
39727 *       can be used. For AIT and SFL, the reference point must be at the
39728 *       origin of longitude and latitude. For SIN, the associated projection
39729 *       parameters must both be zero.
39730 *     - No rotation of the celestial axes is allowed, unless the spatial
39731 *       axes are degenerate (i.e. cover only a single pixel).
39732 *     - The frequency axis in the created header will always describe
39733 *       frequency in the source rest frame. If the supplied FrameSet uses
39734 *       some other standard of rest then suitable conversion will be applied.
39735 *     - The source velocity must be defined. In other words, the SpecFrame
39736 *       attributes SourceVel and SourceVRF must have been assigned values.
39737 *     - The frequency axis in a FITS-CLASS header does not represent
39738 *       absolute frequency, but instead represents offsets from the rest
39739 *       frequency in the standard of rest of the source.
39740 *
39741 *     When writing a FrameSet out using FITS-CLASS encoding, the current
39742 *     Frame may be temporarily modified if this will allow the header
39743 *     to be produced. If this is done, the associated pixel->WCS Mapping
39744 *     will also be modified to take account of the changes to the Frame.
39745 *     The modifications performed include re-ordering axes (WCS axes, not
39746 *     pixel axes), changing spectral coordinate system and standard of
39747 *     rest, changing the celestial coordinate system and reference equinox,
39748 *     and changing axis units.
39749 
39750 *  The NATIVE Encoding:
39751 *     The NATIVE encoding may be used to store a description of any
39752 *     class of AST Object in the form of FITS header cards, and (for
39753 *     most practical purposes) any number of these Object descriptions
39754 *     may be stored within a single set of FITS cards. If multiple
39755 *     Object descriptions are stored, they are written and read
39756 *     sequentially. The NATIVE encoding makes use of unique FITS
39757 *     keywords which are designed not to clash with keywords that have
39758 *     already been used for other purposes (if a potential clash is
39759 *     detected, an alternative keyword is constructed to avoid the
39760 *     clash).
39761 *
39762 *     When reading a NATIVE encoded object from a FitsChan (using
39763 c     astRead), FITS header cards are read, starting at the current
39764 f     AST_READ), FITS header cards are read, starting at the current
39765 *     card (as determined by the Card attribute), until the start of
39766 *     the next Object description is found. This description is then
39767 *     read and converted into an AST Object, for which a pointer is
39768 *     returned. Such a read is always destructive and causes all the
39769 *     FITS header cards involved in the Object description to be
39770 *     removed from the FitsChan, which is left positioned at the
39771 *     following card.
39772 *
39773 *     The Object returned may be of any class, depending on the
39774 *     description that was read, and other AST routines may be used to
39775 *     validate it (for example, by examining its Class or ID attribute
39776 c     using astGetC). If further NATIVE encoded Object descriptions
39777 f     using AST_GETC). If further NATIVE encoded Object descriptions
39778 c     exist in the FitsChan, subsequent calls to astRead will return
39779 f     exist in the FitsChan, subsequent calls to AST_READ will return
39780 *     the Objects they describe in sequence (and destroy their
39781 *     descriptions) until no more remain between the current card and
39782 *     the "end-of-file".
39783 *
39784 c     When astWrite is used to write an Object using NATIVE encoding,
39785 f     When AST_WRITE is used to write an Object using NATIVE encoding,
39786 *     a description of the Object is inserted immediately before the
39787 *     current card (as determined by the Card attribute).  Multiple
39788 *     Object descriptions may be written in this way and are stored
39789 *     separately (and sequentially if the Card attribute is not
39790 *     modified between the writes). A write operation using the NATIVE
39791 *     encoding does not over-write previously written Object
39792 *     descriptions. Note, however, that subsequent behaviour is
39793 *     undefined if an Object description is written inside a
39794 *     previously-written description, so this should be avoided.
39795 *
39796 *     When an Object is written to a FitsChan using NATIVE encoding,
39797 c     astWrite should (barring errors) always transfer data and
39798 f     AST_WRITE should (barring errors) always transfer data and
39799 *     return a value of 1.
39800 
39801 *  Applicability:
39802 *     FitsChan
39803 *        All FitsChans have this attribute.
39804 *att--
39805 */
astMAKE_CLEAR(FitsChan,Encoding,encoding,UNKNOWN_ENCODING)39806 astMAKE_CLEAR(FitsChan,Encoding,encoding,UNKNOWN_ENCODING)
39807 astMAKE_SET(FitsChan,Encoding,int,encoding,(
39808    value == NATIVE_ENCODING ||
39809    value == FITSPC_ENCODING ||
39810    value == FITSWCS_ENCODING ||
39811    value == FITSIRAF_ENCODING ||
39812    value == FITSAIPS_ENCODING ||
39813    value == FITSAIPSPP_ENCODING ||
39814    value == FITSCLASS_ENCODING ||
39815    value == DSS_ENCODING ? value :
39816    (astError( AST__BADAT, "astSetEncoding: Unknown encoding system %d "
39817               "supplied.", status, value ), UNKNOWN_ENCODING )))
39818 astMAKE_TEST(FitsChan,Encoding,( this->encoding != UNKNOWN_ENCODING ))
39819 
39820 /* DefB1950 */
39821 /* ======== */
39822 
39823 /*
39824 *att++
39825 *  Name:
39826 *     DefB1950
39827 
39828 *  Purpose:
39829 *     Use FK4 B1950 as defaults?
39830 
39831 *  Type:
39832 *     Public attribute.
39833 
39834 *  Synopsis:
39835 *     Integer (boolean).
39836 
39837 *  Description:
39838 *     This attribute is a boolean value which specifies a default equinox
39839 *     and reference frame to use when reading a FrameSet from a FitsChan
39840 *     with a foreign (i.e. non-native) encoding. It is only used if the FITS
39841 *     header contains RA and DEC axes but contains no information about the
39842 *     reference frame or equinox. If this is the case, then values of FK4 and
39843 *     B1950 are assumed if the DefB1950 attribute has a non-zero value and
39844 *     ICRS is assumed if DefB1950 is zero. The default value for DefB1950
39845 *     depends on the value of the Encoding attribute: for FITS-WCS encoding
39846 *     the default is zero, and for all other encodings it is one.
39847 
39848 *  Applicability:
39849 *     FitsChan
39850 *        All FitsChans have this attribute.
39851 *att--
39852 */
39853 astMAKE_CLEAR(FitsChan,DefB1950,defb1950,-1)
39854 astMAKE_GET(FitsChan,DefB1950,int,1,(this->defb1950 == -1 ? (astGetEncoding(this)== FITSWCS_ENCODING?0:1): this->defb1950))
39855 astMAKE_SET(FitsChan,DefB1950,int,defb1950,( value ? 1 : 0 ))
39856 astMAKE_TEST(FitsChan,DefB1950,( this->defb1950 != -1 ))
39857 
39858 /* TabOK */
39859 /* ===== */
39860 
39861 /*
39862 *att++
39863 *  Name:
39864 *     TabOK
39865 
39866 *  Purpose:
39867 *     Should the FITS-WCS -TAB algorithm be recognised?
39868 
39869 *  Type:
39870 *     Public attribute.
39871 
39872 *  Synopsis:
39873 *     Integer.
39874 
39875 *  Description:
39876 *     This attribute is an integer value which indicates if the "-TAB"
39877 *     algorithm, defined in FITS-WCS paper III, should be supported by
39878 *     the FitsChan. The default value is zero. A zero or negative value
39879 *     results in no support for -TAB axes (i.e. axes that have "-TAB"
39880 *     in their CTYPE keyword value). In this case, the
39881 c     astWrite
39882 f     AST_WRITE
39883 *     method will return zero if the write operation would required the
39884 *     use of the -TAB algorithm, and the
39885 c     astRead
39886 f     AST_READ
39887 *     method will return
39888 c     a NULL pointer
39889 f     AST__NULL
39890 *     if any axis in the supplied header uses the -TAB algorithm.
39891 
39892 *     If TabOK is set to a non-zero positive integer, these methods will
39893 *     recognise and convert axes described by the -TAB algorithm, as
39894 *     follows:
39895 *
39896 c     The astWrite
39897 f     The AST_WRITE
39898 *     method will generate headers that use the -TAB algorithm (if
39899 *     possible) if no other known FITS-WCS algorithm can be used to
39900 *     describe the supplied FrameSet. This will result in a table of
39901 *     coordinate values and index vectors being stored in the FitsChan.
39902 *     After the write operation, the calling application should check to
39903 *     see if such a table has been stored in the FitsChan. If so, the
39904 *     table should be retrived from the FitsChan using the
39905 c     astGetTables
39906 f     AST_GETTABLES
39907 *     method, and the data (and headers) within it copied into a new
39908 *     FITS binary table extension. See
39909 c     astGetTables
39910 f     AST_GETTABLES
39911 *     for more information. The FitsChan uses a FitsTable object to store
39912 *     the table data and headers. This FitsTable will contain the required
39913 *     columns and headers as described by FITS-WCS paper III - the
39914 *     coordinates array will be in a column named "COORDS", and the index
39915 *     vector(s) will be in columns named "INDEX<i>" (where <i> is the index
39916 *     of the corresponding FITS WCS  axis). Note, index vectors are only
39917 *     created if required. The EXTNAME value will be set to the value of the
39918 *     AST__TABEXTNAME constant (currently "WCS-TAB"). The EXTVER header
39919 *     will be set to the positive integer value assigned to the TabOK
39920 *     attribute. No value will be stored for the EXTLEVEL header, and should
39921 *     therefore be considered to default to 1.
39922 *
39923 c     The astRead
39924 f     The AST_READ
39925 *     method will generate a FrameSet from headers that use the -TAB
39926 *     algorithm so long as the necessary FITS binary tables are made
39927 *     available. There are two ways to do this: firstly, if the application
39928 *     knows which FITS binary tables will be needed, then it can create a
39929 *     Fitstable describing each such table and store it in the FitsChan
39930 *     (using method
39931 c     astPutTables or astPutTable) before invoking the astRead method.
39932 f     AST_PUTTABLES or AST_PUTTABLE) before invoking the AST_READ method.
39933 *     Secondly, if the application does not know which FITS binary tables
39934 *     will be needed by
39935 c     astRead,
39936 f     AST_READ,
39937 *     then it can register a call-back function with the FitsChan using
39938 *     method
39939 c     astTableSource.
39940 f     AST_TABLESOURCE.
39941 *     This call-back function will be called from within
39942 c     astRead
39943 f     AST_READ
39944 *     if and when a -TAB header is encountered. When called, its arguments
39945 *     will give the name, version and level of the FITS extension containing
39946 *     a required table. The call-back function should read this table from
39947 *     an external FITS file, and create a corresponding FitsTable which
39948 *     it should then return to
39949 c     astRead. Note, currently astRead
39950 f     AST_READ. Note, currently AST_READ
39951 *     can only handle -TAB headers that describe 1-dimensional (i.e.
39952 *     separable) axes.
39953 
39954 *  Applicability:
39955 *     FitsChan
39956 *        All FitsChans have this attribute.
39957 *att--
39958 */
39959 astMAKE_CLEAR(FitsChan,TabOK,tabok,-INT_MAX)
39960 astMAKE_GET(FitsChan,TabOK,int,0,(this->tabok == -INT_MAX ? 0 : this->tabok))
39961 astMAKE_SET(FitsChan,TabOK,int,tabok,value)
39962 astMAKE_TEST(FitsChan,TabOK,( this->tabok != -INT_MAX ))
39963 
39964 /* CarLin */
39965 /* ====== */
39966 
39967 /*
39968 *att++
39969 *  Name:
39970 *     CarLin
39971 
39972 *  Purpose:
39973 *     Ignore spherical rotations on CAR projections?
39974 
39975 *  Type:
39976 *     Public attribute.
39977 
39978 *  Synopsis:
39979 *     Integer (boolean).
39980 
39981 *  Description:
39982 *     This attribute is a boolean value which specifies how FITS "CAR"
39983 *     (plate carree, or "Cartesian") projections should be treated when
39984 *     reading a FrameSet from a foreign encoded FITS header. If zero (the
39985 *     default), it is assumed that the CAR projection conforms to the
39986 *     conventions described in the FITS world coordinate system (FITS-WCS)
39987 *     paper II "Representation of Celestial Coordinates in FITS" by
39988 *     M. Calabretta & E.W. Greisen. If CarLin is non-zero, then these
39989 *     conventions are ignored, and it is assumed that the mapping from pixel
39990 *     coordinates to celestial coordinates is a simple linear transformation
39991 *     (hence the attribute name "CarLin"). This is appropriate for some older
39992 *     FITS data which claims to have a "CAR" projection, but which in fact do
39993 *     not conform to the conventions of the FITS-WCS paper.
39994 *
39995 *     The FITS-WCS paper specifies that headers which include a CAR projection
39996 *     represent a linear mapping from pixel coordinates to "native spherical
39997 *     coordinates", NOT celestial coordinates. An extra mapping is then
39998 *     required from native spherical to celestial. This mapping is a 3D
39999 *     rotation and so the overall Mapping from pixel to celestial coordinates
40000 *     is NOT linear. See the FITS-WCS papers for further details.
40001 
40002 *  Applicability:
40003 *     FitsChan
40004 *        All FitsChans have this attribute.
40005 *att--
40006 */
40007 astMAKE_CLEAR(FitsChan,CarLin,carlin,-1)
40008 astMAKE_GET(FitsChan,CarLin,int,1,(this->carlin == -1 ? 0 : this->carlin))
40009 astMAKE_SET(FitsChan,CarLin,int,carlin,( value ? 1 : 0 ))
40010 astMAKE_TEST(FitsChan,CarLin,( this->carlin != -1 ))
40011 
40012 /* PolyTan */
40013 /* ======= */
40014 
40015 /*
40016 *att++
40017 *  Name:
40018 *     PolyTan
40019 
40020 *  Purpose:
40021 *     Use PVi_m keywords to define distorted TAN projection?
40022 
40023 *  Type:
40024 *     Public attribute.
40025 
40026 *  Synopsis:
40027 *     Integer.
40028 
40029 *  Description:
40030 *     This attribute is a boolean value which specifies how FITS "TAN"
40031 *     projections should be treated when reading a FrameSet from a foreign
40032 *     encoded FITS header. If zero, the projection is assumed to conform
40033 *     to the published FITS-WCS standard. If positive, the convention
40034 *     for a distorted TAN projection included in an early draft version
40035 *     of FITS-WCS paper II are assumed. In this convention the
40036 *     coefficients of a polynomial distortion to be applied to
40037 *     intermediate world coordinates are specified by the PVi_m keywords.
40038 *     This convention was removed from the paper before publication and so
40039 *     does not form part of the standard. Indeed, it is incompatible with
40040 *     the published standard because it re-defines the meaning of the
40041 *     first five PVi_m keywords on the longitude axis, which are reserved
40042 *     by the published standard for other purposes. However, headers that
40043 *     use this convention are still to be found, for instance the SCAMP
40044 *     utility (http://www.astromatic.net/software/scamp) creates them.
40045 *
40046 *     The default value for the PolyTan attribute is -1. A negative
40047 *     values causes the used convention to depend on the contents
40048 *     of the FitsChan. If the FitsChan contains any PVi_m keywords for
40049 *     the latitude axis, or if it contains PVi_m keywords for the
40050 *     longitude axis with "m" greater than 4, then the distorted TAN
40051 *     convention is used. Otherwise, the standard convention is used.
40052 
40053 *  Applicability:
40054 *     FitsChan
40055 *        All FitsChans have this attribute.
40056 *att--
40057 */
40058 astMAKE_CLEAR(FitsChan,PolyTan,polytan,-INT_MAX)
40059 astMAKE_SET(FitsChan,PolyTan,int,polytan,value)
40060 astMAKE_TEST(FitsChan,PolyTan,( this->polytan != -INT_MAX ))
40061 astMAKE_GET(FitsChan,PolyTan,int,-1,(this->polytan == -INT_MAX ? -1 : this->polytan))
40062 
40063 /* Iwc */
40064 /* === */
40065 
40066 /*
40067 *att++
40068 *  Name:
40069 *     Iwc
40070 
40071 *  Purpose:
40072 *     Include a Frame representing FITS-WCS intermediate world coordinates?
40073 
40074 *  Type:
40075 *     Public attribute.
40076 
40077 *  Synopsis:
40078 *     Integer (boolean).
40079 
40080 *  Description:
40081 *     This attribute is a boolean value which is used when a FrameSet is
40082 *     read from a FitsChan with a foreign FITS encoding (e.g. FITS-WCS) using
40083 c     astRead.
40084 f     AST_READ.
40085 *     If it has a non-zero value then the returned FrameSet will include
40086 *     Frames representing "intermediate world coordinates" (IWC). These
40087 *     Frames will have Domain name "IWC" for primary axis descriptions, and
40088 *     "IWCa" for secondary axis descriptions, where "a" is replaced by
40089 *     the single alternate axis description character, as used in the
40090 *     FITS-WCS header. The default value for "Iwc" is zero.
40091 *
40092 *     FITS-WCS paper 1 defines IWC as a Cartesian coordinate system with one
40093 *     axis for each WCS axis, and is the coordinate system produced by the
40094 *     rotation matrix (represented by FITS keyword PCi_j, CDi_j, etc).
40095 *     For instance, for a 2-D FITS-WCS header describing projected
40096 *     celestial longitude and latitude, the intermediate world
40097 *     coordinates represent offsets in degrees from the reference point
40098 *     within the plane of projection.
40099 
40100 *  Applicability:
40101 *     FitsChan
40102 *        All FitsChans have this attribute.
40103 *att--
40104 */
40105 astMAKE_CLEAR(FitsChan,Iwc,iwc,-1)
40106 astMAKE_GET(FitsChan,Iwc,int,1,(this->iwc == -1 ? 0 : this->iwc))
40107 astMAKE_SET(FitsChan,Iwc,int,iwc,( value ? 1 : 0 ))
40108 astMAKE_TEST(FitsChan,Iwc,( this->iwc != -1 ))
40109 
40110 /*
40111 *att++
40112 *  Name:
40113 *     CDMatrix
40114 
40115 *  Purpose:
40116 *     Use CDi_j keywords to represent pixel scaling, rotation, etc?
40117 
40118 *  Type:
40119 *     Public attribute.
40120 
40121 *  Synopsis:
40122 *     Integer (boolean).
40123 
40124 *  Description:
40125 *     This attribute is a boolean value which specifies how the linear
40126 *     transformation from pixel coordinates to intermediate world
40127 *     coordinates should be represented within a FitsChan when using
40128 *     FITS-WCS encoding. This transformation describes the scaling,
40129 *     rotation, shear, etc., of the pixel axes.
40130 *
40131 *     If the attribute has a non-zero value then the transformation is
40132 *     represented by a set of CDi_j keywords representing a square matrix
40133 *     (where "i" is the index of an intermediate world coordinate axis
40134 *     and "j" is the index of a pixel axis). If the attribute has a zero
40135 *     value the transformation is represented by a set of PCi_j keywords
40136 *     (which also represent a square matrix) together with a corresponding
40137 *     set of CDELTi keywords representing the axis scalings. See FITS-WCS
40138 *     paper II "Representation of Celestial Coordinates in FITS" by
40139 *     M. Calabretta & E.W. Greisen, for a complete description of these two
40140 *     schemes.
40141 *
40142 *     The default value of the CDMatrix attribute is determined by the
40143 *     contents of the FitsChan at the time the attribute is accessed. If
40144 *     the FitsChan contains any CDi_j keywords then the default value is
40145 *     non-zero. Otherwise it is zero. Note, reading a FrameSet from a
40146 *     FitsChan will in general consume any CDi_j keywords present in the
40147 *     FitsChan. Thus the default value for CDMatrix following a read will
40148 *     usually be zero, even if the FitsChan originally contained some
40149 *     CDi_j keywords. This behaviour is similar to that of the Encoding
40150 *     attribute, the default value for which is determined by the contents
40151 *     of the FitsChan at the time the attribute is accessed. If you wish
40152 *     to retain the original value of the CDMatrix attribute (that is,
40153 *     the value before reading the FrameSet) then you should enquire the
40154 *     default value before doing the read, and then set that value
40155 *     explicitly.
40156 
40157 *  Applicability:
40158 *     FitsChan
40159 *        All FitsChans have this attribute.
40160 *att--
40161 */
40162 astMAKE_CLEAR(FitsChan,CDMatrix,cdmatrix,-1)
40163 astMAKE_SET(FitsChan,CDMatrix,int,cdmatrix,( value ? 1 : 0 ))
40164 astMAKE_TEST(FitsChan,CDMatrix,( this->cdmatrix != -1 ))
40165 
40166 /* Clean */
40167 /* ===== */
40168 
40169 /*
40170 *att++
40171 *  Name:
40172 *     Clean
40173 
40174 *  Purpose:
40175 *     Remove cards used whilst reading even if an error occurs?
40176 
40177 *  Type:
40178 *     Public attribute.
40179 
40180 *  Synopsis:
40181 *     Integer (boolean).
40182 
40183 *  Description:
40184 *     This attribute indicates whether or not cards should be removed from
40185 *     the FitsChan if an error occurs within
40186 c     astRead.
40187 f     AST_READ.
40188 *     A succesful read on a FitsChan always results in the removal of
40189 *     the cards which were involved in the description of the returned
40190 *     Object. However, in the event of an error during the read (for instance
40191 *     if the cards in the FitsChan have illegal values, or if some required
40192 *     cards are missing) no cards will be removed from the FitsChan if
40193 *     the Clean attribute is zero (the default). If Clean is non-zero then
40194 *     any cards which were used in the aborted attempt to read an object
40195 *     will be removed.
40196 *
40197 *     This provides a means of "cleaning" a FitsChan of WCS related cards
40198 *     which works even in the event of the cards not forming a legal WCS
40199 *     description.
40200 
40201 *  Applicability:
40202 *     FitsChan
40203 *        All FitsChans have this attribute.
40204 *att--
40205 */
40206 astMAKE_CLEAR(FitsChan,Clean,clean,-1)
40207 astMAKE_SET(FitsChan,Clean,int,clean,( value ? 1 : 0 ))
40208 astMAKE_TEST(FitsChan,Clean,( this->clean != -1 ))
40209 
40210 /*
40211 *att++
40212 *  Name:
40213 *     FitsAxisOrder
40214 
40215 *  Purpose:
40216 *     Frame title.
40217 
40218 *  Type:
40219 *     Public attribute.
40220 
40221 *  Synopsis:
40222 *     String.
40223 
40224 *  Description:
40225 *     This attribute specifies the order for the WCS axes in any new
40226 *     FITS-WCS headers created using the
40227 c     astWrite
40228 f     AST_WRITE
40229 *     method.
40230 *
40231 *     The value of the FitsAxisOrder attribute can be either "<auto>"
40232 *     (the default value), "<copy>" or a space-separated list of axis
40233 *     symbols:
40234 *
40235 *     "<auto>": causes the WCS axis order to be chosen automatically so that
40236 *     the i'th WCS axis in the new FITS header is the WCS axis which is
40237 *     more nearly parallel to the i'th pixel axis.
40238 *
40239 *     "<copy>": causes the WCS axis order to be set so that the i'th WCS
40240 *     axis in the new FITS header is the i'th WCS axis in the current
40241 *     Frame of the FrameSet being written out to the header.
40242 *
40243 *     "Sym1 Sym2...": the space-separated list is seached in turn for
40244 *     the Symbol attribute of each axis in the current Frame of the
40245 *     FrameSet. The order in which these Symbols occur within the
40246 *     space-separated list defines the order of the WCS axes in the
40247 *     new FITS header. An error is reported if Symbol for a current
40248 *     Frame axis is not present in the supplied list. However, no error
40249 *     is reported if the list contains extra words that do not correspond
40250 *     to the Symbol of any current Frame axis.
40251 
40252 *  Applicability:
40253 *     FitsChan
40254 *        All FitsChans have this attribute.
40255 *att--
40256 */
40257 astMAKE_CLEAR(FitsChan,FitsAxisOrder,fitsaxisorder,astFree( this->fitsaxisorder ))
40258 astMAKE_GET(FitsChan,FitsAxisOrder,const char *,NULL,(this->fitsaxisorder ? this->fitsaxisorder : "<auto>" ))
40259 astMAKE_SET(FitsChan,FitsAxisOrder,const char *,fitsaxisorder,astStore( this->fitsaxisorder, value, strlen( value ) + (size_t) 1 ))
40260 astMAKE_TEST(FitsChan,FitsAxisOrder,( this->fitsaxisorder != NULL ))
40261 
40262 /* FitsDigits. */
40263 /* =========== */
40264 
40265 /*
40266 *att++
40267 *  Name:
40268 *     FitsDigits
40269 
40270 *  Purpose:
40271 *     Digits of precision for floating point FITS values.
40272 
40273 *  Type:
40274 *     Public attribute.
40275 
40276 *  Synopsis:
40277 *     Integer.
40278 
40279 *  Description:
40280 *     This attribute gives the number of significant decimal digits to
40281 *     use when formatting floating point values for inclusion in the
40282 *     FITS header cards within a FitsChan.
40283 *
40284 *     By default, a positive value is used which results in no loss of
40285 c     information, assuming that the value's precision is double.
40286 f     information, assuming that the value is double precision.
40287 *     Usually, this causes no problems.
40288 *
40289 *     However, to adhere strictly to the recommendations of the FITS
40290 *     standard, the width of the formatted value (including sign,
40291 *     decimal point and exponent) ought not to be more than 20
40292 *     characters. If you are concerned about this, you should set
40293 *     FitsDigits to a negative value, such as -15. In this case, the
40294 *     absolute value (+15) indicates the maximum number of significant
40295 *     digits to use, but the actual number used may be fewer than this
40296 *     to ensure that the FITS recommendations are satisfied. When
40297 *     using this approach, the resulting number of significant digits
40298 *     may depend on the value being formatted and on the presence of
40299 *     any sign, decimal point or exponent.
40300 *
40301 *     The value of this attribute is effective when FITS header cards
40302 *     are output, either using
40303 c     astFindFits or by the action of the FitsChan's sink function
40304 f     AST_FINDFITS or by the action of the FitsChan's sink routine
40305 *     when it is finally deleted.
40306 
40307 *  Applicability:
40308 *     FitsChan
40309 *        All FitsChans have this attribute.
40310 *att--
40311 */
40312 astMAKE_CLEAR(FitsChan,FitsDigits,fitsdigits,DBL_DIG)
40313 astMAKE_GET(FitsChan,FitsDigits,int,DBL_DIG,this->fitsdigits)
40314 astMAKE_SET(FitsChan,FitsDigits,int,fitsdigits,value)
40315 astMAKE_TEST(FitsChan,FitsDigits,( this->fitsdigits != DBL_DIG ))
40316 
40317 /* CardComm */
40318 /* ======== */
40319 
40320 /*
40321 *att++
40322 *  Name:
40323 *     CardComm
40324 
40325 *  Purpose:
40326 *     The comment for the current card in a FitsChan.
40327 
40328 *  Type:
40329 *     Public attribute.
40330 
40331 *  Synopsis:
40332 *     String, read-only.
40333 
40334 *  Description:
40335 *     This attribute gives the comment for the current card of the
40336 *     FitsChan. A zero-length string is returned if the card has no comment.
40337 
40338 *  Applicability:
40339 *     FitsChan
40340 *        All FitsChans have this attribute.
40341 *att--
40342 */
40343 
40344 /* CardName */
40345 /* ======== */
40346 
40347 /*
40348 *att++
40349 *  Name:
40350 *     CardName
40351 
40352 *  Purpose:
40353 *     The keyword name of the current card in a FitsChan.
40354 
40355 *  Type:
40356 *     Public attribute.
40357 
40358 *  Synopsis:
40359 *     String, read-only.
40360 
40361 *  Description:
40362 *     This attribute gives the name of the keyword for the
40363 *     current card of the FitsChan.
40364 
40365 *  Applicability:
40366 *     FitsChan
40367 *        All FitsChans have this attribute.
40368 *att--
40369 */
40370 
40371 /* CardType */
40372 /* ======== */
40373 
40374 /*
40375 *att++
40376 *  Name:
40377 *     CardType
40378 
40379 *  Purpose:
40380 *     The data type of the current card in a FitsChan.
40381 
40382 *  Type:
40383 *     Public attribute.
40384 
40385 *  Synopsis:
40386 *     Integer, read-only.
40387 
40388 *  Description:
40389 *     This attribute gives the data type of the keyword value for the
40390 *     current card of the FitsChan. It will be one of the following
40391 *     integer constants: AST__NOTYPE, AST__COMMENT, AST__INT, AST__FLOAT,
40392 *     AST__STRING, AST__COMPLEXF, AST__COMPLEXI, AST__LOGICAL,
40393 *     AST__CONTINUE, AST__UNDEF.
40394 
40395 *  Applicability:
40396 *     FitsChan
40397 *        All FitsChans have this attribute.
40398 *att--
40399 */
40400 
40401 /* Ncard */
40402 /* ===== */
40403 
40404 /*
40405 *att++
40406 *  Name:
40407 *     Ncard
40408 
40409 *  Purpose:
40410 *     Number of FITS header cards in a FitsChan.
40411 
40412 *  Type:
40413 *     Public attribute.
40414 
40415 *  Synopsis:
40416 *     Integer, read-only.
40417 
40418 *  Description:
40419 *     This attribute gives the total number of FITS header cards
40420 *     stored in a FitsChan. It is updated as cards are added or
40421 *     deleted.
40422 
40423 *  Applicability:
40424 *     FitsChan
40425 *        All FitsChans have this attribute.
40426 *att--
40427 */
40428 
40429 /* Nkey */
40430 /* ==== */
40431 
40432 /*
40433 *att++
40434 *  Name:
40435 *     Nkey
40436 
40437 *  Purpose:
40438 *     Number of unique FITS keywords in a FitsChan.
40439 
40440 *  Type:
40441 *     Public attribute.
40442 
40443 *  Synopsis:
40444 *     Integer, read-only.
40445 
40446 *  Description:
40447 *     This attribute gives the total number of unique FITS keywords
40448 *     stored in a FitsChan. It is updated as cards are added or
40449 *     deleted. If no keyword occurrs more than once in the FitsChan, the
40450 *     Ncard and Nkey attributes will be equal. If any keyword occurrs
40451 *     more than once, the Nkey attribute value will be smaller than
40452 *     the Ncard attribute value.
40453 
40454 *  Applicability:
40455 *     FitsChan
40456 *        All FitsChans have this attribute.
40457 *att--
40458 */
40459 
40460 /* Warnings. */
40461 /* ======== */
40462 
40463 /*
40464 *att++
40465 *  Name:
40466 *     Warnings
40467 
40468 *  Purpose:
40469 *     Controls the issuing of warnings about various conditions.
40470 
40471 *  Type:
40472 *     Public attribute.
40473 
40474 *  Synopsis:
40475 *     String
40476 
40477 *  Description:
40478 *     This attribute controls the issuing of warnings about selected
40479 *     conditions when an Object or keyword is read from or written to a
40480 *     FitsChan. The value supplied for the Warnings attribute should
40481 *     consist of a space separated list of condition names (see the
40482 *     AllWarnings attribute for a list of the currently defined names).
40483 *     Each name indicates a condition which should be reported. The default
40484 *     value for Warnings is the string "Tnx Zpx BadCel BadMat BadPV BadCTYPE".
40485 *
40486 *     The text of any warning will be stored within the FitsChan in the
40487 *     form of one or more new header cards with keyword ASTWARN. If
40488 *     required, applications can check the FitsChan for ASTWARN cards
40489 c     (using astFindFits) after the call to astRead or astWrite has been
40490 f     (using AST_FINDFITS) after the call to AST_READ or AST_WRITE has been
40491 *     performed, and report the text of any such cards to the user. ASTWARN
40492 *     cards will be propagated to any output header unless they are
40493 c     deleted from the FitsChan using astDelFits.
40494 f     deleted from the FitsChan using astDelFits.
40495 
40496 *  Notes:
40497 *     This attribute only controls the warnings that are to be stored as
40498 *     a set of header cards in the FitsChan as described above. It has no
40499 *     effect on the storage of warnings in the parent Channel structure.
40500 *     All warnings are stored in the parent Channel structure, from where
40501 *     they can be retrieved using the
40502 c     astWarnings
40503 f     AST_WARNINGS
40504 *     function.
40505 
40506 *  Applicability:
40507 *     FitsChan
40508 *        All FitsChans have this attribute.
40509 *att--
40510 */
40511 
40512 /* Clear the Warnings value by freeing the allocated memory and assigning
40513    a NULL pointer. */
40514 astMAKE_CLEAR(FitsChan,Warnings,warnings,astFree( this->warnings ))
40515 
40516 /* If the Warnings value is not set, supply a default in the form of a
40517    pointer to the constant string "Tnx Zpx BadCel BadMat BadCTYPE". */
40518 astMAKE_GET(FitsChan,Warnings,const char *,NULL,( this->warnings ? this->warnings :
40519                                                             "Tnx Zpx BadPV BadCel BadMat BadCTYPE" ))
40520 
40521 /* Set a Warnings value by freeing any previously allocated memory, allocating
40522    new memory, storing the string and saving the pointer to the copy.
40523    First check that the list does not contain any unknown conditions. If
40524    it does, an error is reported by GoodWarns and the current attribute value
40525    is retained. */
40526 astMAKE_SET(FitsChan,Warnings,const char *,warnings,( GoodWarns( value, status ) ?
40527                                 astStore( this->warnings, value, strlen( value ) + (size_t) 1 ) :
40528                                 this->warnings))
40529 
40530 /* The Warnings value is set if the pointer to it is not NULL. */
40531 astMAKE_TEST(FitsChan,Warnings,( this->warnings != NULL ))
40532 
40533 /* AllWarnings. */
40534 /* ============ */
40535 
40536 /*
40537 *att++
40538 *  Name:
40539 *     AllWarnings
40540 
40541 *  Purpose:
40542 *     A list of all currently available condition names.
40543 
40544 *  Type:
40545 *     Public attribute.
40546 
40547 *  Synopsis:
40548 *     String, read-only
40549 
40550 *  Description:
40551 *     This read-only attribute is a space separated list of all the conditions
40552 *     names recognized by the Warnings attribute. The names are listed
40553 *     below.
40554 
40555 *  Conditions:
40556 *     The following conditions are currently recognised (all are
40557 
40558 *     case-insensitive):
40559 *
40560 *     - "BadCel": This condition arises when reading a FrameSet from a
40561 *     non-Native encoded FitsChan if an unknown celestial co-ordinate
40562 *     system is specified by the CTYPE keywords.
40563 *
40564 *     - "BadCTYPE": This condition arises when reading a FrameSet from a
40565 *     non-Native encoded FitsChan if an illegal algorithm code is specified
40566 *     by a CTYPE keyword, and the illegal code can be converted to an
40567 *     equivalent legal code.
40568 *
40569 *     - "BadLat": This condition arises when reading a FrameSet from a
40570 *     non-Native encoded FitsChan if the latitude of the reference point
40571 *     has an absolute value greater than 90 degrees. The actual absolute
40572 *     value used is set to exactly 90 degrees in these cases.
40573 *
40574 *     - "BadMat": This condition arises if the matrix describing the
40575 *     transformation from pixel offsets to intermediate world coordinates
40576 *     cannot be inverted. This matrix describes the scaling, rotation, shear,
40577 *     etc., applied to the pixel axes, and is specified by keywords such as
40578 *     PCi_j, CDi_j, CROTA, etc. For example, the matrix will not be invertable
40579 *     if any rows or columns consist entirely of zeros. The FITS-WCS Paper I
40580 *     "Representation of World Coordinates in FITS" by Greisen & Calabretta
40581 *     requires that this matrix be invertable. Many operations (such as
40582 *     grid plotting) will not be possible if the matrix cannot be inverted.
40583 *
40584 *     - "BadPV": This condition arises when reading a FrameSet from a
40585 *     non-Native encoded FitsChan. It is issued if a PVi_m header is found
40586 *     that refers to a projection parameter that is not used by the
40587 *     projection type specified by CTYPE, or the PV values are otherwise
40588 *     inappropriate for the projection type.
40589 *
40590 *     - "BadVal": This condition arises when reading a FrameSet from a
40591 *     non-Native encoded FitsChan if it is not possible to convert the
40592 *     value of a FITS keywords to the expected type. For instance, this
40593 *     can occur if the FITS header contains a string value for a keyword
40594 *     which should have a floating point value, or if the keyword has no
40595 *     value at all (i.e. is a comment card).
40596 *
40597 *     - "Distortion": This condition arises when reading a FrameSet from a
40598 *     non-Native encoded FitsChan if any of the CTYPE keywords specify an
40599 *     unsupported distortion code using the "4-3-3" format specified in
40600 *     FITS-WCS paper IV. Such distortion codes are ignored.
40601 *
40602 *     - "NoCTYPE": This condition arises if a default CTYPE value is used
40603 c     within astRead, due to no value being present in the supplied FitsChan.
40604 f     within AST_READ, due to no value being present in the supplied FitsChan.
40605 *     This condition is only tested for when using non-Native encodings.
40606 *
40607 *     - "NoEquinox": This condition arises if a default equinox value is used
40608 c     within astRead, due to no value being present in the supplied FitsChan.
40609 f     within AST_READ, due to no value being present in the supplied FitsChan.
40610 *     This condition is only tested for when using non-Native encodings.
40611 *
40612 *     - "NoRadesys": This condition arises if a default reference frame is
40613 c     used for an equatorial co-ordinate system within astRead, due to no
40614 f     used for an equatorial co-ordinate system within AST_READ, due to no
40615 *     value being present in the supplied FitsChan. This condition is only
40616 *     tested for when using non-Native encodings.
40617 *
40618 *     - "NoLonpole": This condition arises if a default value is used for
40619 c     the LONPOLE keyword within astRead, due to no value being present
40620 f     the LONPOLE keyword within AST_READ, due to no value being present
40621 *     in the supplied FitsChan. This condition is only tested for when
40622 *     using non-Native encodings.
40623 *
40624 *     - "NoLatpole": This condition arises if a default value is used for
40625 c     the LATPOLE keyword within astRead, due to no value being present
40626 f     the LATPOLE keyword within AST_READ, due to no value being present
40627 *     in the supplied FitsChan. This condition is only tested for when
40628 *     using non-Native encodings.
40629 *
40630 *     - "NoMjd-obs": This condition arises if a default value is used for
40631 c     the date of observation within astRead, due to no value being present
40632 f     the date of observation within AST_READ, due to no value being present
40633 *     in the supplied FitsChan. This condition is only tested for when using
40634 *     non-Native encodings.
40635 *
40636 *     - "Tnx": This condition arises if a FrameSet is read from a FITS
40637 *     header containing an IRAF "TNX" projection which includes terms
40638 *     not supproted by AST. Such terms are ignored and so the resulting
40639 *     FrameSet may be inaccurate.
40640 *
40641 *     - "Zpx": This condition arises if a FrameSet is read from a FITS
40642 *     header containing an IRAF "ZPX" projection which includes "lngcor"
40643 *     or "latcor" correction terms. These terms are not supported by AST
40644 *     and are ignored. The resulting FrameSet may therefore be inaccurate.
40645 
40646 *  Applicability:
40647 *     FitsChan
40648 *        All FitsChans have this attribute.
40649 *att--
40650 */
40651 
40652 /* Copy constructor. */
40653 /* ----------------- */
40654 
40655 static void Copy( const AstObject *objin, AstObject *objout, int *status ) {
40656 /*
40657 *  Name:
40658 *     Copy
40659 
40660 *  Purpose:
40661 *     Copy constructor for FitsChan objects.
40662 
40663 *  Type:
40664 *     Private function.
40665 
40666 *  Synopsis:
40667 *     void Copy( const AstObject *objin, AstObject *objout, int *status )
40668 
40669 *  Description:
40670 *     This function implements the copy constructor for FitsChan objects.
40671 
40672 *  Parameters:
40673 *     objin
40674 *        Pointer to the FitsChan to be copied.
40675 *     objout
40676 *        Pointer to the FitsChan being constructed.
40677 *     status
40678 *        Pointer to the inherited status variable.
40679 
40680 *  Notes:
40681 *     - The source and sink functions are not propagated (i.e. the
40682 *     pointers are set NULL in the output FitsChan).
40683 *     - This constructor makes a deep copy, including a copy of the
40684 *     keyword values.
40685 */
40686 
40687 /* Local Variables: */
40688    astDECLARE_GLOBALS        /* Declare the thread specific global data */
40689    const char *class;        /* Pointer to object class */
40690    AstFitsChan *in;          /* Pointer to input FitsChan */
40691    AstFitsChan *out;         /* Pointer to output FitsChan */
40692    int *flags;
40693    int icard;
40694    int old_ignore_used;      /* Original value of external variable ignore_used */
40695 
40696 /* Check the global error status. */
40697    if ( !astOK ) return;
40698 
40699 /* Get a pointer to the structure holding thread-specific global data. */
40700    astGET_GLOBALS(objin);
40701 
40702 /* Obtain pointers to the input and output FitsChans. */
40703    in = (AstFitsChan *) objin;
40704    out = (AstFitsChan *) objout;
40705 
40706 /* Nullify all pointers in the output FitsChan so that the input
40707    data will not be deleted in the event of an error occurring. */
40708    out->card = NULL;
40709    out->head = NULL;
40710    out->keyseq = NULL;
40711    out->keywords = NULL;
40712    out->source = NULL;
40713    out->saved_source = NULL;
40714    out->source_wrap = NULL;
40715    out->sink = NULL;
40716    out->sink_wrap = NULL;
40717    out->warnings = NULL;
40718    out->tabsource = NULL;
40719    out->tabsource_wrap = NULL;
40720 
40721 /* Store the object class. */
40722    class = astGetClass( in );
40723 
40724 /* Ensure all cards are copied, including those already read by astRead. */
40725    old_ignore_used = ignore_used;
40726    ignore_used = 0;
40727 
40728 /* Save the current card index in the input FitsChan. */
40729    icard = astGetCard( in );
40730 
40731 /* Rewind the input FitsChan. */
40732    astClearCard( in );
40733 
40734 /* Copy all the FitsCard structures from input to output. */
40735    while( !astFitsEof( in ) && astOK ){
40736 
40737 /* Get a pointer to the flags mask for this card. */
40738       flags = CardFlags( in, status );
40739 
40740 /* Store a new card in the output, holding the same information as the
40741    input card. */
40742       NewCard( out, CardName( in, status ), CardType( in, status ), CardData( in, NULL, status ),
40743                CardComm( in, status ), (flags?(*flags):0), status );
40744 
40745 /* Move on to the next input card. */
40746       MoveCard( in, 1, "astCopy", class, status );
40747    }
40748 
40749 /* Set the current card in both input and output to the current input
40750    card on entry. */
40751    astSetCard( in, icard );
40752    astSetCard( out, icard );
40753 
40754 /* Copy the list of keyword sequence numbers used. */
40755    if( in->keyseq ) out->keyseq = astCopy( in->keyseq );
40756 
40757 /* Copy the Warnings attribute value */
40758    if( in->warnings ) out->warnings = astStore( NULL, in->warnings,
40759                                                 strlen( in->warnings ) + 1 );
40760 
40761 /* Copy any tables currently in the FitsChan structure. */
40762    if( in->tables ) out->tables = astCopy( in->tables );
40763 
40764 /* Reinstate the original setting of the external ignore_used variable. */
40765    ignore_used = old_ignore_used;
40766 
40767 /* If an error occurred, delete the contents of the output Object. */
40768    if( !astOK ) Delete( objout, status );
40769 }
40770 
40771 /* Destructor. */
40772 /* ----------- */
40773 
Delete(AstObject * obj,int * status)40774 static void Delete( AstObject *obj, int *status ) {
40775 /*
40776 *  Name:
40777 *     Delete
40778 
40779 *  Purpose:
40780 *     Destructor for FitsChan objects.
40781 
40782 *  Type:
40783 *     Private function.
40784 
40785 *  Synopsis:
40786 *     void Delete( AstObject *obj, int *status )
40787 
40788 *  Description:
40789 *     This function implements the destructor for FitsChan objects.
40790 
40791 *  Parameters:
40792 *     obj
40793 *        Pointer to the FitsChan to be deleted.
40794 *     status
40795 *        Pointer to the inherited status variable.
40796 
40797 *  Notes:
40798 *     This function attempts to execute even if the global error status is
40799 *     set.
40800 */
40801 
40802 /* Local Variables: */
40803    AstFitsChan *this;            /* Pointer to FitsChan */
40804 
40805 /* Obtain a pointer to the FitsChan structure. */
40806    this = (AstFitsChan *) obj;
40807 
40808 /* Write out the contents of the FitsChan using the sink function
40809    provided when it was created. */
40810    WriteToSink( this, status );
40811 
40812 /* Remove all cards from the FitsChan. */
40813    EmptyFits( this, status );
40814 }
40815 
40816 /* Dump function. */
40817 /* -------------- */
40818 
Dump(AstObject * this_object,AstChannel * channel,int * status)40819 static void Dump( AstObject *this_object, AstChannel *channel, int *status ) {
40820 /*
40821 *  Name:
40822 *     Dump
40823 
40824 *  Purpose:
40825 *     Dump function for FitsChan objects.
40826 
40827 *  Type:
40828 *     Private function.
40829 
40830 *  Synopsis:
40831 *     void Dump( AstObject *this, AstChannel *channel, int *status )
40832 
40833 *  Description:
40834 *     This function implements the Dump function which writes out data
40835 *     for the FitsChan class to an output Channel.
40836 
40837 *  Parameters:
40838 *     this
40839 *        Pointer to the FitsChan whose data are being written.
40840 *     channel
40841 *        Pointer to the Channel to which the data are being written.
40842 *     status
40843 *        Pointer to the inherited status variable.
40844 */
40845 #define KEY_LEN 50               /* Maximum length of a keyword */
40846 
40847 /* Local Variables: */
40848    AstFitsChan *this;            /* Pointer to the FitsChan structure */
40849    astDECLARE_GLOBALS            /* Declare the thread specific global data */
40850    char buff[ KEY_LEN + 1 ];     /* Buffer for keyword string */
40851    const char *class;            /* Object class */
40852    const char *sval;             /* Pointer to string value */
40853    int cardtype;                 /* Keyword data type */
40854    int flags;                    /* Keyword flags */
40855    int icard;                    /* Index of current card */
40856    int ival;                     /* Integer value */
40857    int ncard;                    /* No. of cards dumped so far */
40858    int old_ignore_used;          /* Original value of external variable ignore_used */
40859    int set;                      /* Attribute value set? */
40860    void *data;                   /* Pointer to keyword data value */
40861 
40862 /* Check the global error status. */
40863    if ( !astOK ) return;
40864 
40865 /* Get a pointer to the structure holding thread-specific global data. */
40866    astGET_GLOBALS(this_object);
40867 
40868 /* Obtain a pointer to the FitsChan structure. */
40869    this = (AstFitsChan *) this_object;
40870 
40871 /* Store the object class. */
40872    class = astGetClass( this );
40873 
40874 /* Save the index of ht ecurrent card. */
40875    icard = astGetCard( this );
40876 
40877 /* Write out values representing the instance variables for the
40878    FitsChan class.  Accompany these with appropriate comment strings,
40879    possibly depending on the values being written.*/
40880 
40881 /* Card. */
40882 /* ----- */
40883    astWriteInt( channel, "Card", 1, 1, icard, "Index of current card" );
40884 
40885 /* Encoding. */
40886 /* --------- */
40887    set = TestEncoding( this, status );
40888    ival = set ? GetEncoding( this, status ) : astGetEncoding( this );
40889    if( ival > UNKNOWN_ENCODING && ival <= MAX_ENCODING ) {
40890       astWriteString( channel, "Encod", set, 1, xencod[ival], "Encoding system" );
40891    } else {
40892       astWriteString( channel, "Encod", set, 1, UNKNOWN_STRING, "Encoding system" );
40893    }
40894 
40895 /* FitsAxisOrder. */
40896 /* -------------- */
40897    set = TestFitsAxisOrder( this, status );
40898    sval = set ? GetFitsAxisOrder( this, status ) : astGetFitsAxisOrder( this );
40899    astWriteString( channel, "FAxOrd", set, 1, sval,
40900                       "Order of WCS axes in new FITS headers" );
40901 
40902 /* FitsDigits. */
40903 /* ----------- */
40904    set = TestFitsDigits( this, status );
40905    ival = set ? GetFitsDigits( this, status ) : astGetFitsDigits( this );
40906    astWriteInt( channel, "FitsDg", set, 1, ival, "No. of digits for floating point values" );
40907 
40908 /* DefB1950 */
40909 /* -------- */
40910    set = TestDefB1950( this, status );
40911    ival = set ? GetDefB1950( this, status ) : astGetDefB1950( this );
40912    astWriteInt( channel, "DfB1950", set, 1, ival, (ival ? "Default to FK4 B1950": "Default to ICRS") );
40913 
40914 /* TabOK */
40915 /* ----- */
40916    set = TestTabOK( this, status );
40917    ival = set ? GetTabOK( this, status ) : astGetTabOK( this );
40918    astWriteInt( channel, "TabOK", set, 1, ival, ( ival > 0 ? "EXTVER value for -TAB headers": "Do not support -TAB CTYPE codes") );
40919 
40920 /* CDMatrix */
40921 /* -------- */
40922    set = TestCDMatrix( this, status );
40923    ival = set ? GetCDMatrix( this, status ) : astGetCDMatrix( this );
40924    astWriteInt( channel, "CdMat", set, 1, ival, (ival ? "Use CD Matrix":"Use PC matrix") );
40925 
40926 /* CarLin */
40927 /* ------ */
40928    set = TestCarLin( this, status );
40929    ival = set ? GetCarLin( this, status ) : astGetCarLin( this );
40930    astWriteInt( channel, "CarLin", set, 1, ival, (ival ? "Use simple linear CAR projections": "Use full FITS-WCS CAR projections") );
40931 
40932 /* PolyTan */
40933 /* ------- */
40934    set = TestPolyTan( this, status );
40935    ival = set ? GetPolyTan( this, status ) : astGetPolyTan( this );
40936    astWriteInt( channel, "PolyTan", set, 0, ival, (ival ? "Use distorted TAN convention": "Use standard TAN convention") );
40937 
40938 /* Iwc */
40939 /* --- */
40940    set = TestIwc( this, status );
40941    ival = set ? GetIwc( this, status ) : astGetIwc( this );
40942    astWriteInt( channel, "Iwc", set, 1, ival, (ival ? "Include an IWC Frame": "Do not include an IWC Frame") );
40943 
40944 /* Clean */
40945 /* ----- */
40946    set = TestClean( this, status );
40947    ival = set ? GetClean( this, status ) : astGetClean( this );
40948    astWriteInt( channel, "Clean", set, 0, ival, "Always remove used cards?" );
40949 
40950 /* Warnings. */
40951 /* --------- */
40952    set = TestWarnings( this, status );
40953    sval = set ? GetWarnings( this, status ) : astGetWarnings( this );
40954    astWriteString( channel, "Warn", set, 1, sval, "Warnings to be reported" );
40955 
40956 /* Now do instance variables which are not attributes. */
40957 /* =================================================== */
40958 
40959 /* Ensure all cards are copied, including those already read by astRead. */
40960    old_ignore_used = ignore_used;
40961    ignore_used = 0;
40962 
40963 /* Rewind the FitsChan. */
40964    astClearCard( this );
40965 
40966 /* Dump each card. */
40967    ncard = 1;
40968    while( !astFitsEof( this ) && astOK ){
40969 
40970 /* Write out the keyword name. */
40971       if( CardName( this, status ) ){
40972          (void) sprintf( buff, "Nm%d", ncard );
40973          astWriteString( channel, buff, 1, 1, CardName( this, status ),
40974                          "FITS keyword name" );
40975       }
40976 
40977 /* Write out the keyword type. */
40978       cardtype = CardType( this, status );
40979       (void) sprintf( buff, "Ty%d", ncard );
40980       astWriteString( channel, buff, 1, 1, type_names[ cardtype ],
40981                       "FITS keyword data type" );
40982 
40983 /* Write out the flag values if any are non-zero. */
40984       flags = *CardFlags( this, status );
40985       if( flags ){
40986          (void) sprintf( buff, "Fl%d", ncard );
40987          astWriteInt( channel, buff, 1, 1, flags, "FITS keyword flags" );
40988       }
40989 
40990 /* Write out the data value, if defined, using the appropriate data type. */
40991       data = CardData( this, NULL, status );
40992       if( data && cardtype != AST__UNDEF ){
40993          if( cardtype == AST__FLOAT ){
40994             (void) sprintf( buff, "Dt%d", ncard );
40995             astWriteDouble( channel, buff, 1, 1, *( (double *) data ),
40996                             "FITS keyword value" );
40997          } else if( cardtype == AST__STRING || cardtype == AST__CONTINUE ){
40998             (void) sprintf( buff, "Dt%d", ncard );
40999             astWriteString( channel, buff, 1, 1, (char *) data,
41000                             "FITS keyword value" );
41001          } else if( cardtype == AST__INT ){
41002             (void) sprintf( buff, "Dt%d", ncard );
41003             astWriteInt( channel, buff, 1, 1, *( (int *) data ),
41004                          "FITS keyword value" );
41005          } else if( cardtype == AST__LOGICAL ){
41006             (void) sprintf( buff, "Dt%d", ncard );
41007             astWriteInt( channel, buff, 1, 1, *( (int *) data ),
41008                          "FITS keyword value" );
41009          } else if( cardtype == AST__COMPLEXF ){
41010             (void) sprintf( buff, "Dr%d", ncard );
41011             astWriteDouble( channel, buff, 1, 1, *( (double *) data ),
41012                             "FITS keyword real value" );
41013             (void) sprintf( buff, "Di%d", ncard );
41014             astWriteDouble( channel, buff, 1, 1, *( ( (double *) data ) + 1 ),
41015                             "FITS keyword imaginary value" );
41016          } else if( cardtype == AST__COMPLEXI ){
41017             (void) sprintf( buff, "Dr%d", ncard );
41018             astWriteInt( channel, buff, 1, 1, *( (int *) data ),
41019                          "FITS keyword real value" );
41020             (void) sprintf( buff, "Di%d", ncard );
41021             astWriteInt( channel, buff, 1, 1, *( ( (int *) data ) + 1 ),
41022                          "FITS keyword imaginary value" );
41023          }
41024       }
41025 
41026 /* Write out the keyword comment. */
41027       if( CardComm( this, status ) ){
41028          (void) sprintf( buff, "Cm%d", ncard );
41029          astWriteString( channel, buff, 1, 1, CardComm( this, status ),
41030                          "FITS keyword comment" );
41031       }
41032 
41033 /* Move on to the next card. */
41034       ncard++;
41035       MoveCard( this, 1, "astDump", class, status );
41036    }
41037 
41038 /* Dump any FitTables. */
41039    if( this->tables ) {
41040       astWriteObject( channel, "Tables", 1, 1, this->tables,
41041                       "A KeyMap holding associated binary tables" );
41042    }
41043 
41044 /* Reinstate the original setting of the external ignore_used variable. */
41045    ignore_used = old_ignore_used;
41046 
41047 /* Reinstate the original current card. */
41048    astSetCard( this, icard );
41049 #undef KEY_LEN
41050 }
41051 
41052 /* Standard class functions. */
41053 /* ========================= */
41054 
41055 /* Implement the astIsAFitsChan and astCheckFitsChan functions using the macros
41056    defined for this purpose in the "object.h" header file. */
astMAKE_ISA(FitsChan,Channel)41057 astMAKE_ISA(FitsChan,Channel)
41058 astMAKE_CHECK(FitsChan)
41059 AstFitsChan *astFitsChan_( const char *(* source)( void ),
41060                            void (* sink)( const char * ),
41061                            const char *options, int *status, ...) {
41062 
41063 /*
41064 *++
41065 *  Name:
41066 c     astFitsChan
41067 f     AST_FITSCHAN
41068 
41069 *  Purpose:
41070 *     Create a FitsChan.
41071 
41072 *  Type:
41073 *     Public function.
41074 
41075 *  Synopsis:
41076 c     #include "fitschan.h"
41077 c     AstFitsChan *astFitsChan( const char *(* source)( void ),
41078 c                               void (* sink)( const char * ),
41079 c                               const char *options, ... )
41080 f     RESULT = AST_FITSCHAN( SOURCE, SINK, OPTIONS, STATUS )
41081 
41082 *  Class Membership:
41083 *     FitsChan constructor.
41084 
41085 *  Description:
41086 *     This function creates a new FitsChan and optionally initialises
41087 *     its attributes.
41088 *
41089 *     A FitsChan is a specialised form of Channel which supports I/O
41090 *     operations involving the use of FITS (Flexible Image Transport
41091 *     System) header cards. Writing an Object to a FitsChan (using
41092 c     astWrite) will, if the Object is suitable, generate a
41093 f     AST_WRITE) will, if the Object is suitable, generate a
41094 *     description of that Object composed of FITS header cards, and
41095 *     reading from a FitsChan will create a new Object from its FITS
41096 *     header card description.
41097 *
41098 *     While a FitsChan is active, it represents a buffer which may
41099 *     contain zero or more 80-character "header cards" conforming to
41100 *     FITS conventions. Any sequence of FITS-conforming header cards
41101 *     may be stored, apart from the "END" card whose existence is
41102 *     merely implied.  The cards may be accessed in any order by using
41103 *     the FitsChan's integer Card attribute, which identifies a "current"
41104 *     card, to which subsequent operations apply. Searches
41105 c     based on keyword may be performed (using astFindFits), new
41106 c     cards may be inserted (astPutFits, astPutCards, astSetFits<X>) and
41107 c     existing ones may be deleted (astDelFits) or changed (astSetFits<X>).
41108 f     based on keyword may be performed (using AST_FINDFITS), new
41109 f     cards may be inserted (AST_PUTFITS, AST_PUTCARDS, AST_SETFITS<X>) and
41110 f     existing ones may be deleted (AST_DELFITS) or changed (AST_SETFITS<X>).
41111 *
41112 *     When you create a FitsChan, you have the option of specifying
41113 *     "source" and "sink" functions which connect it to external data
41114 *     stores by reading and writing FITS header cards. If you provide
41115 *     a source function, it is used to fill the FitsChan with header cards
41116 *     when it is accessed for the first time. If you do not provide a
41117 *     source function, the FitsChan remains empty until you explicitly enter
41118 c     data into it (e.g. using astPutFits, astPutCards, astWrite
41119 f     data into it (e.g. using AST_PUTFITS, AST_PUTCARDS, AST_WRITE
41120 *     or by using the SourceFile attribute to specifying a text file from
41121 *     which headers should be read). When the FitsChan is deleted, any
41122 *     remaining header cards in the FitsChan can be saved in either of
41123 *     two ways: 1) by specifying a value for the SinkFile attribute (the
41124 *     name of a text file to which header cards should be written), or 2)
41125 *     by providing a sink function (used to to deliver header cards to an
41126 *     external data store). If you do not provide a sink function or a
41127 *     value for SinkFile, any header cards remaining when the FitsChan
41128 *     is deleted will be lost, so you should arrange to extract them
41129 *     first if necessary
41130 c     (e.g. using astFindFits or astRead).
41131 f     (e.g. using AST_FINDFITS or AST_READ).
41132 *
41133 *     Coordinate system information may be described using FITS header
41134 *     cards using several different conventions, termed
41135 *     "encodings". When an AST Object is written to (or read from) a
41136 *     FitsChan, the value of the FitsChan's Encoding attribute
41137 *     determines how the Object is converted to (or from) a
41138 *     description involving FITS header cards. In general, different
41139 *     encodings will result in different sets of header cards to
41140 *     describe the same Object. Examples of encodings include the DSS
41141 *     encoding (based on conventions used by the STScI Digitised Sky
41142 *     Survey data), the FITS-WCS encoding (based on a proposed FITS
41143 *     standard) and the NATIVE encoding (a near loss-less way of
41144 *     storing AST Objects in FITS headers).
41145 *
41146 *     The available encodings differ in the range of Objects they can
41147 *     represent, in the number of Object descriptions that can coexist
41148 *     in the same FitsChan, and in their accessibility to other
41149 *     (external) astronomy applications (see the Encoding attribute
41150 *     for details). Encodings are not necessarily mutually exclusive
41151 *     and it may sometimes be possible to describe the same Object in
41152 *     several ways within a particular set of FITS header cards by
41153 *     using several different encodings.
41154 *
41155 c     The detailed behaviour of astRead and astWrite, when used with
41156 f     The detailed behaviour of AST_READ and AST_WRITE, when used with
41157 *     a FitsChan, depends on the encoding in use. In general, however,
41158 c     all use of astRead is destructive, so that FITS header cards
41159 f     all use of AST_READ is destructive, so that FITS header cards
41160 *     are consumed in the process of reading an Object, and are
41161 *     removed from the FitsChan (this deletion can be prevented for
41162 *     specific cards by calling the
41163 c     astRetainFits function).
41164 f     AST_RETAINFITS routine).
41165 *
41166 *     If the encoding in use allows only a single Object description
41167 *     to be stored in a FitsChan (e.g. the DSS, FITS-WCS and FITS-IRAF
41168 c     encodings), then write operations using astWrite will
41169 f     encodings), then write operations using AST_WRITE will
41170 *     over-write any existing Object description using that
41171 *     encoding. Otherwise (e.g. the NATIVE encoding), multiple Object
41172 *     descriptions are written sequentially and may later be read
41173 *     back in the same sequence.
41174 
41175 *  Parameters:
41176 c     source
41177 f     SOURCE = FUNCTION (Given)
41178 c        Pointer to a source function which takes no arguments and
41179 c        returns a pointer to a null-terminated string. This function
41180 c        will be used by the FitsChan to obtain input FITS header
41181 c        cards. On each invocation, it should read the next input card
41182 c        from some external source (such as a FITS file), and return a
41183 c        pointer to the (null-terminated) contents of the card. It
41184 c        should return a NULL pointer when there are no more cards to
41185 c        be read.
41186 c
41187 c        If "source" is NULL, the FitsChan will remain empty until
41188 c        cards are explicitly stored in it (e.g. using astPutCards,
41189 c        astPutFits or via the SourceFile attribute).
41190 f        A source routine, which is a function taking two arguments: a
41191 f        character argument of length 80 to contain a FITS card, and an
41192 f        integer error status argument. It should return an integer value.
41193 f        This function will be used by the FitsChan to obtain input
41194 f        FITS header cards. On each invocation, it should read the
41195 f        next input card from some external source (such as a FITS
41196 f        file), and return the contents of the card via its character
41197 f        argument. It should return a function result of one unless
41198 f        there are no more cards to be read, in which case it should
41199 f        return zero. If an error occurs, it should set its error
41200 f        status argument to an error value before returning.
41201 f
41202 f        If the null routine AST_NULL is supplied as the SOURCE value,
41203 f        the FitsChan will remain empty until cards are explicitly
41204 f        stored in it (e.g. using AST_PUTCARDS, AST_PUTFITS or via the
41205 f        SourceFile attribute).
41206 c     sink
41207 f     SINK = SUBROUTINE (Given)
41208 c        Pointer to a sink function that takes a pointer to a
41209 c        null-terminated string as an argument and returns void.  If
41210 c        no value has been set for the SinkFile attribute, this
41211 c        function will be used by the FitsChan to deliver any FITS
41212 c        header cards it contains when it is finally deleted. On
41213 c        each invocation, it should deliver the contents of the character
41214 c        string passed to it as a FITS header card to some external
41215 c        data store (such as a FITS file).
41216 f        A sink routine, which is a subroutine which takes two
41217 f        arguments: a character argument of length 80 to contain a
41218 f        FITS card, and an integer error status argument. If no
41219 f        value has been set for the SinkFile attribute, this routine
41220 f        will be used by the FitsChan to deliver any FITS header cards
41221 f        it contains when it is finally deleted. On each invocation,
41222 f        it should deliver the contents of the character string passed
41223 f        to it as a FITS header card to some external data store (such
41224 f        as a FITS file).  If an error occurs, it should set its error
41225 f        status argument to an error value before returning.
41226 *
41227 c        If "sink" is NULL,
41228 f        If the null routine AST_NULL is supplied as the SINK value,
41229 *        and no value has been set for the SinkFile attribute, the
41230 *        contents of the FitsChan will be lost when it is deleted.
41231 c     options
41232 f     OPTIONS = CHARACTER * ( * ) (Given)
41233 c        Pointer to a null-terminated string containing an optional
41234 c        comma-separated list of attribute assignments to be used for
41235 c        initialising the new FitsChan. The syntax used is identical to
41236 c        that for the astSet function and may include "printf" format
41237 c        specifiers identified by "%" symbols in the normal way.
41238 f        A character string containing an optional comma-separated
41239 f        list of attribute assignments to be used for initialising the
41240 f        new FitsChan. The syntax used is identical to that for the
41241 f        AST_SET routine.
41242 c     ...
41243 c        If the "options" string contains "%" format specifiers, then
41244 c        an optional list of additional arguments may follow it in
41245 c        order to supply values to be substituted for these
41246 c        specifiers. The rules for supplying these are identical to
41247 c        those for the astSet function (and for the C "printf"
41248 c        function).
41249 *
41250 *        Note, the FITSCHAN_OPTIONS environment variable may be used
41251 *        to specify default options for all newly created FitsChans.
41252 f     STATUS = INTEGER (Given and Returned)
41253 f        The global status.
41254 
41255 *  Returned Value:
41256 c     astFitsChan()
41257 f     AST_FITSCHAN = INTEGER
41258 *        A pointer to the new FitsChan.
41259 
41260 *  Notes:
41261 f     - The names of the routines supplied for the SOURCE and SINK
41262 f     arguments should appear in EXTERNAL statements in the Fortran
41263 f     routine which invokes AST_FITSCHAN. However, this is not generally
41264 f     necessary for the null routine AST_NULL (so long as the AST_PAR
41265 f     include file has been used).
41266 c     - No FITS "END" card will be written via the sink function. You
41267 f     - No FITS "END" card will be written via the sink routine. You
41268 *     should add this card yourself after the FitsChan has been
41269 *     deleted.
41270 *     - A null Object pointer (AST__NULL) will be returned if this
41271 *     function is invoked with the AST error status set, or if it
41272 *     should fail for any reason.
41273 f     - Note that the null routine AST_NULL (one underscore) is
41274 f     different to AST__NULL (two underscores), which is the null Object
41275 f     pointer.
41276 
41277 *  Status Handling:
41278 *     The protected interface to this function includes an extra
41279 *     parameter at the end of the parameter list descirbed above. This
41280 *     parameter is a pointer to the integer inherited status
41281 *     variable: "int *status".
41282 *--
41283 */
41284 
41285 /* Local Variables: */
41286    astDECLARE_GLOBALS            /* Pointer to thread-specific global data */
41287    AstFitsChan *new;             /* Pointer to new FitsChan */
41288    va_list args;                 /* Variable argument list */
41289 
41290 /* Get a pointer to the thread specific global data structure. */
41291    astGET_GLOBALS(NULL);
41292 
41293 /* Check the global status. */
41294    if ( !astOK ) return NULL;
41295 
41296 /* Initialise the FitsChan, allocating memory and initialising the
41297    virtual function table as well if necessary. This interface is for
41298    use by other C functions within AST, and uses the standard "wrapper"
41299    functions included in this class. */
41300    new = astInitFitsChan( NULL, sizeof( AstFitsChan ), !class_init,
41301                           &class_vtab, "FitsChan", source, SourceWrap,
41302                           sink, SinkWrap );
41303 
41304 /* If successful, note that the virtual function table has been
41305    initialised. */
41306    if ( astOK ) {
41307       class_init = 1;
41308 
41309 /* Apply any default options specified by "<class>_OPTIONS" environment
41310    variable. */
41311       astEnvSet( new );
41312 
41313 /* Obtain the variable argument list and pass it along with the
41314    options string to the astVSet method to initialise the new
41315    FitsChan's attributes. */
41316       va_start( args, status );
41317       astVSet( new, options, NULL, args );
41318       va_end( args );
41319 
41320 /* If an error occurred, clean up by deleting the new object. */
41321       if ( !astOK ) new = astDelete( new );
41322    }
41323 
41324 /* Return a pointer to the new FitsChan. */
41325    return new;
41326 }
41327 
astFitsChanId_(const char * (* source)(void),void (* sink)(const char *),const char * options,...)41328 AstFitsChan *astFitsChanId_( const char *(* source)( void ),
41329                              void (* sink)( const char * ),
41330                              const char *options, ... ) {
41331 
41332 /*
41333 *  Name:
41334 *     astFitsChanId_
41335 
41336 *  Purpose:
41337 *     Create a FitsChan.
41338 
41339 *  Type:
41340 *     Private function.
41341 
41342 *  Synopsis:
41343 *     #include "fitschan.h"
41344 *     AstFitsChan *astFitsChanId_( const char *(* source)( void ),
41345 *                                  void (* sink)( const char * ),
41346 *                                  const char *options, ... )
41347 
41348 *  Class Membership:
41349 *     FitsChan constructor.
41350 
41351 *  Description:
41352 *     This function implements the external (public) C interface to the
41353 *     astFitsChan constructor function. Another function (astFitsChanForId)
41354 *     should be called to create a FitsChan for use within other languages.
41355 *     Both functions return an ID value (instead of a true C pointer) to
41356 *     external users, and must be provided because astFitsChan_ has a variable
41357 *     argument list which cannot be encapsulated in a macro (where this conversion would otherwise
41358 *     occur).
41359 *
41360 *     The variable argument list also prevents this function from
41361 *     invoking astFitsChan_ directly, so it must be a re-implementation
41362 *     of it in all respects, except for the final conversion of the
41363 *     result to an ID value.
41364 
41365 *  Parameters:
41366 *     As for astFitsChan_.
41367 
41368 *  Returned Value:
41369 *     The ID value associated with the new FitsChan.
41370 */
41371 
41372 /* Local Variables: */
41373    astDECLARE_GLOBALS            /* Pointer to thread-specific global data */
41374    AstFitsChan *new;             /* Pointer to new FitsChan */
41375    va_list args;                 /* Variable argument list */
41376    int *status;                  /* Pointer to inherited status value */
41377 
41378 /* Get a pointer to the inherited status value. */
41379    status = astGetStatusPtr;
41380 
41381 /* Get a pointer to the thread specific global data structure. */
41382    astGET_GLOBALS(NULL);
41383 
41384 /* Check the global status. */
41385    if ( !astOK ) return NULL;
41386 
41387 /* Initialise the FitsChan, allocating memory and initialising the
41388    virtual function table as well if necessary. This interface is for
41389    use by external C functions and uses the standard "wrapper"
41390    functions included in this class. */
41391    new = astInitFitsChan( NULL, sizeof( AstFitsChan ), !class_init,
41392                           &class_vtab, "FitsChan", source, SourceWrap,
41393                           sink, SinkWrap );
41394 
41395 /* If successful, note that the virtual function table has been
41396    initialised. */
41397    if ( astOK ) {
41398       class_init = 1;
41399 
41400 /* Apply any default options specified by "<class>_OPTIONS" environment
41401    variable. */
41402       astEnvSet( new );
41403 
41404 /* Obtain the variable argument list and pass it along with the
41405    options string to the astVSet method to initialise the new
41406    FitsChan's attributes. */
41407       va_start( args, options );
41408       astVSet( new, options, NULL, args );
41409       va_end( args );
41410 
41411 /* If an error occurred, clean up by deleting the new object. */
41412       if ( !astOK ) new = astDelete( new );
41413    }
41414 
41415 /* Return an ID value for the new FitsChan. */
41416    return astMakeId( new );
41417 }
41418 
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,...)41419 AstFitsChan *astFitsChanForId_( const char *(* source)( void ),
41420                               char *(* source_wrap)( const char *(*)( void ), int * ),
41421                               void (* sink)( const char * ),
41422                               void (* sink_wrap)( void (*)( const char * ),
41423                                                   const char *, int * ),
41424                               const char *options, ... ) {
41425 
41426 /*
41427 *+
41428 *  Name:
41429 *     astFitsChanFor
41430 
41431 *  Purpose:
41432 *     Initialise a FitsChan from a foreign language interface.
41433 
41434 *  Type:
41435 *     Public function.
41436 
41437 *  Synopsis:
41438 *     #include "fitschan.h"
41439 *     AstFitsChan *astFitsChanFor( const char *(* source)( void ),
41440 *                                char *(* source_wrap)( const char *(*)
41441 *                                                       ( void ), int * ),
41442 *                                void (* sink)( const char * ),
41443 *                                void (* sink_wrap)( void (*)( const char * ),
41444 *                                                    const char *, int * ),
41445 *                                const char *options, ... )
41446 
41447 *  Class Membership:
41448 *     FitsChan constructor.
41449 
41450 *  Description:
41451 *     This function creates a new FitsChan from a foreign language
41452 *     interface and optionally initialises its attributes.
41453 *
41454 *     A FitsChan implements FITS input/output for the AST library.
41455 *     Writing an Object to a FitsChan (using astWrite) will generate a
41456 *     textual representation of that Object in terms of FITS header cards,
41457 *     and reading from a FitsChan (using astRead) will create a new Object
41458 *     from its FITS representation.
41459 *
41460 *     Normally, when you use a FitsChan, you should provide "source"
41461 *     and "sink" functions which connect it to an external data store
41462 *     by reading and writing the resulting text. This function also
41463 *     requires you to provide "wrapper" functions which will invoke
41464 *     the source and sink functions.
41465 
41466 *  Parameters:
41467 *     source
41468 *        Pointer to a "source" function which will be used to obtain
41469 *        FITS header cards. Generally, this will be obtained by
41470 *        casting a pointer to a source function which is compatible
41471 *        with the "source_wrap" wrapper function (below). The pointer
41472 *        should later be cast back to its original type by the
41473 *        "source_wrap" function before the function is invoked.
41474 *
41475 *        If "source" is NULL, the FitsChan will remain empty until
41476 *        cards are added explicitly (e.g. using astPutCards or astPutFits).
41477 *     source_wrap
41478 *        Pointer to a function which can be used to invoke the
41479 *        "source" function supplied (above). This wrapper function is
41480 *        necessary in order to hide variations in the nature of the
41481 *        source function, such as may arise when it is supplied by a
41482 *        foreign (non-C) language interface.
41483 *
41484 *        The single parameter of the "source_wrap" function is a
41485 *        pointer to the "source" function, and it should cast this
41486 *        function pointer (as necessary) and invoke the function with
41487 *        appropriate arguments to obtain the next FITS header card.
41488 *        The "source_wrap" function should then return a pointer
41489 *        to a dynamically allocated, null terminated string containing
41490 *        the text that was read. The string will be freed (using
41491 *        astFree) when no longer required and the "source_wrap"
41492 *        function need not concern itself with this. A NULL pointer
41493 *        should be returned if there is no more input to read.
41494 *
41495 *        If "source" is NULL, the FitsChan will remain empty until
41496 *        cards are added explicitly (e.g. using astPutCards or astPutFits).
41497 *     sink
41498 *        Pointer to a "sink" function which will be used to deliver
41499 *        FITS header cards. Generally, this will be obtained by
41500 *        casting a pointer to a sink function which is compatible with
41501 *        the "sink_wrap" wrapper function (below). The pointer should
41502 *        later be cast back to its original type by the "sink_wrap"
41503 *        function before the function is invoked.
41504 *
41505 *        If "sink" is NULL, the contents of the FitsChan will not be
41506 *        written out before being deleted.
41507 *     sink_wrap
41508 *        Pointer to a function which can be used to invoke the "sink"
41509 *        function supplied (above). This wrapper function is necessary
41510 *        in order to hide variations in the nature of the sink
41511 *        function, such as may arise when it is supplied by a foreign
41512 *        (non-C) language interface.
41513 *
41514 *        The first parameter of the "sink_wrap" function is a pointer
41515 *        to the "sink" function, and the second parameter is a pointer
41516 *        to a const, null-terminated character string containing the
41517 *        text to be written.  The "sink_wrap" function should cast the
41518 *        "sink" function pointer (as necessary) and invoke the
41519 *        function with appropriate arguments to deliver the line of
41520 *        output text. The "sink_wrap" function then returns void.
41521 *
41522 *        If "sink_wrap" is NULL, the contents of the FitsChan will not be
41523 *        written out before being deleted.
41524 *     options
41525 *        Pointer to a null-terminated string containing an optional
41526 *        comma-separated list of attribute assignments to be used for
41527 *        initialising the new FitsChan. The syntax used is identical to
41528 *        that for the astSet function and may include "printf" format
41529 *        specifiers identified by "%" symbols in the normal way.
41530 *     ...
41531 *        If the "options" string contains "%" format specifiers, then
41532 *        an optional list of additional arguments may follow it in
41533 *        order to supply values to be substituted for these
41534 *        specifiers. The rules for supplying these are identical to
41535 *        those for the astSet function (and for the C "printf"
41536 *        function).
41537 
41538 *  Returned Value:
41539 *     astFitsChanFor()
41540 *        A pointer to the new FitsChan.
41541 
41542 *  Notes:
41543 *     - A null Object pointer (AST__NULL) will be returned if this
41544 *     function is invoked with the global error status set, or if it
41545 *     should fail for any reason.
41546 *     - This function is only available through the public interface
41547 *     to the FitsChan class (not the protected interface) and is
41548 *     intended solely for use in implementing foreign language
41549 *     interfaces to this class.
41550 *-
41551 
41552 *  Implememtation Notes:
41553 *     - This function behaves exactly like astFitsChanId_, in that it
41554 *     returns ID values and not true C pointers, but it has two
41555 *     additional arguments. These are pointers to the "wrapper
41556 *     functions" which are needed to accommodate foreign language
41557 *     interfaces.
41558 */
41559 
41560 /* Local Variables: */
41561    astDECLARE_GLOBALS            /* Pointer to thread-specific global data */
41562    AstFitsChan *new;             /* Pointer to new FitsChan */
41563    va_list args;                 /* Variable argument list */
41564    int *status;                  /* Pointer to inherited status value */
41565 
41566 /* Get a pointer to the inherited status value. */
41567    status = astGetStatusPtr;
41568 
41569 /* Check the global status. */
41570    if ( !astOK ) return NULL;
41571 
41572 /* Get a pointer to the thread specific global data structure. */
41573    astGET_GLOBALS(NULL);
41574 
41575 /* Initialise the FitsChan, allocating memory and initialising the
41576    virtual function table as well if necessary. */
41577    new = astInitFitsChan( NULL, sizeof( AstFitsChan ), !class_init,
41578                           &class_vtab, "FitsChan", source, source_wrap,
41579                           sink, sink_wrap );
41580 
41581 /* If successful, note that the virtual function table has been
41582    initialised. */
41583    if ( astOK ) {
41584       class_init = 1;
41585 
41586 /* Apply any default options specified by "<class>_OPTIONS" environment
41587    variable. */
41588       astEnvSet( new );
41589 
41590 /* Obtain the variable argument list and pass it along with the
41591    options string to the astVSet method to initialise the new
41592    FitsChan's attributes. */
41593       va_start( args, options );
41594       astVSet( new, options, NULL, args );
41595       va_end( args );
41596 
41597 /* If an error occurred, clean up by deleting the new object. */
41598       if ( !astOK ) new = astDelete( new );
41599    }
41600 
41601 /* Return an ID value for the new FitsChan. */
41602    return astMakeId( new );
41603 }
41604 
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)41605 AstFitsChan *astInitFitsChan_( void *mem, size_t size, int init,
41606                                AstFitsChanVtab *vtab, const char *name,
41607                                const char *(* source)( void ),
41608                                char *(* source_wrap)( const char *(*)( void ), int * ),
41609                                void (* sink)( const char * ),
41610                                void (* sink_wrap)( void (*)( const char * ),
41611                                                    const char *, int * ), int *status ) {
41612 
41613 /*
41614 *+
41615 *  Name:
41616 *     astInitFitsChan
41617 
41618 *  Purpose:
41619 *     Initialise a FitsChan.
41620 
41621 *  Type:
41622 *     Protected function.
41623 
41624 *  Synopsis:
41625 *     #include "fitschan.h"
41626 *     AstFitsChan *astInitFitsChan_( void *mem, size_t size, int init,
41627 *                            AstFitsChanVtab *vtab, const char *name,
41628 *                            const char *(* source)( void ),
41629 *                            char *(* source_wrap)( const char *(*)( void ), int * ),
41630 *                            void (* sink)( const char * ),
41631 *                            void (* sink_wrap)( void (*)( const char * ),
41632 *                                                   const char *, int * ) )
41633 
41634 *  Class Membership:
41635 *     FitsChan initialiser.
41636 
41637 *  Description:
41638 *     This function is provided for use by class implementations to
41639 *     initialise a new FitsChan object. It allocates memory (if
41640 *     necessary) to accommodate the FitsChan plus any additional data
41641 *     associated with the derived class.  It then initialises a
41642 *     FitsChan structure at the start of this memory. If the "init"
41643 *     flag is set, it also initialises the contents of a virtual
41644 *     function table for a FitsChan at the start of the memory passed
41645 *     via the "vtab" parameter.
41646 
41647 *  Parameters:
41648 *     mem
41649 *        A pointer to the memory in which the FitsChan is to be
41650 *        initialised.  This must be of sufficient size to accommodate
41651 *        the FitsChan data (sizeof(FitsChan)) plus any data used by the
41652 *        derived class. If a value of NULL is given, this function
41653 *        will allocate the memory itself using the "size" parameter to
41654 *        determine its size.
41655 *     size
41656 *        The amount of memory used by the FitsChan (plus derived class
41657 *        data).  This will be used to allocate memory if a value of
41658 *        NULL is given for the "mem" parameter. This value is also
41659 *        stored in the FitsChan structure, so a valid value must be
41660 *        supplied even if not required for allocating memory.
41661 *     init
41662 *        A boolean flag indicating if the FitsChan's virtual function
41663 *        table is to be initialised. If this value is non-zero, the
41664 *        virtual function table will be initialised by this function.
41665 *     vtab
41666 *        Pointer to the start of the virtual function table to be
41667 *        associated with the new FitsChan.
41668 *     name
41669 *        Pointer to a constant null-terminated character string which
41670 *        contains the name of the class to which the new object
41671 *        belongs (it is this pointer value that will subsequently be
41672 *        returned by the astGetClass method).
41673 *     source
41674 *        Pointer to a "source" function which will be used to obtain
41675 *        FITS header cards. Generally, this will be obtained by
41676 *        casting a pointer to a source function which is compatible
41677 *        with the "source_wrap" wrapper function (below). The pointer
41678 *        should later be cast back to its original type by the
41679 *        "source_wrap" function before the function is invoked.
41680 *
41681 *        If "source" is NULL, the FitsChan will remain empty until
41682 *        cards are added explicitly (e.g. using astPutCards or astPutFits).
41683 *     source_wrap
41684 *        Pointer to a function which can be used to invoke the
41685 *        "source" function supplied (above). This wrapper function is
41686 *        necessary in order to hide variations in the nature of the
41687 *        source function, such as may arise when it is supplied by a
41688 *        foreign (non-C) language interface.
41689 *
41690 *        The single parameter of the "source_wrap" function is a
41691 *        pointer to the "source" function, and it should cast this
41692 *        function pointer (as necessary) and invoke the function with
41693 *        appropriate arguments to obtain the next FITS header card.
41694 *        The "source_wrap" function should then return a pointer
41695 *        to a dynamically allocated, null terminated string containing
41696 *        the text that was read. The string will be freed (using
41697 *        astFree) when no longer required and the "source_wrap"
41698 *        function need not concern itself with this. A NULL pointer
41699 *        should be returned if there is no more input to read.
41700 *
41701 *        If "source" is NULL, the FitsChan will remain empty until
41702 *        cards are added explicitly (e.g. using astPutCards or astPutFits).
41703 *     sink
41704 *        Pointer to a "sink" function which will be used to deliver
41705 *        FITS header cards. Generally, this will be obtained by
41706 *        casting a pointer to a sink function which is compatible with
41707 *        the "sink_wrap" wrapper function (below). The pointer should
41708 *        later be cast back to its original type by the "sink_wrap"
41709 *        function before the function is invoked.
41710 *
41711 *        If "sink" is NULL, the contents of the FitsChan will not be
41712 *        written out before being deleted.
41713 *     sink_wrap
41714 *        Pointer to a function which can be used to invoke the "sink"
41715 *        function supplied (above). This wrapper function is necessary
41716 *        in order to hide variations in the nature of the sink
41717 *        function, such as may arise when it is supplied by a foreign
41718 *        (non-C) language interface.
41719 *
41720 *        The first parameter of the "sink_wrap" function is a pointer
41721 *        to the "sink" function, and the second parameter is a pointer
41722 *        to a const, null-terminated character string containing the
41723 *        text to be written.  The "sink_wrap" function should cast the
41724 *        "sink" function pointer (as necessary) and invoke the
41725 *        function with appropriate arguments to deliver the line of
41726 *        output text. The "sink_wrap" function then returns void.
41727 *
41728 *        If "sink_wrap" is NULL, the contents of the FitsChan will not be
41729 *        written out before being deleted.
41730 
41731 *  Returned Value:
41732 *     A pointer to the new FitsChan.
41733 
41734 *  Notes:
41735 *     - A null pointer will be returned if this function is invoked
41736 *     with the global error status set, or if it should fail for any
41737 *     reason.
41738 *-
41739 */
41740 
41741 /* Local Variables: */
41742    AstFitsChan *new;              /* Pointer to new FitsChan */
41743 
41744 /* Check the global status. */
41745    if ( !astOK ) return NULL;
41746 
41747 /* If necessary, initialise the virtual function table. */
41748    if ( init ) astInitFitsChanVtab( vtab, name );
41749 
41750 /* Initialise a Channel structure (the parent class) as the first
41751    component within the FitsChan structure, allocating memory if
41752    necessary. I am not sure why FitsChan has its own source_wrap and
41753    sink_wrap items, rather than just using those inherited from Channel.
41754    It may be possible to do away with the fitschan wrappers and just use
41755    the channel wrapper, but I have not yet tried this. Old mail from RFWS
41756    suggests that it may be because the F77 FitsChan source and sink
41757    interfaces handle fixed length strings (80 characters), whereas
41758    Channel sournce and sink handle variable length strings. This needs
41759    investigating. */
41760    new = (AstFitsChan *) astInitChannel( mem, size, 0,
41761                                          (AstChannelVtab *) vtab, name,
41762                                          NULL, NULL, NULL, NULL );
41763    if ( astOK ) {
41764 
41765 /* Initialise the FitsChan data. */
41766 /* ---------------------------- */
41767       new->head = NULL;
41768       new->card = NULL;
41769       new->keyseq = NULL;
41770       new->keywords = NULL;
41771       new->defb1950 = -1;
41772       new->tabok = -INT_MAX;
41773       new->cdmatrix = -1;
41774       new->carlin = -1;
41775       new->polytan = -INT_MAX;
41776       new->iwc = -1;
41777       new->clean = -1;
41778       new->fitsdigits = DBL_DIG;
41779       new->fitsaxisorder = NULL;
41780       new->encoding = UNKNOWN_ENCODING;
41781       new->warnings = NULL;
41782       new->tables = NULL;
41783 
41784 /* Save the pointers to the source and sink functions and the wrapper
41785    functions that invoke them. */
41786       new->source = source;
41787       new->saved_source = NULL;
41788       new->source_wrap = source_wrap;
41789       new->sink = sink;
41790       new->sink_wrap = sink_wrap;
41791       new->tabsource = NULL;
41792       new->tabsource_wrap = NULL;
41793 
41794 /* Rewind the FitsChan so that the next read operation will return the
41795    first card. */
41796       new->card = new->head;
41797 
41798 /* If an error occurred, clean up by deleting the new object. */
41799       if ( !astOK ) new = astDelete( new );
41800    }
41801 
41802 /* Return a pointer to the new object. */
41803    return new;
41804 }
astLoadFitsChan_(void * mem,size_t size,AstFitsChanVtab * vtab,const char * name,AstChannel * channel,int * status)41805 AstFitsChan *astLoadFitsChan_( void *mem, size_t size,
41806                                AstFitsChanVtab *vtab, const char *name,
41807                                AstChannel *channel, int *status ) {
41808 
41809 /*
41810 *+
41811 *  Name:
41812 *     astLoadFitsChan
41813 
41814 *  Purpose:
41815 *     Load a FitsChan.
41816 
41817 *  Type:
41818 *     Protected function.
41819 
41820 *  Synopsis:
41821 *     #include "fitschan.h"
41822 *     AstFitsChan *astLoadFitsChan( void *mem, size_t size,
41823 *                                   AstFitsChanVtab *vtab, const char *name,
41824 *                                   AstChannel *channel )
41825 
41826 *  Class Membership:
41827 *     FitsChan loader.
41828 
41829 *  Description:
41830 *     This function is provided to load a new FitsChan using data read
41831 *     from a Channel. It first loads the data used by the parent class
41832 *     (which allocates memory if necessary) and then initialises a
41833 *     FitsChan structure in this memory, using data read from the input
41834 *     Channel.
41835 *
41836 *     If the "init" flag is set, it also initialises the contents of a
41837 *     virtual function table for a FitsChan at the start of the memory
41838 *     passed via the "vtab" parameter.
41839 
41840 *  Parameters:
41841 *     mem
41842 *        A pointer to the memory into which the FitsChan is to be
41843 *        loaded.  This must be of sufficient size to accommodate the
41844 *        FitsChan data (sizeof(FitsChan)) plus any data used by derived
41845 *        classes. If a value of NULL is given, this function will
41846 *        allocate the memory itself using the "size" parameter to
41847 *        determine its size.
41848 *     size
41849 *        The amount of memory used by the FitsChan (plus derived class
41850 *        data).  This will be used to allocate memory if a value of
41851 *        NULL is given for the "mem" parameter. This value is also
41852 *        stored in the FitsChan structure, so a valid value must be
41853 *        supplied even if not required for allocating memory.
41854 *
41855 *        If the "vtab" parameter is NULL, the "size" value is ignored
41856 *        and sizeof(AstFitsChan) is used instead.
41857 *     vtab
41858 *        Pointer to the start of the virtual function table to be
41859 *        associated with the new FitsChan. If this is NULL, a pointer
41860 *        to the (static) virtual function table for the FitsChan class
41861 *        is used instead.
41862 *     name
41863 *        Pointer to a constant null-terminated character string which
41864 *        contains the name of the class to which the new object
41865 *        belongs (it is this pointer value that will subsequently be
41866 *        returned by the astGetClass method).
41867 *
41868 *        If the "vtab" parameter is NULL, the "name" value is ignored
41869 *        and a pointer to the string "FitsChan" is used instead.
41870 
41871 *  Returned Value:
41872 *     A pointer to the new FitsChan.
41873 
41874 *  Notes:
41875 *     - A null pointer will be returned if this function is invoked
41876 *     with the global error status set, or if it should fail for any
41877 *     reason.
41878 *-
41879 */
41880 #define KEY_LEN 50              /* Maximum length of a keyword */
41881 
41882 /* Local Variables: */
41883    astDECLARE_GLOBALS           /* Pointer to thread-specific global data */
41884    AstFitsChan *new;            /* Pointer to the new FitsChan */
41885    char *comment;               /* Pointer to keyword comment */
41886    char *keynm;                 /* Keyword name */
41887    char *text;                  /* Textual version of integer value */
41888    char buff[ KEY_LEN + 1 ];    /* Buffer for keyword string */
41889    double dval[2];              /* Double precision data values */
41890    int flags;                   /* Keyword flags */
41891    int free_data;               /* Should data memory be freed? */
41892    int ival[2];                 /* Integer data values */
41893    int ncard;                   /* No. of FitsCards read so far */
41894    int type;                    /* Keyword type */
41895    void *data;                  /* Pointer to keyword data value */
41896 
41897 /* Initialise. */
41898    new = NULL;
41899 
41900 /* Check the global error status. */
41901    if ( !astOK ) return new;
41902 
41903 /* Get a pointer to the thread specific global data structure. */
41904    astGET_GLOBALS(channel);
41905 
41906 /* If a NULL virtual function table has been supplied, then this is
41907    the first loader to be invoked for this FitsChan. In this case the
41908    FitsChan belongs to this class, so supply appropriate values to be
41909    passed to the parent class loader (and its parent, etc.). */
41910    if ( !vtab ) {
41911       size = sizeof( AstFitsChan );
41912       vtab = &class_vtab;
41913       name = "FitsChan";
41914 
41915 /* If required, initialise the virtual function table for this class. */
41916       if ( !class_init ) {
41917          astInitFitsChanVtab( vtab, name );
41918          class_init = 1;
41919       }
41920    }
41921 
41922 /* Invoke the parent class loader to load data for all the ancestral
41923    classes of the current one, returning a pointer to the resulting
41924    partly-built FitsChan. */
41925    new = astLoadChannel( mem, size, (AstChannelVtab *) vtab, name,
41926                          channel );
41927    if ( astOK ) {
41928 
41929 /* Read input data. */
41930 /* ================ */
41931 
41932 /* Request the input Channel to read all the input data appropriate to
41933    this class into the internal "values list". */
41934       astReadClassData( channel, "FitsChan" );
41935 
41936 /* Initialise the KeyMap holding the keywords in the FitsChan. */
41937       new->keywords = NULL;
41938 
41939 /* Initialise the list of keyword sequence numbers. */
41940       new->keyseq = NULL;
41941 
41942 /* Set the pointers to the source and sink functions, and their
41943    wrapper functions, to NULL (we cannot restore these since they
41944    refer to process-specific addresses). */
41945       new->source = NULL;
41946       new->saved_source = NULL;
41947       new->source_wrap = NULL;
41948       new->sink = NULL;
41949       new->sink_wrap = NULL;
41950       new->tabsource = NULL;
41951       new->tabsource_wrap = NULL;
41952 
41953 /* Now read each individual data item from this list and use it to
41954    initialise the appropriate instance variable(s) for this class. */
41955 
41956 /* Encoding. */
41957 /* --------- */
41958       text = astReadString( channel, "encod", UNKNOWN_STRING );
41959       if( text && strcmp( text, UNKNOWN_STRING ) ) {
41960          new->encoding = FindString( MAX_ENCODING + 1, xencod, text,
41961                                      "the FitsChan component 'Encod'",
41962                                      "astRead", astGetClass( channel ), status );
41963       } else {
41964          new->encoding = UNKNOWN_ENCODING;
41965       }
41966       if ( TestEncoding( new, status ) ) SetEncoding( new, new->encoding, status );
41967       text = astFree( text );
41968 
41969 /* FitsAxisOrder. */
41970 /* -------------- */
41971       new->fitsaxisorder = astReadString( channel, "faxord", NULL );
41972 
41973 /* FitsDigits. */
41974 /* ----------- */
41975       new->fitsdigits = astReadInt( channel, "fitsdg", DBL_DIG );
41976       if ( TestFitsDigits( new, status ) ) SetFitsDigits( new, new->fitsdigits, status );
41977 
41978 /* DefB1950 */
41979 /* -------- */
41980       new->defb1950 = astReadInt( channel, "dfb1950", -1 );
41981       if ( TestDefB1950( new, status ) ) SetDefB1950( new, new->defb1950, status );
41982 
41983 /* TabOK */
41984 /* ----- */
41985       new->tabok = astReadInt( channel, "tabok", -INT_MAX );
41986       if ( TestTabOK( new, status ) ) SetTabOK( new, new->tabok, status );
41987 
41988 /* CDMatrix */
41989 /* -------- */
41990       new->cdmatrix = astReadInt( channel, "cdmat", -1 );
41991       if ( TestCDMatrix( new, status ) ) SetCDMatrix( new, new->cdmatrix, status );
41992 
41993 /* CarLin */
41994 /* ------ */
41995       new->carlin = astReadInt( channel, "carlin", -1 );
41996       if ( TestCarLin( new, status ) ) SetCarLin( new, new->carlin, status );
41997 
41998 /* PolyTan */
41999 /* ------- */
42000       new->polytan = astReadInt( channel, "polytan", -1 );
42001       if ( TestPolyTan( new, status ) ) SetPolyTan( new, new->polytan, status );
42002 
42003 /* Iwc */
42004 /* --- */
42005       new->iwc = astReadInt( channel, "iwc", -1 );
42006       if ( TestIwc( new, status ) ) SetIwc( new, new->iwc, status );
42007 
42008 /* Clean */
42009 /* ----- */
42010       new->clean = astReadInt( channel, "clean", -1 );
42011       if ( TestClean( new, status ) ) SetClean( new, new->clean, status );
42012 
42013 /* Warnings. */
42014 /* --------- */
42015       new->warnings = astReadString( channel, "warn", NULL );
42016 
42017 /* Card. */
42018 /* ----- */
42019 
42020 /* Initialise the index of the card to be read next. */
42021       ncard = 1;
42022       new->card = NULL;
42023       new->head = NULL;
42024 
42025 /* Load each card. */
42026       type = AST__NOTYPE + 1;
42027       while( type != AST__NOTYPE && astOK ){
42028 
42029 /* Get the keyword type. */
42030          (void) sprintf( buff, "ty%d", ncard );
42031          text = astReadString( channel, buff, " " );
42032          if( strcmp( text, " " ) ) {
42033             type = FindString( 9, type_names, text,
42034                                "a FitsChan keyword data type",
42035                                "astRead", astGetClass( channel ), status );
42036          } else {
42037             type = AST__NOTYPE;
42038          }
42039          text = astFree( text );
42040 
42041 /* Only proceed if the keyword type was found. */
42042          if( type != AST__NOTYPE ){
42043 
42044 /* Get the keyword name. Use a default blank name. */
42045             (void) sprintf( buff, "nm%d", ncard );
42046             keynm = astReadString( channel, buff, "        " );
42047 
42048 /* Get the data value, using the appropriate data type, unless the
42049    keyword is a comment keyword or is undefined. */
42050             free_data = 0;
42051             if( type == AST__FLOAT ){
42052                (void) sprintf( buff, "dt%d", ncard );
42053                dval[ 0 ] = astReadDouble( channel, buff, AST__BAD );
42054                data = (void *) dval;
42055             } else if( type == AST__STRING || type == AST__CONTINUE ){
42056                (void) sprintf( buff, "dt%d", ncard );
42057                data = (void *) astReadString( channel, buff, "" );
42058                free_data = 1;
42059             } else if( type == AST__INT ){
42060                (void) sprintf( buff, "dt%d", ncard );
42061                ival[ 0 ] = astReadInt( channel, buff, 0 );
42062                data = (void *) ival;
42063             } else if( type == AST__LOGICAL ){
42064                (void) sprintf( buff, "dt%d", ncard );
42065                ival[ 0 ] = astReadInt( channel, buff, 0 );
42066                data = (void *) ival;
42067             } else if( type == AST__COMPLEXF ){
42068                (void) sprintf( buff, "dr%d", ncard );
42069                dval[ 0 ] = astReadDouble( channel, buff, AST__BAD );
42070                (void) sprintf( buff, "di%d", ncard );
42071                dval[ 1 ] = astReadDouble( channel, buff, AST__BAD );
42072                data = (void *) dval;
42073             } else if( type == AST__COMPLEXI ){
42074                (void) sprintf( buff, "dr%d", ncard );
42075                ival[ 0 ] = astReadInt( channel, buff, 0 );
42076                (void) sprintf( buff, "di%d", ncard );
42077                ival[ 1 ] = astReadInt( channel, buff, 0 );
42078                data = (void *) ival;
42079             } else {
42080                data = NULL;
42081             }
42082 
42083 /* Get the keyword flags (only written by versions of AST later than
42084    V1.4). These are packed into an int. */
42085             (void) sprintf( buff, "fl%d", ncard );
42086             flags = astReadInt( channel, buff, 0 );
42087 
42088 /* If the flags were not found, use the keyword deletion flag written by
42089    AST V1.4 and earlier. */
42090             if( !flags ) {
42091                (void) sprintf( buff, "dl%d", ncard );
42092                flags = astReadInt( channel, buff, 0 );
42093             }
42094 
42095 /* Get the keyword comment. */
42096             (void) sprintf( buff, "cm%d", ncard );
42097             comment = astReadString( channel, buff, NULL );
42098 
42099 /* Append a new card to the output FitsChan. */
42100             NewCard( new, keynm, type, data, comment, flags, status );
42101 
42102 /* Free the character strings, and data (if required). */
42103             comment = (char *) astFree( (void *) comment );
42104             keynm = (char *) astFree( (void *) keynm );
42105             if( free_data ) data = astFree( data );
42106          }
42107 
42108 /* Move on to the next card. */
42109          ncard++;
42110       }
42111 
42112 /* Set up the current card index. */
42113       astSetCard( new, astReadInt( channel, "card", 0 ) );
42114 
42115 /* Load any FitTables. */
42116       new->tables =  astReadObject( channel, "tables", NULL );
42117    }
42118 
42119 /* If an error occurred, clean up by deleting the new FitsChan. */
42120    if ( !astOK ) new = astDelete( new );
42121 
42122 /* Return the new FitsChan pointer. */
42123    return new;
42124 }
42125 
42126 /* Virtual function interfaces. */
42127 /* ============================ */
42128 
42129 /* These provide the external interface to the virtual functions defined by
42130    this class. Each simply checks the global error status and then locates and
42131    executes the appropriate member function, using the function pointer stored
42132    in the object's virtual function table (this pointer is located using the
42133    astMEMBER macro defined in "object.h").
42134    Note that the member function may not be the one defined here, as it may
42135    have been over-ridden by a derived class. However, it should still have the
42136    same interface. */
42137 
astWriteFits_(AstFitsChan * this,int * status)42138 void astWriteFits_( AstFitsChan *this, int *status ){
42139    if( !this ) return;
42140    (**astMEMBER(this,FitsChan,WriteFits))(this, status );
42141 }
42142 
astReadFits_(AstFitsChan * this,int * status)42143 void astReadFits_( AstFitsChan *this, int *status ){
42144    if( !astOK ) return;
42145    (**astMEMBER(this,FitsChan,ReadFits))(this, status );
42146 }
42147 
astEmptyFits_(AstFitsChan * this,int * status)42148 void astEmptyFits_( AstFitsChan *this, int *status ){
42149    if( !this ) return;
42150    (**astMEMBER(this,FitsChan,EmptyFits))(this, status );
42151 }
42152 
astShowFits_(AstFitsChan * this,int * status)42153 void astShowFits_( AstFitsChan *this, int *status ){
42154    if( !this ) return;
42155    (**astMEMBER(this,FitsChan,ShowFits))(this, status );
42156 }
42157 
astPutCards_(AstFitsChan * this,const char * cards,int * status)42158 void astPutCards_( AstFitsChan *this, const char *cards, int *status ){
42159    if( !astOK ) return;
42160    (**astMEMBER(this,FitsChan,PutCards))(this,cards, status );
42161 }
42162 
astPutFits_(AstFitsChan * this,const char * card,int overwrite,int * status)42163 void astPutFits_( AstFitsChan *this, const char *card, int overwrite, int *status ){
42164    if( !astOK ) return;
42165    (**astMEMBER(this,FitsChan,PutFits))(this,card,overwrite, status );
42166 }
42167 
astDelFits_(AstFitsChan * this,int * status)42168 void astDelFits_( AstFitsChan *this, int *status ){
42169    if( !astOK ) return;
42170    (**astMEMBER(this,FitsChan,DelFits))(this, status );
42171 }
42172 
astPurgeWCS_(AstFitsChan * this,int * status)42173 void astPurgeWCS_( AstFitsChan *this, int *status ){
42174    if( !astOK ) return;
42175    (**astMEMBER(this,FitsChan,PurgeWCS))(this, status );
42176 }
42177 
astGetTables_(AstFitsChan * this,int * status)42178 AstKeyMap *astGetTables_( AstFitsChan *this, int *status ){
42179    if( !astOK ) return NULL;
42180    return (**astMEMBER(this,FitsChan,GetTables))(this, status );
42181 }
42182 
astPutTables_(AstFitsChan * this,AstKeyMap * tables,int * status)42183 void astPutTables_( AstFitsChan *this, AstKeyMap *tables, int *status ){
42184    if( !astOK ) return;
42185    (**astMEMBER(this,FitsChan,PutTables))(this, tables, status );
42186 }
42187 
astPutTable_(AstFitsChan * this,AstFitsTable * table,const char * extnam,int * status)42188 void astPutTable_( AstFitsChan *this, AstFitsTable *table, const char *extnam,
42189                    int *status ){
42190    if( !astOK ) return;
42191    (**astMEMBER(this,FitsChan,PutTable))(this, table, extnam, status );
42192 }
42193 
astRemoveTables_(AstFitsChan * this,const char * key,int * status)42194 void astRemoveTables_( AstFitsChan *this, const char *key, int *status ){
42195    if( !astOK ) return;
42196    (**astMEMBER(this,FitsChan,RemoveTables))(this, key, status );
42197 }
42198 
astRetainFits_(AstFitsChan * this,int * status)42199 void astRetainFits_( AstFitsChan *this, int *status ){
42200    if( !astOK ) return;
42201    (**astMEMBER(this,FitsChan,RetainFits))(this, status );
42202 }
42203 
astFitsEof_(AstFitsChan * this,int * status)42204 int astFitsEof_( AstFitsChan *this, int *status ){
42205    if( !this ) return 1;
42206    return (**astMEMBER(this,FitsChan,FitsEof))( this, status );
42207 }
42208 
astSetFitsCom_(AstFitsChan * this,const char * name,const char * comment,int overwrite,int * status)42209 void astSetFitsCom_( AstFitsChan *this, const char *name,
42210                          const char *comment, int overwrite, int *status ) {
42211    if ( !astOK ) return;
42212    (**astMEMBER(this,FitsChan,SetFitsCom))( this, name, comment, overwrite, status );
42213 }
42214 
astSetFitsI_(AstFitsChan * this,const char * name,int value,const char * comment,int overwrite,int * status)42215 void astSetFitsI_( AstFitsChan *this, const char *name, int value,
42216                      const char *comment, int overwrite, int *status ) {
42217    if ( !astOK ) return;
42218    (**astMEMBER(this,FitsChan,SetFitsI))( this, name, value, comment, overwrite, status );
42219 }
42220 
astSetFitsF_(AstFitsChan * this,const char * name,double value,const char * comment,int overwrite,int * status)42221 void astSetFitsF_( AstFitsChan *this, const char *name, double value,
42222                        const char *comment, int overwrite, int *status ) {
42223    if ( !astOK ) return;
42224    (**astMEMBER(this,FitsChan,SetFitsF))( this, name, value, comment, overwrite, status );
42225 }
42226 
astSetFitsS_(AstFitsChan * this,const char * name,const char * value,const char * comment,int overwrite,int * status)42227 void astSetFitsS_( AstFitsChan *this, const char *name, const char *value,
42228                         const char *comment, int overwrite, int *status ) {
42229    if ( !astOK ) return;
42230    (**astMEMBER(this,FitsChan,SetFitsS))( this, name, value, comment, overwrite, status );
42231 }
42232 
astSetFitsCN_(AstFitsChan * this,const char * name,const char * value,const char * comment,int overwrite,int * status)42233 void astSetFitsCN_( AstFitsChan *this, const char *name, const char *value,
42234                     const char *comment, int overwrite, int *status ) {
42235    if ( !astOK ) return;
42236    (**astMEMBER(this,FitsChan,SetFitsCN))( this, name, value, comment, overwrite, status );
42237 }
42238 
astSetFitsCF_(AstFitsChan * this,const char * name,double * value,const char * comment,int overwrite,int * status)42239 void astSetFitsCF_( AstFitsChan *this, const char *name, double *value,
42240                           const char *comment, int overwrite, int *status ) {
42241    if ( !astOK ) return;
42242    (**astMEMBER(this,FitsChan,SetFitsCF))( this, name, value, comment, overwrite, status );
42243 }
42244 
astSetFitsCI_(AstFitsChan * this,const char * name,int * value,const char * comment,int overwrite,int * status)42245 void astSetFitsCI_( AstFitsChan *this, const char *name, int *value,
42246                           const char *comment, int overwrite, int *status ) {
42247    if ( !astOK ) return;
42248    (**astMEMBER(this,FitsChan,SetFitsCI))( this, name, value, comment, overwrite, status );
42249 }
42250 
astSetFitsL_(AstFitsChan * this,const char * name,int value,const char * comment,int overwrite,int * status)42251 void astSetFitsL_( AstFitsChan *this, const char *name, int value,
42252                          const char *comment, int overwrite, int *status ) {
42253    if ( !astOK ) return;
42254    (**astMEMBER(this,FitsChan,SetFitsL))( this, name, value, comment, overwrite, status );
42255 }
42256 
astSetFitsU_(AstFitsChan * this,const char * name,const char * comment,int overwrite,int * status)42257 void astSetFitsU_( AstFitsChan *this, const char *name, const char *comment,
42258                    int overwrite, int *status ) {
42259    if ( !astOK ) return;
42260    (**astMEMBER(this,FitsChan,SetFitsU))( this, name, comment, overwrite, status );
42261 }
42262 
astSetFitsCM_(AstFitsChan * this,const char * comment,int overwrite,int * status)42263 void astSetFitsCM_( AstFitsChan *this, const char *comment, int overwrite, int *status ) {
42264    if ( !astOK ) return;
42265    (**astMEMBER(this,FitsChan,SetFitsCM))( this, comment, overwrite, status );
42266 }
42267 
astClearCard_(AstFitsChan * this,int * status)42268 void astClearCard_( AstFitsChan *this, int *status ){
42269    if( !this ) return;
42270    (**astMEMBER(this,FitsChan,ClearCard))( this, status );
42271 }
42272 
astSetCard_(AstFitsChan * this,int card,int * status)42273 void astSetCard_( AstFitsChan *this, int card, int *status ){
42274    if( !this ) return;
42275    (**astMEMBER(this,FitsChan,SetCard))( this, card, status );
42276 }
42277 
astTestCard_(AstFitsChan * this,int * status)42278 int astTestCard_( AstFitsChan *this, int *status ){
42279    if( !this ) return 0;
42280    return (**astMEMBER(this,FitsChan,TestCard))( this, status );
42281 }
42282 
astGetCard_(AstFitsChan * this,int * status)42283 int astGetCard_( AstFitsChan *this, int *status ){
42284    if( !this ) return 0;
42285    return (**astMEMBER(this,FitsChan,GetCard))( this, status );
42286 }
42287 
astGetNcard_(AstFitsChan * this,int * status)42288 int astGetNcard_( AstFitsChan *this, int *status ){
42289    if( !this ) return 0;
42290    return (**astMEMBER(this,FitsChan,GetNcard))( this, status );
42291 }
42292 
astGetCardType_(AstFitsChan * this,int * status)42293 int astGetCardType_( AstFitsChan *this, int *status ){
42294    if( !this ) return AST__NOTYPE;
42295    return (**astMEMBER(this,FitsChan,GetCardType))( this, status );
42296 }
42297 
astGetCardComm_(AstFitsChan * this,int * status)42298 const char *astGetCardComm_( AstFitsChan *this, int *status ){
42299    if( !this ) return NULL;
42300    return (**astMEMBER(this,FitsChan,GetCardComm))( this, status );
42301 }
42302 
astGetCardName_(AstFitsChan * this,int * status)42303 const char *astGetCardName_( AstFitsChan *this, int *status ){
42304    if( !this ) return NULL;
42305    return (**astMEMBER(this,FitsChan,GetCardName))( this, status );
42306 }
42307 
astGetNkey_(AstFitsChan * this,int * status)42308 int astGetNkey_( AstFitsChan *this, int *status ){
42309    if( !this ) return 0;
42310    return (**astMEMBER(this,FitsChan,GetNkey))( this, status );
42311 }
42312 
astGetClean_(AstFitsChan * this,int * status)42313 int astGetClean_( AstFitsChan *this, int *status ){
42314    if( !this ) return 0;
42315    return (**astMEMBER(this,FitsChan,GetClean))( this, status );
42316 }
42317 
astGetAllWarnings_(AstFitsChan * this,int * status)42318 const char *astGetAllWarnings_( AstFitsChan *this, int *status ){
42319    if( !this ) return NULL;
42320    return (**astMEMBER(this,FitsChan,GetAllWarnings))( this, status );
42321 }
42322 
astGetFitsCF_(AstFitsChan * this,const char * name,double * value,int * status)42323 int astGetFitsCF_( AstFitsChan *this, const char *name, double *value, int *status ){
42324    if( !astOK ) return 0;
42325    return (**astMEMBER(this,FitsChan,GetFitsCF))( this, name, value, status );
42326 }
42327 
astGetFitsCI_(AstFitsChan * this,const char * name,int * value,int * status)42328 int astGetFitsCI_( AstFitsChan *this, const char *name, int *value, int *status ){
42329    if( !astOK ) return 0;
42330    return (**astMEMBER(this,FitsChan,GetFitsCI))( this, name, value, status );
42331 }
42332 
astGetFitsF_(AstFitsChan * this,const char * name,double * value,int * status)42333 int astGetFitsF_( AstFitsChan *this, const char *name, double *value, int *status ){
42334    if( !astOK ) return 0;
42335    return (**astMEMBER(this,FitsChan,GetFitsF))( this, name, value, status );
42336 }
42337 
astGetFitsI_(AstFitsChan * this,const char * name,int * value,int * status)42338 int astGetFitsI_( AstFitsChan *this, const char *name, int *value, int *status ){
42339    if( !astOK ) return 0;
42340    return (**astMEMBER(this,FitsChan,GetFitsI))( this, name, value, status );
42341 }
42342 
astGetFitsL_(AstFitsChan * this,const char * name,int * value,int * status)42343 int astGetFitsL_( AstFitsChan *this, const char *name, int *value, int *status ){
42344    if( !astOK ) return 0;
42345    return (**astMEMBER(this,FitsChan,GetFitsL))( this, name, value, status );
42346 }
42347 
astTestFits_(AstFitsChan * this,const char * name,int * there,int * status)42348 int astTestFits_( AstFitsChan *this, const char *name, int *there, int *status ){
42349    if( there ) *there = 0;
42350    if( !astOK ) return 0;
42351    return (**astMEMBER(this,FitsChan,TestFits))( this, name, there, status );
42352 }
42353 
astGetFitsS_(AstFitsChan * this,const char * name,char ** value,int * status)42354 int astGetFitsS_( AstFitsChan *this, const char *name, char **value, int *status ){
42355    if( !astOK ) return 0;
42356    return (**astMEMBER(this,FitsChan,GetFitsS))( this, name, value, status );
42357 }
42358 
astGetFitsCN_(AstFitsChan * this,const char * name,char ** value,int * status)42359 int astGetFitsCN_( AstFitsChan *this, const char *name, char **value, int *status ){
42360    if( !astOK ) return 0;
42361    return (**astMEMBER(this,FitsChan,GetFitsCN))( this, name, value, status );
42362 }
42363 
astFitsGetCom_(AstFitsChan * this,const char * name,char ** comment,int * status)42364 int astFitsGetCom_( AstFitsChan *this, const char *name, char **comment, int *status ){
42365    if( !astOK ) return 0;
42366    return (**astMEMBER(this,FitsChan,FitsGetCom))( this, name, comment, status );
42367 }
42368 
astKeyFields_(AstFitsChan * this,const char * filter,int maxfld,int * ubnd,int * lbnd,int * status)42369 int astKeyFields_( AstFitsChan *this, const char *filter, int maxfld,
42370                 int *ubnd, int *lbnd, int *status ){
42371    if( !astOK ) return 0;
42372    return (**astMEMBER(this,FitsChan,KeyFields))( this, filter, maxfld,
42373            ubnd, lbnd, status );
42374 }
42375 
astFindFits_(AstFitsChan * this,const char * name,char * card,int inc,int * status)42376 int astFindFits_( AstFitsChan *this, const char *name, char *card, int inc, int *status ){
42377    if( !astOK ) return 0;
42378    return (**astMEMBER(this,FitsChan,FindFits))( this, name, card, inc, status );
42379 }
42380 
astGetEncoding_(AstFitsChan * this,int * status)42381 int astGetEncoding_( AstFitsChan *this, int *status ){
42382    if( !astOK ) return UNKNOWN_ENCODING;
42383    return (**astMEMBER(this,FitsChan,GetEncoding))( this, status );
42384 }
42385 
astGetCDMatrix_(AstFitsChan * this,int * status)42386 int astGetCDMatrix_( AstFitsChan *this, int *status ){
42387    if( !astOK ) return 0;
42388    return (**astMEMBER(this,FitsChan,GetCDMatrix))( this, status );
42389 }
astSetTableSource_(AstFitsChan * this,void (* tabsource)(void),void (* tabsource_wrap)(void (*)(void),AstFitsChan *,const char *,int,int,int *),int * status)42390 void astSetTableSource_( AstFitsChan *this,
42391                          void (*tabsource)( void ),
42392                          void (*tabsource_wrap)( void (*)( void ),
42393                                                  AstFitsChan *, const char *,
42394                                                  int, int, int * ),
42395                          int *status ){
42396    if( !astOK ) return;
42397    (**astMEMBER(this,FitsChan,SetTableSource))( this, tabsource,
42398                                                 tabsource_wrap, status );
42399 }
astTableSource_(AstFitsChan * this,void (* tabsource)(AstFitsChan *,const char *,int,int,int *),int * status)42400 void astTableSource_( AstFitsChan *this,
42401                       void (* tabsource)( AstFitsChan *, const char *,
42402                                           int, int, int * ),
42403                       int *status ){
42404    if( !astOK ) return;
42405    (**astMEMBER(this,FitsChan,TableSource))( this, tabsource, status );
42406 }
42407 
42408 /*
42409  * A diagnostic function which lists the contents of a FitsChan to
42410  * standard output.
42411  */
42412 
42413 /*
42414 static void ListFC( AstFitsChan *, const char * );
42415 
42416 static void ListFC( AstFitsChan *this, const char *ttl ) {
42417    FitsCard *cardo;
42418    char card[ 81 ];
42419    printf("%s\n----------------------------------------\n", ttl );
42420    cardo = (FitsCard *) this->card;
42421    astClearCard( this );
42422    while( !astFitsEof( this ) && astOK ){
42423       FormatCard( this, card, "List" );
42424       if( this->card == cardo ) {
42425          printf( "%s   <<<<< currrent card <<<<< \n", card );
42426       } else {
42427          printf( "%s\n", card );
42428       }
42429       MoveCard( this, 1, "List", "FitsChan" );
42430    }
42431    this->card = cardo;
42432 }
42433 */
42434 
42435 
42436 
42437 
42438 
42439 
42440 
42441 
42442 
42443 
42444 
42445