1 /*
2 *class++
3 *  Name:
4 *     SkyFrame
5 
6 *  Purpose:
7 *     Celestial coordinate system description.
8 
9 *  Constructor Function:
10 c     astSkyFrame
11 f     AST_SKYFRAME
12 
13 *  Description:
14 *     A SkyFrame is a specialised form of Frame which describes
15 *     celestial longitude/latitude coordinate systems. The particular
16 *     celestial coordinate system to be represented is specified by
17 *     setting the SkyFrame's System attribute (currently, the default
18 *     is ICRS) qualified, as necessary, by a mean Equinox value and/or
19 *     an Epoch.
20 *
21 *     For each of the supported celestial coordinate systems, a SkyFrame
22 *     can apply an optional shift of origin to create a coordinate system
23 *     representing offsets within the celestial coordinate system from some
24 *     specified reference point. This offset coordinate system can also be
25 *     rotated to define new longitude and latitude axes. See attributes
26 *     SkyRef, SkyRefIs, SkyRefP and AlignOffset.
27 *
28 *     All the coordinate values used by a SkyFrame are in
29 *     radians. These may be formatted in more conventional ways for
30 c     display by using astFormat.
31 f     display by using AST_FORMAT.
32 
33 *  Inheritance:
34 *     The SkyFrame class inherits from the Frame class.
35 
36 *  Attributes:
37 *     In addition to those attributes common to all Frames, every
38 *     SkyFrame also has the following attributes:
39 *
40 *     - AlignOffset: Align SkyFrames using the offset coordinate system?
41 *     - AsTime(axis): Format celestial coordinates as times?
42 *     - Equinox: Epoch of the mean equinox
43 *     - IsLatAxis: Is the specified axis the latitude axis?
44 *     - IsLonAxis: Is the specified axis the longitude axis?
45 *     - LatAxis: Index of the latitude axis
46 *     - LonAxis: Index of the longitude axis
47 *     - NegLon: Display longitude values in the range [-pi,pi]?
48 *     - Projection: Sky projection description.
49 *     - SkyRef: Position defining location of the offset coordinate system
50 *     - SkyRefIs: Selects the nature of the offset coordinate system
51 *     - SkyRefP: Position defining orientation of the offset coordinate system
52 
53 *  Functions:
54 *     In addition to those
55 c     functions
56 f     routines
57 *     applicable to all Frames, the following
58 c     functions
59 f     routines
60 *     may also be applied to all SkyFrames:
61 *
62 c     - astSkyOffsetMap: Obtain a Mapping from absolute to offset coordinates
63 f     - AST_SKYOFFSETMAP: Obtain a Mapping from absolute to offset coordinates
64 
65 *  Copyright:
66 *     Copyright (C) 1997-2006 Council for the Central Laboratory of the
67 *     Research Councils
68 *     Copyright (C) 2010 Science & Technology Facilities Council.
69 *     All Rights Reserved.
70 
71 *  Licence:
72 *     This program is free software: you can redistribute it and/or
73 *     modify it under the terms of the GNU Lesser General Public
74 *     License as published by the Free Software Foundation, either
75 *     version 3 of the License, or (at your option) any later
76 *     version.
77 *
78 *     This program is distributed in the hope that it will be useful,
79 *     but WITHOUT ANY WARRANTY; without even the implied warranty of
80 *     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
81 *     GNU Lesser General Public License for more details.
82 *
83 *     You should have received a copy of the GNU Lesser General
84 *     License along with this program.  If not, see
85 *     <http://www.gnu.org/licenses/>.
86 
87 *  Authors:
88 *     RFWS: R.F. Warren-Smith (Starlink)
89 *     DSB: David S. Berry (Starlink)
90 *     BEC: Brad Cavanagh (JAC, Hawaii)
91 
92 *  History:
93 *     4-MAR-1996 (RFWS):
94 *        Original version.
95 *     17-MAY-1996 (RFWS):
96 *        Tidied up, etc.
97 *     31-JUL-1996 (RFWS):
98 *        Added support for attributes and a public interface.
99 *     11-SEP-1996 (RFWS):
100 *        Added Gap (written by DSB).
101 *     24-SEP-1996 (RFWS):
102 *        Added I/O facilities.
103 *     27-FEB-1997 (RFWS):
104 *        Improved the public prologues.
105 *     27-MAY-1997 (RFWS):
106 *        Modified to use a new public interface to the SlaMap class
107 *        and to use the astSimplify method to remove redundant
108 *        conversions.
109 *     16-JUN-1997 (RFWS):
110 *        Fixed bug in axis associations returned by astMatch if axes
111 *        were swapped.
112 *     16-JUL-1997 (RFWS):
113 *        Added Projection attribute.
114 *     14-NOV-1997 (RFWS):
115 *        Corrected the omission of axis permutations from astNorm.
116 *     21-JAN-1998 (RFWS):
117 *        Ensure that Title and Domain values appropriate to a SkyFrame
118 *        are preserved if a Frame result is generated by SubFrame.
119 *     26-FEB-1998 (RFWS):
120 *        Over-ride the astUnformat method.
121 *     3-APR-2001 (DSB):
122 *        Added "Unknown" option for the System attribute. Added read-only
123 *        attributes LatAxis and LonAxis.
124 *     21-JUN-2001 (DSB):
125 *        Added astAngle and astOffset2.
126 *     4-SEP-2001 (DSB):
127 *        Added NegLon attribute, and astResolve method.
128 *     9-SEP-2001 (DSB):
129 *        Added astBear method.
130 *     21-SEP-2001 (DSB):
131 *        Removed astBear method.
132 *     10-OCT-2002 (DSB):
133 *        Moved definitions of macros for SkyFrame system values from
134 *        this file into skyframe.h.
135 *     24-OCT-2002 (DSB):
136 *        Modified MakeSkyMapping so that any two SkyFrames with system=unknown
137 *        are assumed to be related by a UnitMap. previously, they were
138 *        considered to be unrelated, resulting in no ability to convert from
139 *        one to the other. This could result for instance in astConvert
140 *        being unable to find a maping from a SkyFrame to itself.
141 *     15-NOV-2002 (DSB):
142 *        Moved System and Epoch attributes to the Frame class.
143 *     8-JAN-2003 (DSB):
144 *        Changed private InitVtab method to protected astInitSkyFrameVtab
145 *        method.
146 *     11-JUN-2003 (DSB):
147 *        Added ICRS option for System attribute, and made it the default
148 *        in place of FK5.
149 *     27-SEP-2003 (DSB):
150 *        Added HELIOECLIPTIC option for System attribute.
151 *     19-APR-2004 (DSB):
152 *        Added SkyRef, SkyRefIs, SkyRefP and AlignOffset attributes.
153 *     8-SEP-2004 (DSB):
154 *        Added astResolvePoints method.
155 *     2-DEC-2004 (DSB):
156 *        Added System "J2000"
157 *     27-JAN-2005 (DSB):
158 *        Fix memory leaks in astLoadSkyFrame_ and Match.
159 *     7-APR-2005 (DSB):
160 *        Allow SkyRefIs to be set to "Ignored".
161 *     12-MAY-2005 (DSB):
162 *        Override astNormBox method.
163 *     15-AUG-2005 (DSB):
164 *        Added AZEL system.
165 *     13-SEP-2005 (DSB):
166 *        Override astClearSystem so that SkyRef/SkyRefPcan be converted
167 *        from the original System to the default System.
168 *     19-SEP-2005 (DSB):
169 *        Changed default for SkyRefIs from ORIGIN to IGNORED.
170 *     14-FEB-2006 (DSB):
171 *        Override astGetObjSize.
172 *     22-FEB-2006 (DSB):
173 *        Store the Local Apparent Sidereal Time in the SkyFrame structure
174 *        in order to avoid expensive re-computations.
175 *     22-AUG-2006 (DSB):
176 *        Ensure the cached Local Apparent Siderial Time is initialised
177 *        when initialising or loading a SkyFrame.
178 *     22-SEP-2006 (DSB):
179 *        Report an error in SetSystem if it is not possible to convert
180 *        from old to new systems.
181 *     3-OCT-2006 (DSB):
182 *        Added Equation of Equinoxes to the SkyFrame structure.
183 *     6-OCT-2006 (DSB):
184 *        - Guard against annulling null pointers in subFrame.
185 *        - Add Dut1 attribute
186 *        - Use linear approximation for LAST over short periods (less
187 *          than 0.001 of a day)
188 *        - Remove Equation of Equinoxes from the SkyFrame structure.
189 *     10-OCT-2006 (DSB):
190 *        Use "AlOff" instead of "AlignOffset" as the external channel name
191 *        for the AlignOffset attribute. The longer form exceeded the
192 *        limit that can be used by the Channel class.
193 *     14-OCT-2006 (DSB):
194 *        - Move Dut1 attribute to the Frame class.
195 *        - Use the TimeFrame class to do the TDB->LAST conversions.
196 *     17-JAN-2007 (DSB):
197 *        - Use a UnitMap to align offset coordinate systems in two
198 *        SkyFrames, regardless of other attribute values.
199 *        - Only align in offset coordinates if both target and template
200 *        have a non-zero value for AlignOffset.
201 *     23-JAN-2007 (DSB):
202 *        Modified so that a SkyFrame can be used as a template to find a
203 *        SkyFrame contained within a CmpFrame. This involves changes in
204 *        Match and the removal of the local versions of SetMaxAxes and
205 *        SetMinAxes.
206 *     4-JUL-2007 (DSB):
207 *        Modified GetLast to use the correct solar to sidereal conversion
208 *        factor. As a consequence the largest acceptable epoch gap before
209 *        the LAST needs to be recalculated has been increased.
210 *     11-JUL-2007 (DSB):
211 *        Override astSetEpoch and astClearEpoch by implementations which
212 *        update the LAST value stored in the SkyFrame.
213 *     7-AUG-2007 (DSB):
214 *        - Set a value for the CentreZero attribute when extracting a
215 *        SkyAxis from a SkyFrame in function SubFrame.
216 *        - In SubFrame, clear extended attributes such as System after
217 *        all axis attributes have been "fixated.
218 *     30-AUG-2007 (DSB):
219 *        Override astSetDut1 and astClearDut1 by implementations which
220 *        update the LAST value stored in the SkyFrame.
221 *     31-AUG-2007 (DSB):
222 *        - Cache the magnitude of the diurnal aberration vector in the
223 *        SkyFrame structure for use when correcting for diurnal aberration.
224 *        - Modify the azel conversions to include correction for diurnal
225 *        aberration.
226 *        - Override astClearObsLat and astSetObsLat by implementations which
227 *        reset the magnitude of the diurnal aberration vector.
228 *     3-SEP-2007 (DSB):
229 *        In SubFrame, since AlignSystem is extended by the SkyFrame class
230 *        it needs to be cleared before invoking the parent SubFrame
231 *        method in cases where the result Frame is not a SkyFrame.
232 *     2-OCT-2007 (DSB):
233 *        In Overlay, clear AlignSystem as well as System before calling
234 *        the parent overlay method.
235 *     10-OCT-2007 (DSB):
236 *        In MakeSkyMapping, correct the usage of variables "system" and
237 *        "align_sys" when aligning in AZEL.
238 *     18-OCT-2007 (DSB):
239 *        Compare target and template AlignSystem values in Match, rather
240 *        than comparing target and result AlignSystem values in MakeSkyMapping
241 *        (since result is basically a copy of target).
242 *     27-NOV-2007 (DSB):
243 *        - Modify SetSystem to ensure that SkyRef and SkyRefP position are
244 *        always transformed as absolute values, rather than as offset
245 *        values.
246 *        - Modify SubMatch so that a value of zero is assumed for
247 *        AlignOffset when restoring thre integrity of a FrameSet.
248 *     15-DEC-2008 (DSB):
249 *        Improve calculation of approximate Local Apparent Sidereal time
250 *        by finding and using the ratio of solar to sidereal time
251 *        independently for each approximation period.
252 *     14-JAN-2009 (DSB):
253 *        Override the astIntersect method.
254 *     21-JAN-2009 (DSB):
255 *        Fix mis-use of results buffers for GetFormat and GetAttrib.
256 *     16-JUN-2009 (DSB):
257 *        All sky coordinate systems currently supported by SkyFrame are
258 *        left handed. So fix GetDirection method to return zero for all
259 *        longitude axes and 1 for all latitude axes.
260 *     18-JUN-2009 (DSB):
261 *        Incorporate the new ObsAlt attribute.
262 *     23-SEP-2009 (DSB):
263 *        Allow some rounding error when checking for changes in SetObsLon
264 *        and SetDut1. This reduces the number of times the expensive
265 *        calculation of LAST is performed.
266 *     24-SEP-2009 (DSB);
267 *        Create a static cache of LAST values stored in the class virtual
268 *        function table. These are used in preference to calculating a new
269 *        value from scratch.
270 *     25-SEP-2009 (DSB);
271 *        Do not calculate LAST until it is needed.
272 *     12-OCT-2009 (DSB);
273 *        - Handle 2.PI->0 discontinuity in cached LAST values.
274 *     12-OCT-2009 (BEC);
275 *        - Fix bug in caching LAST value.
276 *     31-OCT-2009 (DSB);
277 *        Correct SetCachedLAST to handle cases where the epoch to be
278 *        stored is smaller than any epoch already in the table.
279 *     24-NOV-2009 (DSB):
280 *        - In CalcLast, only use end values form the table of stored
281 *        LAST values if the corresponding epochs are within 0.001 of
282 *        a second of the required epoch (this tolerance used to be
283 *        0.1 seconds).
284 *        - Do not clear the cached LAST value in SetEpoch and ClearEpoch.
285 *     8-MAR-2010 (DSB):
286 *        Add astSkyOffsetMap method.
287 *     7-APR-2010 (DSB):
288 *        Add IsLatAxis and IsLonAxis attributes.
289 *     11-MAY-2010 (DSB):
290 *        In SetSystem, clear SkyRefP as well as SkyRef.
291 *     22-MAR-2011 (DSB):
292 *        Override astFrameGrid method.
293 *     29-APR-2011 (DSB):
294 *        Prevent astFindFrame from matching a subclass template against a
295 *        superclass target.
296 *     23-MAY-2011 (DSB):
297 *        Truncate returned PointSet in function FrameGrid to exclude unused points.
298 *     24-MAY-2011 (DSB):
299 *        When clearing or setting the System attribute, clear SkyRef rather
300 *        than reporting an error if the Mapping from the old System to the
301 *        new System is unknown.
302 *     30-NOV-2011 (DSB):
303 *        When aligning two SkyFrames in the system specified by AlignSystem,
304 *        do not assume inappropriate default equinox values for systems
305 *        that are not referred to the equinox specified by the Equinox attribute.
306 *     26-APR-2012 (DSB):
307 *        - Correct Dump function so that any axis permutation is taken into
308 *        account when dumping SkyFrame attributes that have a separate value
309 *        for each axis (e.g. SkyRef and SkyRefP).
310 *        - Take axis permutation into account when setting a new value
311 *        for attributes that have a separate value for each axis (e.g.
312 *        SkyRef and SkyRefP).
313 *        - Remove the code that overrides ClearEpoch and SetEpoch (these
314 *        overrides have not been needed since the changes made on
315 *        24/11/2009).
316 *     27-APR-2012 (DSB):
317 *        - Correct astLoadSkyFrame function so that any axis permutation is
318 *        taken into account when loading SkyFrame attributes that have a
319 *        separate value for each axis.
320 *     25-JUL-2013 (DSB):
321 *        Use a single table of cached LAST values for all threads, rather
322 *        than a separate table for each thread. The problem with a table per
323 *        thread  is that if you have N threads, each table contains only
324 *        one N'th of the total number of cached values, resulting in
325 *        poorer accuracy, and small variations in interpolated LAST value
326 *        depending on the way the cached values are distributed amongst the
327 *        N threads.
328 *     6-AST-2013 (DSB):
329 *        Fix the use of the read-write lock that is used to serialise
330 *        access to the table of cached LAST values. This bug could
331 *        cause occasional problems where an AST pointer would became
332 *        invalid for no apparent reason.
333 *     21-FEB-2014 (DSB):
334 *        Rounding errors in the SkyLineDef constructor could result in the line
335 *        between coincident points being given a non-zero length.
336 *class--
337 */
338 
339 /* Module Macros. */
340 /* ============== */
341 /* Set the name of the class we are implementing. This indicates to
342    the header files that define class interfaces that they should make
343    "protected" symbols available. */
344 #define astCLASS SkyFrame
345 
346 /* Define the first and last acceptable System values. */
347 #define FIRST_SYSTEM AST__FK4
348 #define LAST_SYSTEM AST__AZEL
349 
350 /* Speed of light (AU per day) (from SLA_AOPPA) */
351 #define C 173.14463331
352 
353 /* Ratio between solar and sidereal time (from SLA_AOPPA) */
354 #define SOLSID 1.00273790935
355 
356 /* Define values for the different values of the SkyRefIs attribute. */
357 #define POLE_STRING "Pole"
358 #define ORIGIN_STRING "Origin"
359 #define IGNORED_STRING "Ignored"
360 
361 /* Define other numerical constants for use in this module. */
362 #define GETATTRIB_BUFF_LEN 200
363 #define GETFORMAT_BUFF_LEN 50
364 #define GETLABEL_BUFF_LEN 40
365 #define GETSYMBOL_BUFF_LEN 20
366 #define GETTITLE_BUFF_LEN 200
367 
368 /* A macro which returns a flag indicating if the supplied system is
369    references to the equinox specified by the Equinox attribute. */
370 #define EQREF(system) \
371 ((system==AST__FK4||system==AST__FK4_NO_E||system==AST__FK5||system==AST__ECLIPTIC)?1:0)
372 
373 /*
374 *
375 *  Name:
376 *     MAKE_CLEAR
377 
378 *  Purpose:
379 *     Implement a method to clear a single value in a multi-valued attribute.
380 
381 *  Type:
382 *     Private macro.
383 
384 *  Synopsis:
385 *     #include "skyframe.h"
386 *     MAKE_CLEAR(attr,component,assign,nval)
387 
388 *  Class Membership:
389 *     Defined by the SkyFrame class.
390 
391 *  Description:
392 *     This macro expands to an implementation of a private member function of
393 *     the form:
394 *
395 *        static void Clear<Attribute>( AstSkyFrame *this, int axis )
396 *
397 *     and an external interface function of the form:
398 *
399 *        void astClear<Attribute>_( AstSkyFrame *this, int axis )
400 *
401 *     which implement a method for clearing a single value in a specified
402 *     multi-valued attribute for an axis of a SkyFrame.
403 
404 *  Parameters:
405 *     attr
406 *        The name of the attribute to be cleared, as it appears in the function
407 *        name (e.g. Label in "astClearLabelAt").
408 *     component
409 *        The name of the class structure component that holds the attribute
410 *        value.
411 *     assign
412 *        An expression that evaluates to the value to assign to the component
413 *        to clear its value.
414 *     nval
415 *        Specifies the number of values in the multi-valued attribute. The
416 *        "axis" values supplied to the created function should be in the
417 *        range zero to (nval - 1).
418 
419 *  Notes:
420 *     -  To avoid problems with some compilers, you should not leave any white
421 *     space around the macro arguments.
422 *
423 */
424 
425 /* Define the macro. */
426 #define MAKE_CLEAR(attr,component,assign,nval) \
427 \
428 /* Private member function. */ \
429 /* ------------------------ */ \
430 static void Clear##attr( AstSkyFrame *this, int axis, int *status ) { \
431 \
432    int axis_p; \
433 \
434 /* Check the global error status. */ \
435    if ( !astOK ) return; \
436 \
437 /* Validate and permute the axis index. */ \
438    axis_p = astValidateAxis( this, axis, 1, "astClear" #attr ); \
439 \
440 /* Assign the "clear" value. */ \
441    if( astOK ) { \
442       this->component[ axis_p ] = (assign); \
443    } \
444 } \
445 \
446 /* External interface. */ \
447 /* ------------------- */ \
448 void astClear##attr##_( AstSkyFrame *this, int axis, int *status ) { \
449 \
450 /* Check the global error status. */ \
451    if ( !astOK ) return; \
452 \
453 /* Invoke the required method via the virtual function table. */ \
454    (**astMEMBER(this,SkyFrame,Clear##attr))( this, axis, status ); \
455 }
456 
457 
458 /*
459 *
460 *  Name:
461 *     MAKE_GET
462 
463 *  Purpose:
464 *     Implement a method to get a single value in a multi-valued attribute.
465 
466 *  Type:
467 *     Private macro.
468 
469 *  Synopsis:
470 *     #include "skyframe.h"
471 *     MAKE_GET(attr,type,bad_value,assign,nval)
472 
473 *  Class Membership:
474 *     Defined by the SkyFrame class.
475 
476 *  Description:
477 *     This macro expands to an implementation of a private member function of
478 *     the form:
479 *
480 *        static <Type> Get<Attribute>( AstSkyFrame *this, int axis )
481 *
482 *     and an external interface function of the form:
483 *
484 *        <Type> astGet<Attribute>_( AstSkyFrame *this, int axis )
485 *
486 *     which implement a method for getting a single value from a specified
487 *     multi-valued attribute for an axis of a SkyFrame.
488 
489 *  Parameters:
490 *     attr
491 *        The name of the attribute whose value is to be obtained, as it
492 *        appears in the function name (e.g. Label in "astGetLabel").
493 *     type
494 *        The C type of the attribute.
495 *     bad_value
496 *        A constant value to return if the global error status is set, or if
497 *        the function fails.
498 *     assign
499 *        An expression that evaluates to the value to be returned. This can
500 *        use the string "axis" to represent the zero-based value index.
501 *     nval
502 *        Specifies the number of values in the multi-valued attribute. The
503 *        "axis" values supplied to the created function should be in the
504 *        range zero to (nval - 1).
505 
506 *  Notes:
507 *     -  To avoid problems with some compilers, you should not leave any white
508 *     space around the macro arguments.
509 *
510 */
511 
512 /* Define the macro. */
513 #define MAKE_GET(attr,type,bad_value,assign,nval) \
514 \
515 /* Private member function. */ \
516 /* ------------------------ */ \
517 static type Get##attr( AstSkyFrame *this, int axis, int *status ) { \
518    int axis_p;                   /* Permuted axis index */ \
519    type result;                  /* Result to be returned */ \
520 \
521 /* Initialise */\
522    result = (bad_value); \
523 \
524 /* Check the global error status. */ \
525    if ( !astOK ) return result; \
526 \
527 /* Validate and permute the axis index. */ \
528    axis_p = astValidateAxis( this, axis, 1, "astGet" #attr ); \
529 \
530 /* Assign the result value. */ \
531    if( astOK ) { \
532       result = (assign); \
533    } \
534 \
535 /* Check for errors and clear the result if necessary. */ \
536    if ( !astOK ) result = (bad_value); \
537 \
538 /* Return the result. */ \
539    return result; \
540 } \
541 /* External interface. */ \
542 /* ------------------- */  \
543 type astGet##attr##_( AstSkyFrame *this, int axis, int *status ) { \
544 \
545 /* Check the global error status. */ \
546    if ( !astOK ) return (bad_value); \
547 \
548 /* Invoke the required method via the virtual function table. */ \
549    return (**astMEMBER(this,SkyFrame,Get##attr))( this, axis, status ); \
550 }
551 
552 /*
553 *
554 *  Name:
555 *     MAKE_SET
556 
557 *  Purpose:
558 *     Implement a method to set a single value in a multi-valued attribute
559 *     for a SkyFrame.
560 
561 *  Type:
562 *     Private macro.
563 
564 *  Synopsis:
565 *     #include "skyframe.h"
566 *     MAKE_SET(attr,type,component,assign,nval)
567 
568 *  Class Membership:
569 *     Defined by the SkyFrame class.
570 
571 *  Description:
572 *     This macro expands to an implementation of a private member function of
573 *     the form:
574 *
575 *        static void Set<Attribute>( AstSkyFrame *this, int axis, <Type> value )
576 *
577 *     and an external interface function of the form:
578 *
579 *        void astSet<Attribute>_( AstSkyFrame *this, int axis, <Type> value )
580 *
581 *     which implement a method for setting a single value in a specified
582 *     multi-valued attribute for a SkyFrame.
583 
584 *  Parameters:
585 *      attr
586 *         The name of the attribute to be set, as it appears in the function
587 *         name (e.g. Label in "astSetLabelAt").
588 *      type
589 *         The C type of the attribute.
590 *      component
591 *         The name of the class structure component that holds the attribute
592 *         value.
593 *      assign
594 *         An expression that evaluates to the value to be assigned to the
595 *         component.
596 *      nval
597 *         Specifies the number of values in the multi-valued attribute. The
598 *         "axis" values supplied to the created function should be in the
599 *         range zero to (nval - 1).
600 
601 *  Notes:
602 *     -  To avoid problems with some compilers, you should not leave any white
603 *     space around the macro arguments.
604 *-
605 */
606 
607 /* Define the macro. */
608 #define MAKE_SET(attr,type,component,assign,nval) \
609 \
610 /* Private member function. */ \
611 /* ------------------------ */ \
612 static void Set##attr( AstSkyFrame *this, int axis, type value, int *status ) { \
613 \
614    int axis_p; \
615 \
616 /* Check the global error status. */ \
617    if ( !astOK ) return; \
618 \
619 /* Validate and permute the axis index. */ \
620    axis_p = astValidateAxis( this, axis, 1, "astSet" #attr ); \
621 \
622 /* Store the new value in the structure component. */ \
623    if( astOK ) { \
624       this->component[ axis_p ] = (assign); \
625    } \
626 } \
627 \
628 /* External interface. */ \
629 /* ------------------- */ \
630 void astSet##attr##_( AstSkyFrame *this, int axis, type value, int *status ) { \
631 \
632 /* Check the global error status. */ \
633    if ( !astOK ) return; \
634 \
635 /* Invoke the required method via the virtual function table. */ \
636    (**astMEMBER(this,SkyFrame,Set##attr))( this, axis, value, status ); \
637 }
638 
639 /*
640 *
641 *  Name:
642 *     MAKE_TEST
643 
644 *  Purpose:
645 *     Implement a method to test if a single value has been set in a
646 *     multi-valued attribute for a class.
647 
648 *  Type:
649 *     Private macro.
650 
651 *  Synopsis:
652 *     #include "skyframe.h"
653 *     MAKE_TEST(attr,assign,nval)
654 
655 *  Class Membership:
656 *     Defined by the SkyFrame class.
657 
658 *  Description:
659 *     This macro expands to an implementation of a private member function of
660 *     the form:
661 *
662 *        static int Test<Attribute>( AstSkyFrame *this, int axis )
663 *
664 *     and an external interface function of the form:
665 *
666 *        int astTest<Attribute>_( AstSkyFrame *this, int axis )
667 *
668 *     which implement a method for testing if a single value in a specified
669 *     multi-valued attribute has been set for a class.
670 
671 *  Parameters:
672 *      attr
673 *         The name of the attribute to be tested, as it appears in the function
674 *         name (e.g. Label in "astTestLabelAt").
675 *      assign
676 *         An expression that evaluates to 0 or 1, to be used as the returned
677 *         value. This can use the string "axis" to represent the zero-based
678 *         index of the value within the attribute.
679 *      nval
680 *         Specifies the number of values in the multi-valued attribute. The
681 *         "axis" values supplied to the created function should be in the
682 *         range zero to (nval - 1).
683 
684 *  Notes:
685 *     -  To avoid problems with some compilers, you should not leave any white
686 *     space around the macro arguments.
687 *-
688 */
689 
690 /* Define the macro. */
691 #define MAKE_TEST(attr,assign,nval) \
692 \
693 /* Private member function. */ \
694 /* ------------------------ */ \
695 static int Test##attr( AstSkyFrame *this, int axis, int *status ) { \
696    int result;                   /* Value to return */ \
697    int axis_p;                   /* Permuted axis index */ \
698 \
699 /* Initialise */ \
700    result =0; \
701 \
702 /* Check the global error status. */ \
703    if ( !astOK ) return result; \
704 \
705 /* Validate and permute the axis index. */ \
706    axis_p = astValidateAxis( this, axis, 1, "astTest" #attr ); \
707 \
708 /* Assign the result value. */ \
709    if( astOK ) { \
710       result = (assign); \
711    } \
712 \
713 /* Check for errors and clear the result if necessary. */ \
714    if ( !astOK ) result = 0; \
715 \
716 /* Return the result. */ \
717    return result; \
718 } \
719 /* External interface. */ \
720 /* ------------------- */ \
721 int astTest##attr##_( AstSkyFrame *this, int axis, int *status ) { \
722 \
723 /* Check the global error status. */ \
724    if ( !astOK ) return 0; \
725 \
726 /* Invoke the required method via the virtual function table. */ \
727    return (**astMEMBER(this,SkyFrame,Test##attr))( this, axis, status ); \
728 }
729 
730 
731 /* Header files. */
732 /* ============= */
733 /* Interface definitions. */
734 /* ---------------------- */
735 
736 #include "globals.h"             /* Thread-safe global data access */
737 #include "error.h"               /* Error reporting facilities */
738 #include "memory.h"              /* Memory allocation facilities */
739 #include "globals.h"             /* Thread-safe global data access */
740 #include "object.h"              /* Base Object class */
741 #include "pointset.h"            /* Sets of points (for AST__BAD) */
742 #include "unitmap.h"             /* Unit Mappings */
743 #include "permmap.h"             /* Coordinate permutations */
744 #include "cmpmap.h"              /* Compound Mappings */
745 #include "slamap.h"              /* SLALIB sky coordinate Mappings */
746 #include "timemap.h"             /* Time conversions */
747 #include "skyaxis.h"             /* Sky axes */
748 #include "frame.h"               /* Parent Frame class */
749 #include "matrixmap.h"           /* Matrix multiplication */
750 #include "sphmap.h"              /* Cartesian<->Spherical transformations */
751 #include "skyframe.h"            /* Interface definition for this class */
752 #include "pal.h"                 /* SLALIB library interface */
753 #include "wcsmap.h"              /* Factors of PI */
754 #include "timeframe.h"           /* Time system transformations */
755 
756 /* Error code definitions. */
757 /* ----------------------- */
758 #include "ast_err.h"             /* AST error codes */
759 
760 /* C header files. */
761 /* --------------- */
762 #include <ctype.h>
763 #include <float.h>
764 #include <limits.h>
765 #include <math.h>
766 #include <stdarg.h>
767 #include <stddef.h>
768 #include <stdio.h>
769 #include <string.h>
770 
771 /* Type Definitions. */
772 /* ================= */
773 
774 /* Cached Line structure. */
775 /* ---------------------- */
776 /* This structure contains information describing a line segment within a
777    SkyFrame. It differs from the AstLineDef defined in frame.h because
778    positions are represented by 3D (x,y,z) cartesian coords rather than
779    2D (long,lat) coords. */
780 
781 typedef struct SkyLineDef {
782    AstFrame *frame;            /* Pointer to Frame in which the line is defined */
783    double length;              /* Line length */
784    int infinite;               /* Disregard the start and end of the line? */
785    double start[3];            /* Unit vector defining start of line */
786    double end[3];              /* Unit vector defining end of line */
787    double dir[3];              /* Unit vector defining line direction */
788    double q[3];                /* Unit vector perpendicular to line */
789    double start_2d[2];
790    double end_2d[2];
791 } SkyLineDef;
792 
793 /* Module Variables. */
794 /* ================= */
795 
796 /* Address of this static variable is used as a unique identifier for
797    member of this class. */
798 static int class_check;
799 
800 /* Pointers to parent class methods which are used or extended by this
801    class. */
802 static AstSystemType (* parent_getalignsystem)( AstFrame *, int * );
803 static AstSystemType (* parent_getsystem)( AstFrame *, int * );
804 static const char *(* parent_format)( AstFrame *, int, double, int * );
805 static const char *(* parent_getattrib)( AstObject *, const char *, int * );
806 static const char *(* parent_getdomain)( AstFrame *, int * );
807 static const char *(* parent_getformat)( AstFrame *, int, int * );
808 static const char *(* parent_getlabel)( AstFrame *, int, int * );
809 static const char *(* parent_getsymbol)( AstFrame *, int, int * );
810 static const char *(* parent_gettitle)( AstFrame *, int * );
811 static const char *(* parent_getunit)( AstFrame *, int, int * );
812 static double (* parent_gap)( AstFrame *, int, double, int *, int * );
813 static double (* parent_getbottom)( AstFrame *, int, int * );
814 static double (* parent_getepoch)( AstFrame *, int * );
815 static double (* parent_gettop)( AstFrame *, int, int * );
816 static int (* parent_getdirection)( AstFrame *, int, int * );
817 static int (* parent_getobjsize)( AstObject *, int * );
818 static int (* parent_match)( AstFrame *, AstFrame *, int, int **, int **, AstMapping **, AstFrame **, int * );
819 static int (* parent_subframe)( AstFrame *, AstFrame *, int, const int *, const int *, AstMapping **, AstFrame **, int * );
820 static int (* parent_testattrib)( AstObject *, const char *, int * );
821 static int (* parent_testformat)( AstFrame *, int, int * );
822 static int (* parent_unformat)( AstFrame *, int, const char *, double *, int * );
823 static void (* parent_clearattrib)( AstObject *, const char *, int * );
824 static void (* parent_cleardut1)( AstFrame *, int * );
825 static void (* parent_clearformat)( AstFrame *, int, int * );
826 static void (* parent_clearobsalt)( AstFrame *, int * );
827 static void (* parent_clearobslat)( AstFrame *, int * );
828 static void (* parent_clearobslon)( AstFrame *, int * );
829 static void (* parent_clearsystem)( AstFrame *, int * );
830 static void (* parent_overlay)( AstFrame *, const int *, AstFrame *, int * );
831 static void (* parent_setattrib)( AstObject *, const char *, int * );
832 static void (* parent_setdut1)( AstFrame *, double, int * );
833 static void (* parent_setformat)( AstFrame *, int, const char *, int * );
834 static void (* parent_setobsalt)( AstFrame *, double, int * );
835 static void (* parent_setobslat)( AstFrame *, double, int * );
836 static void (* parent_setobslon)( AstFrame *, double, int * );
837 static void (* parent_setsystem)( AstFrame *, AstSystemType, int * );
838 
839 /* Factors for converting between hours, degrees and radians. */
840 static double hr2rad;
841 static double deg2rad;
842 static double pi;
843 static double piby2;
844 
845 /* Table of cached Local Apparent Sidereal Time values and corresponding
846    epochs. */
847 static int nlast_tables = 0;
848 static AstSkyLastTable **last_tables = NULL;
849 
850 
851 /* Define macros for accessing each item of thread specific global data. */
852 #ifdef THREAD_SAFE
853 
854 /* Define how to initialise thread-specific globals. */
855 #define GLOBAL_inits \
856    globals->Class_Init = 0; \
857    globals->GetAttrib_Buff[ 0 ] = 0; \
858    globals->GetFormat_Buff[ 0 ] = 0; \
859    globals->GetLabel_Buff[ 0 ] = 0; \
860    globals->GetSymbol_Buff[ 0 ] = 0; \
861    globals->GetTitle_Buff[ 0 ] = 0; \
862    globals->GetTitle_Buff2[ 0 ] = 0; \
863    globals->TDBFrame = NULL; \
864    globals->LASTFrame = NULL; \
865 
866 /* Create the function that initialises global data for this module. */
867 astMAKE_INITGLOBALS(SkyFrame)
868 
869 /* Define macros for accessing each item of thread specific global data. */
870 #define class_init astGLOBAL(SkyFrame,Class_Init)
871 #define class_vtab astGLOBAL(SkyFrame,Class_Vtab)
872 #define getattrib_buff astGLOBAL(SkyFrame,GetAttrib_Buff)
873 #define getformat_buff astGLOBAL(SkyFrame,GetFormat_Buff)
874 #define getlabel_buff astGLOBAL(SkyFrame,GetLabel_Buff)
875 #define getsymbol_buff astGLOBAL(SkyFrame,GetSymbol_Buff)
876 #define gettitle_buff astGLOBAL(SkyFrame,GetTitle_Buff)
877 #define gettitle_buff2 astGLOBAL(SkyFrame,GetTitle_Buff2)
878 #define tdbframe astGLOBAL(SkyFrame,TDBFrame)
879 #define lastframe astGLOBAL(SkyFrame,LASTFrame)
880 
881 
882 
883 static pthread_mutex_t mutex2 = PTHREAD_MUTEX_INITIALIZER;
884 #define LOCK_MUTEX2 pthread_mutex_lock( &mutex2 );
885 #define UNLOCK_MUTEX2 pthread_mutex_unlock( &mutex2 );
886 
887 /* A read-write lock used to protect the table of cached LAST values so
888    that multiple threads can read simultaneously so long as no threads are
889    writing to the table. */
890 static pthread_rwlock_t rwlock1=PTHREAD_RWLOCK_INITIALIZER;
891 #define LOCK_WLOCK1 pthread_rwlock_wrlock( &rwlock1 );
892 #define LOCK_RLOCK1 pthread_rwlock_rdlock( &rwlock1 );
893 #define UNLOCK_RWLOCK1 pthread_rwlock_unlock( &rwlock1 );
894 
895 /* If thread safety is not needed, declare and initialise globals at static
896    variables. */
897 #else
898 
899 /* Buffer returned by GetAttrib. */
900 static char getattrib_buff[ GETATTRIB_BUFF_LEN + 1 ];
901 
902 /* Buffer returned by GetFormat. */
903 static char getformat_buff[ GETFORMAT_BUFF_LEN + 1 ];
904 
905 /* Default GetLabel string buffer */
906 static char getlabel_buff[ GETLABEL_BUFF_LEN + 1 ];
907 
908 /* Default GetSymbol buffer */
909 static char getsymbol_buff[ GETSYMBOL_BUFF_LEN + 1 ];
910 
911 /* Default Title string buffer */
912 static char gettitle_buff[ AST__SKYFRAME_GETTITLE_BUFF_LEN + 1 ];
913 static char gettitle_buff2[ AST__SKYFRAME_GETTITLE_BUFF_LEN + 1 ];
914 
915 /* TimeFrames for doing TDB<->LAST conversions. */
916 static AstTimeFrame *tdbframe = NULL;
917 static AstTimeFrame *lastframe = NULL;
918 
919 
920 /* Define the class virtual function table and its initialisation flag
921    as static variables. */
922 static AstSkyFrameVtab class_vtab;   /* Virtual function table */
923 static int class_init = 0;       /* Virtual function table initialised? */
924 
925 #define LOCK_MUTEX2
926 #define UNLOCK_MUTEX2
927 
928 #define LOCK_WLOCK1
929 #define LOCK_RLOCK1
930 #define UNLOCK_RWLOCK1
931 
932 #endif
933 
934 
935 /* Prototypes for Private Member Functions. */
936 /* ======================================== */
937 static AstLineDef *LineDef( AstFrame *, const double[2], const double[2], int * );
938 static AstMapping *SkyOffsetMap( AstSkyFrame *, int * );
939 static AstPointSet *FrameGrid( AstFrame *, int, const double *, const double *, int * );
940 static AstPointSet *ResolvePoints( AstFrame *, const double [], const double [], AstPointSet *, AstPointSet *, int * );
941 static AstSystemType GetAlignSystem( AstFrame *, int * );
942 static AstSystemType GetSystem( AstFrame *, int * );
943 static AstSystemType SystemCode( AstFrame *, const char *, int * );
944 static AstSystemType ValidateSystem( AstFrame *, AstSystemType, const char *, int * );
945 static const char *Format( AstFrame *, int, double, int * );
946 static const char *GetAttrib( AstObject *, const char *, int * );
947 static const char *GetDomain( AstFrame *, int * );
948 static const char *GetFormat( AstFrame *, int, int * );
949 static const char *GetLabel( AstFrame *, int, int * );
950 static const char *GetProjection( AstSkyFrame *, int * );
951 static const char *GetSymbol( AstFrame *, int, int * );
952 static const char *GetTitle( AstFrame *, int * );
953 static const char *GetUnit( AstFrame *, int, int * );
954 static const char *SystemString( AstFrame *, AstSystemType, int * );
955 static double Angle( AstFrame *, const double[], const double[], const double[], int * );
956 static double CalcLAST( AstSkyFrame *, double, double, double, double, double, int * );
957 static double Distance( AstFrame *, const double[], const double[], int * );
958 static double Gap( AstFrame *, int, double, int *, int * );
959 static double GetBottom( AstFrame *, int, int * );
960 static double GetCachedLAST( AstSkyFrame *, double, double, double, double, double, int * );
961 static double GetEpoch( AstFrame *, int * );
962 static double GetEquinox( AstSkyFrame *, int * );
963 static void SetCachedLAST( AstSkyFrame *, double, double, double, double, double, double, int * );
964 static void SetLast( AstSkyFrame *, int * );
965 static double GetTop( AstFrame *, int, int * );
966 static double Offset2( AstFrame *, const double[2], double, double, double[2], int * );
967 static double GetDiurab( AstSkyFrame *, int * );
968 static double GetLAST( AstSkyFrame *, int * );
969 static int GetActiveUnit( AstFrame *, int * );
970 static int GetAsTime( AstSkyFrame *, int, int * );
971 static int GetDirection( AstFrame *, int, int * );
972 static int GetIsLatAxis( AstSkyFrame *, int, int * );
973 static int GetIsLonAxis( AstSkyFrame *, int, int * );
974 static int GetLatAxis( AstSkyFrame *, int * );
975 static int GetLonAxis( AstSkyFrame *, int * );
976 static int GetNegLon( AstSkyFrame *, int * );
977 static int GetObjSize( AstObject *, int * );
978 static int IsEquatorial( AstSystemType, int * );
979 static int LineContains( AstFrame *, AstLineDef *, int, double *, int * );
980 static int LineCrossing( AstFrame *, AstLineDef *, AstLineDef *, double **, int * );
981 static int LineIncludes( SkyLineDef *, double[3], int * );
982 static int MakeSkyMapping( AstSkyFrame *, AstSkyFrame *, AstSystemType, AstMapping **, int * );
983 static int Match( AstFrame *, AstFrame *, int, int **, int **, AstMapping **, AstFrame **, int * );
984 static int SubFrame( AstFrame *, AstFrame *, int, const int *, const int *, AstMapping **, AstFrame **, int * );
985 static int TestActiveUnit( AstFrame *, int * );
986 static int TestAsTime( AstSkyFrame *, int, int * );
987 static int TestAttrib( AstObject *, const char *, int * );
988 static int TestEquinox( AstSkyFrame *, int * );
989 static int TestNegLon( AstSkyFrame *, int * );
990 static int TestProjection( AstSkyFrame *, int * );
991 static int Unformat( AstFrame *, int, const char *, double *, int * );
992 static void ClearAsTime( AstSkyFrame *, int, int * );
993 static void ClearAttrib( AstObject *, const char *, int * );
994 static void ClearDut1( AstFrame *, int * );
995 static void ClearEquinox( AstSkyFrame *, int * );
996 static void ClearNegLon( AstSkyFrame *, int * );
997 static void ClearObsAlt( AstFrame *, int * );
998 static void ClearObsLat( AstFrame *, int * );
999 static void ClearObsLon( AstFrame *, int * );
1000 static void ClearProjection( AstSkyFrame *, int * );
1001 static void ClearSystem( AstFrame *, int * );
1002 static void Copy( const AstObject *, AstObject *, int * );
1003 static void Delete( AstObject *, int * );
1004 static void Dump( AstObject *, AstChannel *, int * );
1005 static void Intersect( AstFrame *, const double[2], const double[2], const double[2], const double[2], double[2], int * );
1006 static void LineOffset( AstFrame *, AstLineDef *, double, double, double[2], int * );
1007 static void MatchAxesX( AstFrame *, AstFrame *, int *, int * );
1008 static void Norm( AstFrame *, double[], int * );
1009 static void NormBox( AstFrame *, double[], double[], AstMapping *, int * );
1010 static void Offset( AstFrame *, const double[], const double[], double, double[], int * );
1011 static void Overlay( AstFrame *, const int *, AstFrame *, int * );
1012 static void Resolve( AstFrame *, const double [], const double [], const double [], double [], double *, double *, int * );
1013 static void SetAsTime( AstSkyFrame *, int, int, int * );
1014 static void SetAttrib( AstObject *, const char *, int * );
1015 static void SetDut1( AstFrame *, double, int * );
1016 static void SetEquinox( AstSkyFrame *, double, int * );
1017 static void SetNegLon( AstSkyFrame *, int, int * );
1018 static void SetObsAlt( AstFrame *, double, int * );
1019 static void SetObsLat( AstFrame *, double, int * );
1020 static void SetObsLon( AstFrame *, double, int * );
1021 static void SetProjection( AstSkyFrame *, const char *, int * );
1022 static void SetSystem( AstFrame *, AstSystemType, int * );
1023 static void Shapp( double, double *, double *, double, double *, int * );
1024 static void Shcal( double, double, double, double *, double *, int * );
1025 static void VerifyMSMAttrs( AstSkyFrame *, AstSkyFrame *, int, const char *, const char *, int * );
1026 
1027 static double GetSkyRef( AstSkyFrame *, int, int * );
1028 static int TestSkyRef( AstSkyFrame *, int, int * );
1029 static void SetSkyRef( AstSkyFrame *, int, double, int * );
1030 static void ClearSkyRef( AstSkyFrame *, int, int * );
1031 
1032 static double GetSkyRefP( AstSkyFrame *, int, int * );
1033 static int TestSkyRefP( AstSkyFrame *, int, int * );
1034 static void SetSkyRefP( AstSkyFrame *, int, double, int * );
1035 static void ClearSkyRefP( AstSkyFrame *, int, int * );
1036 
1037 static int GetSkyRefIs( AstSkyFrame *, int * );
1038 static int TestSkyRefIs( AstSkyFrame *, int * );
1039 static void SetSkyRefIs( AstSkyFrame *, int, int * );
1040 static void ClearSkyRefIs( AstSkyFrame *, int * );
1041 
1042 static int GetAlignOffset( AstSkyFrame *, int * );
1043 static int TestAlignOffset( AstSkyFrame *, int * );
1044 static void SetAlignOffset( AstSkyFrame *, int, int * );
1045 static void ClearAlignOffset( AstSkyFrame *, int * );
1046 
1047 /* Member functions. */
1048 /* ================= */
Angle(AstFrame * this_frame,const double a[],const double b[],const double c[],int * status)1049 static double Angle( AstFrame *this_frame, const double a[],
1050                      const double b[], const double c[], int *status ) {
1051 /*
1052 *  Name:
1053 *     Angle
1054 
1055 *  Purpose:
1056 *     Calculate the angle subtended by two points at a third point.
1057 
1058 *  Type:
1059 *     Private function.
1060 
1061 *  Synopsis:
1062 *     #include "skyframe.h"
1063 *     double Angle( AstFrame *this_frame, const double a[],
1064 *                   const double b[], const double c[], int *status )
1065 
1066 *  Class Membership:
1067 *     SkyFrame member function (over-rides the astAngle method
1068 *     inherited from the Frame class).
1069 
1070 *  Description:
1071 *     This function finds the angle at point B between the line
1072 *     joining points A and B, and the line joining points C
1073 *     and B. These lines will in fact be geodesic curves (great circles).
1074 
1075 *  Parameters:
1076 *     this
1077 *        Pointer to the SkyFrame.
1078 *     a
1079 *        An array of double, with one element for each SkyFrame axis,
1080 *        containing the coordinates of the first point.
1081 *     b
1082 *        An array of double, with one element for each SkyFrame axis,
1083 *        containing the coordinates of the second point.
1084 *     c
1085 *        An array of double, with one element for each SkyFrame axis,
1086 *        containing the coordinates of the third point.
1087 *     status
1088 *        Pointer to the inherited status variable.
1089 
1090 *  Returned Value:
1091 *     The angle in radians, from the line AB to the line CB, in
1092 *     the range $\pm \pi$ with positive rotation is in the same sense
1093 *     as rotation from axis 2 to axis 1.
1094 
1095 *  Notes:
1096 *     - This function will return a "bad" result value (AST__BAD) if
1097 *     any of the input coordinates has this value.
1098 *     - A "bad" value will also be returned if points A and B are
1099 *     co-incident, or if points B and C are co-incident.
1100 *     - A "bad" value will also be returned if this function is
1101 *     invoked with the AST error status set, or if it should fail for
1102 *     any reason.
1103 */
1104 
1105    AstSkyFrame *this;            /* Pointer to SkyFrame structure */
1106    const int *perm;              /* Axis permutation array */
1107    double aa[ 2 ];               /* Permuted a coordinates */
1108    double anga;                  /* Angle from north to the line BA */
1109    double angc;                  /* Angle from north to the line BC */
1110    double bb[ 2 ];               /* Permuted b coordinates */
1111    double cc[ 2 ];               /* Permuted c coordinates */
1112    double result;                /* Value to return */
1113 
1114 /* Initialise. */
1115    result = AST__BAD;
1116 
1117 /* Check the global error status. */
1118    if ( !astOK ) return result;
1119 
1120 /* Obtain a pointer to the SkyFrame structure. */
1121    this = (AstSkyFrame *) this_frame;
1122 
1123 /* Obtain a pointer to the SkyFrame's axis permutation array. */
1124    perm = astGetPerm( this );
1125    if ( astOK ) {
1126 
1127 /* Check that all supplied coordinates are OK. */
1128       if ( ( a[ 0 ] != AST__BAD ) && ( a[ 1 ] != AST__BAD ) &&
1129            ( b[ 0 ] != AST__BAD ) && ( b[ 1 ] != AST__BAD ) &&
1130            ( c[ 0 ] != AST__BAD ) && ( c[ 1 ] != AST__BAD ) ) {
1131 
1132 /* Apply the axis permutation array to obtain the coordinates of the
1133    three points in the required (longitude,latitude) order. */
1134          aa[ perm[ 0 ] ] = a[ 0 ];
1135          aa[ perm[ 1 ] ] = a[ 1 ];
1136          bb[ perm[ 0 ] ] = b[ 0 ];
1137          bb[ perm[ 1 ] ] = b[ 1 ];
1138          cc[ perm[ 0 ] ] = c[ 0 ];
1139          cc[ perm[ 1 ] ] = c[ 1 ];
1140 
1141 /* Check that A and B are not co-incident. */
1142          if( aa[ 0 ] != bb[ 0 ] || aa[ 1 ] != bb[ 1 ] ) {
1143 
1144 /* Check that C and B are not co-incident. */
1145             if( cc[ 0 ] != bb[ 0 ] || cc[ 1 ] != bb[ 1 ] ) {
1146 
1147 /* Find the angle from north to the line BA. */
1148                anga = palDbear( bb[ 0 ], bb[ 1 ], aa[ 0 ], aa[ 1 ] );
1149 
1150 /* Find the angle from north to the line BC. */
1151                angc = palDbear( bb[ 0 ], bb[ 1 ], cc[ 0 ], cc[ 1 ] );
1152 
1153 /* Find the difference. */
1154                result = angc - anga;
1155 
1156 /* This value is the angle from north, but we want the angle from axis 2.
1157    If the axes have been swapped so that axis 2 is actually the longitude
1158    axis, then we need to correct this result. */
1159                if( perm[ 0 ] != 0 ) result = piby2 - result;
1160 
1161 /* Fold the result into the range +/- PI. */
1162                result = palDrange( result );
1163             }
1164          }
1165       }
1166    }
1167 
1168 /* Return the result. */
1169    return result;
1170 }
1171 
CalcLAST(AstSkyFrame * this,double epoch,double obslon,double obslat,double obsalt,double dut1,int * status)1172 static double CalcLAST( AstSkyFrame *this, double epoch, double obslon,
1173                         double obslat, double obsalt, double dut1,
1174                         int *status ) {
1175 /*
1176 *  Name:
1177 *     CalcLAST
1178 
1179 *  Purpose:
1180 *     Calculate the Local Appearent Sidereal Time for a SkyFrame.
1181 
1182 *  Type:
1183 *     Private function.
1184 
1185 *  Synopsis:
1186 *     #include "skyframe.h"
1187 *     double CalcLAST( AstSkyFrame *this, double epoch, double obslon,
1188 *                      double obslat, double obsalt, double dut1,
1189 *                      int *status )
1190 
1191 *  Class Membership:
1192 *     SkyFrame member function.
1193 
1194 *  Description:
1195 *     This function calculates and returns the Local Apparent Sidereal Time
1196 *     at the given epoch, etc.
1197 
1198 *  Parameters:
1199 *     this
1200 *        Pointer to the SkyFrame.
1201 *     epoch
1202 *        The epoch (MJD).
1203 *     obslon
1204 *        Observatory geodetic longitude (radians)
1205 *     obslat
1206 *        Observatory geodetic latitude (radians)
1207 *     obsalt
1208 *        Observatory geodetic altitude (metres)
1209 *     dut1
1210 *        The UT1-UTC correction, in seconds.
1211 *     status
1212 *        Pointer to the inherited status variable.
1213 
1214 *  Returned Value:
1215 *    The Local Apparent Sidereal Time, in radians.
1216 
1217 *  Notes:
1218 *     -  A value of AST__BAD will be returned if this function is invoked
1219 *     with the global error status set, or if it should fail for any reason.
1220 */
1221 
1222 /* Local Variables: */
1223    astDECLARE_GLOBALS /* Declare the thread specific global data */
1224    AstFrameSet *fs;   /* Mapping from TDB offset to LAST offset */
1225    double epoch0;     /* Supplied epoch value */
1226    double result;     /* Returned LAST value */
1227 
1228 /* Get a pointer to the structure holding thread-specific global data. */
1229    astGET_GLOBALS(this);
1230 
1231 /* Check the global error status. */
1232    if ( !astOK ) return AST__BAD;
1233 
1234 /* See if the required LAST value can be determined from the cached LAST
1235    values in the SkyFrame virtual function table. */
1236    result = GetCachedLAST( this, epoch, obslon, obslat, obsalt, dut1,
1237                            status );
1238 
1239 /* If not, we do an exact calculation from scratch. */
1240    if( result == AST__BAD ) {
1241 
1242 /* If not yet done, create two TimeFrames. Note, this is done here
1243    rather than in astInitSkyFrameVtab in order to avoid infinite vtab
1244    initialisation loops (caused by the TimeFrame class containing a
1245    static SkyFrame). */
1246       if( ! tdbframe ) {
1247          astBeginPM;
1248          tdbframe = astTimeFrame( "system=mjd,timescale=tdb", status );
1249          lastframe = astTimeFrame( "system=mjd,timescale=last", status );
1250          astEndPM;
1251       }
1252 
1253 /* For better accuracy, use this integer part of the epoch as the origin of
1254    the two TimeFrames. */
1255       astSetTimeOrigin( tdbframe, (int) epoch );
1256       astSetTimeOrigin( lastframe, (int) epoch );
1257 
1258 /* Convert the absolute Epoch value to an offset from the above origin. */
1259       epoch0 = epoch;
1260       epoch -= (int) epoch;
1261 
1262 /* Store the observers position in the two TimeFrames. */
1263       astSetObsLon( tdbframe, obslon );
1264       astSetObsLon( lastframe, obslon );
1265 
1266       astSetObsLat( tdbframe, obslat );
1267       astSetObsLat( lastframe, obslat );
1268 
1269       astSetObsAlt( tdbframe, obsalt );
1270       astSetObsAlt( lastframe, obsalt );
1271 
1272 /* Store the DUT1 value. */
1273       astSetDut1( tdbframe, dut1 );
1274       astSetDut1( lastframe, dut1 );
1275 
1276 /* Get the conversion from tdb mjd offset to last mjd offset. */
1277       fs = astConvert( tdbframe, lastframe, "" );
1278 
1279 /* Use it to transform the SkyFrame Epoch from TDB offset to LAST offset. */
1280       astTran1( fs, 1, &epoch, 1, &epoch );
1281       fs = astAnnul( fs );
1282 
1283 /* Convert the LAST offset from days to radians. */
1284       result = ( epoch - (int) epoch )*2*AST__DPI;
1285 
1286 /* Cache the new LAST value in the SkyFrame virtual function table. */
1287       SetCachedLAST( this, result, epoch0, obslon, obslat, obsalt, dut1,
1288                      status );
1289    }
1290 
1291 /* Return the required LAST value. */
1292    return result;
1293 }
1294 
ClearAsTime(AstSkyFrame * this,int axis,int * status)1295 static void ClearAsTime( AstSkyFrame *this, int axis, int *status ) {
1296 /*
1297 *  Name:
1298 *     ClearAsTime
1299 
1300 *  Purpose:
1301 *     Clear the value of the AsTime attribute for a SkyFrame's axis.
1302 
1303 *  Type:
1304 *     Private function.
1305 
1306 *  Synopsis:
1307 *     #include "skyframe.h"
1308 *     void ClearAsTime( AstSkyFrame *this, int axis, int *status )
1309 
1310 *  Class Membership:
1311 *     SkyFrame member function.
1312 
1313 *  Description:
1314 *     This function clears any value that has been set for the AsTime
1315 *     attribute for a specified axis of a SkyFrame. This attribute indicates
1316 *     whether axis values should be formatted as times (as opposed to angles)
1317 *     by default.
1318 
1319 *  Parameters:
1320 *     this
1321 *        Pointer to the SkyFrame.
1322 *     axis
1323 *        Index of the axis for which the value is to be cleared (zero based).
1324 *     status
1325 *        Pointer to the inherited status variable.
1326 
1327 *  Returned Value:
1328 *     void.
1329 */
1330 
1331 /* Local Variables: */
1332    AstAxis *ax;                  /* Pointer to Axis object */
1333 
1334 /* Check the global error status. */
1335    if ( !astOK ) return;
1336 
1337 /* Validate the axis index. */
1338    (void) astValidateAxis( this, axis, 1, "astClearAsTime" );
1339 
1340 /* Obtain a pointer to the Axis object. */
1341    ax = astGetAxis( this, axis );
1342 
1343 /* If the Axis is a SkyAxis, clear the AsTime attribute (if it is not a
1344    SkyAxis, it will not have this attribute anyway). */
1345    if ( astIsASkyAxis( ax ) ) astClearAxisAsTime( ax );
1346 
1347 /* Annul the Axis pointer. */
1348    ax = astAnnul( ax );
1349 }
1350 
ClearAttrib(AstObject * this_object,const char * attrib,int * status)1351 static void ClearAttrib( AstObject *this_object, const char *attrib, int *status ) {
1352 /*
1353 *  Name:
1354 *     ClearAttrib
1355 
1356 *  Purpose:
1357 *     Clear an attribute value for a SkyFrame.
1358 
1359 *  Type:
1360 *     Private function.
1361 
1362 *  Synopsis:
1363 *     #include "skyframe.h"
1364 *     void ClearAttrib( AstObject *this, const char *attrib, int *status )
1365 
1366 *  Class Membership:
1367 *     SkyFrame member function (over-rides the astClearAttrib protected
1368 *     method inherited from the Frame class).
1369 
1370 *  Description:
1371 *     This function clears the value of a specified attribute for a
1372 *     SkyFrame, so that the default value will subsequently be used.
1373 
1374 *  Parameters:
1375 *     this
1376 *        Pointer to the SkyFrame.
1377 *     attrib
1378 *        Pointer to a null terminated string specifying the attribute
1379 *        name.  This should be in lower case with no surrounding white
1380 *        space.
1381 *     status
1382 *        Pointer to the inherited status variable.
1383 
1384 *  Notes:
1385 *     - This function uses one-based axis numbering so that it is
1386 *     suitable for external (public) use.
1387 */
1388 
1389 /* Local Variables: */
1390    AstSkyFrame *this;            /* Pointer to the SkyFrame structure */
1391    int axis;                     /* SkyFrame axis number */
1392    int len;                      /* Length of attrib string */
1393    int nc;                       /* No. characters read by astSscanf */
1394 
1395 /* Check the global error status. */
1396    if ( !astOK ) return;
1397 
1398 /* Obtain a pointer to the SkyFrame structure. */
1399    this = (AstSkyFrame *) this_object;
1400 
1401 /* Obtain the length of the "attrib" string. */
1402    len = strlen( attrib );
1403 
1404 /* Check the attribute name and clear the appropriate attribute. */
1405 
1406 /* AsTime(axis). */
1407 /* ------------- */
1408    if ( nc = 0,
1409         ( 1 == astSscanf( attrib, "astime(%d)%n", &axis, &nc ) )
1410         && ( nc >= len ) ) {
1411       astClearAsTime( this, axis - 1 );
1412 
1413 /* Equinox. */
1414 /* -------- */
1415    } else if ( !strcmp( attrib, "equinox" ) ) {
1416       astClearEquinox( this );
1417 
1418 /* NegLon. */
1419 /* ------- */
1420    } else if ( !strcmp( attrib, "neglon" ) ) {
1421       astClearNegLon( this );
1422 
1423 /* Projection. */
1424 /* ----------- */
1425    } else if ( !strcmp( attrib, "projection" ) ) {
1426       astClearProjection( this );
1427 
1428 /* SkyRef. */
1429 /* ------- */
1430    } else if ( !strcmp( attrib, "skyref" ) ) {
1431       astClearSkyRef( this, 0 );
1432       astClearSkyRef( this, 1 );
1433 
1434 /* SkyRef(axis). */
1435 /* ------------- */
1436    } else if ( nc = 0,
1437         ( 1 == astSscanf( attrib, "skyref(%d)%n", &axis, &nc ) )
1438         && ( nc >= len ) ) {
1439       astClearSkyRef( this, axis - 1 );
1440 
1441 /* SkyRefP. */
1442 /* -------- */
1443    } else if ( !strcmp( attrib, "skyrefp" ) ) {
1444       astClearSkyRefP( this, 0 );
1445       astClearSkyRefP( this, 1 );
1446 
1447 /* SkyRefP(axis). */
1448 /* ------------- */
1449    } else if ( nc = 0,
1450         ( 1 == astSscanf( attrib, "skyrefp(%d)%n", &axis, &nc ) )
1451         && ( nc >= len ) ) {
1452       astClearSkyRefP( this, axis - 1 );
1453 
1454 /* SkyRefIs. */
1455 /* --------- */
1456    } else if ( !strcmp( attrib, "skyrefis" ) ) {
1457       astClearSkyRefIs( this );
1458 
1459 /* AlignOffset. */
1460 /* ------------ */
1461    } else if ( !strcmp( attrib, "alignoffset" ) ) {
1462       astClearAlignOffset( this );
1463 
1464 /* If the name was not recognised, test if it matches any of the
1465    read-only attributes of this class. If it does, then report an
1466    error. */
1467    } else if ( !strncmp( attrib, "islataxis", 9 ) ||
1468                !strncmp( attrib, "islonaxis", 9 ) ||
1469                !strcmp( attrib, "lataxis" ) ||
1470                !strcmp( attrib, "lonaxis" ) ) {
1471       astError( AST__NOWRT, "astClear: Invalid attempt to clear the \"%s\" "
1472                 "value for a %s.", status, attrib, astGetClass( this ) );
1473       astError( AST__NOWRT, "This is a read-only attribute." , status);
1474 
1475 /* If the attribute is not recognised, pass it on to the parent method
1476    for further interpretation. */
1477    } else {
1478       (*parent_clearattrib)( this_object, attrib, status );
1479    }
1480 }
1481 
ClearDut1(AstFrame * this,int * status)1482 static void ClearDut1( AstFrame *this, int *status ) {
1483 /*
1484 *  Name:
1485 *     ClearDut1
1486 
1487 *  Purpose:
1488 *     Clear the value of the Dut1 attribute for a SkyFrame.
1489 
1490 *  Type:
1491 *     Private function.
1492 
1493 *  Synopsis:
1494 *     #include "skyframe.h"
1495 *     void ClearDut1( AstFrame *this, int *status )
1496 
1497 *  Class Membership:
1498 *     SkyFrame member function (over-rides the astClearDut1 method
1499 *     inherited from the Frame class).
1500 
1501 *  Description:
1502 *     This function clears the Dut1 value and updates the LAST value
1503 *     stored in the SkyFrame.
1504 
1505 *  Parameters:
1506 *     this
1507 *        Pointer to the SkyFrame.
1508 *     status
1509 *        Pointer to the inherited status variable.
1510 
1511 */
1512 
1513 /* Local Variables: */
1514    double orig;
1515 
1516 /* Check the global error status. */
1517    if ( !astOK ) return;
1518 
1519 /* Note the original value */
1520    orig = astGetDut1( this );
1521 
1522 /* Invoke the parent method to clear the Frame Dut1 */
1523    (*parent_cleardut1)( this, status );
1524 
1525 /* If the DUT1 value has changed significantly, indicate that the LAST value
1526    will need to be re-calculated when it is next needed. */
1527    if( fabs( orig - astGetDut1( this ) ) > 1.0E-6 ) {
1528       ( (AstSkyFrame *) this )->last = AST__BAD;
1529       ( (AstSkyFrame *) this )->eplast = AST__BAD;
1530       ( (AstSkyFrame *) this )->klast = AST__BAD;
1531    }
1532 }
1533 
ClearObsAlt(AstFrame * this,int * status)1534 static void ClearObsAlt( AstFrame *this, int *status ) {
1535 /*
1536 *  Name:
1537 *     ClearObsAlt
1538 
1539 *  Purpose:
1540 *     Clear the value of the ObsAlt attribute for a SkyFrame.
1541 
1542 *  Type:
1543 *     Private function.
1544 
1545 *  Synopsis:
1546 *     #include "skyframe.h"
1547 *     void ClearObsAlt( AstFrame *this, int *status )
1548 
1549 *  Class Membership:
1550 *     SkyFrame member function (over-rides the astClearObsAlt method
1551 *     inherited from the Frame class).
1552 
1553 *  Description:
1554 *     This function clears the ObsAlt value.
1555 
1556 *  Parameters:
1557 *     this
1558 *        Pointer to the SkyFrame.
1559 *     status
1560 *        Pointer to the inherited status variable.
1561 
1562 */
1563 
1564 /* Local Variables: */
1565    double orig;
1566 
1567 /* Check the global error status. */
1568    if ( !astOK ) return;
1569 
1570 /* Note the original value */
1571    orig = astGetObsAlt( this );
1572 
1573 /* Invoke the parent method to clear the Frame ObsAlt. */
1574    (*parent_clearobsalt)( this, status );
1575 
1576 /* If the altitude has changed significantly, indicate that the LAST value
1577    and magnitude of the diurnal aberration vector will need to be
1578    re-calculated when next needed. */
1579    if( fabs( orig - astGetObsAlt( this ) ) > 0.001 ) {
1580       ( (AstSkyFrame *) this )->last = AST__BAD;
1581       ( (AstSkyFrame *) this )->eplast = AST__BAD;
1582       ( (AstSkyFrame *) this )->klast = AST__BAD;
1583       ( (AstSkyFrame *) this )->diurab = AST__BAD;
1584    }
1585 }
1586 
ClearObsLat(AstFrame * this,int * status)1587 static void ClearObsLat( AstFrame *this, int *status ) {
1588 /*
1589 *  Name:
1590 *     ClearObsLat
1591 
1592 *  Purpose:
1593 *     Clear the value of the ObsLat attribute for a SkyFrame.
1594 
1595 *  Type:
1596 *     Private function.
1597 
1598 *  Synopsis:
1599 *     #include "skyframe.h"
1600 *     void ClearObsLat( AstFrame *this, int *status )
1601 
1602 *  Class Membership:
1603 *     SkyFrame member function (over-rides the astClearObsLat method
1604 *     inherited from the Frame class).
1605 
1606 *  Description:
1607 *     This function clears the ObsLat value.
1608 
1609 *  Parameters:
1610 *     this
1611 *        Pointer to the SkyFrame.
1612 *     status
1613 *        Pointer to the inherited status variable.
1614 
1615 */
1616 
1617 /* Local Variables: */
1618    double orig;
1619 
1620 /* Check the global error status. */
1621    if ( !astOK ) return;
1622 
1623 /* Note the original value */
1624    orig = astGetObsLat( this );
1625 
1626 /* Invoke the parent method to clear the Frame ObsLat. */
1627    (*parent_clearobslat)( this, status );
1628 
1629 /* If the altitude has changed significantly, indicate that the LAST value
1630    and magnitude of the diurnal aberration vector will need to be
1631    re-calculated when next needed. */
1632    if( fabs( orig - astGetObsLat( this ) ) > 1.0E-8 ) {
1633       ( (AstSkyFrame *) this )->last = AST__BAD;
1634       ( (AstSkyFrame *) this )->eplast = AST__BAD;
1635       ( (AstSkyFrame *) this )->klast = AST__BAD;
1636       ( (AstSkyFrame *) this )->diurab = AST__BAD;
1637    }
1638 }
1639 
ClearObsLon(AstFrame * this,int * status)1640 static void ClearObsLon( AstFrame *this, int *status ) {
1641 /*
1642 *  Name:
1643 *     ClearObsLon
1644 
1645 *  Purpose:
1646 *     Clear the value of the ObsLon attribute for a SkyFrame.
1647 
1648 *  Type:
1649 *     Private function.
1650 
1651 *  Synopsis:
1652 *     #include "skyframe.h"
1653 *     void ClearObsLon( AstFrame *this, int *status )
1654 
1655 *  Class Membership:
1656 *     SkyFrame member function (over-rides the astClearObsLon method
1657 *     inherited from the Frame class).
1658 
1659 *  Description:
1660 *     This function clears the ObsLon value.
1661 
1662 *  Parameters:
1663 *     this
1664 *        Pointer to the SkyFrame.
1665 *     status
1666 *        Pointer to the inherited status variable.
1667 
1668 */
1669 
1670 /* Local Variables: */
1671    double orig;
1672 
1673 /* Check the global error status. */
1674    if ( !astOK ) return;
1675 
1676 /* Note the original value */
1677    orig = astGetObsLon( this );
1678 
1679 /* Invoke the parent method to clear the Frame ObsLon. */
1680    (*parent_clearobslon)( this, status );
1681 
1682 /* If the longitude has changed significantly, indicate that the LAST value
1683    will need to be re-calculated when it is next needed. */
1684    if( fabs( orig - astGetObsLon( this ) ) > 1.0E-8 ) {
1685       ( (AstSkyFrame *) this )->last = AST__BAD;
1686       ( (AstSkyFrame *) this )->eplast = AST__BAD;
1687       ( (AstSkyFrame *) this )->klast = AST__BAD;
1688    }
1689 }
1690 
ClearSystem(AstFrame * this_frame,int * status)1691 static void ClearSystem( AstFrame *this_frame, int *status ) {
1692 /*
1693 *  Name:
1694 *     ClearSystem
1695 
1696 *  Purpose:
1697 *     Clear the System attribute for a SkyFrame.
1698 
1699 *  Type:
1700 *     Private function.
1701 
1702 *  Synopsis:
1703 *     #include "skyframe.h"
1704 *     void ClearSystem( AstFrame *this_frame, int *status )
1705 
1706 *  Class Membership:
1707 *     SkyFrame member function (over-rides the astClearSystem protected
1708 *     method inherited from the Frame class).
1709 
1710 *  Description:
1711 *     This function clears the System attribute for a SkyFrame.
1712 
1713 *  Parameters:
1714 *     this
1715 *        Pointer to the SkyFrame.
1716 *     status
1717 *        Pointer to the inherited status variable.
1718 
1719 */
1720 
1721 /* Local Variables: */
1722    AstFrameSet *fs;              /* FrameSet to be used as the Mapping */
1723    AstSkyFrame *sfrm;            /* Copy of original SkyFrame */
1724    AstSkyFrame *this;            /* Pointer to SkyFrame structure */
1725    double xin[ 2 ];              /* Axis 0 values */
1726    double yin[ 2 ];              /* Axis 1 values */
1727    double xout[ 2 ];             /* Axis 0 values */
1728    double yout[ 2 ];             /* Axis 1 values */
1729    int skyref_set;               /* Is either SkyRef attribute set? */
1730    int skyrefp_set;              /* Is either SkyRefP attribute set? */
1731 
1732 /* Check the global error status. */
1733    if ( !astOK ) return;
1734 
1735 /* Obtain a pointer to the SkyFrame structure. */
1736    this = (AstSkyFrame *) this_frame;
1737 
1738 /* See if either the SkyRef or SkyRefP attribute is set. */
1739    skyref_set = astTestSkyRef( this, 0 ) || astTestSkyRef( this, 1 );
1740    skyrefp_set = astTestSkyRefP( this, 0 ) || astTestSkyRefP( this, 1 );
1741 
1742 /* If so, we will need to transform their values into the new coordinate
1743    system. Save a copy of the SkyFrame with its original System value. */
1744    sfrm = ( skyref_set || skyrefp_set )?astCopy( this ):NULL;
1745 
1746 /* Use the parent method to clear the System value. */
1747    (*parent_clearsystem)( this_frame, status );
1748 
1749 /* Now modify the SkyRef and SkyRefP attributes if necessary. */
1750    if( sfrm ) {
1751 
1752 /* Save the SkyRef and SkyRefP values. */
1753       xin[ 0 ] = astGetSkyRef( sfrm, 0 );
1754       xin[ 1 ] = astGetSkyRefP( sfrm, 0 );
1755       yin[ 0 ] = astGetSkyRef( sfrm, 1 );
1756       yin[ 1 ] = astGetSkyRefP( sfrm, 1 );
1757 
1758 /* Clear the SkyRef values to avoid infinite recursion in the following
1759    call to astConvert. */
1760       if( skyref_set ) {
1761          astClearSkyRef( sfrm, 0 );
1762          astClearSkyRef( sfrm, 1 );
1763          astClearSkyRef( this, 0 );
1764          astClearSkyRef( this, 1 );
1765       }
1766 
1767 /* Get the Mapping from the original System to the default System. Invoking
1768    astConvert will recursively invoke ClearSystem again. This is why we need
1769    to be careful to ensure that SkyRef is cleared above - doing so ensure
1770    we do not end up with infinite recursion. */
1771       fs = astConvert( sfrm, this, "" );
1772 
1773 /* Check the Mapping was found. */
1774       if( fs ) {
1775 
1776 /* Use the Mapping to find the SkyRef and SkyRefP positions in the default
1777    coordinate system. */
1778          astTran2( fs, 2, xin, yin, 1, xout, yout );
1779 
1780 /* Store the values as required. */
1781          if( skyref_set ) {
1782             astSetSkyRef( this, 0, xout[ 0 ] );
1783             astSetSkyRef( this, 1, yout[ 0 ] );
1784          }
1785 
1786          if( skyrefp_set ) {
1787             astSetSkyRefP( this, 0, xout[ 1 ] );
1788             astSetSkyRefP( this, 1, yout[ 1 ] );
1789          }
1790 
1791 /* Free resources. */
1792          fs = astAnnul( fs );
1793 
1794 /* If the Mapping is not defined, we cannot convert the SkyRef or SkyRefP
1795    positions in the new Frame so clear them. */
1796       } else {
1797          if( skyref_set ) {
1798             astClearSkyRef( this, 0 );
1799             astClearSkyRef( this, 1 );
1800          }
1801          if( skyrefp_set ) {
1802             astClearSkyRefP( this, 0 );
1803             astClearSkyRefP( this, 1 );
1804          }
1805       }
1806 
1807 /* Free resources. */
1808       sfrm = astAnnul( sfrm );
1809    }
1810 }
1811 
Distance(AstFrame * this_frame,const double point1[],const double point2[],int * status)1812 static double Distance( AstFrame *this_frame,
1813                         const double point1[], const double point2[], int *status ) {
1814 /*
1815 *  Name:
1816 *     Distance
1817 
1818 *  Purpose:
1819 *     Calculate the distance between two points.
1820 
1821 *  Type:
1822 *     Private function.
1823 
1824 *  Synopsis:
1825 *     #include "skyframe.h"
1826 *     double Distance( AstFrame *this,
1827 *                      const double point1[], const double point2[], int *status )
1828 
1829 *  Class Membership:
1830 *     SkyFrame member function (over-rides the astDistance method
1831 *     inherited from the Frame class).
1832 
1833 *  Description:
1834 *     This function finds the distance between two points whose
1835 *     SkyFrame coordinates are given. The distance calculated is that
1836 *     along the geodesic curve (i.e. great circle) that joins the two
1837 *     points.
1838 
1839 *  Parameters:
1840 *     this
1841 *        Pointer to the SkyFrame.
1842 *     point1
1843 *        An array of double, with one element for each SkyFrame axis,
1844 *        containing the coordinates of the first point.
1845 *     point2
1846 *        An array of double, with one element for each SkyFrame axis,
1847 *        containing the coordinates of the second point.
1848 *     status
1849 *        Pointer to the inherited status variable.
1850 
1851 *  Returned Value:
1852 *     The distance between the two points, in radians.
1853 
1854 *  Notes:
1855 *     - This function will return a "bad" result value (AST__BAD) if
1856 *     any of the input coordinates has this value.
1857 *     - A "bad" value will also be returned if this function is
1858 *     invoked with the AST error status set or if it should fail for
1859 *     any reason.
1860 */
1861 
1862 /* Local Variables: */
1863    AstSkyFrame *this;            /* Pointer to SkyFrame structure */
1864    const int *perm;              /* Axis permutation array */
1865    double p1[ 2 ];               /* Permuted point1 coordinates */
1866    double p2[ 2 ];               /* Permuted point2 coordinates */
1867    double result;                /* Value to return */
1868 
1869 /* Initialise. */
1870    result = AST__BAD;
1871 
1872 /* Check the global error status. */
1873    if ( !astOK ) return result;
1874 
1875 /* Obtain a pointer to the SkyFrame structure. */
1876    this = (AstSkyFrame *) this_frame;
1877 
1878 /* Obtain a pointer to the SkyFrame's axis permutation array. */
1879    perm = astGetPerm( this );
1880    if ( astOK ) {
1881 
1882 /* Check that all supplied coordinates are OK. */
1883       if ( ( point1[ 0 ] != AST__BAD ) && ( point1[ 1 ] != AST__BAD ) &&
1884            ( point2[ 0 ] != AST__BAD ) && ( point2[ 1 ] != AST__BAD ) ) {
1885 
1886 /* Apply the axis permutation array to obtain the coordinates of the
1887    two points in the required (longitude,latitude) order. */
1888          p1[ perm[ 0 ] ] = point1[ 0 ];
1889          p1[ perm[ 1 ] ] = point1[ 1 ];
1890          p2[ perm[ 0 ] ] = point2[ 0 ];
1891          p2[ perm[ 1 ] ] = point2[ 1 ];
1892 
1893 /* Calculate the great circle distance between the points in radians. */
1894          result = palDsep( p1[ 0 ], p1[ 1 ], p2[ 0 ], p2[ 1 ] );
1895       }
1896    }
1897 
1898 /* Return the result. */
1899    return result;
1900 }
1901 
Format(AstFrame * this_frame,int axis,double value,int * status)1902 static const char *Format( AstFrame *this_frame, int axis, double value, int *status ) {
1903 /*
1904 *  Name:
1905 *     Format
1906 
1907 *  Purpose:
1908 *     Format a coordinate value for a SkyFrame axis.
1909 
1910 *  Type:
1911 *     Private function.
1912 
1913 *  Synopsis:
1914 *     #include "skyframe.h"
1915 *     const char *Format( AstFrame *this, int axis, double value, int *status )
1916 
1917 *  Class Membership:
1918 *     SkyFrame member function (over-rides the astFormat method inherited
1919 *     from the Frame class).
1920 
1921 *  Description:
1922 *     This function returns a pointer to a string containing the formatted
1923 *     (character) version of a coordinate value for a SkyFrame axis. The
1924 *     formatting applied is that specified by a previous invocation of the
1925 *     astSetFormat method. A suitable default format is applied if necessary,
1926 *     and this may depend on which sky coordinate system the SkyFrame
1927 *     describes.
1928 
1929 *  Parameters:
1930 *     this
1931 *        Pointer to the SkyFrame.
1932 *     axis
1933 *        The number of the axis (zero-based) for which formatting is to be
1934 *        performed.
1935 *     value
1936 *        The coordinate value to be formatted, in radians.
1937 *     status
1938 *        Pointer to the inherited status variable.
1939 
1940 *  Returned Value:
1941 *     A pointer to a null-terminated string containing the formatted value.
1942 
1943 *  Notes:
1944 *     -  A NULL pointer will be returned if this function is invoked with the
1945 *     global error status set, or if it should fail for any reason.
1946 */
1947 
1948 /* Local Variables: */
1949    AstSkyFrame *this;            /* Pointer to the SkyFrame structure */
1950    const char *result;           /* Pointer value to return */
1951    int format_set;               /* Format attribute set? */
1952 
1953 /* Check the global error status. */
1954    if ( !astOK ) return NULL;
1955 
1956 /* Obtain a pointer to the SkyFrame structure. */
1957    this = (AstSkyFrame *) this_frame;
1958 
1959 /* Validate the axis index. */
1960    (void) astValidateAxis( this, axis, 1, "astFormat" );
1961 
1962 /* Determine if a Format value has been set for the axis and set a temporary
1963    value if it has not. Use the GetFormat member function for this class
1964    together with member functions inherited from the parent class (rather than
1965    using the object's methods directly) because if any of these methods have
1966    been over-ridden by a derived class the Format string syntax may no longer
1967    be compatible with this class. */
1968    format_set = (*parent_testformat)( this_frame, axis, status );
1969    if ( !format_set ) {
1970       (*parent_setformat)( this_frame, axis, GetFormat( this_frame, axis, status ), status );
1971    }
1972 
1973 /* Use the Format member function inherited from the parent class to format the
1974    value and return a pointer to the resulting string. */
1975    result = (*parent_format)( this_frame, axis, value, status );
1976 
1977 /* If necessary, clear any temporary Format value that was set above. */
1978    if ( !format_set ) (*parent_clearformat)( this_frame, axis, status );
1979 
1980 /* If an error occurred, clear the returned value. */
1981    if ( !astOK ) result = NULL;
1982 
1983 /* Return the result. */
1984    return result;
1985 }
1986 
FrameGrid(AstFrame * this_object,int size,const double * lbnd,const double * ubnd,int * status)1987 static AstPointSet *FrameGrid( AstFrame *this_object, int size, const double *lbnd,
1988                                const double *ubnd, int *status ){
1989 /*
1990 *  Name:
1991 *     FrameGrid
1992 
1993 *  Purpose:
1994 *     Return a grid of points covering a rectangular area of a Frame.
1995 
1996 *  Type:
1997 *     Private function.
1998 
1999 *  Synopsis:
2000 *     #include "skyframe.h"
2001 *     AstPointSet *FrameGrid( AstFrame *this_frame, int size,
2002 *                             const double *lbnd, const double *ubnd,
2003 *                             int *status )
2004 
2005 *  Class Membership:
2006 *     SkyFrame member function (over-rides the protected astFrameGrid
2007 *     method inherited from the Frame class).
2008 
2009 *  Description:
2010 *     This function returns a PointSet containing positions spread
2011 *     approximately evenly throughtout a specified rectangular area of
2012 *     the Frame.
2013 
2014 *  Parameters:
2015 *     this
2016 *        Pointer to the Frame.
2017 *     size
2018 *        The preferred number of points in the returned PointSet. The
2019 *        actual number of points in the returned PointSet may be
2020 *        different, but an attempt is made to stick reasonably closely to
2021 *        the supplied value.
2022 *     lbnd
2023 *        Pointer to an array holding the lower bound of the rectangular
2024 *        area on each Frame axis. The array should have one element for
2025 *        each Frame axis.
2026 *     ubnd
2027 *        Pointer to an array holding the upper bound of the rectangular
2028 *        area on each Frame axis. The array should have one element for
2029 *        each Frame axis.
2030 
2031 *  Returned Value:
2032 *     A pointer to a new PointSet holding the grid of points.
2033 
2034 *  Notes:
2035 *     - A NULL pointer is returned if an error occurs.
2036 */
2037 
2038 /* Local Variables: */
2039    AstPointSet *result;
2040    AstSkyFrame *this;
2041    double **ptr;
2042    double box_area;
2043    double cl;
2044    double dlon;
2045    double hilat;
2046    double hilon;
2047    double inclon;
2048    double lat_size;
2049    double lat;
2050    double lon;
2051    double lolon;
2052    double lon_size;
2053    double lolat;
2054    double totlen;
2055    int ilat;
2056    int ilon;
2057    int imer;
2058    int ip;
2059    int ipar;
2060    int ipmax;
2061    int nmer;
2062    int npar;
2063 
2064 /* Initialise. */
2065    result = NULL;
2066 
2067 /* Check the global error status. */
2068    if ( !astOK ) return result;
2069 
2070 /* Obtain a pointer to the SkyFrame structure. */
2071    this = (AstSkyFrame *) this_object;
2072 
2073 /* Get the zero-based indices of the longitude and latitude axes. */
2074    ilon = astGetLonAxis( this );
2075    ilat = 1 - ilon;
2076 
2077 /* The latitude bounds may not be the right way round so check for it. */
2078    if( lbnd[ ilat ] <= ubnd[ ilat ] ) {
2079       lolat = lbnd[ ilat ];
2080       hilat = ubnd[ ilat ];
2081    } else {
2082       lolat = ubnd[ ilat ];
2083       hilat = lbnd[ ilat ];
2084    }
2085 
2086 /* Check all bounds are good. Also check the size is positive. */
2087    lolon = lbnd[ ilon ];
2088    hilon = ubnd[ ilon ];
2089    if( size > 0 && lolat != AST__BAD && hilat != AST__BAD &&
2090        lolon != AST__BAD && hilon != AST__BAD ) {
2091 
2092 /* Ensure the longitude bounds are in the range 0-2PI. */
2093       lolon = palDranrm( lolon );
2094       hilon = palDranrm( hilon );
2095 
2096 /* If the upper longitude limit is less than the lower limit, add 2.PI */
2097       if( hilon <= lolon &&
2098           ubnd[ ilon ] != lbnd[ ilon ] ) hilon += 2*AST__DPI;
2099 
2100 /* Get the total area of the box in steradians. */
2101       dlon = hilon - lolon;
2102       box_area = fabs( dlon*( sin( hilat ) - sin( lolat ) ) );
2103 
2104 /* Get the nominal size of a square grid cell, in radians. */
2105       lat_size = sqrt( box_area/size );
2106 
2107 /* How many parallels should we use to cover the box? Ensure we use at
2108    least two. These parallels pass through the centre of the grid cells. */
2109       npar = (int)( 0.5 + ( hilat - lolat )/lat_size );
2110       if( npar < 2 ) npar = 2;
2111 
2112 /* Find the actual sample size implied by this number of parallels. */
2113       lat_size = ( hilat - lolat )/npar;
2114 
2115 /* Find the total arc length of the parallels. */
2116       totlen = 0.0;
2117       lat = lolat + 0.5*lat_size;
2118       for( ipar = 0; ipar < npar; ipar++ ) {
2119          totlen += dlon*cos( lat );
2120          lat += lat_size;
2121       }
2122 
2123 /* If we space "size" samples evenly over this total arc-length, what is
2124    the arc-distance between samples? */
2125       lon_size = totlen/size;
2126 
2127 /* Create a PointSet in which to store the grid. Make it bigger than
2128    necessary in order to leave room for extra samples caused by integer
2129    truncation. */
2130       ipmax = 2*size;
2131       result = astPointSet( ipmax, 2, " ", status );
2132       ptr = astGetPoints( result );
2133       if( astOK ) {
2134 
2135 /* Loop over all the parallels. */
2136          ip = 0;
2137          lat = lolat + 0.5*lat_size;
2138          for( ipar = 0; ipar < npar; ipar++ ) {
2139 
2140 /* Get the longitude increment between samples on this parallel. */
2141             cl = cos( lat );
2142             inclon = ( cl != 0.0 ) ? lon_size/cl : 0.0;
2143 
2144 /* Get the number of longitude samples for this parallel. Reduce it if
2145    it would extend beyond the end of the PointSet. */
2146             nmer = dlon/inclon;
2147             if( ip + nmer >= ipmax ) nmer = ipmax - ip;
2148 
2149 /* Adjust the longitude increment to take up any slack caused by the
2150    above integer division. */
2151             inclon = dlon/nmer;
2152 
2153 /* Produce the samples for the current parallel. */
2154             lon = lolon + 0.5*inclon;
2155             for( imer = 0; imer < nmer; imer++ ) {
2156                ptr[ ilon ][ ip ] = lon;
2157                ptr[ ilat ][ ip ] = lat;
2158 
2159                lon += inclon;
2160                ip++;
2161             }
2162 
2163 /* Get the latitude on the next parallel. */
2164             lat += lat_size;
2165          }
2166 
2167 /* Truncate the PointSet to exclude unused elements at the end. */
2168          astSetNpoint( result, ip );
2169       }
2170 
2171 /* Report error if supplied values were bad. */
2172    } else if( astOK ) {
2173       if( size < 1 ) {
2174          astError( AST__ATTIN, "astFrameGrid(%s): The supplied grid "
2175                    "size (%d) is invalid (programming error).",
2176                    status, astGetClass( this ), size );
2177       } else {
2178          astError( AST__ATTIN, "astFrameGrid(%s): One of more of the "
2179                    "supplied bounds is AST__BAD (programming error).",
2180                    status, astGetClass( this ) );
2181       }
2182    }
2183 
2184 /* Annul the returned PointSet if an error has occurred. */
2185    if( !astOK ) result = astAnnul( result );
2186 
2187 /* Return the PointSet holding the grid. */
2188    return result;
2189 }
2190 
Gap(AstFrame * this_frame,int axis,double gap,int * ntick,int * status)2191 static double Gap( AstFrame *this_frame, int axis, double gap, int *ntick, int *status ) {
2192 /*
2193 *  Name:
2194 *     Gap
2195 
2196 *  Purpose:
2197 *     Find a "nice" gap for tabulating SkyFrame axis values.
2198 
2199 *  Type:
2200 *     Private function.
2201 
2202 *  Synopsis:
2203 *     #include "skyframe.h"
2204 *     double Gap( AstFrame *this, int axis, double gap, int *ntick, int *status )
2205 
2206 *  Class Membership:
2207 *     SkyFrame member function (over-rides the protected astGap method
2208 *     inherited from the Frame class).
2209 
2210 *  Description:
2211 *     This function returns a gap size which produces a nicely spaced
2212 *     series of formatted values for a SkyFrame axis, the returned gap
2213 *     size being as close as possible to the supplied target gap
2214 *     size. It also returns a convenient number of divisions into
2215 *     which the gap can be divided.
2216 
2217 *  Parameters:
2218 *     this
2219 *        Pointer to the SkyFrame.
2220 *     axis
2221 *        The number of the axis (zero-based) for which a gap is to be found.
2222 *     gap
2223 *        The target gap size.
2224 *     ntick
2225 *        Address of an int in which to return a convenient number of
2226 *        divisions into which the gap can be divided.
2227 *     status
2228 *        Pointer to the inherited status variable.
2229 
2230 *  Returned Value:
2231 *     The nice gap size.
2232 
2233 *  Notes:
2234 *     - A value of zero is returned if the target gap size is zero.
2235 *     - A negative gap size is returned if the supplied gap size is negative.
2236 *     - A value of zero will be returned if this function is invoked
2237 *     with the global error status set, or if it should fail for any
2238 *     reason.
2239 */
2240 
2241 /* Local Variables: */
2242    AstSkyFrame *this;            /* Pointer to the SkyFrame structure */
2243    double result;                /* Gap value to return */
2244    int format_set;               /* Format attribute set? */
2245 
2246 /* Check the global error status. */
2247    if ( !astOK ) return 0.0;
2248 
2249 /* Obtain a pointer to the SkyFrame structure. */
2250    this = (AstSkyFrame *) this_frame;
2251 
2252 /* Validate the axis index. */
2253    (void) astValidateAxis( this, axis, 1, "astGap" );
2254 
2255 /* Determine if a Format value has been set for the axis and set a
2256    temporary value if it has not. Use the GetFormat member function
2257    for this class together with member functions inherited from the
2258    parent class (rather than using the object's methods directly)
2259    because if any of these methods have been over-ridden by a derived
2260    class the Format string syntax may no longer be compatible with
2261    this class. */
2262    format_set = (*parent_testformat)( this_frame, axis, status );
2263    if ( !format_set ) {
2264       (*parent_setformat)( this_frame, axis, GetFormat( this_frame, axis, status ), status );
2265    }
2266 
2267 /* Use the Gap member function inherited from the parent class to find
2268    the gap size. */
2269    result = (*parent_gap)( this_frame, axis, gap, ntick, status );
2270 
2271 /* If necessary, clear any temporary Format value that was set above. */
2272    if ( !format_set ) (*parent_clearformat)( this_frame, axis, status );
2273 
2274 /* If an error occurred, clear the returned value. */
2275    if ( !astOK ) result = 0.0;
2276 
2277 /* Return the result. */
2278    return result;
2279 }
2280 
GetObjSize(AstObject * this_object,int * status)2281 static int GetObjSize( AstObject *this_object, int *status ) {
2282 /*
2283 *  Name:
2284 *     GetObjSize
2285 
2286 *  Purpose:
2287 *     Return the in-memory size of an Object.
2288 
2289 *  Type:
2290 *     Private function.
2291 
2292 *  Synopsis:
2293 *     #include "skyframe.h"
2294 *     int GetObjSize( AstObject *this, int *status )
2295 
2296 *  Class Membership:
2297 *     SkyFrame member function (over-rides the astGetObjSize protected
2298 *     method inherited from the parent class).
2299 
2300 *  Description:
2301 *     This function returns the in-memory size of the supplied SkyFrame,
2302 *     in bytes.
2303 
2304 *  Parameters:
2305 *     this
2306 *        Pointer to the SkyFrame.
2307 *     status
2308 *        Pointer to the inherited status variable.
2309 
2310 *  Returned Value:
2311 *     The Object size, in bytes.
2312 
2313 *  Notes:
2314 *     - A value of zero will be returned if this function is invoked
2315 *     with the global status set, or if it should fail for any reason.
2316 */
2317 
2318 /* Local Variables: */
2319    AstSkyFrame *this;         /* Pointer to SkyFrame structure */
2320    int result;                /* Result value to return */
2321 
2322 /* Initialise. */
2323    result = 0;
2324 
2325 /* Check the global error status. */
2326    if ( !astOK ) return result;
2327 
2328 /* Obtain a pointers to the SkyFrame structure. */
2329    this = (AstSkyFrame *) this_object;
2330 
2331 /* Invoke the GetObjSize method inherited from the parent class, and then
2332    add on any components of the class structure defined by thsi class
2333    which are stored in dynamically allocated memory. */
2334    result = (*parent_getobjsize)( this_object, status );
2335    result += astTSizeOf( this->projection );
2336 
2337 /* If an error occurred, clear the result value. */
2338    if ( !astOK ) result = 0;
2339 
2340 /* Return the result, */
2341    return result;
2342 }
2343 
GetActiveUnit(AstFrame * this_frame,int * status)2344 static int GetActiveUnit( AstFrame *this_frame, int *status ) {
2345 /*
2346 *  Name:
2347 *     GetActiveUnit
2348 
2349 *  Purpose:
2350 *     Obtain the value of the ActiveUnit flag for a SkyFrame.
2351 
2352 *  Type:
2353 *     Private function.
2354 
2355 *  Synopsis:
2356 *     #include "skyframe.h"
2357 *     int GetActiveUnit( AstFrame *this_frame, int *status )
2358 
2359 *  Class Membership:
2360 *     SkyFrame member function (over-rides the astGetActiveUnit protected
2361 *     method inherited from the Frame class).
2362 
2363 *  Description:
2364 *    This function returns the value of the ActiveUnit flag for a
2365 *    SkyFrame, which is always 0.
2366 
2367 *  Parameters:
2368 *     this
2369 *        Pointer to the SkyFrame.
2370 *     status
2371 *        Pointer to the inherited status variable.
2372 
2373 *  Returned Value:
2374 *     The value to use for the ActiveUnit flag (0).
2375 
2376 */
2377    return 0;
2378 }
2379 
GetAsTime(AstSkyFrame * this,int axis,int * status)2380 static int GetAsTime( AstSkyFrame *this, int axis, int *status ) {
2381 /*
2382 *  Name:
2383 *     GetAsTime
2384 
2385 *  Purpose:
2386 *     Obtain the value of the AsTime attribute for a SkyFrame's axis.
2387 
2388 *  Type:
2389 *     Private function.
2390 
2391 *  Synopsis:
2392 *     #include "skyframe.h"
2393 *     int GetAsTime( AstSkyFrame *this, int axis, int *status )
2394 
2395 *  Class Membership:
2396 *     SkyFrame member function.
2397 
2398 *  Description:
2399 *     This function returns the boolean value of the AsTime attribute for a
2400 *     specified axis of a SkyFrame. This value indicates whether axis values
2401 *     should be formatted as times (as opposed to angles) by default.
2402 
2403 *  Parameters:
2404 *     this
2405 *        Pointer to the SkyFrame.
2406 *     axis
2407 *        Index of the axis for which information is required (zero based).
2408 *     status
2409 *        Pointer to the inherited status variable.
2410 
2411 *  Returned Value:
2412 *     Zero or one, according to the setting of the AsTime attribute (if no
2413 *     value has previously been set, a suitable default is returned).
2414 
2415 *  Notes:
2416 *     -  A value of zero will be returned if this function is invoked with the
2417 *     global error status set, or if it should fail for any reason.
2418 */
2419 
2420 /* Local Variables: */
2421    AstAxis *ax;                  /* Pointer to Axis object */
2422    int axis_p;                   /* Permuted axis index */
2423    int result;                   /* Result to be returned */
2424 
2425 /* Check the global error status. */
2426    if ( !astOK ) return 0;
2427 
2428 /* Initialise. */
2429    result = 0;
2430 
2431 /* Validate and permute the axis index. */
2432    axis_p = astValidateAxis( this, axis, 1, "astGetAsTime" );
2433 
2434 /* Obtain a pointer to the required Axis object. */
2435    ax = astGetAxis( this, axis );
2436 
2437 /* Determine if the AsTime attribute has been set for the axis (this can only
2438    be the case if the object is a SkyAxis). If the attribute is set, obtain its
2439    value. */
2440    if ( astIsASkyAxis( ax ) && astTestAxisAsTime( ax ) ) {
2441       result = astGetAxisAsTime( ax );
2442 
2443 /* Otherwise, check which (permuted) axis is involved. Only the first
2444    (longitude) axis may be displayed as a time by default. */
2445    } else if ( axis_p == 0 ) {
2446 
2447 /* Test for those coordinate systems which normally have their longitude axes
2448    displayed as times (basically, those that involve the Earth's equator) and
2449    set the returned value appropriately. */
2450       result = IsEquatorial( astGetSystem( this ), status );
2451    }
2452 
2453 /* Annul the Axis object pointer. */
2454    ax = astAnnul( ax );
2455 
2456 /* Return the result. */
2457    return result;
2458 }
2459 
GetAttrib(AstObject * this_object,const char * attrib,int * status)2460 static const char *GetAttrib( AstObject *this_object, const char *attrib, int *status ) {
2461 /*
2462 *  Name:
2463 *     GetAttrib
2464 
2465 *  Purpose:
2466 *     Get the value of a specified attribute for a SkyFrame.
2467 
2468 *  Type:
2469 *     Private function.
2470 
2471 *  Synopsis:
2472 *     #include "skyframe.h"
2473 *     const char *GetAttrib( AstObject *this, const char *attrib, int *status )
2474 
2475 *  Class Membership:
2476 *     SkyFrame member function (over-rides the protected astGetAttrib
2477 *     method inherited from the Frame class).
2478 
2479 *  Description:
2480 *     This function returns a pointer to the value of a specified
2481 *     attribute for a SkyFrame, formatted as a character string.
2482 
2483 *  Parameters:
2484 *     this
2485 *        Pointer to the SkyFrame.
2486 *     attrib
2487 *        Pointer to a null-terminated string containing the name of
2488 *        the attribute whose value is required. This name should be in
2489 *        lower case, with all white space removed.
2490 *     status
2491 *        Pointer to the inherited status variable.
2492 
2493 *  Returned Value:
2494 *     - Pointer to a null-terminated string containing the attribute
2495 *     value.
2496 
2497 *  Notes:
2498 *     - This function uses one-based axis numbering so that it is
2499 *     suitable for external (public) use.
2500 *     - The returned string pointer may point at memory allocated
2501 *     within the SkyFrame, or at static memory. The contents of the
2502 *     string may be over-written or the pointer may become invalid
2503 *     following a further invocation of the same function or any
2504 *     modification of the SkyFrame. A copy of the string should
2505 *     therefore be made if necessary.
2506 *     - A NULL pointer will be returned if this function is invoked
2507 *     with the global error status set, or if it should fail for any
2508 *     reason.
2509 */
2510 
2511 /* Local Variables: */
2512    astDECLARE_GLOBALS            /* Declare the thread specific global data */
2513    AstSkyFrame *this;            /* Pointer to the SkyFrame structure */
2514    const char *cval;             /* Pointer to character attribute value */
2515    const char *result;           /* Pointer value to return */
2516    double dval;                  /* Floating point attribute value */
2517    double equinox;               /* Equinox attribute value (as MJD) */
2518    int as_time;                  /* AsTime attribute value */
2519    int axis;                     /* SkyFrame axis number */
2520    int ival;                     /* Integer attribute value */
2521    int len;                      /* Length of attrib string */
2522    int nc;                       /* No. characters read by astSscanf */
2523    int neglon;                   /* Display long. values as [-pi,pi]? */
2524 
2525 /* Initialise. */
2526    result = NULL;
2527 
2528 /* Check the global error status. */
2529    if ( !astOK ) return result;
2530 
2531 /* Get a pointer to the structure holding thread-specific global data. */
2532    astGET_GLOBALS(this_object);
2533 
2534 /* Obtain a pointer to the SkyFrame structure. */
2535    this = (AstSkyFrame *) this_object;
2536 
2537 /* Obtain the length of the attrib string. */
2538    len = strlen( attrib );
2539 
2540 /* Compare "attrib" with each recognised attribute name in turn,
2541    obtaining the value of the required attribute. If necessary, write
2542    the value into "getattrib_buff" as a null-terminated string in an appropriate
2543    format.  Set "result" to point at the result string. */
2544 
2545 /* AsTime(axis). */
2546 /* ------------- */
2547    if ( nc = 0,
2548         ( 1 == astSscanf( attrib, "astime(%d)%n", &axis, &nc ) )
2549         && ( nc >= len ) ) {
2550       as_time = astGetAsTime( this, axis - 1 );
2551       if ( astOK ) {
2552          (void) sprintf( getattrib_buff, "%d", as_time );
2553          result = getattrib_buff;
2554       }
2555 
2556 /* Equinox. */
2557 /* -------- */
2558    } else if ( !strcmp( attrib, "equinox" ) ) {
2559       equinox = astGetEquinox( this );
2560       if ( astOK ) {
2561 
2562 /* Format the Equinox as decimal years. Use a Besselian epoch if it
2563    will be less than 1984.0, otherwise use a Julian epoch. */
2564          result = astFmtDecimalYr( ( equinox < palEpj2d( 1984.0 ) ) ?
2565                                    palEpb( equinox ) : palEpj( equinox ),
2566                                    DBL_DIG );
2567       }
2568 
2569 /* IsLatAxis(axis) */
2570 /* --------------- */
2571    } else if ( nc = 0,
2572                ( 1 == astSscanf( attrib, "islataxis(%d)%n", &axis, &nc ) )
2573                && ( nc >= len ) ) {
2574       ival = astGetIsLatAxis( this, axis - 1 );
2575       if ( astOK ) {
2576          (void) sprintf( getattrib_buff, "%d", ival );
2577          result = getattrib_buff;
2578       }
2579 
2580 /* IsLonAxis(axis) */
2581 /* --------------- */
2582    } else if ( nc = 0,
2583                ( 1 == astSscanf( attrib, "islonaxis(%d)%n", &axis, &nc ) )
2584                && ( nc >= len ) ) {
2585       ival = astGetIsLonAxis( this, axis - 1 );
2586       if ( astOK ) {
2587          (void) sprintf( getattrib_buff, "%d", ival );
2588          result = getattrib_buff;
2589       }
2590 
2591 /* LatAxis */
2592 /* -------- */
2593    } else if ( !strcmp( attrib, "lataxis" ) ) {
2594       axis = astGetLatAxis( this );
2595       if ( astOK ) {
2596          (void) sprintf( getattrib_buff, "%d", axis + 1 );
2597          result = getattrib_buff;
2598       }
2599 
2600 /* LonAxis */
2601 /* -------- */
2602    } else if ( !strcmp( attrib, "lonaxis" ) ) {
2603       axis = astGetLonAxis( this );
2604       if ( astOK ) {
2605          (void) sprintf( getattrib_buff, "%d", axis + 1 );
2606          result = getattrib_buff;
2607       }
2608 
2609 /* NegLon */
2610 /* ------ */
2611    } else if ( !strcmp( attrib, "neglon" ) ) {
2612       neglon = astGetNegLon( this );
2613       if ( astOK ) {
2614          (void) sprintf( getattrib_buff, "%d", neglon );
2615          result = getattrib_buff;
2616       }
2617 
2618 /* Projection. */
2619 /* ----------- */
2620    } else if ( !strcmp( attrib, "projection" ) ) {
2621       result = astGetProjection( this );
2622 
2623 /* SkyRef. */
2624 /* ------- */
2625    } else if ( !strcmp( attrib, "skyref" ) ) {
2626       cval = astFormat( this, 0, astGetSkyRef( this, 0 ) );
2627       if ( astOK ) {
2628          nc = sprintf( getattrib_buff, "%s, ", cval );
2629          cval = astFormat( this, 1, astGetSkyRef( this, 1 ) );
2630          if ( astOK ) {
2631             (void) sprintf( getattrib_buff + nc, "%s", cval );
2632             result = getattrib_buff;
2633          }
2634       }
2635 
2636 /* SkyRef(axis). */
2637 /* ------------- */
2638    } else if ( nc = 0,
2639         ( 1 == astSscanf( attrib, "skyref(%d)%n", &axis, &nc ) )
2640         && ( nc >= len ) ) {
2641       dval = astGetSkyRef( this, axis - 1 );
2642       if ( astOK ) {
2643          (void) sprintf( getattrib_buff, "%.*g", DBL_DIG, dval );
2644          result = getattrib_buff;
2645       }
2646 
2647 /* SkyRefP. */
2648 /* -------- */
2649    } else if ( !strcmp( attrib, "skyrefp" ) ) {
2650       cval = astFormat( this, 0, astGetSkyRefP( this, 0 ) );
2651       if ( astOK ) {
2652          nc = sprintf( getattrib_buff, "%s, ", cval );
2653          cval = astFormat( this, 1, astGetSkyRefP( this, 1 ) );
2654          if ( astOK ) {
2655             (void) sprintf( getattrib_buff + nc, "%s", cval );
2656             result = getattrib_buff;
2657          }
2658       }
2659 
2660 /* SkyRefP(axis). */
2661 /* -------------- */
2662    } else if ( nc = 0,
2663         ( 1 == astSscanf( attrib, "skyrefp(%d)%n", &axis, &nc ) )
2664         && ( nc >= len ) ) {
2665       dval = astGetSkyRefP( this, axis - 1 );
2666       if ( astOK ) {
2667          (void) sprintf( getattrib_buff, "%.*g", DBL_DIG, dval );
2668          result = getattrib_buff;
2669       }
2670 
2671 /* SkyRefIs. */
2672 /* --------- */
2673    } else if ( !strcmp( attrib, "skyrefis" ) ) {
2674       ival = astGetSkyRefIs( this );
2675       if ( astOK ) {
2676          if( ival == AST__POLE_REF ){
2677             result = POLE_STRING;
2678          } else if( ival == AST__IGNORED_REF ){
2679             result = IGNORED_STRING;
2680          } else {
2681             result = ORIGIN_STRING;
2682          }
2683       }
2684 
2685 /* AlignOffset */
2686 /* ----------- */
2687    } else if ( !strcmp( attrib, "alignoffset" ) ) {
2688       ival = astGetAlignOffset( this );
2689       if ( astOK ) {
2690          (void) sprintf( getattrib_buff, "%d", ival );
2691          result = getattrib_buff;
2692       }
2693 
2694 /* If the attribute name was not recognised, pass it on to the parent
2695    method for further interpretation. */
2696    } else {
2697       result = (*parent_getattrib)( this_object, attrib, status );
2698    }
2699 
2700 /* Return the result. */
2701    return result;
2702 }
2703 
GetDirection(AstFrame * this_frame,int axis,int * status)2704 static int GetDirection( AstFrame *this_frame, int axis, int *status ) {
2705 /*
2706 *  Name:
2707 *     GetDirection
2708 
2709 *  Purpose:
2710 *     Obtain the value of the Direction attribute for a SkyFrame axis.
2711 
2712 *  Type:
2713 *     Private function.
2714 
2715 *  Synopsis:
2716 *     #include "skyframe.h"
2717 *     int GetDirection( AstFrame *this_frame, int axis, int *status )
2718 
2719 *  Class Membership:
2720 *     SkyFrame member function (over-rides the astGetDirection method inherited
2721 *     from the Frame class).
2722 
2723 *  Description:
2724 *     This function returns the value of the Direction attribute for a
2725 *     specified axis of a SkyFrame. A suitable default value is returned if no
2726 *     Direction value has previously been set.
2727 
2728 *  Parameters:
2729 *     this
2730 *        Pointer to the SkyFrame.
2731 *     axis
2732 *        Axis index (zero-based) identifying the axis for which information is
2733 *        required.
2734 *     status
2735 *        Pointer to the inherited status variable.
2736 
2737 *  Returned Value:
2738 *     Zero or one, depending on the Direction attribute value.
2739 
2740 *  Notes:
2741 *     -  A value of zero will be returned if this function is invoked with the
2742 *     global error status set, or if it should fail for any reason.
2743 */
2744 
2745 /* Local Variables: */
2746    AstSkyFrame *this;            /* Pointer to the SkyFrame structure */
2747    int axis_p;                   /* Permuted axis index */
2748    int result;                   /* Result to be returned */
2749 
2750 /* Check the global error status. */
2751    if ( !astOK ) return 0;
2752 
2753 /* Initialise. */
2754    result = 0;
2755 
2756 /* Obtain a pointer to the SkyFrame structure. */
2757    this = (AstSkyFrame *) this_frame;
2758 
2759 /* Validate and permute the axis index. */
2760    axis_p = astValidateAxis( this, axis, 1, "astGetDirection" );
2761 
2762 /* Check if a value has been set for the axis Direction attribute. If so,
2763    obtain its value. */
2764    if ( astTestDirection( this, axis ) ) {
2765       result = (*parent_getdirection)( this_frame, axis, status );
2766 
2767 /* Otherwise, we will generate a default Direction value. Currently all
2768    systems supported by SkyFrame are left handed, so all longitude axes
2769    are reversed and all latitude axes are not reversed. */
2770    } else if( axis_p == 0 ) {
2771       result = 0;
2772    } else {
2773       result = 1;
2774    }
2775 
2776 /* If an error occurred, clear the result value. */
2777    if ( !astOK ) result = 0;
2778 
2779 /* Return the result. */
2780    return result;
2781 }
2782 
GetBottom(AstFrame * this_frame,int axis,int * status)2783 static double GetBottom( AstFrame *this_frame, int axis, int *status ) {
2784 /*
2785 *  Name:
2786 *     GetBottom
2787 
2788 *  Purpose:
2789 *     Obtain the value of the Bottom attribute for a SkyFrame axis.
2790 
2791 *  Type:
2792 *     Private function.
2793 
2794 *  Synopsis:
2795 *     #include "skyframe.h"
2796 *     double GetBottom( AstFrame *this_frame, int axis, int *status )
2797 
2798 *  Class Membership:
2799 *     SkyFrame member function (over-rides the astGetBottom method inherited
2800 *     from the Frame class).
2801 
2802 *  Description:
2803 *     This function returns the value of the Bottom attribute for a
2804 *     specified axis of a SkyFrame. A suitable default value is returned if no
2805 *     value has previously been set.
2806 
2807 *  Parameters:
2808 *     this
2809 *        Pointer to the SkyFrame.
2810 *     axis
2811 *        Axis index (zero-based) identifying the axis for which information is
2812 *        required.
2813 *     status
2814 *        Pointer to the inherited status variable.
2815 
2816 *  Returned Value:
2817 *     The Bottom value to use.
2818 
2819 *  Notes:
2820 *     -  A value of -DBL_MAX will be returned if this function is invoked
2821 *     with the global error status set, or if it should fail for any reason.
2822 */
2823 
2824 /* Local Variables: */
2825    AstSkyFrame *this;            /* Pointer to the SkyFrame structure */
2826    int axis_p;                   /* Permuted axis index */
2827    double result;                /* Result to be returned */
2828 
2829 /* Check the global error status. */
2830    if ( !astOK ) return -DBL_MAX;
2831 
2832 /* Initialise. */
2833    result = -DBL_MAX;
2834 
2835 /* Obtain a pointer to the SkyFrame structure. */
2836    this = (AstSkyFrame *) this_frame;
2837 
2838 /* Validate and permute the axis index. */
2839    axis_p = astValidateAxis( this, axis, 1, "astGetBottom" );
2840 
2841 /* Check if a value has been set for the axis Bottom attribute. If so,
2842    obtain its value. */
2843    if ( astTestBottom( this, axis ) ) {
2844       result = (*parent_getbottom)( this_frame, axis, status );
2845 
2846 /* Otherwise, we will return a default Bottom value appropriate to the
2847    SkyFrame class. */
2848    } else {
2849 
2850 /* If it is a latitude axis return -pi/2. */
2851       if( axis_p == 1 ) {
2852          result = -piby2;
2853 
2854 /* If it is a longitude value return -DBL_MAX (i.e. no lower limit). */
2855       } else {
2856          result = -DBL_MAX;
2857       }
2858    }
2859 
2860 /* If an error occurred, clear the result value. */
2861    if ( !astOK ) result = -DBL_MAX;
2862 
2863 /* Return the result. */
2864    return result;
2865 }
2866 
GetCachedLAST(AstSkyFrame * this,double epoch,double obslon,double obslat,double obsalt,double dut1,int * status)2867 static double GetCachedLAST( AstSkyFrame *this, double epoch, double obslon,
2868                              double obslat, double obsalt, double dut1,
2869                              int *status ) {
2870 /*
2871 *  Name:
2872 *     GetCachedLAST
2873 
2874 *  Purpose:
2875 *     Attempt to get a LAST value from the cache in the SkyFrame vtab.
2876 
2877 *  Type:
2878 *     Private function.
2879 
2880 *  Synopsis:
2881 *     #include "skyframe.h"
2882 *     double GetCachedLAST( AstSkyFrame *this, double epoch, double obslon,
2883 *                           double obslat, double obsalt, double dut1,
2884 *                           int *status )
2885 
2886 *  Class Membership:
2887 *     SkyFrame member function.
2888 
2889 *  Description:
2890 *     This function searches the static cache of LAST values held in the
2891 *     SkyFrame virtual function table for a value that corresponds to the
2892 *     supplied parameter values. If one is found, it is returned.
2893 *     Otherwise AST__BAD is found.
2894 
2895 *  Parameters:
2896 *     this
2897 *        Pointer to the SkyFrame.
2898 *     epoch
2899 *        The epoch (MJD).
2900 *     obslon
2901 *        Observatory geodetic longitude (radians)
2902 *     obslat
2903 *        Observatory geodetic latitude (radians)
2904 *     obsalt
2905 *        Observatory geodetic altitude (metres)
2906 *     dut1
2907 *        The UT1-UTC correction, in seconds.
2908 *     status
2909 *        Pointer to the inherited status variable.
2910 
2911 *  Returned Value:
2912 *    The Local Apparent Sidereal Time, in radians.
2913 
2914 *  Notes:
2915 *     -  A value of AST__BAD will be returned if this function is invoked
2916 *     with the global error status set, or if it should fail for any reason.
2917 */
2918 
2919 /* Local Variables: */
2920    astDECLARE_GLOBALS
2921    AstSkyLastTable *table;
2922    double *ep;
2923    double *lp;
2924    double dep;
2925    double result;
2926    int ihi;
2927    int ilo;
2928    int itable;
2929    int itest;
2930 
2931 /* Get a pointer to the structure holding thread-specific global data. */
2932    astGET_GLOBALS(this);
2933 
2934 /* Initialise */
2935    result = AST__BAD;
2936 
2937 /* Check the global error status. */
2938    if ( !astOK ) return result;
2939 
2940 /* Wait until the table is not being written to by any thread. This also
2941    prevents a thread from writing to the table whilst we are reading it. */
2942    LOCK_RLOCK1
2943 
2944 /* Loop round every LAST table held in the vtab. Each table refers to a
2945    different observatory position and/or DUT1 value. */
2946    for( itable = 0; itable < nlast_tables; itable++ ) {
2947       table = last_tables[ itable ];
2948 
2949 /* See if the table refers to the given position and dut1 value, allowing
2950    some small tolerance. */
2951       if( fabs( table->obslat - obslat ) < 2.0E-7 &&
2952           fabs( table->obslon - obslon ) < 2.0E-7 &&
2953           fabs( table->obsalt - obsalt ) < 1.0 &&
2954           fabs( table->dut1 - dut1 ) < 1.0E-5 ) {
2955 
2956 /* Get pointers to the array of epoch and corresponding LAST values in
2957    the table. */
2958          ep = table->epoch;
2959          lp = table->last;
2960 
2961 /* The values in the epoch array are monotonic increasing. Do a binary chop
2962    within the table's epoch array to find the earliest entry that has a
2963    value equal to or greater than the supplied epoch value. */
2964          ilo = 0;
2965          ihi = table->nentry - 1;
2966          while( ihi > ilo ) {
2967             itest = ( ilo + ihi )/2;
2968             if( ep[ itest ] >= epoch ) {
2969                ihi = itest;
2970             } else {
2971                ilo = itest + 1;
2972             }
2973          }
2974 
2975 /* Get the difference between the epoch at the entry selected above and
2976    the requested epoch. */
2977          dep = ep[ ilo ] - epoch;
2978 
2979 /* If the entry selected above is the first entry in the table, it can
2980    only be used if it is within 0.001 second of the requested epoch. */
2981          if( ilo == 0 ) {
2982             if( fabs( dep ) < 0.001/86400.0 ) {
2983                result = lp[ 0 ];
2984             }
2985 
2986 /* If the list of epoch values contained no value that was greater than
2987    the supplied epoch value, then we can use the last entry if
2988    it is no more than 0.001 second away from the requested epoch. */
2989          } else if( dep <= 0.0 ) {
2990             if( fabs( dep ) < 0.001/86400.0 ) {
2991                 result = lp[ ilo ];
2992             }
2993 
2994 
2995 /* Otherwise, see if the entry selected above is sufficiently close to
2996    its lower neighbour (i.e. closer than 0.4 days) to allow a reasonably
2997    accurate LAST value to be determined by interpolation. */
2998          } else if( ep[ ilo ] - ep[ ilo - 1 ] < 0.4 ) {
2999             ep += ilo - 1;
3000             lp += ilo - 1;
3001             result = *lp + ( epoch - *ep )*( lp[ 1 ] - *lp )/( ep[ 1 ] - *ep );
3002 
3003 /* If the neighbouring point is too far away for interpolation to be
3004    reliable, then we can only use the point if it is within 0.001 seconds of
3005    the requested epoch. */
3006          } else if( fabs( dep ) < 0.001/86400.0 ) {
3007             result = lp[ ilo ];
3008          }
3009 
3010 /* If we have found the right table, we do not need to look at any other
3011    tables, so leave the table loop. */
3012          break;
3013       }
3014    }
3015 
3016 /* Indicate that threads may now write to the table. */
3017    UNLOCK_RWLOCK1
3018 
3019 /* Ensure the returned value is within the range 0 - 2.PI. */
3020    if( result != AST__BAD ) {
3021       while( result > 2*AST__DPI ) result -= 2*AST__DPI;
3022       while( result < 0.0 ) result += 2*AST__DPI;
3023    }
3024 
3025 /* Return the required LAST value. */
3026    return result;
3027 }
3028 
GetEpoch(AstFrame * this_frame,int * status)3029 static double GetEpoch( AstFrame *this_frame, int *status ) {
3030 /*
3031 *  Name:
3032 *     GetEpoch
3033 
3034 *  Purpose:
3035 *     Obtain the value of the Epoch attribute for a SkyFrame axis.
3036 
3037 *  Type:
3038 *     Private function.
3039 
3040 *  Synopsis:
3041 *     #include "skyframe.h"
3042 *     double GetEpoch( AstFrame *this_frame, int *status )
3043 
3044 *  Class Membership:
3045 *     SkyFrame member function (over-rides the astGetEpoch method inherited
3046 *     from the Frame class).
3047 
3048 *  Description:
3049 *     This function returns the value of the Epoch attribute for a
3050 *     SkyFrame. A suitable default value is returned if no value has
3051 *     previously been set.
3052 
3053 *  Parameters:
3054 *     this
3055 *        Pointer to the SkyFrame.
3056 *     status
3057 *        Pointer to the inherited status variable.
3058 
3059 *  Returned Value:
3060 *     The Epoch value to use.
3061 
3062 *  Notes:
3063 *     -  A value of AST__BAD will be returned if this function is invoked
3064 *     with the global error status set, or if it should fail for any reason.
3065 */
3066 
3067 /* Local Variables: */
3068    AstSkyFrame *this;            /* Pointer to the SkyFrame structure */
3069    AstSystemType system;         /* System attribute */
3070    double result;                /* Result to be returned */
3071 
3072 /* Check the global error status. */
3073    if ( !astOK ) return AST__BAD;
3074 
3075 /* Initialise. */
3076    result = AST__BAD;
3077 
3078 /* Obtain a pointer to the SkyFrame structure. */
3079    this = (AstSkyFrame *) this_frame;
3080 
3081 /* Check if a value has been set for the Epoch attribute. If so, obtain its
3082    value. */
3083    if ( astTestEpoch( this ) ) {
3084       result = (*parent_getepoch)( this_frame, status );
3085 
3086 /* Otherwise, we will return a default Epoch value appropriate to the
3087    SkyFrame class. */
3088    } else {
3089 
3090 /* Provide a default value of B1950.0 or J2000.0 depending on the System
3091    setting. */
3092       system = astGetSystem( this );
3093       if( system  == AST__FK4 || system == AST__FK4_NO_E ) {
3094          result = palEpb2d( 1950.0 );
3095       } else {
3096          result = palEpj2d( 2000.0 );
3097       }
3098    }
3099 
3100 /* If an error occurred, clear the result value. */
3101    if ( !astOK ) result = AST__BAD;
3102 
3103 /* Return the result. */
3104    return result;
3105 }
3106 
GetTop(AstFrame * this_frame,int axis,int * status)3107 static double GetTop( AstFrame *this_frame, int axis, int *status ) {
3108 /*
3109 *  Name:
3110 *     GetTop
3111 
3112 *  Purpose:
3113 *     Obtain the value of the Top attribute for a SkyFrame axis.
3114 
3115 *  Type:
3116 *     Private function.
3117 
3118 *  Synopsis:
3119 *     #include "skyframe.h"
3120 *     double GetTop( AstFrame *this_frame, int axis, int *status )
3121 
3122 *  Class Membership:
3123 *     SkyFrame member function (over-rides the astGetTop method inherited
3124 *     from the Frame class).
3125 
3126 *  Description:
3127 *     This function returns the value of the Top attribute for a
3128 *     specified axis of a SkyFrame. A suitable default value is returned if no
3129 *     value has previously been set.
3130 
3131 *  Parameters:
3132 *     this
3133 *        Pointer to the SkyFrame.
3134 *     axis
3135 *        Axis index (zero-based) identifying the axis for which information is
3136 *        required.
3137 *     status
3138 *        Pointer to the inherited status variable.
3139 
3140 *  Returned Value:
3141 *     The Top value to use.
3142 
3143 *  Notes:
3144 *     -  A value of DBL_MAX will be returned if this function is invoked
3145 *     with the global error status set, or if it should fail for any reason.
3146 */
3147 
3148 /* Local Variables: */
3149    AstSkyFrame *this;            /* Pointer to the SkyFrame structure */
3150    int axis_p;                   /* Permuted axis index */
3151    double result;                /* Result to be returned */
3152 
3153 /* Check the global error status. */
3154    if ( !astOK ) return DBL_MAX;
3155 
3156 /* Initialise. */
3157    result = DBL_MAX;
3158 
3159 /* Obtain a pointer to the SkyFrame structure. */
3160    this = (AstSkyFrame *) this_frame;
3161 
3162 /* Validate and permute the axis index. */
3163    axis_p = astValidateAxis( this, axis, 1, "astGetTop" );
3164 
3165 /* Check if a value has been set for the axis Top attribute. If so,
3166    obtain its value. */
3167    if ( astTestTop( this, axis ) ) {
3168       result = (*parent_gettop)( this_frame, axis, status );
3169 
3170 /* Otherwise, we will return a default Top value appropriate to the
3171    SkyFrame class. */
3172    } else {
3173 
3174 /* If this is a latitude axis return pi/2. */
3175       if( axis_p == 1 ) {
3176          result = piby2;
3177 
3178 /* If it is a longitude value return DBL_MAX (i.e. no upper limit). */
3179       } else {
3180          result = DBL_MAX;
3181       }
3182    }
3183 
3184 /* If an error occurred, clear the result value. */
3185    if ( !astOK ) result = DBL_MAX;
3186 
3187 /* Return the result. */
3188    return result;
3189 }
3190 
GetDomain(AstFrame * this_frame,int * status)3191 static const char *GetDomain( AstFrame *this_frame, int *status ) {
3192 /*
3193 *  Name:
3194 *     GetDomain
3195 
3196 *  Purpose:
3197 *     Obtain a pointer to the Domain attribute string for a SkyFrame.
3198 
3199 *  Type:
3200 *     Private function.
3201 
3202 *  Synopsis:
3203 *     #include "skyframe.h"
3204 *     const char *GetDomain( AstFrame *this, int *status )
3205 
3206 *  Class Membership:
3207 *     SkyFrame member function (over-rides the astGetDomain protected
3208 *     method inherited from the Frame class).
3209 
3210 *  Description:
3211 *    This function returns a pointer to the Domain attribute string
3212 *    for a SkyFrame.
3213 
3214 *  Parameters:
3215 *     this
3216 *        Pointer to the SkyFrame.
3217 *     status
3218 *        Pointer to the inherited status variable.
3219 
3220 *  Returned Value:
3221 *     A pointer to a constant null-terminated string containing the
3222 *     Domain value.
3223 
3224 *  Notes:
3225 *     - The returned pointer or the string it refers to may become
3226 *     invalid following further invocation of this function or
3227 *     modification of the SkyFrame.
3228 *     - A NULL pointer is returned if this function is invoked with
3229 *     the global error status set or if it should fail for any reason.
3230 */
3231 
3232 /* Local Variables: */
3233    AstSkyFrame *this;            /* Pointer to SkyFrame structure */
3234    const char *result;           /* Pointer value to return */
3235 
3236 /* Initialise. */
3237    result = NULL;
3238 
3239 /* Check the global error status. */
3240    if ( !astOK ) return result;
3241 
3242 /* Obtain a pointer to the SkyFrame structure. */
3243    this = (AstSkyFrame *) this_frame;
3244 
3245 /* If a Domain attribute string has been set, invoke the parent method
3246    to obtain a pointer to it. */
3247    if ( astTestDomain( this ) ) {
3248       result = (*parent_getdomain)( this_frame, status );
3249 
3250 /* Otherwise, provide a pointer to a suitable default string. */
3251    } else {
3252       result = "SKY";
3253    }
3254 
3255 /* Return the result. */
3256    return result;
3257 }
3258 
GetFormat(AstFrame * this_frame,int axis,int * status)3259 static const char *GetFormat( AstFrame *this_frame, int axis, int *status ) {
3260 /*
3261 *  Name:
3262 *     GetFormat
3263 
3264 *  Purpose:
3265 *     Access the Format string for a SkyFrame axis.
3266 
3267 *  Type:
3268 *     Private function.
3269 
3270 *  Synopsis:
3271 *     #include "skyframe.h"
3272 *     const char *GetFormat( AstFrame *this, int axis )
3273 
3274 *  Class Membership:
3275 *     SkyFrame member function (over-rides the astGetFormat method inherited
3276 *     from the Frame class).
3277 
3278 *  Description:
3279 *     This function returns a pointer to the Format string for a specified axis
3280 *     of a SkyFrame. A pointer to a suitable default string is returned if no
3281 *     Format value has previously been set.
3282 
3283 *  Parameters:
3284 *     this
3285 *        Pointer to the SkyFrame.
3286 *     axis
3287 *        Axis index (zero-based) identifying the axis for which information is
3288 *        required.
3289 
3290 *  Returned Value:
3291 *     Pointer to a null-terminated character string containing the requested
3292 *     information.
3293 
3294 *  Notes:
3295 *     -  A NULL pointer will be returned if this function is invoked with the
3296 *     global error status set, or if it should fail for any reason.
3297 */
3298 
3299 /* Local Variables: */
3300    astDECLARE_GLOBALS            /* Declare the thread specific global data */
3301    AstAxis *ax;                  /* Pointer to Axis object */
3302    AstSkyFrame *this;            /* Pointer to the SkyFrame structure */
3303    const char *result;           /* Pointer value to return */
3304    int as_time;                  /* Value of AsTime attribute */
3305    int as_time_set;              /* AsTime attribute set? */
3306    int axis_p;                   /* Permuted axis index */
3307    int digits;                   /* Number of digits of precision */
3308    int is_latitude;              /* Value of IsLatitude attribute */
3309    int is_latitude_set;          /* IsLatitude attribute set? */
3310    int parent;                   /* Use parent method? */
3311    int skyaxis;                  /* Is the Axis a SkyAxis? */
3312 
3313 /* Check the global error status. */
3314    if ( !astOK ) return NULL;
3315 
3316 /* Get a pointer to the structure holding thread-specific global data. */
3317    astGET_GLOBALS(this_frame);
3318 
3319 /* Initialise. */
3320    result = NULL;
3321    as_time_set = 0;
3322    is_latitude = 0;
3323    is_latitude_set = 0;
3324 
3325 /* Obtain a pointer to the SkyFrame structure. */
3326    this = (AstSkyFrame *) this_frame;
3327 
3328 /* Validate and permute the axis index. */
3329    axis_p = astValidateAxis( this, axis, 1, "astGetFormat" );
3330 
3331 /* Obtain a pointer to the Axis structure. */
3332    ax = astGetAxis( this, axis );
3333 
3334 /* Decide whether the parent astGetFormat method is able to provide the format
3335    string we require. We must use the parent method if the Axis is not a
3336    SkyAxis, because the syntax of the Format string would become unsuitable
3337    for use with the Axis astFormat method if it was over-ridden here. We also
3338    use the parent method to return a Format pointer if an explicit Format
3339    string has already been set. */
3340    skyaxis = astIsASkyAxis( ax );
3341    parent = ( !skyaxis || (*parent_testformat)( this_frame, axis, status ) );
3342 
3343 /* If neither of the above conditions apply, we may still be able to use the
3344    parent method if the Axis (actually a SkyAxis) is required to behave as a
3345    normal RA or DEC axis, as this is the standard behaviour provided by the
3346    SkyAxis class. Examine the SkyFrame's System attribute to determine if its
3347    axes should behave in this way. */
3348    if ( !parent ) parent = IsEquatorial( astGetSystem( this ), status );
3349 
3350 /* If using the parent method and dealing with a SkyAxis, determine the
3351    settings of any attributes that may affect the Format string. */
3352    if ( astOK ) {
3353       if ( parent ) {
3354          if ( skyaxis ) {
3355             as_time_set = astTestAsTime( this, axis );
3356             is_latitude_set = astTestAxisIsLatitude( ax );
3357             is_latitude = astGetAxisIsLatitude( ax );
3358 
3359 /* If no AsTime value is set for the axis, set a temporary value as determined
3360    by the astGetAsTime method, which supplies suitable defaults for the axes of
3361    a SkyFrame. */
3362             if ( !as_time_set ) {
3363                astSetAsTime( this, axis, astGetAsTime( this, axis ) );
3364 	    }
3365 
3366 /* Temporarly over-ride the SkyAxis IsLatitude attribute, regardless of its
3367    setting, as the second axis of a SkyFrame is always the latitude axis. */
3368             astSetAxisIsLatitude( ax, axis_p == 1 );
3369          }
3370 
3371 /* Invoke the parent method to obtain a pointer to the Format string. */
3372          result = (*parent_getformat)( this_frame, axis, status );
3373 
3374 /* Now restore the attributes that were temporarily over-ridden above to their
3375    previous states. */
3376          if ( skyaxis ) {
3377             if ( !as_time_set ) astClearAsTime( this, axis );
3378             if ( !is_latitude_set ) {
3379                astClearAxisIsLatitude( ax );
3380             } else {
3381                astSetAxisIsLatitude( ax, is_latitude );
3382             }
3383          }
3384 
3385 /* If the parent method is unsuitable, we must construct a new Format string
3386    here. This affects only those coordinate systems whose axes do not behave
3387    like standard RA/DEC axes (e.g. typically ecliptic, galactic and
3388    supergalactic coordinates). For these, we format values as decimal degrees
3389    (or decimal hours if the AsTime attribute is set). Obtain the AsTime
3390    value. */
3391       } else {
3392          as_time = astGetAsTime( this, axis );
3393 
3394 /* Determine how many digits of precision to use. This is obtained from the
3395    SkyAxis Digits attribute (if set), otherwise from the Digits attribute of
3396    the enclosing SkyFrame. */
3397          if ( astTestAxisDigits( ax ) ) {
3398             digits = astGetAxisDigits( ax );
3399          } else {
3400             digits = astGetDigits( this );
3401          }
3402 
3403 /* If a time format is required, generate a Format string using decimal
3404    hours. */
3405          if ( astOK ) {
3406             if ( as_time ) {
3407                if ( digits <= 2 ) {
3408                   result = "h";
3409                } else {
3410                   (void) sprintf( getformat_buff, "h.%d", digits - 2 );
3411                   result = getformat_buff;
3412                }
3413 
3414 /* Otherwise use decimal degrees. */
3415             } else {
3416                if ( digits <= 3 ) {
3417                   result = "d";
3418                } else {
3419                   (void) sprintf( getformat_buff, "d.%d", digits - 3 );
3420                   result = getformat_buff;
3421                }
3422 	    }
3423 	 }
3424       }
3425    }
3426 
3427 /* Annul the Axis pointer. */
3428    ax = astAnnul( ax );
3429 
3430 /* If an error occurred, clear the returned value. */
3431    if ( !astOK ) result = NULL;
3432 
3433 /* Return the result. */
3434    return result;
3435 }
3436 
GetLabel(AstFrame * this,int axis,int * status)3437 static const char *GetLabel( AstFrame *this, int axis, int *status ) {
3438 /*
3439 *  Name:
3440 *     GetLabel
3441 
3442 *  Purpose:
3443 *     Access the Label string for a SkyFrame axis.
3444 
3445 *  Type:
3446 *     Private function.
3447 
3448 *  Synopsis:
3449 *     #include "skyframe.h"
3450 *     const char *GetLabel( AstFrame *this, int axis, int *status )
3451 
3452 *  Class Membership:
3453 *     SkyFrame member function (over-rides the astGetLabel method inherited
3454 *     from the Frame class).
3455 
3456 *  Description:
3457 *     This function returns a pointer to the Label string for a specified axis
3458 *     of a SkyFrame.
3459 
3460 *  Parameters:
3461 *     this
3462 *        Pointer to the SkyFrame.
3463 *     axis
3464 *        Axis index (zero-based) identifying the axis for which information is
3465 *        required.
3466 *     status
3467 *        Pointer to the inherited status variable.
3468 
3469 *  Returned Value:
3470 *     Pointer to a constant null-terminated character string containing the
3471 *     requested information.
3472 
3473 *  Notes:
3474 *     -  A NULL pointer will be returned if this function is invoked with the
3475 *     global error status set, or if it should fail for any reason.
3476 */
3477 
3478 /* Local Variables: */
3479    astDECLARE_GLOBALS            /* Declare the thread specific global data */
3480    AstSystemType system;         /* Code identifying type of sky coordinates */
3481    const char *result;           /* Pointer to label string */
3482    int axis_p;                   /* Permuted axis index */
3483 
3484 /* Check the global error status. */
3485    if ( !astOK ) return NULL;
3486 
3487 /* Get a pointer to the structure holding thread-specific global data. */
3488    astGET_GLOBALS(this);
3489 
3490 /* Initialise. */
3491    result = NULL;
3492 
3493 /* Validate and permute the axis index. */
3494    axis_p = astValidateAxis( this, axis, 1, "astGetLabel" );
3495 
3496 /* Check if a value has been set for the required axis label string. If so,
3497    invoke the parent astGetLabel method to obtain a pointer to it. */
3498    if ( astTestLabel( this, axis ) ) {
3499       result = (*parent_getlabel)( this, axis, status );
3500 
3501 /* Otherwise, identify the sky coordinate system described by the SkyFrame. */
3502    } else {
3503       system = astGetSystem( this );
3504 
3505 /* If OK, supply a pointer to a suitable default label string. */
3506       if ( astOK ) {
3507 
3508 /* Equatorial coordinate systems. */
3509          if ( IsEquatorial( system, status ) ) {
3510 	    result = ( axis_p == 0 ) ? "Right ascension" :
3511 	                               "Declination";
3512 
3513 /* Ecliptic coordinates. */
3514          } else if ( system == AST__ECLIPTIC ) {
3515 	    result = ( axis_p == 0 ) ? "Ecliptic longitude" :
3516                                        "Ecliptic latitude";
3517 
3518 /* Helio-ecliptic coordinates. */
3519          } else if ( system == AST__HELIOECLIPTIC ) {
3520 	    result = ( axis_p == 0 ) ? "Helio-ecliptic longitude" :
3521                                        "Helio-ecliptic latitude";
3522 
3523 /* AzEl coordinates. */
3524          } else if ( system == AST__AZEL ) {
3525 	    result = ( axis_p == 0 ) ? "Azimuth" :
3526                                        "Elevation";
3527 
3528 /* Galactic coordinates. */
3529          } else if ( system == AST__GALACTIC ) {
3530 	    result = ( axis_p == 0 ) ? "Galactic longitude" :
3531                                        "Galactic latitude";
3532 
3533 /* Supergalactic coordinates. */
3534          } else if ( system == AST__SUPERGALACTIC ) {
3535 	    result = ( axis_p == 0 ) ? "Supergalactic longitude" :
3536                                        "Supergalactic latitude";
3537 
3538 /* Unknown spherical coordinates. */
3539          } else if ( system == AST__UNKNOWN ) {
3540 	    result = ( axis_p == 0 ) ? "Longitude" :
3541                                        "Latitude";
3542 
3543 /* Report an error if the coordinate system was not recognised. */
3544          } else {
3545 	    astError( AST__SCSIN, "astGetLabel(%s): Corrupt %s contains "
3546 		      "invalid sky coordinate system identification code "
3547 		      "(%d).", status, astGetClass( this ), astGetClass( this ),
3548 		      (int) system );
3549          }
3550 
3551 /* If the SkyRef attribute has a set value, append " offset" to the label. */
3552          if( astGetSkyRefIs( this ) != AST__IGNORED_REF &&
3553              ( astTestSkyRef( this, 0 ) || astTestSkyRef( this, 1 ) ) ) {
3554             sprintf( getlabel_buff, "%s offset", result );
3555             result = getlabel_buff;
3556          }
3557       }
3558    }
3559 
3560 /* Return the result. */
3561    return result;
3562 }
3563 
GetDiurab(AstSkyFrame * this,int * status)3564 static double GetDiurab( AstSkyFrame *this, int *status ) {
3565 /*
3566 *  Name:
3567 *     GetDiurab
3568 
3569 *  Purpose:
3570 *     Return the magnitude of the diurnal aberration vector.
3571 
3572 *  Type:
3573 *     Private function.
3574 
3575 *  Synopsis:
3576 *     #include "skyframe.h"
3577 *     double GetDiurab( AstSkyFrame *this, int *status )
3578 
3579 *  Class Membership:
3580 *     SkyFrame member function
3581 
3582 *  Description:
3583 *     This function returns the  magnitude of the diurnal aberration
3584 *     vector.
3585 
3586 *  Parameters:
3587 *     this
3588 *        Pointer to the SkyFrame.
3589 *     status
3590 *        Pointer to the inherited status variable.
3591 
3592 *  Returned Value:
3593 *     The magnitude of the diurnal aberration vector.
3594 
3595 */
3596 
3597 /* Local Variables: */
3598    double uau;
3599    double vau;
3600 
3601 /* Check the global error status. */
3602    if ( !astOK ) return AST__BAD;
3603 
3604 /* If the magnitude of the diurnal aberration vector has not yet been
3605    found, find it now, and cache it in the SkyFrame structure. The cached
3606    value will be reset to AST__BAD if the ObsLat attribute value is
3607    changed. This code is transliterated from SLA_AOPPA. */
3608    if( this->diurab == AST__BAD ) {
3609       palGeoc( astGetObsLat( this ), astGetObsAlt( this ), &uau, &vau );
3610       this->diurab = 2*AST__DPI*uau*SOLSID/C;
3611    }
3612 
3613 /* Return the result, */
3614    return this->diurab;
3615 }
3616 
GetLAST(AstSkyFrame * this,int * status)3617 static double GetLAST( AstSkyFrame *this, int *status ) {
3618 /*
3619 *  Name:
3620 *     GetLAST
3621 
3622 *  Purpose:
3623 *     Return the Local Apparent Sidereal Time for the SkyFrame.
3624 
3625 *  Type:
3626 *     Private function.
3627 
3628 *  Synopsis:
3629 *     #include "skyframe.h"
3630 *     double GetLAST( AstSkyFrame *this, int *status )
3631 
3632 *  Class Membership:
3633 *     SkyFrame member function
3634 
3635 *  Description:
3636 *     This function returns the Local Apparent Sidereal Time (LAST)
3637 *     at the moment intime given by the Epoch attribute of the SkyFrame.
3638 
3639 *  Parameters:
3640 *     this
3641 *        Pointer to the SkyFrame.
3642 *     status
3643 *        Pointer to the inherited status variable.
3644 
3645 *  Returned Value:
3646 *     The LAST value.
3647 
3648 */
3649 
3650 /* Local Variables: */
3651    double dlast;                 /* Change in LAST */
3652    double epoch;                 /* Epoch (TDB MJD) */
3653    double last1;                 /* LAST at end of current interval */
3654    double result;                /* Result value to return */
3655    double delta_epoch;           /* Change in Epoch */
3656 
3657 /* Initialise. */
3658    result = 0;
3659 
3660 /* Check the global error status. */
3661    if ( !astOK ) return result;
3662 
3663 /* The "last" component of the SkyFrame structure holds the accurate
3664    LAST at the moment in time given by the "eplast" (a TDB MJD) component
3665    of the SkyFrame structure. If the current value of the SkyFrame's
3666    Epoch attribute is not much different to "eplast" (within 0.4 of a day),
3667    then the returned LAST value is the "last" value plus the difference
3668    between Epoch and "eplast", converted from solar to sidereal time,
3669    then converted to radians. This approximation seems to be good to less
3670    than a tenth of an arcsecond. If this approximation cannot be used,
3671    invoke SetLast to recalculate the accurate LAST and update the "eplast"
3672    and "last" values. */
3673    if( this->eplast != AST__BAD ) {
3674       epoch = astGetEpoch( this );
3675       delta_epoch = epoch - this->eplast;
3676 
3677 /* Return the current LAST value if the epoch has not changed. */
3678       if( delta_epoch == 0.0 ) {
3679          result = this->last;
3680 
3681 /* If the previous full calculation of LAST was less than 0.4 days ago,
3682    use a linear approximation to LAST. */
3683       } else if( fabs( delta_epoch ) < 0.4 ) {
3684 
3685 /* If we do not know the ratio of sidereal to solar time at the current
3686    epoch, calculate it now. This involves a full calculation of LAST at
3687    the end of the current linear approximation period. */
3688          if( this->klast == AST__BAD ) {
3689             last1 = CalcLAST( this, this->eplast + 0.4, astGetObsLon( this ),
3690                               astGetObsLat( this ), astGetObsAlt( this ),
3691                               astGetDut1( this ), status );
3692 
3693 /* Ensure the change in LAST is positive so that we get a positive ratio. */
3694             dlast = last1 - this->last;
3695             if( dlast < 0.0 ) dlast += 2*AST__DPI;
3696             this->klast = 2*AST__DPI*0.4/dlast;
3697          }
3698 
3699 /* Now use the ratio of solar to sidereal time to calculate the linear
3700    approximation to LAST. */
3701          result = this->last + 2*AST__DPI*delta_epoch/this->klast;
3702 
3703 /* If the last accurate calculation of LAST was more than 0.4 days ago,
3704    do a full accurate calculation. */
3705       } else {
3706          SetLast( this, status );
3707          result = this->last;
3708       }
3709 
3710 /* If we have not yet done an accurate calculation of LAST, do one now. */
3711    } else {
3712       SetLast( this, status );
3713       result = this->last;
3714    }
3715 
3716 /* Return the result, */
3717    return result;
3718 }
3719 
GetIsLatAxis(AstSkyFrame * this,int axis,int * status)3720 static int GetIsLatAxis( AstSkyFrame *this, int axis, int *status ) {
3721 /*
3722 *  Name:
3723 *     GetIsLatAxis
3724 
3725 *  Purpose:
3726 *     Test an axis to see if it is a latitude axis.
3727 
3728 *  Type:
3729 *     Private function.
3730 
3731 *  Synopsis:
3732 *     #include "skyframe.h"
3733 *     int GetIsLatAxis( AstSkyFrame *this, int axis, int *status )
3734 
3735 *  Class Membership:
3736 *     SkyFrame member function.
3737 
3738 *  Description:
3739 *     This function tests if a SkyFrame axis is a celestial latitude axis.
3740 
3741 *  Parameters:
3742 *     this
3743 *        Pointer to the SkyFrame.
3744 *     axis
3745 *        Zero based axis index.
3746 *     status
3747 *        Pointer to the inherited status variable.
3748 
3749 *  Returned Value:
3750 *     One if the supplied axis is a celestial latitude axis, and zero
3751 *     otherwise.
3752 
3753 *  Notes:
3754 *     -  A value of zero will be returned if this function is invoked with the
3755 *     global error status set, or if it should fail for any reason.
3756 */
3757 
3758 /* Local Variables: */
3759    int result;                   /* Result to be returned */
3760 
3761 /* Check the global error status. */
3762    if ( !astOK ) return 0;
3763 
3764 /* Get the index of the latitude axis and compare to the supplied axis
3765    index. */
3766    result = ( axis == astGetLatAxis( this ) );
3767 
3768 /* Return the result. */
3769    return astOK ? result : 0;
3770 
3771 }
3772 
GetIsLonAxis(AstSkyFrame * this,int axis,int * status)3773 static int GetIsLonAxis( AstSkyFrame *this, int axis, int *status ) {
3774 /*
3775 *  Name:
3776 *     GetIsLonAxis
3777 
3778 *  Purpose:
3779 *     Test an axis to see if it is a longitude axis.
3780 
3781 *  Type:
3782 *     Private function.
3783 
3784 *  Synopsis:
3785 *     #include "skyframe.h"
3786 *     int GetIsLonAxis( AstSkyFrame *this, int axis, int *status )
3787 
3788 *  Class Membership:
3789 *     SkyFrame member function.
3790 
3791 *  Description:
3792 *     This function tests if a SkyFrame axis is a celestial longitude axis.
3793 
3794 *  Parameters:
3795 *     this
3796 *        Pointer to the SkyFrame.
3797 *     axis
3798 *        Zero based axis index.
3799 *     status
3800 *        Pointer to the inherited status variable.
3801 
3802 *  Returned Value:
3803 *     One if the supplied axis is a celestial longitude axis, and zero
3804 *     otherwise.
3805 
3806 *  Notes:
3807 *     -  A value of zero will be returned if this function is invoked with the
3808 *     global error status set, or if it should fail for any reason.
3809 */
3810 
3811 /* Local Variables: */
3812    int result;                   /* Result to be returned */
3813 
3814 /* Check the global error status. */
3815    if ( !astOK ) return 0;
3816 
3817 /* Get the index of the longitude axis and compare to the supplied axis
3818    index. */
3819    result = ( axis == astGetLonAxis( this ) );
3820 
3821 /* Return the result. */
3822    return astOK ? result : 0;
3823 
3824 }
3825 
GetLatAxis(AstSkyFrame * this,int * status)3826 static int GetLatAxis( AstSkyFrame *this, int *status ) {
3827 /*
3828 *  Name:
3829 *     GetLatAxis
3830 
3831 *  Purpose:
3832 *     Obtain the index of the latitude axis of a SkyFrame.
3833 
3834 *  Type:
3835 *     Private function.
3836 
3837 *  Synopsis:
3838 *     #include "skyframe.h"
3839 *     int GetLatAxis( AstSkyFrame *this, int *status )
3840 
3841 *  Class Membership:
3842 *     SkyFrame member function.
3843 
3844 *  Description:
3845 *     This function returns the zero-based index of the latitude axis of
3846 *     a SkyFrame, taking into account any current axis permutation.
3847 
3848 *  Parameters:
3849 *     this
3850 *        Pointer to the SkyFrame.
3851 *     status
3852 *        Pointer to the inherited status variable.
3853 
3854 *  Returned Value:
3855 *     The zero based axis index (0 or 1) of the latitude axis.
3856 
3857 *  Notes:
3858 *     -  A value of one will be returned if this function is invoked with the
3859 *     global error status set, or if it should fail for any reason.
3860 */
3861 
3862 /* Local Variables: */
3863    int result;                   /* Result to be returned */
3864    const int *perm;              /* Axis permutation array */
3865 
3866 /* Check the global error status. */
3867    if ( !astOK ) return 1;
3868 
3869 /* Initialise. */
3870    result = 1;
3871 
3872 /* Obtain a pointer to the SkyFrame's axis permutation array. */
3873    perm = astGetPerm( this );
3874    if ( astOK ) {
3875 
3876 /* Identify the latitude axis. */
3877       if( perm[ 0 ] == 1 ) {
3878          result = 0;
3879       } else {
3880          result = 1;
3881       }
3882 
3883    }
3884 
3885 /* Return the result. */
3886    return result;
3887 
3888 }
3889 
GetLonAxis(AstSkyFrame * this,int * status)3890 static int GetLonAxis( AstSkyFrame *this, int *status ) {
3891 /*
3892 *  Name:
3893 *     GetLonAxis
3894 
3895 *  Purpose:
3896 *     Obtain the index of the longitude axis of a SkyFrame.
3897 
3898 *  Type:
3899 *     Private function.
3900 
3901 *  Synopsis:
3902 *     #include "skyframe.h"
3903 *     int GetLonAxis( AstSkyFrame *this, int *status )
3904 
3905 *  Class Membership:
3906 *     SkyFrame member function.
3907 
3908 *  Description:
3909 *     This function returns the zero-based index of the longitude axis of
3910 *     a SkyFrame, taking into account any current axis permutation.
3911 
3912 *  Parameters:
3913 *     this
3914 *        Pointer to the SkyFrame.
3915 *     status
3916 *        Pointer to the inherited status variable.
3917 
3918 *  Returned Value:
3919 *     The zero based axis index (0 or 1) of the longitude axis.
3920 
3921 *  Notes:
3922 *     -  A value of zero will be returned if this function is invoked with the
3923 *     global error status set, or if it should fail for any reason.
3924 */
3925 
3926 /* Local Variables: */
3927    int result;                   /* Result to be returned */
3928    const int *perm;              /* Axis permutation array */
3929 
3930 /* Check the global error status. */
3931    if ( !astOK ) return 0;
3932 
3933 /* Initialise. */
3934    result = 0;
3935 
3936 /* Obtain a pointer to the SkyFrame's axis permutation array. */
3937    perm = astGetPerm( this );
3938    if ( astOK ) {
3939 
3940 /* Identify the longitude axis. */
3941       if( perm[ 0 ] == 0 ) {
3942          result = 0;
3943       } else {
3944          result = 1;
3945       }
3946 
3947    }
3948 
3949 /* Return the result. */
3950    return result;
3951 
3952 }
3953 
GetSkyRefP(AstSkyFrame * this,int axis,int * status)3954 static double GetSkyRefP( AstSkyFrame *this, int axis, int *status ) {
3955 /*
3956 *  Name:
3957 *     GetSkyRefP
3958 
3959 *  Purpose:
3960 *     Obtain the value of the SkyRefP attribute for a SkyFrame axis.
3961 
3962 *  Type:
3963 *     Private function.
3964 
3965 *  Synopsis:
3966 *     #include "skyframe.h"
3967 *     double GetSkyRefP( AstSkyFrame *this, int axis, int *status )
3968 
3969 *  Class Membership:
3970 *     SkyFrame member function.
3971 
3972 *  Description:
3973 *     This function returns the value of the SkyRefP attribute for a
3974 *     SkyFrame axis, providing suitable defaults.
3975 
3976 *  Parameters:
3977 *     this
3978 *        Pointer to the SkyFrame.
3979 *     axis
3980 *        Axis index (zero-based) identifying the axis for which information is
3981 *        required.
3982 *     status
3983 *        Pointer to the inherited status variable.
3984 
3985 *  Returned Value:
3986 *     The SkyRefP value to be used.
3987 
3988 *  Notes:
3989 *     -  A value of zero will be returned if this function is invoked with the
3990 *     global error status set, or if it should fail for any reason.
3991 */
3992 
3993 /* Local Variables: */
3994    double result;                /* Returned value */
3995    int axis_p;                   /* Permuted axis index */
3996 
3997 /* Initialise. */
3998    result = 0.0;
3999 
4000 /* Check the global error status. */
4001    if ( !astOK ) return result;
4002 
4003 /* Validate and permute the axis index. */
4004    axis_p = astValidateAxis( this, axis, 1, "astGetSkyRefP" );
4005 
4006 /* Check if a value has been set for the required axis. If so, return it. */
4007    if( this->skyrefp[ axis_p ] != AST__BAD ) {
4008       result = this->skyrefp[ axis_p ];
4009 
4010 /* Otherwise, return the default value */
4011    } else {
4012 
4013 /* The default longitude value is always zero. */
4014       if( axis_p == 0 ) {
4015          result= 0.0;
4016 
4017 /* The default latitude value depends on SkyRef. The usual default is the
4018    north pole. The exception to this is if the SkyRef attribute identifies
4019    either the north or the south pole, in which case the origin is used as
4020    the default. Allow some tolerance. */
4021       } else if( fabs( cos( this->skyref[ 1 ] ) ) > 1.0E-10 ) {
4022          result = pi/2;
4023 
4024       } else {
4025          result = 0.0;
4026       }
4027    }
4028 
4029 /* Return the result. */
4030    return result;
4031 }
4032 
GetSymbol(AstFrame * this,int axis,int * status)4033 static const char *GetSymbol( AstFrame *this, int axis, int *status ) {
4034 /*
4035 *  Name:
4036 *     GetSymbol
4037 
4038 *  Purpose:
4039 *     Obtain a pointer to the Symbol string for a SkyFrame axis.
4040 
4041 *  Type:
4042 *     Private function.
4043 
4044 *  Synopsis:
4045 *     #include "skyframe.h"
4046 *     const char *GetSymbol( AstFrame *this, int axis, int *status )
4047 
4048 *  Class Membership:
4049 *     SkyFrame member function (over-rides the astGetSymbol method inherited
4050 *     from the Frame class).
4051 
4052 *  Description:
4053 *     This function returns a pointer to the Symbol string for a specified axis
4054 *     of a SkyFrame.
4055 
4056 *  Parameters:
4057 *     this
4058 *        Pointer to the SkyFrame.
4059 *     axis
4060 *        Axis index (zero-based) identifying the axis for which information is
4061 *        required.
4062 *     status
4063 *        Pointer to the inherited status variable.
4064 
4065 *  Returned Value:
4066 *     Pointer to a constant null-terminated character string containing the
4067 *     requested information.
4068 
4069 *  Notes:
4070 *     -  A NULL pointer will be returned if this function is invoked with the
4071 *     global error status set, or if it should fail for any reason.
4072 */
4073 
4074 /* Local Variables: */
4075    astDECLARE_GLOBALS            /* Declare the thread specific global data */
4076    AstSystemType system;         /* Code identifying type of sky coordinates */
4077    const char *result;           /* Pointer to symbol string */
4078    int axis_p;                   /* Permuted axis index */
4079 
4080 /* Check the global error status. */
4081    if ( !astOK ) return NULL;
4082 
4083 /* Get a pointer to the structure holding thread-specific global data. */
4084    astGET_GLOBALS(this);
4085 
4086 /* Initialise. */
4087    result = NULL;
4088 
4089 /* Validate and permute the axis index. */
4090    axis_p = astValidateAxis( this, axis, 1, "astGetSymbol" );
4091 
4092 /* Check if a value has been set for the required axis symbol string. If so,
4093    invoke the parent astGetSymbol method to obtain a pointer to it. */
4094    if ( astTestSymbol( this, axis ) ) {
4095       result = (*parent_getsymbol)( this, axis, status );
4096 
4097 /* Otherwise, identify the sky coordinate system described by the SkyFrame. */
4098    } else {
4099       system = astGetSystem( this );
4100 
4101 /* If OK, supply a pointer to a suitable default Symbol string. */
4102       if ( astOK ) {
4103 
4104 /* Equatorial coordinate systems. */
4105          if ( IsEquatorial( system, status ) ) {
4106 	    result = ( axis_p == 0 ) ? "RA" : "Dec";
4107 
4108 /* Ecliptic coordinates. */
4109          } else if ( system == AST__ECLIPTIC ) {
4110 	    result = ( axis_p == 0 ) ? "Lambda" : "Beta";
4111 
4112 /* Helio-ecliptic coordinates. */
4113          } else if ( system == AST__HELIOECLIPTIC ) {
4114 	    result = ( axis_p == 0 ) ? "Lambda" : "Beta";
4115 
4116 /* AzEl coordinates. */
4117          } else if ( system == AST__AZEL ) {
4118 	    result = ( axis_p == 0 ) ? "Az" : "El";
4119 
4120 /* Galactic coordinates. */
4121          } else if ( system == AST__GALACTIC ) {
4122 	    result = ( axis_p == 0 ) ? "l" : "b";
4123 
4124 /* Supergalactic coordinates. */
4125          } else if ( system == AST__SUPERGALACTIC ) {
4126 	    result = ( axis_p == 0 ) ? "SGL" : "SGB";
4127 
4128 /* Unknown spherical coordinates. */
4129          } else if ( system == AST__UNKNOWN ) {
4130 	    result = ( axis_p == 0 ) ? "Lon" : "Lat";
4131 
4132 /* Report an error if the coordinate system was not recognised. */
4133          } else {
4134 	    astError( AST__SCSIN, "astGetSymbol(%s): Corrupt %s contains "
4135 		      "invalid sky coordinate system identification code "
4136 		      "(%d).", status, astGetClass( this ), astGetClass( this ),
4137 		      (int) system );
4138          }
4139 
4140 /* If the SkyRef attribute had a set value, prepend "D" (for "delta") to the
4141    Symbol. */
4142          if( astGetSkyRefIs( this ) != AST__IGNORED_REF &&
4143              ( astTestSkyRef( this, 0 ) || astTestSkyRef( this, 1 ) ) ) {
4144             sprintf( getsymbol_buff, "D%s", result );
4145             result = getsymbol_buff;
4146          }
4147       }
4148    }
4149 
4150 /* Return the result. */
4151    return result;
4152 }
4153 
GetAlignSystem(AstFrame * this_frame,int * status)4154 static AstSystemType GetAlignSystem( AstFrame *this_frame, int *status ) {
4155 /*
4156 *  Name:
4157 *     GetAlignSystem
4158 
4159 *  Purpose:
4160 *     Obtain the AlignSystem attribute for a SkyFrame.
4161 
4162 *  Type:
4163 *     Private function.
4164 
4165 *  Synopsis:
4166 *     #include "skyframe.h"
4167 *     AstSystemType GetAlignSystem( AstFrame *this_frame, int *status )
4168 
4169 *  Class Membership:
4170 *     SkyFrame member function (over-rides the astGetAlignSystem protected
4171 *     method inherited from the Frame class).
4172 
4173 *  Description:
4174 *     This function returns the AlignSystem attribute for a SkyFrame.
4175 
4176 *  Parameters:
4177 *     this
4178 *        Pointer to the SkyFrame.
4179 *     status
4180 *        Pointer to the inherited status variable.
4181 
4182 *  Returned Value:
4183 *     The AlignSystem value.
4184 
4185 */
4186 
4187 /* Local Variables: */
4188    AstSkyFrame *this;            /* Pointer to SkyFrame structure */
4189    AstSystemType result;         /* Value to return */
4190 
4191 /* Initialise. */
4192    result = AST__BADSYSTEM;
4193 
4194 /* Check the global error status. */
4195    if ( !astOK ) return result;
4196 
4197 /* Obtain a pointer to the SkyFrame structure. */
4198    this = (AstSkyFrame *) this_frame;
4199 
4200 /* If a AlignSystem attribute has been set, invoke the parent method to obtain
4201    it. */
4202    if ( astTestAlignSystem( this ) ) {
4203       result = (*parent_getalignsystem)( this_frame, status );
4204 
4205 /* Otherwise, provide a suitable default. */
4206    } else {
4207       result = AST__ICRS;
4208    }
4209 
4210 /* Return the result. */
4211    return result;
4212 }
4213 
GetSystem(AstFrame * this_frame,int * status)4214 static AstSystemType GetSystem( AstFrame *this_frame, int *status ) {
4215 /*
4216 *  Name:
4217 *     GetSystem
4218 
4219 *  Purpose:
4220 *     Obtain the System attribute for a SkyFrame.
4221 
4222 *  Type:
4223 *     Private function.
4224 
4225 *  Synopsis:
4226 *     #include "skyframe.h"
4227 *     AstSystemType GetSystem( AstFrame *this_frame, int *status )
4228 
4229 *  Class Membership:
4230 *     SkyFrame member function (over-rides the astGetSystem protected
4231 *     method inherited from the Frame class).
4232 
4233 *  Description:
4234 *     This function returns the System attribute for a SkyFrame.
4235 
4236 *  Parameters:
4237 *     this
4238 *        Pointer to the SkyFrame.
4239 *     status
4240 *        Pointer to the inherited status variable.
4241 
4242 *  Returned Value:
4243 *     The System value.
4244 
4245 *  Notes:
4246 *     - AST__BADSYSTEM is returned if this function is invoked with
4247 *     the global error status set or if it should fail for any reason.
4248 */
4249 
4250 /* Local Variables: */
4251    AstSkyFrame *this;            /* Pointer to SkyFrame structure */
4252    AstSystemType result;         /* Value to return */
4253 
4254 /* Initialise. */
4255    result = AST__BADSYSTEM;
4256 
4257 /* Check the global error status. */
4258    if ( !astOK ) return result;
4259 
4260 /* Obtain a pointer to the SkyFrame structure. */
4261    this = (AstSkyFrame *) this_frame;
4262 
4263 /* If a System attribute has been set, invoke the parent method to obtain
4264    it. */
4265    if ( astTestSystem( this ) ) {
4266       result = (*parent_getsystem)( this_frame, status );
4267 
4268 /* Otherwise, provide a suitable default. */
4269    } else {
4270       result = AST__ICRS;
4271    }
4272 
4273 /* Return the result. */
4274    return result;
4275 }
4276 
GetTitle(AstFrame * this_frame,int * status)4277 static const char *GetTitle( AstFrame *this_frame, int *status ) {
4278 /*
4279 *  Name:
4280 *     GetTitle
4281 
4282 *  Purpose:
4283 *     Obtain a pointer to the Title string for a SkyFrame.
4284 
4285 *  Type:
4286 *     Private function.
4287 
4288 *  Synopsis:
4289 *     #include "skyframe.h"
4290 *     const char *GetTitle( AstFrame *this_frame, int *status )
4291 
4292 *  Class Membership:
4293 *     SkyFrame member function (over-rides the astGetTitle method inherited
4294 *     from the Frame class).
4295 
4296 *  Description:
4297 *     This function returns a pointer to the Title string for a SkyFrame.
4298 *     A pointer to a suitable default string is returned if no Title value has
4299 *     previously been set.
4300 
4301 *  Parameters:
4302 *     this
4303 *        Pointer to the SkyFrame.
4304 *     status
4305 *        Pointer to the inherited status variable.
4306 
4307 *  Returned Value:
4308 *     Pointer to a null-terminated character string containing the requested
4309 *     information.
4310 
4311 *  Notes:
4312 *     -  A NULL pointer will be returned if this function is invoked with the
4313 *     global error status set, or if it should fail for any reason.
4314 */
4315 
4316 /* Local Variables: */
4317    astDECLARE_GLOBALS            /* Declare the thread specific global data */
4318    AstSkyFrame *this;            /* Pointer to SkyFrame structure */
4319    AstSystemType system;         /* Code identifying type of sky coordinates */
4320    const char *extra;            /* Pointer to extra information */
4321    const char *p;                /* Character pointer */
4322    const char *projection;       /* Pointer to sky projection description */
4323    const char *result;           /* Pointer to result string */
4324    const char *word;             /* Pointer to critical word */
4325    double epoch;                 /* Value of Epoch attribute */
4326    double equinox;               /* Value of Equinox attribute */
4327    int lextra;                   /* Length of extra information */
4328    int offset;                   /* Using offset coordinate system? */
4329    int pos;                      /* Buffer position to enter text */
4330 
4331 /* Check the global error status. */
4332    if ( !astOK ) return NULL;
4333 
4334 /* Get a pointer to the structure holding thread-specific global data. */
4335    astGET_GLOBALS(this_frame);
4336 
4337 /* Initialise. */
4338    result = NULL;
4339    pos = 0;
4340 
4341 /* Obtain a pointer to the SkyFrame structure. */
4342    this = (AstSkyFrame *) this_frame;
4343 
4344 /* See if a Title string has been set. If so, use the parent astGetTitle
4345    method to obtain a pointer to it. */
4346    if ( astTestTitle( this ) ) {
4347       result = (*parent_gettitle)( this_frame, status );
4348 
4349 /* Otherwise, we will generate a default Title string. Obtain the values of the
4350    SkyFrame's attributes that determine what this string will be. */
4351    } else {
4352       epoch = astGetEpoch( this );
4353       equinox = astGetEquinox( this );
4354       projection = astGetProjection( this );
4355       system = astGetSystem( this );
4356 
4357 /* See if an offset coordinate system is being used.*/
4358       offset = ( astTestSkyRef( this, 0 ) || astTestSkyRef( this, 1 ) )
4359                && ( astGetSkyRefIs( this ) != AST__IGNORED_REF );
4360 
4361 /* Use this to determine if the word "coordinates" or "offsets" should be
4362    used.*/
4363       word = offset ? "offsets" : "coordinates";
4364 
4365 /* Classify the coordinate system type and create an appropriate Title
4366    string.  (Note that when invoking the astFmtDecimalYr function we must
4367    use a separate sprintf on each occasion so as not to over-write its
4368    internal buffer before the result string has been used.) */
4369       if ( astOK ) {
4370          result = gettitle_buff;
4371          switch ( system ) {
4372 
4373 /* FK4 equatorial coordinates. */
4374 /* --------------------------- */
4375 /* Display the Equinox and Epoch values. */
4376 	 case AST__FK4:
4377 	    pos = sprintf( gettitle_buff, "FK4 equatorial %s", word );
4378             if( astTestEquinox( this ) || astGetUseDefs( this ) ) {
4379    	       pos += sprintf( gettitle_buff + pos, "; mean equinox B%s",
4380 		               astFmtDecimalYr( palEpb( equinox ), 9 ) );
4381             }
4382             if( astTestEpoch( this ) || astGetUseDefs( this ) ) {
4383                pos += sprintf( gettitle_buff + pos,
4384                                "; epoch B%s", astFmtDecimalYr( palEpb( epoch ), 9 ) );
4385             }
4386 	    break;
4387 
4388 /* FK4 coordinates with no E-terms of aberration. */
4389 /* ---------------------------------------------- */
4390 /* Display the Equinox and Epoch values. */
4391 	 case AST__FK4_NO_E:
4392 	    pos = sprintf( gettitle_buff, "FK4 equatorial %s; no E-terms", word );
4393             if( astTestEquinox( this ) || astGetUseDefs( this ) ) {
4394    	       pos += sprintf( gettitle_buff + pos, "; mean equinox B%s",
4395 		               astFmtDecimalYr( palEpb( equinox ), 9 ) );
4396             }
4397             if( astTestEpoch( this ) || astGetUseDefs( this ) ) {
4398                pos += sprintf( gettitle_buff + pos,
4399                                "; epoch B%s", astFmtDecimalYr( palEpb( epoch ), 9 ) );
4400             }
4401 	    break;
4402 
4403 /* FK5 equatorial coordinates. */
4404 /* --------------------------- */
4405 /* Display only the Equinox value. */
4406 	 case AST__FK5:
4407 	    pos = sprintf( gettitle_buff, "FK5 equatorial %s", word );
4408             if( astTestEquinox( this ) || astGetUseDefs( this ) ) {
4409    	       pos += sprintf( gettitle_buff + pos, "; mean equinox J%s",
4410 		               astFmtDecimalYr( palEpj( equinox ), 9 ) );
4411             }
4412 	    break;
4413 
4414 /* J2000 equatorial coordinates. */
4415 /* ----------------------------- */
4416 /* Based on the dynamically determined mean equator and equinox of J2000,
4417    rather than on a model such as FK4 or FK5 */
4418 	 case AST__J2000:
4419 	    pos = sprintf( gettitle_buff, "J2000 equatorial %s", word );
4420 	    break;
4421 
4422 /* ICRS coordinates. */
4423 /* ----------------- */
4424 /* ICRS is only like RA/Dec by co-incidence, it is not really an
4425    equatorial system by definition. */
4426 	 case AST__ICRS:
4427 	    pos = sprintf( gettitle_buff, "ICRS %s", word );
4428 	    break;
4429 
4430 /* AzEl coordinates. */
4431 /* ----------------- */
4432 	 case AST__AZEL:
4433 	    pos = sprintf( gettitle_buff, "Horizon (Azimuth/Elevation) %s", word );
4434 	    break;
4435 
4436 /* Geocentric apparent equatorial coordinates. */
4437 /* ------------------------------------------ */
4438 /* Display only the Epoch value. */
4439 	 case AST__GAPPT:
4440 	    pos = sprintf( gettitle_buff,
4441                            "Geocentric apparent equatorial %s; "
4442                            "; epoch J%s", word, astFmtDecimalYr( palEpj( epoch ), 9 ) );
4443 	    break;
4444 
4445 /* Ecliptic coordinates. */
4446 /* --------------------- */
4447 /* Display only the Equinox value. */
4448 	 case AST__ECLIPTIC:
4449 	    pos = sprintf( gettitle_buff, "Ecliptic %s", word );
4450             if( astTestEquinox( this ) || astGetUseDefs( this ) ) {
4451    	       pos += sprintf( gettitle_buff + pos, "; mean equinox J%s",
4452 		               astFmtDecimalYr( palEpj( equinox ), 9 ) );
4453             }
4454 	    break;
4455 
4456 /* Helio-ecliptic coordinates. */
4457 /* --------------------------- */
4458 /* Display only the Epoch value (equinox is fixed). */
4459 	 case AST__HELIOECLIPTIC:
4460 	    pos = sprintf( gettitle_buff, "Helio-ecliptic %s; mean equinox J2000", word );
4461             if( astTestEpoch( this ) || astGetUseDefs( this ) ) {
4462    	       pos += sprintf( gettitle_buff + pos, "; epoch J%s",
4463 		               astFmtDecimalYr( palEpj( epoch ), 9 ) );
4464             }
4465 	    break;
4466 
4467 /* Galactic coordinates. */
4468 /* --------------------- */
4469 /* Do not display an Equinox or Epoch value. */
4470 	 case AST__GALACTIC:
4471 	    pos = sprintf( gettitle_buff, "IAU (1958) galactic %s", word );
4472 	    break;
4473 
4474 /* Supergalactic coordinates. */
4475 /* -------------------------- */
4476 /* Do not display an Equinox or Epoch value. */
4477 	 case AST__SUPERGALACTIC:
4478 	    pos = sprintf( gettitle_buff,
4479                            "De Vaucouleurs supergalactic %s", word );
4480 	    break;
4481 
4482 /* Unknown coordinates. */
4483 /* -------------------------- */
4484 	 case AST__UNKNOWN:
4485 	    pos = sprintf( gettitle_buff,
4486                            "Spherical %s", word );
4487 	    break;
4488 
4489 /* Report an error if the coordinate system was not recognised. */
4490 	 default:
4491 	    astError( AST__SCSIN, "astGetTitle(%s): Corrupt %s contains "
4492 		      "invalid sky coordinate system identification code "
4493 		      "(%d).", status, astGetClass( this ), astGetClass( this ),
4494 		     (int) system );
4495 	    break;
4496          }
4497 
4498 /* If OK, we add either a description of the sky projection, or (if used)
4499    a description of the origin or pole of the offset coordinate system.
4500    We include only one of these two strings in order to keep the length
4501    of the title down to a reasonable value.*/
4502          if ( astOK ) {
4503 
4504 /* If the SkyRef attribute has set values, create a description of the offset
4505    coordinate system. */
4506             if( offset ){
4507                word = ( astGetSkyRefIs( this ) == AST__POLE_REF )?"pole":"origin";
4508                lextra = sprintf( gettitle_buff2, "%s at %s ", word,
4509                            astFormat( this, 0, astGetSkyRef( this, 0 ) ) );
4510                lextra += sprintf( gettitle_buff2 + lextra, "%s",
4511                            astFormat( this, 1, astGetSkyRef( this, 1 ) ) );
4512                extra = gettitle_buff2;
4513 
4514 /* Otherwise, get the sky projection description. */
4515             } else {
4516                extra = projection;
4517 
4518 /* Determine the length of the extra information, after removing trailing
4519    white space. */
4520                for ( lextra = (int) strlen( extra ); lextra > 0; lextra-- ) {
4521                   if ( !isspace( extra[ lextra - 1 ] ) ) break;
4522                }
4523             }
4524 
4525 /* If non-blank extra information is available, append it to the title string,
4526    checking that the end of the buffer is not over-run. */
4527             if ( lextra ) {
4528                p = "; ";
4529                while ( ( pos < AST__SKYFRAME_GETTITLE_BUFF_LEN ) && *p ) gettitle_buff[ pos++ ] = *p++;
4530                p = extra;
4531                while ( ( pos < AST__SKYFRAME_GETTITLE_BUFF_LEN ) &&
4532                        ( p < ( extra + lextra ) ) ) gettitle_buff[ pos++ ] = *p++;
4533                if( extra == projection ) {
4534                   p = " projection";
4535                   while ( ( pos < AST__SKYFRAME_GETTITLE_BUFF_LEN ) && *p ) gettitle_buff[ pos++ ] = *p++;
4536                }
4537                gettitle_buff[ pos ] = '\0';
4538             }
4539          }
4540       }
4541    }
4542 
4543 /* If an error occurred, clear the returned pointer value. */
4544    if ( !astOK ) result = NULL;
4545 
4546 /* Return the result. */
4547    return result;
4548 }
4549 
GetUnit(AstFrame * this_frame,int axis,int * status)4550 static const char *GetUnit( AstFrame *this_frame, int axis, int *status ) {
4551 /*
4552 *  Name:
4553 *     GetUnit
4554 
4555 *  Purpose:
4556 *     Obtain a pointer to the Unit string for a SkyFrame's axis.
4557 
4558 *  Type:
4559 *     Private function.
4560 
4561 *  Synopsis:
4562 *     #include "skyframe.h"
4563 *     const char *GetUnit( AstFrame *this_frame, int axis )
4564 
4565 *  Class Membership:
4566 *     SkyFrame member function (over-rides the astGetUnit method inherited
4567 *     from the Frame class).
4568 
4569 *  Description:
4570 *     This function returns a pointer to the Unit string for a specified axis
4571 *     of a SkyFrame. If the Unit attribute has not been set for the axis, a
4572 *     pointer to a suitable default string is returned instead. This string may
4573 *     depend on the value of the Format attribute for the axis and, in turn, on
4574 *     the type of sky coordinate system that the SkyFrame describes.
4575 
4576 *  Parameters:
4577 *     this
4578 *        Pointer to the SkyFrame.
4579 *     axis
4580 *        The number of the axis (zero-based) for which information is required.
4581 
4582 *  Returned Value:
4583 *     A pointer to a null-terminated string containing the Unit value.
4584 
4585 *  Notes:
4586 *     -  A NULL pointer will be returned if this function is invoked with the
4587 *     global error status set, or if it should fail for any reason.
4588 */
4589 
4590 /* Local Variables: */
4591    AstSkyFrame *this;            /* Pointer to the SkyFrame structure */
4592    const char *result;           /* Pointer value to return */
4593    int format_set;               /* Format attribute set? */
4594 
4595 /* Check the global error status. */
4596    if ( !astOK ) return NULL;
4597 
4598 /* Obtain a pointer to the SkyFrame structure. */
4599    this = (AstSkyFrame *) this_frame;
4600 
4601 /* Validate the axis index. */
4602    (void) astValidateAxis( this, axis, 1, "astGetUnit" );
4603 
4604 /* The Unit value may depend on the value of the Format attribute, so
4605    determine if a Format value has been set for the axis and set a
4606    temporary value if it has not. Use the GetFormat member function
4607    for this class together with member functions inherited from the
4608    parent class (rather than using the object's methods directly)
4609    because if any of these methods have been over-ridden by a derived
4610    class the Format string syntax may no longer be compatible with
4611    this class. */
4612    format_set = (*parent_testformat)( this_frame, axis, status );
4613    if ( !format_set ) {
4614       (*parent_setformat)( this_frame, axis, GetFormat( this_frame, axis, status ), status );
4615    }
4616 
4617 /* Use the parent GetUnit method to return a pointer to the required Unit
4618    string. */
4619    result = (*parent_getunit)( this_frame, axis, status );
4620 
4621 /* If necessary, clear any temporary Format value that was set above. */
4622    if ( !format_set ) (*parent_clearformat)( this_frame, axis, status );
4623 
4624 /* If an error occurred, clear the returned value. */
4625    if ( !astOK ) result = NULL;
4626 
4627 /* Return the result. */
4628    return result;
4629 }
4630 
astInitSkyFrameVtab_(AstSkyFrameVtab * vtab,const char * name,int * status)4631 void astInitSkyFrameVtab_(  AstSkyFrameVtab *vtab, const char *name, int *status ) {
4632 /*
4633 *+
4634 *  Name:
4635 *     astInitSkyFrameVtab
4636 
4637 *  Purpose:
4638 *     Initialise a virtual function table for a SkyFrame.
4639 
4640 *  Type:
4641 *     Protected function.
4642 
4643 *  Synopsis:
4644 *     #include "skyframe.h"
4645 *     void astInitSkyFrameVtab( AstSkyFrameVtab *vtab, const char *name )
4646 
4647 *  Class Membership:
4648 *     SkyFrame vtab initialiser.
4649 
4650 *  Description:
4651 *     This function initialises the component of a virtual function
4652 *     table which is used by the SkyFrame class.
4653 
4654 *  Parameters:
4655 *     vtab
4656 *        Pointer to the virtual function table. The components used by
4657 *        all ancestral classes will be initialised if they have not already
4658 *        been initialised.
4659 *     name
4660 *        Pointer to a constant null-terminated character string which contains
4661 *        the name of the class to which the virtual function table belongs (it
4662 *        is this pointer value that will subsequently be returned by the Object
4663 *        astClass function).
4664 *-
4665 */
4666 
4667 /* Local Variables: */
4668    astDECLARE_GLOBALS            /* Pointer to thread-specific global data */
4669    AstFrameVtab *frame;          /* Pointer to Frame component of Vtab */
4670    AstObjectVtab *object;        /* Pointer to Object component of Vtab */
4671    int stat;                     /* SLALIB status */
4672 
4673 /* Check the local error status. */
4674    if ( !astOK ) return;
4675 
4676 /* Get a pointer to the thread specific global data structure. */
4677    astGET_GLOBALS(NULL);
4678 
4679 /* Initialize the component of the virtual function table used by the
4680    parent class. */
4681    astInitFrameVtab( (AstFrameVtab *) vtab, name );
4682 
4683 /* Store a unique "magic" value in the virtual function table. This
4684    will be used (by astIsASkyFrame) to determine if an object belongs
4685    to this class.  We can conveniently use the address of the (static)
4686    class_check variable to generate this unique value. */
4687    vtab->id.check = &class_check;
4688    vtab->id.parent = &(((AstFrameVtab *) vtab)->id);
4689 
4690 /* Initialise member function pointers. */
4691 /* ------------------------------------ */
4692 /* Store pointers to the member functions (implemented here) that
4693    provide virtual methods for this class. */
4694    vtab->ClearAsTime = ClearAsTime;
4695    vtab->ClearEquinox = ClearEquinox;
4696    vtab->ClearNegLon = ClearNegLon;
4697    vtab->ClearProjection = ClearProjection;
4698    vtab->GetAsTime = GetAsTime;
4699    vtab->GetEquinox = GetEquinox;
4700    vtab->GetNegLon = GetNegLon;
4701    vtab->GetIsLatAxis = GetIsLatAxis;
4702    vtab->GetIsLonAxis = GetIsLonAxis;
4703    vtab->GetLatAxis = GetLatAxis;
4704    vtab->GetLonAxis = GetLonAxis;
4705    vtab->GetProjection = GetProjection;
4706    vtab->SetAsTime = SetAsTime;
4707    vtab->SetEquinox = SetEquinox;
4708    vtab->SetNegLon = SetNegLon;
4709    vtab->SetProjection = SetProjection;
4710    vtab->SkyOffsetMap = SkyOffsetMap;
4711    vtab->TestAsTime = TestAsTime;
4712    vtab->TestEquinox = TestEquinox;
4713    vtab->TestNegLon = TestNegLon;
4714    vtab->TestProjection = TestProjection;
4715 
4716    vtab->TestSkyRef = TestSkyRef;
4717    vtab->SetSkyRef = SetSkyRef;
4718    vtab->GetSkyRef = GetSkyRef;
4719    vtab->ClearSkyRef = ClearSkyRef;
4720 
4721    vtab->TestSkyRefP = TestSkyRefP;
4722    vtab->SetSkyRefP = SetSkyRefP;
4723    vtab->GetSkyRefP = GetSkyRefP;
4724    vtab->ClearSkyRefP = ClearSkyRefP;
4725 
4726    vtab->TestSkyRefIs = TestSkyRefIs;
4727    vtab->SetSkyRefIs = SetSkyRefIs;
4728    vtab->GetSkyRefIs = GetSkyRefIs;
4729    vtab->ClearSkyRefIs = ClearSkyRefIs;
4730 
4731    vtab->TestAlignOffset = TestAlignOffset;
4732    vtab->SetAlignOffset = SetAlignOffset;
4733    vtab->GetAlignOffset = GetAlignOffset;
4734    vtab->ClearAlignOffset = ClearAlignOffset;
4735 
4736 /* Save the inherited pointers to methods that will be extended, and
4737    replace them with pointers to the new member functions. */
4738    object = (AstObjectVtab *) vtab;
4739    frame = (AstFrameVtab *) vtab;
4740    parent_getobjsize = object->GetObjSize;
4741    object->GetObjSize = GetObjSize;
4742 
4743    parent_clearattrib = object->ClearAttrib;
4744    object->ClearAttrib = ClearAttrib;
4745    parent_getattrib = object->GetAttrib;
4746    object->GetAttrib = GetAttrib;
4747    parent_setattrib = object->SetAttrib;
4748    object->SetAttrib = SetAttrib;
4749    parent_testattrib = object->TestAttrib;
4750    object->TestAttrib = TestAttrib;
4751 
4752    parent_gettop = frame->GetTop;
4753    frame->GetTop = GetTop;
4754 
4755    parent_setobsalt = frame->SetObsAlt;
4756    frame->SetObsAlt = SetObsAlt;
4757 
4758    parent_setobslat = frame->SetObsLat;
4759    frame->SetObsLat = SetObsLat;
4760 
4761    parent_setobslon = frame->SetObsLon;
4762    frame->SetObsLon = SetObsLon;
4763 
4764    parent_clearobslon = frame->ClearObsLon;
4765    frame->ClearObsLon = ClearObsLon;
4766 
4767    parent_clearobsalt = frame->ClearObsAlt;
4768    frame->ClearObsAlt = ClearObsAlt;
4769 
4770    parent_clearobslat = frame->ClearObsLat;
4771    frame->ClearObsLat = ClearObsLat;
4772 
4773    parent_getbottom = frame->GetBottom;
4774    frame->GetBottom = GetBottom;
4775 
4776    parent_getepoch = frame->GetEpoch;
4777    frame->GetEpoch = GetEpoch;
4778 
4779    parent_format = frame->Format;
4780    frame->Format = Format;
4781    parent_gap = frame->Gap;
4782    frame->Gap = Gap;
4783    parent_getdirection = frame->GetDirection;
4784    frame->GetDirection = GetDirection;
4785    parent_getdomain = frame->GetDomain;
4786    frame->GetDomain = GetDomain;
4787    parent_getsystem = frame->GetSystem;
4788    frame->GetSystem = GetSystem;
4789    parent_setsystem = frame->SetSystem;
4790    frame->SetSystem = SetSystem;
4791    parent_clearsystem = frame->ClearSystem;
4792    frame->ClearSystem = ClearSystem;
4793    parent_getalignsystem = frame->GetAlignSystem;
4794    frame->GetAlignSystem = GetAlignSystem;
4795    parent_getformat = frame->GetFormat;
4796    frame->GetFormat = GetFormat;
4797    parent_getlabel = frame->GetLabel;
4798    frame->GetLabel = GetLabel;
4799    parent_getsymbol = frame->GetSymbol;
4800    frame->GetSymbol = GetSymbol;
4801    parent_gettitle = frame->GetTitle;
4802    frame->GetTitle = GetTitle;
4803    parent_getunit = frame->GetUnit;
4804    frame->GetUnit = GetUnit;
4805    parent_match = frame->Match;
4806    frame->Match = Match;
4807    parent_overlay = frame->Overlay;
4808    frame->Overlay = Overlay;
4809    parent_subframe = frame->SubFrame;
4810    frame->SubFrame = SubFrame;
4811    parent_unformat = frame->Unformat;
4812    frame->Unformat = Unformat;
4813 
4814    parent_setdut1 = frame->SetDut1;
4815    frame->SetDut1 = SetDut1;
4816 
4817    parent_cleardut1 = frame->ClearDut1;
4818    frame->ClearDut1 = ClearDut1;
4819 
4820 /* Store replacement pointers for methods which will be over-ridden by new
4821    member functions implemented here. */
4822    frame->Angle = Angle;
4823    frame->Distance = Distance;
4824    frame->FrameGrid = FrameGrid;
4825    frame->Intersect = Intersect;
4826    frame->Norm = Norm;
4827    frame->NormBox = NormBox;
4828    frame->Resolve = Resolve;
4829    frame->ResolvePoints = ResolvePoints;
4830    frame->Offset = Offset;
4831    frame->Offset2 = Offset2;
4832    frame->ValidateSystem = ValidateSystem;
4833    frame->SystemString = SystemString;
4834    frame->SystemCode = SystemCode;
4835    frame->LineDef = LineDef;
4836    frame->LineContains = LineContains;
4837    frame->LineCrossing = LineCrossing;
4838    frame->LineOffset = LineOffset;
4839    frame->GetActiveUnit = GetActiveUnit;
4840    frame->TestActiveUnit = TestActiveUnit;
4841    frame->MatchAxesX = MatchAxesX;
4842 
4843 /* Store pointers to inherited methods that will be invoked explicitly
4844    by this class. */
4845    parent_clearformat = frame->ClearFormat;
4846    parent_setformat = frame->SetFormat;
4847    parent_testformat = frame->TestFormat;
4848 
4849 /* Declare the copy constructor, destructor and class dump
4850    function. */
4851    astSetCopy( vtab, Copy );
4852    astSetDelete( vtab, Delete );
4853    astSetDump( vtab, Dump, "SkyFrame",
4854                "Description of celestial coordinate system" );
4855 
4856 /* Initialize constants for converting between hours, degrees and
4857    radians, etc.. */
4858    LOCK_MUTEX2
4859    palDtf2r( 1, 0, 0.0, &hr2rad, &stat );
4860    palDaf2r( 1, 0, 0.0, &deg2rad, &stat );
4861    palDaf2r( 180, 0, 0.0, &pi, &stat );
4862    piby2 = 0.5*pi;
4863    UNLOCK_MUTEX2
4864 
4865 /* If we have just initialised the vtab for the current class, indicate
4866    that the vtab is now initialised, and store a pointer to the class
4867    identifier in the base "object" level of the vtab. */
4868    if( vtab == &class_vtab ) {
4869       class_init = 1;
4870       astSetVtabClassIdentifier( vtab, &(vtab->id) );
4871    }
4872 }
4873 
Intersect(AstFrame * this_frame,const double a1[2],const double a2[2],const double b1[2],const double b2[2],double cross[2],int * status)4874 static void Intersect( AstFrame *this_frame, const double a1[2],
4875                        const double a2[2], const double b1[2],
4876                        const double b2[2], double cross[2],
4877                        int *status ) {
4878 /*
4879 *  Name:
4880 *     Intersect
4881 
4882 *  Purpose:
4883 *     Find the point of intersection between two geodesic curves.
4884 
4885 *  Type:
4886 *     Private function.
4887 
4888 *  Synopsis:
4889 *     #include "skyframe.h"
4890 *     void Intersect( AstFrame *this_frame, const double a1[2],
4891 *                      const double a2[2], const double b1[2],
4892 *                      const double b2[2], double cross[2],
4893 *                      int *status )
4894 
4895 *  Class Membership:
4896 *     SkyFrame member function (over-rides the astIntersect method
4897 *     inherited from the Frame class).
4898 
4899 *  Description:
4900 *     This function finds the coordinate values at the point of
4901 *     intersection between two geodesic curves. Each curve is specified
4902 *     by two points on the curve.
4903 
4904 *  Parameters:
4905 *     this
4906 *        Pointer to the SkyFrame.
4907 *     a1
4908 *        An array of double, with one element for each Frame axis.
4909 *        This should contain the coordinates of a point on the first
4910 *        geodesic curve.
4911 *     a2
4912 *        An array of double, with one element for each Frame axis.
4913 *        This should contain the coordinates of a second point on the
4914 *        first geodesic curve.
4915 *     b1
4916 *        An array of double, with one element for each Frame axis.
4917 *        This should contain the coordinates of a point on the second
4918 *        geodesic curve.
4919 *     b2
4920 *        An array of double, with one element for each Frame axis.
4921 *        This should contain the coordinates of a second point on
4922 *        the second geodesic curve.
4923 *     cross
4924 *        An array of double, with one element for each Frame axis
4925 *        in which the coordinates of the required intersection
4926 *        point will be returned. These will be AST__BAD if the curves do
4927 *        not intersect.
4928 *     status
4929 *        Pointer to the inherited status variable.
4930 
4931 *  Notes:
4932 *     - The geodesic curve used by this function is the path of
4933 *     shortest distance between two points, as defined by the
4934 *     astDistance function.
4935 *     - This function will return "bad" coordinate values (AST__BAD)
4936 *     if any of the input coordinates has this value.
4937 *     - For SkyFrames each curve will be a great circle, and in general
4938 *     each pair of curves will intersect at two diametrically opposite
4939 *     points on the sky. The returned position is the one which is
4940 *     closest to point "a1".
4941 */
4942 
4943 /* Local Variables: */
4944    AstSkyFrame *this;          /* Pointer to the SkyFrame structure */
4945    const int *perm;            /* Pointer to axis permutation array */
4946    double aa1[ 2 ];            /* Permuted coordinates for a1 */
4947    double aa2[ 2 ];            /* Permuted coordinates for a2 */
4948    double bb1[ 2 ];            /* Permuted coordinates for b1 */
4949    double bb2[ 2 ];            /* Permuted coordinates for b2 */
4950    double cc[ 2 ];             /* Permuted coords at intersection */
4951    double d1;                  /* Cos(distance from a1 to vp) */
4952    double d2;                  /* Cos(distance from a1 to -vp) */
4953    double na[ 3 ];             /* Normal to the a1/a2 great circle */
4954    double nb[ 3 ];             /* Normal to the b1/b2 great circle */
4955    double va1[ 3 ];            /* Vector pointing at a1 */
4956    double va2[ 3 ];            /* Vector pointing at a2 */
4957    double vb1[ 3 ];            /* Vector pointing at b1 */
4958    double vb2[ 3 ];            /* Vector pointing at b2 */
4959    double vmod;                /* Length of "vp" */
4960    double vp[ 3 ];             /* Vector pointing at the intersection */
4961    double vpn[ 3 ];            /* Normalised vp */
4962    int iaxis;                  /* Axis index */
4963 
4964 /* Initialise. */
4965    cross[ 0 ] = AST__BAD;
4966    cross[ 1 ] = AST__BAD;
4967 
4968 /* Check the global error status. */
4969    if ( !astOK ) return;
4970 
4971 /* Obtain a pointer to the SkyFrame structure. */
4972    this = (AstSkyFrame *) this_frame;
4973 
4974 /* Check that all supplied values are OK. */
4975    if ( ( a1[ 0 ] != AST__BAD ) && ( a1[ 1 ] != AST__BAD ) &&
4976         ( a2[ 0 ] != AST__BAD ) && ( a2[ 1 ] != AST__BAD ) &&
4977         ( b1[ 0 ] != AST__BAD ) && ( b1[ 1 ] != AST__BAD ) &&
4978         ( b2[ 0 ] != AST__BAD ) && ( b2[ 1 ] != AST__BAD ) ) {
4979 
4980 /* Obtain a pointer to the SkyFrame's axis permutation array. */
4981       perm = astGetPerm( this );
4982       if ( astOK ) {
4983 
4984 /* Apply the axis permutation array to obtain the coordinates of
4985    the points in the required (longitude,latitude) order. */
4986          for( iaxis = 0; iaxis < 2; iaxis++ ) {
4987             aa1[ perm[ iaxis ] ] = a1[ iaxis ];
4988             aa2[ perm[ iaxis ] ] = a2[ iaxis ];
4989             bb1[ perm[ iaxis ] ] = b1[ iaxis ];
4990             bb2[ perm[ iaxis ] ] = b2[ iaxis ];
4991          }
4992 
4993 /* Convert each (lon,lat) pair into a unit length 3-vector. */
4994          palDcs2c( aa1[ 0 ], aa1[ 1 ], va1 );
4995          palDcs2c( aa2[ 0 ], aa2[ 1 ], va2 );
4996          palDcs2c( bb1[ 0 ], bb1[ 1 ], vb1 );
4997          palDcs2c( bb2[ 0 ], bb2[ 1 ], vb2 );
4998 
4999 /* Find the normal vectors to the two great cicles. */
5000          palDvxv( va1, va2, na );
5001          palDvxv( vb1, vb2, nb );
5002 
5003 /* The cross product of the two normal vectors points to one of the
5004    two diametrically opposite intersections. */
5005          palDvxv( na, nb, vp );
5006 
5007 /* Normalise the "vp" vector, also obtaining its original modulus. */
5008          palDvn( vp, vpn, &vmod );
5009          if( vmod != 0.0 ) {
5010 
5011 /* We want the intersection which is closest to "a1". The dot product
5012    gives the cos(distance) between two positions. So find the dot
5013    product between "a1" and "vpn", and then between "a1" and the point
5014    diametrically opposite "vpn". */
5015             d1 = palDvdv( vpn, va1 );
5016             vpn[ 0 ] = -vpn[ 0 ];
5017             vpn[ 1 ] = -vpn[ 1 ];
5018             vpn[ 2 ] = -vpn[ 2 ];
5019             d2 = palDvdv( vpn, va1 );
5020 
5021 /* Revert to "vpn" if it is closer to "a1". */
5022             if( d1 > d2 ) {
5023                vpn[ 0 ] = -vpn[ 0 ];
5024                vpn[ 1 ] = -vpn[ 1 ];
5025                vpn[ 2 ] = -vpn[ 2 ];
5026             }
5027 
5028 /* Convert the vector back into a (lon,lat) pair, and put the longitude
5029    into the range 0 to 2.pi. */
5030             palDcc2s( vpn, cc, cc + 1 );
5031             *cc = palDranrm( *cc );
5032 
5033 /* Permute the result coordinates to undo the effect of the SkyFrame
5034    axis permutation array. */
5035             cross[ 0 ] = cc[ perm[ 0 ] ];
5036             cross[ 1 ] = cc[ perm[ 1 ] ];
5037          }
5038       }
5039    }
5040 }
5041 
IsEquatorial(AstSystemType system,int * status)5042 static int IsEquatorial( AstSystemType system, int *status ) {
5043 /*
5044 *  Name:
5045 *     IsEquatorial
5046 
5047 *  Purpose:
5048 *     Test for an equatorial sky coordinate system.
5049 
5050 *  Type:
5051 *     Private function.
5052 
5053 *  Synopsis:
5054 *     #include "skyframe.h"
5055 *     int IsEquatorial( AstSystemType system, int *status )
5056 
5057 *  Class Membership:
5058 *     SkyFrame member function.
5059 
5060 *  Description:
5061 *     This function returns a boolean value to indicate if a sky coordinate
5062 *     system is equatorial.
5063 
5064 *  Parameters:
5065 *     system
5066 *        Code to identify the sky coordinate system.
5067 *     status
5068 *        Pointer to the inherited status variable.
5069 
5070 *  Returned Value:
5071 *     Non-zero if the sky coordinate system is equatorial, otherwise zero.
5072 
5073 *  Notes:
5074 *     -  A value of zero is returned if this function is invoked with the
5075 *     global error status set or if it should fail for any reason.
5076 */
5077 
5078 /* Local Variables: */
5079    int result;                   /* Result value to return */
5080 
5081 /* Check the global error status. */
5082    if ( !astOK ) return 0;
5083 
5084 /* Determine if the sky coordinate system is an equatorial one. Note,
5085    ICRS is not equatorial by definition, but is included here because it
5086    is normally treated as an equatorial system in terms of the axis
5087    labels, formats, etc. */
5088    result = ( ( system == AST__FK4 ) ||
5089               ( system == AST__FK4_NO_E ) ||
5090               ( system == AST__ICRS ) ||
5091               ( system == AST__FK5 ) ||
5092               ( system == AST__J2000 ) ||
5093               ( system == AST__GAPPT ) );
5094 
5095 /* Return the result. */
5096    return result;
5097 }
5098 
LineContains(AstFrame * this,AstLineDef * l,int def,double * point,int * status)5099 static int LineContains( AstFrame *this, AstLineDef *l, int def, double *point, int *status ) {
5100 /*
5101 *  Name:
5102 *     LineContains
5103 
5104 *  Purpose:
5105 *     Determine if a line contains a point.
5106 
5107 *  Type:
5108 *     Private function.
5109 
5110 *  Synopsis:
5111 *     #include "skyframe.h"
5112 *     int LineContains( AstFrame *this, AstLineDef *l, int def, double *point, int *status )
5113 
5114 *  Class Membership:
5115 *     SkyFrame member function (over-rides the protected astLineContains
5116 *     method inherited from the Frame class).
5117 
5118 *  Description:
5119 *     This function determines if the supplied point is on the supplied
5120 *     line within the supplied Frame. The start point of the line is
5121 *     considered to be within the line, but the end point is not. The tests
5122 *     are that the point of closest approach of the line to the point should
5123 *     be between the start and end, and that the distance from the point to
5124 *     the point of closest aproach should be less than 1.0E-7 of the length
5125 *     of the line.
5126 
5127 *  Parameters:
5128 *     this
5129 *        Pointer to the Frame.
5130 *     l
5131 *        Pointer to the structure defining the line.
5132 *     def
5133 *        Should be set non-zero if the "point" array was created by a
5134 *        call to astLineCrossing (in which case it may contain extra
5135 *        information following the axis values),and zero otherwise.
5136 *     point
5137 *        Point to an array containing the axis values of the point to be
5138 *        tested, possibly followed by extra cached information (see "def").
5139 *     status
5140 *        Pointer to the inherited status variable.
5141 
5142 *  Returned Value:
5143 *     A non-zero value is returned if the line contains the point.
5144 
5145 *  Notes:
5146 *     - The pointer supplied for "l" should have been created using the
5147 *     astLineDef method. These structures contained cached information about
5148 *     the lines which improve the efficiency of this method when many
5149 *     repeated calls are made. An error will be reported if the structure
5150 *     does not refer to the Frame specified by "this".
5151 *     - Zero will be returned if this function is invoked with the global
5152 *     error status set, or if it should fail for any reason.
5153 *-
5154 */
5155 
5156 /* Local Variables: */
5157    SkyLineDef *sl;               /* SkyLine information */
5158    const int *perm;              /* Pointer to axis permutation array */
5159    double *b;                    /* Pointer to Cartesian coords array */
5160    double bb[3];                 /* Buffer for Cartesian coords */
5161    double p1[2];                 /* Buffer for Spherical coords */
5162    double t1, t2;
5163    int result;                   /* Returned value */
5164 
5165 /* Initialise */
5166    result =0;
5167 
5168 /* Check the global error status. */
5169    if ( !astOK ) return result;
5170 
5171 /* Check that the line refers to the supplied Frame. */
5172    if( l->frame != this ) {
5173       astError( AST__INTER, "astLineContains(%s): The supplied line does "
5174                 "not relate to the supplied %s (AST internal programming "
5175                 "error).", status, astGetClass( this ), astGetClass( this ) );
5176 
5177 /* Check the axis values are good */
5178    } else if( point[ 0 ] != AST__BAD && point[ 1 ] != AST__BAD ){
5179 
5180 /* Get a pointer to an array holding the corresponding Cartesian coords. */
5181       if( def ) {
5182          b = point + 2;
5183 
5184       } else {
5185          perm = astGetPerm( this );
5186          if ( perm ) {
5187             p1[ perm[ 0 ] ] = point[ 0 ];
5188             p1[ perm[ 1 ] ] = point[ 1 ];
5189             palDcs2c( p1[ 0 ], p1[ 1 ], bb );
5190             b = bb;
5191          } else {
5192             b = NULL;
5193          }
5194       }
5195 
5196 /* Recast the supplied AstLineDef into a SkyLineDef to get the different
5197    structure (we know from the above check on the Frame that it is safe to
5198    do this). */
5199       sl = (SkyLineDef *) l;
5200 
5201 /* Check that the point of closest approach of the line to the point is
5202    within the limits of the line. */
5203       if( LineIncludes( sl, b, status ) ){
5204 
5205 /* Check that the point is 90 degrees away from the pole of the great
5206    circle containing the line. */
5207         t1 = palDvdv( sl->q, b );
5208         t2 = 1.0E-7*sl->length;
5209         if( t2 < 1.0E-10 ) t2 = 1.0E-10;
5210         if( fabs( t1 ) <= t2 ) result = 1;
5211      }
5212    }
5213 
5214 /* Return the result. */
5215    return result;
5216 }
5217 
LineCrossing(AstFrame * this,AstLineDef * l1,AstLineDef * l2,double ** cross,int * status)5218 static int LineCrossing( AstFrame *this, AstLineDef *l1, AstLineDef *l2,
5219                          double **cross, int *status ) {
5220 /*
5221 *  Name:
5222 *     LineCrossing
5223 
5224 *  Purpose:
5225 *     Determine if two lines cross.
5226 
5227 *  Type:
5228 *     Private function.
5229 
5230 *  Synopsis:
5231 *     #include "skyframe.h"
5232 *     int LineCrossing( AstFrame *this, AstLineDef *l1, AstLineDef *l2,
5233 *                       double **cross, int *status )
5234 
5235 *  Class Membership:
5236 *     SkyFrame member function (over-rides the protected astLineCrossing
5237 *     method inherited from the Frame class).
5238 
5239 *  Description:
5240 *     This function determines if the two suplied line segments cross,
5241 *     and if so returns the axis values at the point where they cross.
5242 *     A flag is also returned indicating if the crossing point occurs
5243 *     within the length of both line segments, or outside one or both of
5244 *     the line segments.
5245 
5246 *  Parameters:
5247 *     this
5248 *        Pointer to the Frame.
5249 *     l1
5250 *        Pointer to the structure defining the first line.
5251 *     l2
5252 *        Pointer to the structure defining the second line.
5253 *     cross
5254 *        Pointer to a location at which to put a pointer to a dynamically
5255 *        alocated array containing the axis values at the crossing. If
5256 *        NULL is supplied no such array is returned. Otherwise, the returned
5257 *        array should be freed using astFree when no longer needed. If the
5258 *        lines are parallel (i.e. do not cross) then AST__BAD is returned for
5259 *        all axis values. Note usable axis values are returned even if the
5260 *        lines cross outside the segment defined by the start and end points
5261 *        of the lines. The order of axes in the returned array will take
5262 *        account of the current axis permutation array if appropriate. Note,
5263 *        sub-classes such as SkyFrame may append extra values to the end
5264 *        of the basic frame axis values. A NULL pointer is returned if an
5265 *        error occurs.
5266 *     status
5267 *        Pointer to the inherited status variable.
5268 
5269 *  Returned Value:
5270 *     A non-zero value is returned if the lines cross at a point which is
5271 *     within the [start,end) segment of both lines. If the crossing point
5272 *     is outside this segment on either line, or if the lines are parallel,
5273 *     zero is returned. Note, the start point is considered to be inside
5274 *     the length of the segment, but the end point is outside.
5275 
5276 *  Notes:
5277 *     - The pointers supplied for "l1" and "l2" should have been created
5278 *     using the astLineDef method. These structures contained cached
5279 *     information about the lines which improve the efficiency of this method
5280 *     when many repeated calls are made. An error will be reported if
5281 *     either structure does not refer to the Frame specified by "this".
5282 *     - Zero will be returned if this function is invoked with the global
5283 *     error status set, or if it should fail for any reason.
5284 */
5285 
5286 /* Local Variables: */
5287    SkyLineDef *sl1;              /* SkyLine information for line 1 */
5288    SkyLineDef *sl2;              /* SkyLine information for line 2 */
5289    const int *perm;              /* Pointer to axis permutation array */
5290    double *crossing;             /* Pointer to returned array */
5291    double *b;                    /* Pointer to Cartesian coords */
5292    double len;                   /* Vector length */
5293    double p[ 2 ];                /* Temporary (lon,lat) pair */
5294    double temp[ 3 ];             /* Temporary vector */
5295    int result;                   /* Returned value */
5296 
5297 /* Initialise */
5298    result = 0;
5299    if( cross ) *cross = NULL;
5300 
5301 /* Check the global error status. */
5302    if ( !astOK ) return result;
5303 
5304 /* Allocate returned array (2 elements for the lon and lat values, plus 3
5305    for the corresponding (x,y,z) coords). */
5306    crossing = astMalloc( sizeof(double)*5 );
5307 
5308 /* Check that both lines refer to the supplied Frame. */
5309    if( l1->frame != this ) {
5310       astError( AST__INTER, "astLineCrossing(%s): First supplied line does "
5311                 "not relate to the supplied %s (AST internal programming "
5312                 "error).", status, astGetClass( this ), astGetClass( this ) );
5313 
5314    } else if( l2->frame != this ) {
5315       astError( AST__INTER, "astLineCrossing(%s): Second supplied line does "
5316                 "not relate to the supplied %s (AST internal programming "
5317                 "error).", status, astGetClass( this ), astGetClass( this ) );
5318 
5319 /* Recast the supplied AstLineDefs into a SkyLineDefs to get the different
5320    structure (we know from the above check on the Frame that it is safe to
5321    do this). */
5322    } else if( crossing ){
5323       sl1 = (SkyLineDef *) l1;
5324       sl2 = (SkyLineDef *) l2;
5325 
5326 /* Point of intersection of the two great circles is perpendicular to the
5327    pole vectors of both great circles. Put the Cartesian coords in elements
5328    2 to 4 of the returned array. */
5329       palDvxv( sl1->q, sl2->q, temp );
5330       b = crossing + 2;
5331       palDvn( temp, b, &len );
5332 
5333 /* See if this point is within the length of both arcs. If so return it. */
5334       if( LineIncludes( sl2, b, status ) && LineIncludes( sl1, b, status ) ) {
5335          result = 1;
5336 
5337 /* If not, see if the negated b vector is within the length of both arcs.
5338    If so return it. Otherwise, we return zero. */
5339       } else {
5340          b[ 0 ] *= -1.0;
5341          b[ 1 ] *= -1.0;
5342          b[ 2 ] *= -1.0;
5343          if( LineIncludes( sl2, b, status ) && LineIncludes( sl1, b, status ) ) result = 1;
5344       }
5345 
5346 /* Store the spherical coords in elements 0 and 1 of the returned array. */
5347       palDcc2s( b, p, p + 1 );
5348 
5349 /* Permute the spherical axis value into the order used by the SkyFrame. */
5350       perm = astGetPerm( this );
5351       if( perm ){
5352          crossing[ 0 ] = p[ perm[ 0 ] ];
5353          crossing[ 1 ] = p[ perm[ 1 ] ];
5354       }
5355    }
5356 
5357 /* If an error occurred, return 0. */
5358    if( !astOK ) {
5359       result = 0;
5360       crossing = astFree( crossing );
5361    }
5362 
5363 /* Return the array */
5364    if( cross ) {
5365       *cross = crossing;
5366    } else {
5367       crossing = astFree( crossing );
5368    }
5369 
5370 /* Return the result. */
5371    return result;
5372 }
5373 
LineDef(AstFrame * this,const double start[2],const double end[2],int * status)5374 static AstLineDef *LineDef( AstFrame *this, const double start[2],
5375                             const double end[2], int *status ) {
5376 /*
5377 *  Name:
5378 *     LineDef
5379 
5380 *  Purpose:
5381 *     Creates a structure describing a line segment in a 2D Frame.
5382 
5383 *  Type:
5384 *     Private function.
5385 
5386 *  Synopsis:
5387 *     #include "skyframe.h"
5388 *     AstLineDef *LineDef( AstFrame *this, const double start[2],
5389 *                          const double end[2], int *status )
5390 
5391 *  Class Membership:
5392 *     SkyFrame member function (over-rides the protected astLineDef
5393 *     method inherited from the Frame class).
5394 
5395 *  Description:
5396 *     This function creates a structure containing information describing a
5397 *     given line segment within the supplied 2D Frame. This may include
5398 *     information which allows other methods such as astLineCrossing to
5399 *     function more efficiently. Thus the returned structure acts as a
5400 *     cache to store intermediate values used by these other methods.
5401 
5402 *  Parameters:
5403 *     this
5404 *        Pointer to the Frame. Must have 2 axes.
5405 *     start
5406 *        An array of 2 doubles marking the start of the line segment.
5407 *     end
5408 *        An array of 2 doubles marking the end of the line segment.
5409 *     status
5410 *        Pointer to the inherited status variable.
5411 
5412 *  Returned Value:
5413 *     Pointer to the memory structure containing the description of the
5414 *     line. This structure should be freed using astFree when no longer
5415 *     needed. A NULL pointer is returned (without error) if any of the
5416 *     supplied axis values are AST__BAD.
5417 
5418 *  Notes:
5419 *     - A null pointer will be returned if this function is invoked
5420 *     with the global error status set, or if it should fail for any
5421 *     reason.
5422 */
5423 
5424 /* Local Variables: */
5425    SkyLineDef *result;           /* Returned value */
5426    const int *perm;              /* Axis permutation array */
5427    double le;                    /* Length of end vector */
5428    double len;                   /* Permuted point1 coordinates */
5429    double ls;                    /* Length of start vector */
5430    double p1[ 2 ];               /* Permuted point1 coordinates */
5431    double p2[ 2 ];               /* Permuted point2 coordinates */
5432    double temp[3];               /* Cartesian coords at offset position */
5433 
5434 /* Initialise */
5435    result = NULL;
5436 
5437 /* Check the global error status. */
5438    if ( !astOK ) return NULL;
5439 
5440 /* Check the axis values are good */
5441    if( start[ 0 ] != AST__BAD && start[ 1 ] != AST__BAD &&
5442        end[ 0 ] != AST__BAD && end[ 1 ] != AST__BAD ) {
5443 
5444 /* Allocate memory for the returned structure. */
5445       result = astMalloc( sizeof( SkyLineDef ) );
5446 
5447 /* Obtain a pointer to the SkyFrame's axis permutation array. */
5448       perm = astGetPerm( this );
5449       if ( perm ) {
5450 
5451 /* Apply the axis permutation array to obtain the coordinates of the two
5452    input points in the required (longitude,latitude) order. */
5453          p1[ perm[ 0 ] ] = start[ 0 ];
5454          p1[ perm[ 1 ] ] = start[ 1 ];
5455          p2[ perm[ 0 ] ] = end[ 0 ];
5456          p2[ perm[ 1 ] ] = end[ 1 ];
5457 
5458 /* Convert each point into a 3-vector of unit length and store in the
5459    returned structure. */
5460          palDcs2c( p1[ 0 ], p1[ 1 ], result->start );
5461          palDcs2c( p2[ 0 ], p2[ 1 ], result->end );
5462 
5463 /* Calculate the great circle distance between the points in radians and
5464    store in the result structure. Correct for rounding errors in palDcs2c
5465    that can result in the vectors not having exactly unit length. */
5466          result->length = palDvdv( result->start, result->end );
5467          ls = result->start[0]*result->start[0] +
5468               result->start[1]*result->start[1] +
5469               result->start[2]*result->start[2];
5470          le = result->end[0]*result->end[0] +
5471               result->end[1]*result->end[1] +
5472               result->end[2]*result->end[2];
5473          result->length = acos( result->length/sqrt( ls*le ) );
5474 
5475 /* Find a unit vector representing the pole of the system in which the
5476    equator is given by the great circle. This is such that going the
5477    short way from the start to the end, the pole is to the left of the
5478    line as seen by the observer (i.e. from the centre of the sphere).
5479    If the line has zero length, or 180 degrees length, the pole is
5480    undefined, so we use an arbitrary value. */
5481          if( result->length == 0.0 || result->length > pi - 5.0E-11 ) {
5482             palDcs2c( p1[ 0 ] + 0.01, p1[ 1 ] + 0.01, temp );
5483             palDvxv( temp, result->start, result->dir );
5484          } else {
5485             palDvxv( result->end, result->start, result->dir );
5486          }
5487          palDvn( result->dir, result->q, &len );
5488 
5489 /* Also store a point which is 90 degrees along the great circle from the
5490    start. */
5491          palDvxv( result->start, result->q, result->dir );
5492 
5493 /* Store a pointer to the defining SkyFrame. */
5494          result->frame = this;
5495 
5496 /* Indicate that the line is considered to be terminated at the start and
5497    end points. */
5498          result->infinite = 0;
5499 
5500 /* Normalise the spherical start and end positions stored in the returned
5501    structure. */
5502          result->start_2d[ 0 ] = start[ 0 ];
5503          result->start_2d[ 1 ] = start[ 1 ];
5504          result->end_2d[ 0 ] = end[ 0 ];
5505          result->end_2d[ 1 ] = end[ 1 ];
5506 
5507          astNorm( this, result->start_2d );
5508          astNorm( this, result->end_2d );
5509       }
5510    }
5511 
5512 /* Free the returned pointer if an error occurred. */
5513    if( !astOK ) result = astFree( result );
5514 
5515 /* Return a pointer to the output structure. */
5516    return (AstLineDef *) result;
5517 }
5518 
LineIncludes(SkyLineDef * l,double point[3],int * status)5519 static int LineIncludes( SkyLineDef *l, double point[3], int *status ) {
5520 /*
5521 *  Name:
5522 *     LineIncludes
5523 
5524 *  Purpose:
5525 *     Determine if a line includes a point which is known to be in the
5526 *     great circle.
5527 
5528 *  Type:
5529 *     Private function.
5530 
5531 *  Synopsis:
5532 *     #include "skyframe.h"
5533 *     int LineIncludes( SkyLineDef *l, double point[3], int *status )
5534 
5535 *  Class Membership:
5536 *     SkyFrame member function (over-rides the protected astLineIncludes
5537 *     method inherited from the Frame class).
5538 
5539 *  Description:
5540 *     The supplied point is assumed to be a point on the great circle of
5541 *     which the supplied line is a segment. This function returns true if
5542 *     "point" is within the bounds of the segment (the end point of the
5543 *     line is assumed * not to be part of the segment).
5544 
5545 *  Parameters:
5546 *     l
5547 *        Pointer to the structure defining the line.
5548 *     point
5549 *        An array holding the Cartesian coords of the point to be tested.
5550 *     status
5551 *        Pointer to the inherited status variable.
5552 
5553 *  Returned Value:
5554 *     A non-zero value is returned if the line includes the point.
5555 
5556 *  Notes:
5557 *     - Zero will be returned if this function is invoked with the global
5558 *     error status set, or if it should fail for any reason.
5559 */
5560 
5561 /* Local Variables: */
5562    double t1, t2, t3;
5563 
5564 /* Check the global error status. */
5565    if ( !astOK ) return 0;
5566 
5567 /* If the line is of infite length, it is assumed to include the supplied
5568    point. */
5569    if( l->infinite ) return 1;
5570 
5571 /* Otherwise, get the unsigned distance of the point from the start of the
5572    line in the range 0 - 180 degs. Check it is less than the line length.
5573    Then check that the point is not more than 90 degs away from the quarter
5574    point. */
5575    t1 = palDvdv( l->start, point );
5576    t2 = acos( t1 );
5577    t3 = palDvdv( l->dir, point );
5578    return ( ((l->length > 0) ? t2 < l->length : t2 == 0.0 ) && t3 >= -1.0E-8 );
5579 }
5580 
LineOffset(AstFrame * this,AstLineDef * line,double par,double prp,double point[2],int * status)5581 static void LineOffset( AstFrame *this, AstLineDef *line, double par,
5582                         double prp, double point[2], int *status ){
5583 /*
5584 *  Name:
5585 *     LineOffset
5586 
5587 *  Purpose:
5588 *     Find a position close to a line.
5589 
5590 *  Type:
5591 *     Private function.
5592 
5593 *  Synopsis:
5594 *     #include "skyframe.h"
5595 *     void LineOffset( AstFrame *this, AstLineDef *line, double par,
5596 *                      double prp, double point[2], int *status )
5597 
5598 *  Class Membership:
5599 *     SkyFrame member function (over-rides the protected astLineOffset
5600 *     method inherited from the Frame class).
5601 
5602 *  Description:
5603 *     This function returns a position formed by moving a given distance along
5604 *     the supplied line, and then a given distance away from the supplied line.
5605 
5606 *  Parameters:
5607 *     this
5608 *        Pointer to the Frame.
5609 *     line
5610 *        Pointer to the structure defining the line.
5611 *     par
5612 *        The distance to move along the line from the start towards the end.
5613 *     prp
5614 *        The distance to move at right angles to the line. Positive
5615 *        values result in movement to the left of the line, as seen from
5616 *        the observer, when moving from start towards the end.
5617 *     status
5618 *        Pointer to the inherited status variable.
5619 
5620 *  Notes:
5621 *     - The pointer supplied for "line" should have been created using the
5622 *     astLineDef method. This structure contains cached information about the
5623 *     line which improves the efficiency of this method when many repeated
5624 *     calls are made. An error will be reported if the structure does not
5625 *     refer to the Frame specified by "this".
5626 *-
5627 */
5628 
5629 /* Local Variables; */
5630     SkyLineDef *sl;
5631     const int *perm;
5632     double c;
5633     double nx;
5634     double ny;
5635     double nz;
5636     double p[2];
5637     double s;
5638     double v[3];
5639 
5640 /* Check the global error status. */
5641    if ( !astOK ) return;
5642 
5643 /* Check that the line refers to the supplied Frame. */
5644    if( line->frame != this ) {
5645       astError( AST__INTER, "astLineOffset(%s): The supplied line does "
5646                 "not relate to the supplied %s (AST internal programming "
5647                 "error).", status, astGetClass( this ), astGetClass( this ) );
5648 
5649 /* This implementation uses spherical geometry. */
5650    } else {
5651 
5652 /* Get a pointer to the SkyLineDef structure. */
5653       sl = (SkyLineDef *) line;
5654 
5655 /* Move a distance par from start to end. */
5656       c = cos( par );
5657       s = sin( par );
5658       nx = c * sl->start[ 0 ] + s * sl->dir[ 0 ];
5659       ny = c * sl->start[ 1 ] + s * sl->dir[ 1 ];
5660       nz = c * sl->start[ 2 ] + s * sl->dir[ 2 ];
5661 
5662 /* Move a distance prp from this point towards the pole point. */
5663       if( prp != 0.0 ) {
5664          c = cos( prp );
5665          s = sin( prp );
5666          v[ 0 ] = c * nx + s * sl->q[ 0 ];
5667          v[ 1 ] = c * ny + s * sl->q[ 1 ];
5668          v[ 2 ] = c * nz + s * sl->q[ 2 ];
5669       } else {
5670          v[ 0 ] = nx;
5671          v[ 1 ] = ny;
5672          v[ 2 ] = nz;
5673       }
5674 
5675 /* Convert to lon/lat */
5676       palDcc2s( v, p, p + 1 );
5677 
5678 /* Permute the spherical axis value into the order used by the SkyFrame. */
5679       perm = astGetPerm( this );
5680       if( perm ){
5681          point[ 0 ] = p[ perm[ 0 ] ];
5682          point[ 1 ] = p[ perm[ 1 ] ];
5683       }
5684    }
5685 }
5686 
MakeSkyMapping(AstSkyFrame * target,AstSkyFrame * result,AstSystemType align_sys,AstMapping ** map,int * status)5687 static int MakeSkyMapping( AstSkyFrame *target, AstSkyFrame *result,
5688                            AstSystemType align_sys, AstMapping **map, int *status ) {
5689 /*
5690 *  Name:
5691 *     MakeSkyMapping
5692 
5693 *  Purpose:
5694 *     Generate a Mapping between two SkyFrames.
5695 
5696 *  Type:
5697 *     Private function.
5698 
5699 *  Synopsis:
5700 *     #include "skyframe.h"
5701 *     int MakeSkyMapping( AstSkyFrame *target, AstSkyFrame *result,
5702 *                         AstSystemType align_sys, AstMapping **map, int *status )
5703 
5704 *  Class Membership:
5705 *     SkyFrame member function.
5706 
5707 *  Description:
5708 *     This function takes two SkyFrames and generates a Mapping that
5709 *     converts between them, taking account of differences in their
5710 *     coordinate systems, equinox value, epoch, etc. (but not allowing
5711 *     for any axis permutations).
5712 
5713 *  Parameters:
5714 *     target
5715 *        Pointer to the first SkyFrame.
5716 *     result
5717 *        Pointer to the second SkyFrame.
5718 *     align_sys
5719 *        The system in which to align the two SkyFrames.
5720 *     map
5721 *        Pointer to a location which is to receive a pointer to the
5722 *        returned Mapping. The forward transformation of this Mapping
5723 *        will convert from "target" coordinates to "result"
5724 *        coordinates, and the inverse transformation will convert in
5725 *        the opposite direction (all coordinate values in radians).
5726 *     status
5727 *        Pointer to the inherited status variable.
5728 
5729 *  Returned Value:
5730 *     Non-zero if the Mapping could be generated, or zero if the two
5731 *     SkyFrames are sufficiently un-related that no meaningful Mapping
5732 *     can be produced.
5733 
5734 *  Notes:
5735 *     A value of zero is returned if this function is invoked with the
5736 *     global error status set or if it should fail for any reason.
5737 */
5738 
5739 /* Local Constants: */
5740 #define MAX_ARGS 4               /* Max arguments for an SlaMap conversion */
5741 
5742 /* Local Variables: */
5743    AstMapping *omap;             /* Mapping from coorinates to offsets */
5744    AstMapping *tmap2;            /* Temporary Mapping */
5745    AstMapping *tmap;             /* Temporary Mapping */
5746    AstSlaMap *slamap;            /* Pointer to SlaMap */
5747    AstSystemType result_system;  /* Code to identify result coordinate system */
5748    AstSystemType system;         /* Code to identify coordinate system */
5749    AstSystemType target_system;  /* Code to identify target coordinate system */
5750    double args[ MAX_ARGS ];      /* Conversion argument array */
5751    double epoch;                 /* Epoch as Modified Julian Date */
5752    double epoch_B;               /* Besselian epoch as decimal years */
5753    double epoch_J;               /* Julian epoch as decimal years */
5754    double equinox;               /* Equinox as Modified Julian Date */
5755    double equinox_B;             /* Besselian equinox as decimal years */
5756    double equinox_J;             /* Julian equinox as decimal years */
5757    double diurab;                /* Magnitude of diurnal aberration vector */
5758    double last;                  /* Local Apparent Sidereal Time */
5759    double lat;                   /* Observers latitude */
5760    double result_epoch;          /* Result frame Epoch */
5761    double result_equinox;        /* Result frame Epoch */
5762    double target_epoch;          /* Target frame Epoch */
5763    double target_equinox;        /* Target frame Epoch */
5764    int match;                    /* Mapping can be generated? */
5765    int step1;                    /* Convert target to FK5 J2000? */
5766    int step2;                    /* Convert FK5 J2000 to align sys? */
5767    int step3;                    /* Convert align sys to FK5 J2000? */
5768    int step4;                    /* Convert FK5 J2000 to result? */
5769 
5770 /* Check the global error status. */
5771    if ( !astOK ) return 0;
5772 
5773 /* Initialise the returned values. */
5774    match = 1;
5775    *map = NULL;
5776 
5777 /* Initialise variables to avoid "used of uninitialised variable"
5778    messages from dumb compilers. */
5779    epoch_B = 0.0;
5780    epoch_J = 0.0;
5781    equinox_B = 0.0;
5782    equinox_J = 0.0;
5783 
5784 /* Get the two epoch values. */
5785    result_epoch = astGetEpoch( result );
5786    target_epoch = astGetEpoch( target );
5787 
5788 /* Get the two equinox values. */
5789    result_equinox = astGetEquinox( result );
5790    target_equinox = astGetEquinox( target );
5791 
5792 /* Get the two system values. */
5793    result_system = astGetSystem( result );
5794    target_system = astGetSystem( target );
5795 
5796 /* If either system is not references to the equinox given by the Equinox
5797    attribute, then use the equinox of the other system rather than
5798    adopting the arbitrary default of J2000. */
5799    if( !EQREF(result_system) ) result_equinox = target_equinox;
5800    if( !EQREF(target_system) ) target_equinox = result_equinox;
5801 
5802 /* If both systems are unknown, assume they are the same. Return a UnitMap.
5803    We need to do this, otherwise a simple change of Title (for instance)
5804    will result in a FrameSet whose current Frame has System=AST__UNKNOWN
5805    loosing its integrity. */
5806    if( target_system == AST__UNKNOWN && result_system == AST__UNKNOWN ) {
5807       *map = (AstMapping *) astUnitMap( 2, "", status );
5808       return 1;
5809    }
5810 
5811 /* The total Mapping is divided into two parts in series; the first part
5812    converts from the target SkyFrame to the alignment system, using the
5813    Epoch and Equinox of the target Frame, the second part converts from
5814    the alignment system to the result SkyFrame, using the Epoch and Equinox
5815    of the result Frame. Each of these parts has an arbitrary input and an
5816    output system, and therefore could be implemented using a collection
5817    of NxN conversions. To reduce the complexity, each part is implement
5818    by converting from the input system to FK5 J2000, and then from FK5
5819    J2000 to the output system. This scheme required only N conversions
5820    rather than NxN. Thus overall the total Mapping is made up of 4 steps
5821    in series. Some of these steps may be ommitted if they are effectively
5822    a UnitMap. Determine which steps need to be included. Assume all need
5823    to be done to begin with. */
5824    step1 = 1;
5825    step2 = 1;
5826    step3 = 1;
5827    step4 = 1;
5828 
5829 /* If the target system is the same as the alignment system, neither of the
5830    first 2 steps need be done. */
5831    if( target_system == align_sys ) {
5832       step1 = 0;
5833       step2 = 0;
5834    }
5835 
5836 /* If the result system is the same as the alignment system, neither of the
5837    last 2 steps need be done. */
5838    if( result_system == align_sys ) {
5839       step3 = 0;
5840       step4 = 0;
5841    }
5842 
5843 /* If the two epochs are the same, or if the alignment system is FK5 J2000,
5844    steps 2 and 3 are not needed. */
5845    if( step2 && step3 ) {
5846       if( align_sys == AST__FK5 || result_epoch == target_epoch ) {
5847       step2 = 0;
5848       step3 = 0;
5849       }
5850    }
5851 
5852 /* None are needed if the target and result SkyFrames have the same
5853    System, Epoch and Equinox. */
5854    if(  result_system == target_system &&
5855         result_epoch == target_epoch &&
5856         result_equinox == target_equinox ) {
5857       step1 = 0;
5858       step2 = 0;
5859       step3 = 0;
5860       step4 = 0;
5861    }
5862 
5863 /* Create an initial (null) SlaMap. */
5864    slamap = astSlaMap( 0, "", status );
5865 
5866 /* Define local macros as shorthand for adding sky coordinate
5867    conversions to this SlaMap.  Each macro simply stores details of
5868    the additional arguments in the "args" array and then calls
5869    astSlaAdd. The macros differ in the number of additional argument
5870    values. */
5871    #define TRANSFORM_0(cvt) \
5872            astSlaAdd( slamap, cvt, NULL );
5873 
5874    #define TRANSFORM_1(cvt,arg0) \
5875            args[ 0 ] = arg0; \
5876            astSlaAdd( slamap, cvt, args );
5877 
5878    #define TRANSFORM_2(cvt,arg0,arg1) \
5879            args[ 0 ] = arg0; \
5880            args[ 1 ] = arg1; \
5881            astSlaAdd( slamap, cvt, args );
5882 
5883    #define TRANSFORM_3(cvt,arg0,arg1,arg2) \
5884            args[ 0 ] = arg0; \
5885            args[ 1 ] = arg1; \
5886            args[ 2 ] = arg2; \
5887            astSlaAdd( slamap, cvt, args );
5888 
5889    #define TRANSFORM_4(cvt,arg0,arg1,arg2,arg3) \
5890            args[ 0 ] = arg0; \
5891            args[ 1 ] = arg1; \
5892            args[ 2 ] = arg2; \
5893            args[ 3 ] = arg3; \
5894            astSlaAdd( slamap, cvt, args );
5895 
5896 /* Convert _to_ FK5 J2000.0 coordinates. */
5897 /* ===================================== */
5898 /* The overall conversion is formulated in four phases. In this first
5899    phase, we convert from the target coordinate system to intermediate sky
5900    coordinates expressed using the FK5 system, mean equinox J2000.0. */
5901 
5902 /* Obtain the sky coordinate system, equinox, epoch, etc, of the target
5903    SkyFrame. */
5904    system = target_system;
5905    equinox = target_equinox;
5906    epoch = target_epoch;
5907    last = GetLAST( target, status );
5908    diurab = GetDiurab( target, status );
5909    lat = astGetObsLat( target );
5910    if( astOK && step1 ) {
5911 
5912 /* Convert the equinox and epoch values (stored as Modified Julian
5913    Dates) into the equivalent Besselian and Julian epochs (as decimal
5914    years). */
5915       equinox_B = palEpb( equinox );
5916       equinox_J = palEpj( equinox );
5917       epoch_B = palEpb( epoch );
5918       epoch_J = palEpj( epoch );
5919 
5920 /* Formulate the conversion... */
5921 
5922 /* From FK4. */
5923 /* --------- */
5924 /* If necessary, apply the old-style FK4 precession model to bring the
5925    equinox to B1950.0, with rigorous handling of the E-terms of
5926    aberration. Then convert directly to FK5 J2000.0 coordinates. */
5927       if ( system == AST__FK4 ) {
5928          VerifyMSMAttrs( target, result, 1, "Equinox Epoch", "astMatch", status );
5929          if ( equinox_B != 1950.0 ) {
5930             TRANSFORM_1( "SUBET", equinox_B )
5931             TRANSFORM_2( "PREBN", equinox_B, 1950.0 )
5932             TRANSFORM_1( "ADDET", 1950.0 )
5933          }
5934          TRANSFORM_1( "FK45Z", epoch_B )
5935 
5936 /* From FK4 with no E-terms. */
5937 /* ------------------------- */
5938 /* This is the same as above, except that we do not need to subtract
5939       the E-terms initially as they are already absent. */
5940       } else if ( system == AST__FK4_NO_E ) {
5941          VerifyMSMAttrs( target, result, 1, "Equinox Epoch", "astMatch", status );
5942          if ( equinox_B != 1950.0 ) {
5943             TRANSFORM_2( "PREBN", equinox_B, 1950.0 )
5944          }
5945          TRANSFORM_1( "ADDET", 1950.0 )
5946          TRANSFORM_1( "FK45Z", epoch_B )
5947 
5948 /* From FK5. */
5949 /* --------- */
5950 /* We simply need to apply a precession correction for the change of
5951    equinox.  Omit even this if the equinox is already J2000.0. */
5952       } else if ( system == AST__FK5 ) {
5953          VerifyMSMAttrs( target, result, 1, "Equinox", "astMatch", status );
5954          if ( equinox_J != 2000.0 ) {
5955             TRANSFORM_2( "PREC", equinox_J, 2000.0 );
5956          }
5957 
5958 /* From J2000. */
5959 /* ----------- */
5960 /* Convert from J2000 to ICRS, then from ICRS to FK5. */
5961       } else if ( system == AST__J2000 ) {
5962          VerifyMSMAttrs( target, result, 1, "Epoch", "astMatch", status );
5963          TRANSFORM_0( "J2000H" )
5964          TRANSFORM_1( "HFK5Z", epoch_J );
5965 
5966 /* From geocentric apparent. */
5967 /* ------------------------- */
5968 /* This conversion is supported directly by SLALIB. */
5969       } else if ( system == AST__GAPPT ) {
5970          VerifyMSMAttrs( target, result, 1, "Epoch", "astMatch", status );
5971          TRANSFORM_2( "AMP", epoch, 2000.0 )
5972 
5973 /* From ecliptic coordinates. */
5974 /* -------------------------- */
5975 /* This conversion is supported directly by SLALIB. */
5976       } else if ( system == AST__ECLIPTIC ) {
5977          VerifyMSMAttrs( target, result, 1, "Equinox", "astMatch", status );
5978          TRANSFORM_1( "ECLEQ", equinox )
5979 
5980 /* From helio-ecliptic coordinates. */
5981 /* -------------------------------- */
5982       } else if ( system == AST__HELIOECLIPTIC ) {
5983          VerifyMSMAttrs( target, result, 1, "Epoch", "astMatch", status );
5984          TRANSFORM_1( "HEEQ", epoch )
5985 
5986 /* From galactic coordinates. */
5987 /* -------------------------- */
5988 /* This conversion is supported directly by SLALIB. */
5989       } else if ( system == AST__GALACTIC ) {
5990          TRANSFORM_0( "GALEQ" )
5991 
5992 /* From ICRS. */
5993 /* ---------- */
5994 /* This conversion is supported directly by SLALIB. */
5995       } else if ( system == AST__ICRS ) {
5996          VerifyMSMAttrs( target, result, 1, "Epoch", "astMatch", status );
5997          TRANSFORM_1( "HFK5Z", epoch_J );
5998 
5999 /* From supergalactic coordinates. */
6000 /* ------------------------------- */
6001 /* Convert to galactic coordinates and then to FK5 J2000.0
6002       equatorial. */
6003       } else if ( system == AST__SUPERGALACTIC ) {
6004          TRANSFORM_0( "SUPGAL" )
6005          TRANSFORM_0( "GALEQ" )
6006 
6007 /* From AzEl. */
6008 /* ---------- */
6009 /* Rotate from horizon to equator (H2E), shift hour angle into RA (H2R),
6010       go from geocentric apparent to FK5 J2000. */
6011       } else if ( system == AST__AZEL ) {
6012          VerifyMSMAttrs( target, result, 1, "ObsLon ObsLat Epoch", "astMatch", status );
6013          TRANSFORM_2( "H2E", lat, diurab )
6014          TRANSFORM_1( "H2R", last )
6015          TRANSFORM_2( "AMP", epoch, 2000.0 )
6016 
6017 /* From unknown coordinates. */
6018 /* ------------------------- */
6019 /* No conversion is possible. */
6020       } else if ( system == AST__UNKNOWN ) {
6021          match = 0;
6022       }
6023    }
6024 
6025 /* Convert _from_ FK5 J2000.0 coordinates _to_ the alignment system. */
6026 /* ============================================================ */
6027 /* In this second phase, we convert to the system given by the align_sys
6028    argument (if required), still using the properties of the target Frame. */
6029    if ( astOK && match && step2 ) {
6030 
6031 /* Align in FK4. */
6032 /* --------------- */
6033 /* Convert directly from FK5 J2000.0 to FK4 B1950.0 coordinates at the
6034    appropriate epoch. Then, if necessary, apply the old-style FK4
6035    precession model to bring the equinox to that required, with
6036    rigorous handling of the E-terms of aberration. */
6037       if ( align_sys == AST__FK4 ) {
6038          VerifyMSMAttrs( target, result, 1, "Equinox Epoch", "astMatch", status );
6039          TRANSFORM_1( "FK54Z", epoch_B )
6040          if ( equinox_B != 1950.0 ) {
6041             TRANSFORM_1( "SUBET", 1950.0 )
6042             TRANSFORM_2( "PREBN", 1950.0, equinox_B )
6043             TRANSFORM_1( "ADDET", equinox_B )
6044          }
6045 
6046 /* Align in FK4 with no E-terms. */
6047 /* ------------------------------- */
6048 /* This is the same as above, except that we do not need to add the
6049    E-terms at the end. */
6050       } else if ( align_sys == AST__FK4_NO_E ) {
6051          VerifyMSMAttrs( target, result, 1, "Equinox Epoch", "astMatch", status );
6052          TRANSFORM_1( "FK54Z", epoch_B )
6053          TRANSFORM_1( "SUBET", 1950.0 )
6054          if ( equinox_B != 1950.0 ) {
6055             TRANSFORM_2( "PREBN", 1950.0, equinox_B )
6056          }
6057 
6058 /* Align in FK5. */
6059 /* ------------- */
6060 /* We simply need to apply a precession correction for the change of
6061    equinox.  Omit even this if the required equinox is J2000.0. */
6062       } else if ( align_sys == AST__FK5 ) {
6063          VerifyMSMAttrs( target, result, 1, "Equinox", "astMatch", status );
6064          if ( equinox_J != 2000.0 ) {
6065             TRANSFORM_2( "PREC", 2000.0, equinox_J )
6066          }
6067 
6068 /* Align in J2000. */
6069 /* --------------- */
6070 /* Mov from FK5 to ICRS, and from ICRS to J2000. */
6071       } else if ( align_sys == AST__J2000 ) {
6072          VerifyMSMAttrs( target, result, 1, "Epoch", "astMatch", status );
6073          TRANSFORM_1( "FK5HZ", epoch_J )
6074          TRANSFORM_0( "HJ2000" )
6075 
6076 /* Align in geocentric apparent. */
6077 /* ------------------------------- */
6078 /* This conversion is supported directly by SLALIB. */
6079       } else if ( align_sys == AST__GAPPT ) {
6080          VerifyMSMAttrs( target, result, 1, "Epoch", "astMatch", status );
6081          TRANSFORM_2( "MAP", 2000.0, epoch )
6082 
6083 /* Align in ecliptic coordinates. */
6084 /* -------------------------------- */
6085 /* This conversion is supported directly by SLALIB. */
6086       } else if ( align_sys == AST__ECLIPTIC ) {
6087          VerifyMSMAttrs( target, result, 1, "Equinox", "astMatch", status );
6088          TRANSFORM_1( "EQECL", equinox )
6089 
6090 /* Align in helio-ecliptic coordinates. */
6091 /* ------------------------------------ */
6092       } else if ( align_sys == AST__HELIOECLIPTIC ) {
6093          VerifyMSMAttrs( target, result, 1, "Epoch", "astMatch", status );
6094          TRANSFORM_1( "EQHE", epoch )
6095 
6096 /* Align in galactic coordinates. */
6097 /* -------------------------------- */
6098 /* This conversion is supported directly by SLALIB. */
6099       } else if ( align_sys == AST__GALACTIC ) {
6100          TRANSFORM_0( "EQGAL" )
6101 
6102 /* Align in ICRS. */
6103 /* -------------- */
6104 /* This conversion is supported directly by SLALIB. */
6105       } else if ( align_sys == AST__ICRS ) {
6106          VerifyMSMAttrs( target, result, 1, "Epoch", "astMatch", status );
6107          TRANSFORM_1( "FK5HZ", epoch_J )
6108 
6109 /* Align in supergalactic coordinates. */
6110 /* ------------------------------------- */
6111 /* Convert to galactic coordinates and then to supergalactic. */
6112       } else if ( align_sys == AST__SUPERGALACTIC ) {
6113          TRANSFORM_0( "EQGAL" )
6114          TRANSFORM_0( "GALSUP" )
6115 
6116 /* Align in AzEl coordinates. */
6117 /* -------------------------- */
6118 /* Go from FK5 J2000 to geocentric apparent (MAP), shift RA into hour angle
6119    (R2H), rotate from equator to horizon (E2H). */
6120       } else if ( align_sys == AST__AZEL ) {
6121          VerifyMSMAttrs( target, result, 1, "ObsLon ObsLat Epoch", "astMatch", status );
6122          TRANSFORM_2( "MAP", 2000.0, epoch )
6123          TRANSFORM_1( "R2H", last )
6124          TRANSFORM_2( "E2H", lat, diurab )
6125 
6126 /* Align in unknown coordinates. */
6127 /* ------------------------------- */
6128 /* No conversion is possible. */
6129       } else if ( align_sys == AST__UNKNOWN ) {
6130          match = 0;
6131       }
6132    }
6133 
6134 /* Convert _from_ the alignment system _to_ FK5 J2000.0 coordinates */
6135 /* =========================================================== */
6136 /* In this third phase, we convert from the alignment system (if required)
6137    to the intermediate FK5 J2000 system, using the properties of the
6138    result SkyFrame. */
6139 
6140 /* Obtain the sky coordinate system, equinox, epoch, etc, of the result
6141    SkyFrame. */
6142    system = result_system;
6143    equinox = result_equinox;
6144    epoch = result_epoch;
6145    diurab = GetDiurab( result, status );
6146    last = GetLAST( result, status );
6147    lat = astGetObsLat( result );
6148 
6149 /* Convert the equinox and epoch values (stored as Modified Julian
6150    Dates) into the equivalent Besselian and Julian epochs (as decimal
6151    years). */
6152    if( astOK ) {
6153       equinox_B = palEpb( equinox );
6154       equinox_J = palEpj( equinox );
6155       epoch_B = palEpb( epoch );
6156       epoch_J = palEpj( epoch );
6157    }
6158 
6159 /* Check we need to do the conversion. */
6160    if ( astOK && match && step3 ) {
6161 
6162 /* Formulate the conversion... */
6163 
6164 /* From FK4. */
6165 /* --------- */
6166 /* If necessary, apply the old-style FK4 precession model to bring the
6167    equinox to B1950.0, with rigorous handling of the E-terms of
6168    aberration. Then convert directly to FK5 J2000.0 coordinates. */
6169       if ( align_sys == AST__FK4 ) {
6170          VerifyMSMAttrs( target, result, 3, "Equinox Epoch", "astMatch", status );
6171          if ( equinox_B != 1950.0 ) {
6172             TRANSFORM_1( "SUBET", equinox_B )
6173             TRANSFORM_2( "PREBN", equinox_B, 1950.0 )
6174             TRANSFORM_1( "ADDET", 1950.0 )
6175          }
6176          TRANSFORM_1( "FK45Z", epoch_B )
6177 
6178 /* From FK4 with no E-terms. */
6179 /* ------------------------- */
6180 /* This is the same as above, except that we do not need to subtract
6181    the E-terms initially as they are already absent. */
6182       } else if ( align_sys == AST__FK4_NO_E ) {
6183          VerifyMSMAttrs( target, result, 3, "Equinox Epoch", "astMatch", status );
6184          if ( equinox_B != 1950.0 ) {
6185             TRANSFORM_2( "PREBN", equinox_B, 1950.0 )
6186          }
6187          TRANSFORM_1( "ADDET", 1950.0 )
6188          TRANSFORM_1( "FK45Z", epoch_B )
6189 
6190 /* From FK5. */
6191 /* --------- */
6192 /* We simply need to apply a precession correction for the change of
6193    equinox.  Omit even this if the equinox is already J2000.0. */
6194       } else if ( align_sys == AST__FK5 ) {
6195          VerifyMSMAttrs( target, result, 3, "Equinox", "astMatch", status );
6196          if ( equinox_J != 2000.0 ) {
6197             TRANSFORM_2( "PREC", equinox_J, 2000.0 );
6198          }
6199 
6200 /* From geocentric apparent. */
6201 /* ------------------------- */
6202 /* This conversion is supported directly by SLALIB. */
6203       } else if ( align_sys == AST__GAPPT ) {
6204          VerifyMSMAttrs( target, result, 3, "Epoch", "astMatch", status );
6205          TRANSFORM_2( "AMP", epoch, 2000.0 )
6206 
6207 /* From ecliptic coordinates. */
6208 /* -------------------------- */
6209 /* This conversion is supported directly by SLALIB. */
6210       } else if ( align_sys == AST__ECLIPTIC ) {
6211          VerifyMSMAttrs( target, result, 3, "Equinox", "astMatch", status );
6212          TRANSFORM_1( "ECLEQ", equinox )
6213 
6214 /* From helio-ecliptic coordinates. */
6215 /* -------------------------------- */
6216       } else if ( align_sys == AST__HELIOECLIPTIC ) {
6217          VerifyMSMAttrs( target, result, 3, "Epoch", "astMatch", status );
6218          TRANSFORM_1( "HEEQ", epoch )
6219 
6220 /* From galactic coordinates. */
6221 /* -------------------------- */
6222 /* This conversion is supported directly by SLALIB. */
6223       } else if ( align_sys == AST__GALACTIC ) {
6224          TRANSFORM_0( "GALEQ" )
6225 
6226 /* From ICRS. */
6227 /* ---------- */
6228 /* This conversion is supported directly by SLALIB. */
6229       } else if ( align_sys == AST__ICRS ) {
6230          VerifyMSMAttrs( target, result, 3, "Epoch", "astMatch", status );
6231          TRANSFORM_1( "HFK5Z", epoch_J )
6232 
6233 /* From J2000. */
6234 /* ----------- */
6235 /* From J2000 to ICRS, and from ICRS to FK5. */
6236       } else if ( align_sys == AST__J2000 ) {
6237          VerifyMSMAttrs( target, result, 3, "Epoch", "astMatch", status );
6238          TRANSFORM_0( "J2000H" )
6239          TRANSFORM_1( "HFK5Z", epoch_J )
6240 
6241 /* From supergalactic coordinates. */
6242 /* ------------------------------- */
6243 /* Convert to galactic coordinates and then to FK5 J2000.0
6244       equatorial. */
6245       } else if ( align_sys == AST__SUPERGALACTIC ) {
6246          TRANSFORM_0( "SUPGAL" )
6247          TRANSFORM_0( "GALEQ" )
6248 
6249 /* From AzEl. */
6250 /* ---------- */
6251 /* Rotate from horizon to equator (H2E), shift hour angle into RA (H2R),
6252       go from geocentric apparent to FK5 J2000. */
6253       } else if ( align_sys == AST__AZEL ) {
6254          VerifyMSMAttrs( target, result, 3, "ObsLon ObsLat Epoch", "astMatch", status );
6255          TRANSFORM_2( "H2E", lat, diurab )
6256          TRANSFORM_1( "H2R", last )
6257          TRANSFORM_2( "AMP", epoch, 2000.0 )
6258 
6259 /* From unknown coordinates. */
6260 /* ------------------------------- */
6261 /* No conversion is possible. */
6262       } else if ( align_sys == AST__UNKNOWN ) {
6263          match = 0;
6264       }
6265    }
6266 
6267 /* Convert _from_ FK5 J2000.0 coordinates. */
6268 /* ======================================= */
6269 /* In this fourth and final phase, we convert to the result coordinate
6270    system from the intermediate FK5 J2000 sky coordinates generated above. */
6271    if ( astOK && match && step4 ) {
6272 
6273 /* To FK4. */
6274 /* ------- */
6275 /* Convert directly from FK5 J2000.0 to FK4 B1950.0 coordinates at the
6276    appropriate epoch. Then, if necessary, apply the old-style FK4
6277    precession model to bring the equinox to that required, with
6278    rigorous handling of the E-terms of aberration. */
6279       if ( system == AST__FK4 ) {
6280          VerifyMSMAttrs( target, result, 3, "Equinox Epoch", "astMatch", status );
6281          TRANSFORM_1( "FK54Z", epoch_B )
6282          if ( equinox_B != 1950.0 ) {
6283             TRANSFORM_1( "SUBET", 1950.0 )
6284             TRANSFORM_2( "PREBN", 1950.0, equinox_B )
6285             TRANSFORM_1( "ADDET", equinox_B )
6286          }
6287 
6288 /* To FK4 with no E-terms. */
6289 /* ----------------------- */
6290 /* This is the same as above, except that we do not need to add the
6291    E-terms at the end. */
6292       } else if ( system == AST__FK4_NO_E ) {
6293          VerifyMSMAttrs( target, result, 3, "Equinox Epoch", "astMatch", status );
6294          TRANSFORM_1( "FK54Z", epoch_B )
6295          TRANSFORM_1( "SUBET", 1950.0 )
6296          if ( equinox_B != 1950.0 ) {
6297             TRANSFORM_2( "PREBN", 1950.0, equinox_B )
6298          }
6299 
6300 /* To FK5. */
6301 /* ------- */
6302 /* We simply need to apply a precession correction for the change of
6303    equinox.  Omit even this if the required equinox is J2000.0. */
6304       } else if ( system == AST__FK5 ) {
6305          VerifyMSMAttrs( target, result, 3, "Equinox", "astMatch", status );
6306          if ( equinox_J != 2000.0 ) {
6307             TRANSFORM_2( "PREC", 2000.0, equinox_J )
6308          }
6309 
6310 /* To geocentric apparent. */
6311 /* ----------------------- */
6312 /* This conversion is supported directly by SLALIB. */
6313       } else if ( system == AST__GAPPT ) {
6314          VerifyMSMAttrs( target, result, 3, "Epoch", "astMatch", status );
6315          TRANSFORM_2( "MAP", 2000.0, epoch )
6316 
6317 /* To ecliptic coordinates. */
6318 /* ------------------------ */
6319 /* This conversion is supported directly by SLALIB. */
6320       } else if ( system == AST__ECLIPTIC ) {
6321          VerifyMSMAttrs( target, result, 3, "Equinox", "astMatch", status );
6322          TRANSFORM_1( "EQECL", equinox )
6323 
6324 /* To helio-ecliptic coordinates. */
6325 /* ------------------------------ */
6326       } else if ( system == AST__HELIOECLIPTIC ) {
6327          VerifyMSMAttrs( target, result, 3, "Epoch", "astMatch", status );
6328          TRANSFORM_1( "EQHE", epoch )
6329 
6330 /* To galactic coordinates. */
6331 /* ------------------------ */
6332 /* This conversion is supported directly by SLALIB. */
6333       } else if ( system == AST__GALACTIC ) {
6334          TRANSFORM_0( "EQGAL" )
6335 
6336 /* To ICRS. */
6337 /* -------- */
6338 /* This conversion is supported directly by SLALIB. */
6339       } else if ( system == AST__ICRS ) {
6340          VerifyMSMAttrs( target, result, 3, "Epoch", "astMatch", status );
6341          TRANSFORM_1( "FK5HZ", epoch_J )
6342 
6343 /* To J2000. */
6344 /* --------- */
6345 /* From FK5 to ICRS, then from ICRS to J2000. */
6346       } else if ( system == AST__J2000 ) {
6347          VerifyMSMAttrs( target, result, 3, "Epoch", "astMatch", status );
6348          TRANSFORM_1( "FK5HZ", epoch_J )
6349          TRANSFORM_0( "HJ2000" )
6350 
6351 /* To supergalactic coordinates. */
6352 /* ----------------------------- */
6353 /* Convert to galactic coordinates and then to supergalactic. */
6354       } else if ( system == AST__SUPERGALACTIC ) {
6355          TRANSFORM_0( "EQGAL" )
6356          TRANSFORM_0( "GALSUP" )
6357 
6358 /* To AzEl */
6359 /* ------- */
6360 /* Go from FK5 J2000 to geocentric apparent (MAP), shift RA into hour angle
6361    (R2H), rotate from equator to horizon (E2H). */
6362       } else if ( system == AST__AZEL ) {
6363          VerifyMSMAttrs( target, result, 3, "ObsLon ObsLat Epoch", "astMatch", status );
6364          TRANSFORM_2( "MAP", 2000.0, epoch )
6365          TRANSFORM_1( "R2H", last )
6366          TRANSFORM_2( "E2H", lat, diurab )
6367 
6368 /* To unknown coordinates. */
6369 /* ----------------------------- */
6370 /* No conversion is possible. */
6371       } else if ( system == AST__UNKNOWN ) {
6372          match = 0;
6373       }
6374    }
6375 
6376 /* Now need to take account of the possibility that the input or output
6377    SkyFrame may represent an offset system rather than a coordinate system.
6378    Form the Mapping from the target coordinate system to the associated
6379    offset system. A UnitMap is returned if the target does not use an
6380    offset system. */
6381    omap = SkyOffsetMap( target, status );
6382 
6383 /* Invert it to get the Mapping from the actual used system (whther
6384    offsets or coordinates) to the coordinate system. */
6385    astInvert( omap );
6386 
6387 /* Combine it with the slamap created earlier, so that its coordinate
6388    outputs feed the inputs of the slamap. Annul redundant pointers
6389    afterwards. */
6390    tmap = (AstMapping *) astCmpMap( omap, slamap, 1, "", status );
6391    omap = astAnnul( omap );
6392    slamap =astAnnul( slamap );
6393 
6394 /* Now form the Mapping from the result coordinate system to the associated
6395    offset system. A UnitMap is returned if the result does not use an
6396    offset system. */
6397    omap = SkyOffsetMap( result, status );
6398 
6399 /* Combine it with the above CmpMap, so that the CmpMap outputs feed the
6400    new Mapping inputs. Annul redundant pointers afterwards. */
6401    tmap2 = (AstMapping *) astCmpMap( tmap, omap, 1, "", status );
6402    omap =astAnnul( omap );
6403    tmap =astAnnul( tmap );
6404 
6405 /* Simplify the Mapping produced above (this eliminates any redundant
6406    conversions) and annul the original pointer. */
6407    *map = astSimplify( tmap2 );
6408    tmap2 = astAnnul( tmap2 );
6409 
6410 /* If an error occurred, annul the returned Mapping and clear the
6411    returned values. */
6412    if ( !astOK ) {
6413       *map = astAnnul( *map );
6414       match = -1;
6415    }
6416 
6417 /* Return the result. */
6418    return match;
6419 
6420 /* Undefine macros local to this function. */
6421 #undef MAX_ARGS
6422 #undef TRANSFORM_0
6423 #undef TRANSFORM_1
6424 #undef TRANSFORM_2
6425 #undef TRANSFORM_3
6426 }
6427 
Match(AstFrame * template_frame,AstFrame * target,int matchsub,int ** template_axes,int ** target_axes,AstMapping ** map,AstFrame ** result,int * status)6428 static int Match( AstFrame *template_frame, AstFrame *target, int matchsub,
6429                   int **template_axes, int **target_axes, AstMapping **map,
6430                   AstFrame **result, int *status ) {
6431 /*
6432 *  Name:
6433 *     Match
6434 
6435 *  Purpose:
6436 *     Determine if conversion is possible between two coordinate systems.
6437 
6438 *  Type:
6439 *     Private function.
6440 
6441 *  Synopsis:
6442 *     #include "skyframe.h"
6443 *     int Match( AstFrame *template, AstFrame *target, int matchsub,
6444 *                int **template_axes, int **target_axes,
6445 *                AstMapping **map, AstFrame **result, int *status )
6446 
6447 *  Class Membership:
6448 *     SkyFrame member function (over-rides the protected astMatch method
6449 *     inherited from the Frame class).
6450 
6451 *  Description:
6452 *     This function matches a "template" SkyFrame to a "target" Frame and
6453 *     determines whether it is possible to convert coordinates between them.
6454 *     If it is, a mapping that performs the transformation is returned along
6455 *     with a new Frame that describes the coordinate system that results when
6456 *     this mapping is applied to the "target" coordinate system. In addition,
6457 *     information is returned to allow the axes in this "result" Frame to be
6458 *     associated with the corresponding axes in the "target" and "template"
6459 *     Frames from which they are derived.
6460 
6461 *  Parameters:
6462 *     template
6463 *        Pointer to the template SkyFrame. This describes the coordinate system
6464 *        (or set of possible coordinate systems) into which we wish to convert
6465 *        our coordinates.
6466 *     target
6467 *        Pointer to the target Frame. This describes the coordinate system in
6468 *        which we already have coordinates.
6469 *     matchsub
6470 *        If zero then a match only occurs if the template is of the same
6471 *        class as the target, or of a more specialised class. If non-zero
6472 *        then a match can occur even if this is not the case.
6473 *     template_axes
6474 *        Address of a location where a pointer to int will be returned if the
6475 *        requested coordinate conversion is possible. This pointer will point
6476 *        at a dynamically allocated array of integers with one element for each
6477 *        axis of the "result" Frame (see below). It must be freed by the caller
6478 *        (using astFree) when no longer required.
6479 *
6480 *        For each axis in the result Frame, the corresponding element of this
6481 *        array will return the index of the template SkyFrame axis from which
6482 *        it is derived. If it is not derived from any template SkyFrame axis,
6483 *        a value of -1 will be returned instead.
6484 *     target_axes
6485 *        Address of a location where a pointer to int will be returned if the
6486 *        requested coordinate conversion is possible. This pointer will point
6487 *        at a dynamically allocated array of integers with one element for each
6488 *        axis of the "result" Frame (see below). It must be freed by the caller
6489 *        (using astFree) when no longer required.
6490 *
6491 *        For each axis in the result Frame, the corresponding element of this
6492 *        array will return the index of the target Frame axis from which it
6493 *        is derived. If it is not derived from any target Frame axis, a value
6494 *        of -1 will be returned instead.
6495 *     map
6496 *        Address of a location where a pointer to a new Mapping will be
6497 *        returned if the requested coordinate conversion is possible. If
6498 *        returned, the forward transformation of this Mapping may be used to
6499 *        convert coordinates between the "target" Frame and the "result"
6500 *        Frame (see below) and the inverse transformation will convert in the
6501 *        opposite direction.
6502 *     result
6503 *        Address of a location where a pointer to a new Frame will be returned
6504 *        if the requested coordinate conversion is possible. If returned, this
6505 *        Frame describes the coordinate system that results from applying the
6506 *        returned Mapping (above) to the "target" coordinate system. In
6507 *        general, this Frame will combine attributes from (and will therefore
6508 *        be more specific than) both the target and the template Frames. In
6509 *        particular, when the template allows the possibility of transformaing
6510 *        to any one of a set of alternative coordinate systems, the "result"
6511 *        Frame will indicate which of the alternatives was used.
6512 *     status
6513 *        Pointer to the inherited status variable.
6514 
6515 *  Returned Value:
6516 *     A non-zero value is returned if the requested coordinate conversion is
6517 *     possible. Otherwise zero is returned (this will not in itself result in
6518 *     an error condition).
6519 
6520 *  Notes:
6521 *     -  A value of zero will be returned if this function is invoked with the
6522 *     global error status set, or if it should fail for any reason.
6523 
6524 *  Implementation Notes:
6525 *     This implementation addresses the matching of a SkyFrame class object to
6526 *     any other class of Frame. A SkyFrame will match any class of SkyFrame
6527 *     (i.e. possibly from a derived class) but will not match a less
6528 *     specialised class of Frame.
6529 */
6530 
6531 /* Local Variables: */
6532    AstFrame *frame0;          /* Pointer to Frame underlying axis 0 */
6533    AstFrame *frame1;          /* Pointer to Frame underlying axis 1 */
6534    AstSkyFrame *template;     /* Pointer to template SkyFrame structure */
6535    int iaxis;                 /* Axis index */
6536    int iaxis0;                /* Axis index underlying axis 0 */
6537    int iaxis1;                /* Axis index underlying axis 1 */
6538    int match;                 /* Coordinate conversion possible? */
6539    int swap1;                 /* Template axes swapped? */
6540    int swap2;                 /* Target axes swapped? */
6541    int swap;                  /* Additional axis swap needed? */
6542    int target_axis0;          /* Index of 1st SkyFrame axis in the target */
6543    int target_axis1;          /* Index of 2nd SkyFrame axis in the target */
6544    int target_naxes;          /* Number of target axes */
6545 
6546 /* Initialise the returned values. */
6547    *template_axes = NULL;
6548    *target_axes = NULL;
6549    *map = NULL;
6550    *result = NULL;
6551    match = 0;
6552 
6553 /* Check the global error status. */
6554    if ( !astOK ) return match;
6555 
6556 /* Initialise variables to avoid "used of uninitialised variable"
6557    messages from dumb compilers. */
6558    swap = 0;
6559    target_axis0 = -1;
6560    target_axis1 = -1;
6561 
6562 /* Obtain a pointer to the template SkyFrame structure. */
6563    template = (AstSkyFrame *) template_frame;
6564 
6565 /* Obtain the number of axes in the target Frame. */
6566    target_naxes = astGetNaxes( target );
6567 
6568 /* The first criterion for a match is that the template matches as a
6569    Frame class object. This ensures that the number of axes (2) and
6570    domain, etc. of the target Frame are suitable. Invoke the parent
6571    "astMatch" method to verify this. */
6572    match = (*parent_match)( template_frame, target, matchsub,
6573                             template_axes, target_axes, map, result, status );
6574 
6575 /* If a match was found, annul the returned objects, which are not
6576    needed, but keep the memory allocated for the axis association
6577    arrays, which we will re-use. */
6578    if ( astOK && match ) {
6579       *map = astAnnul( *map );
6580       *result = astAnnul( *result );
6581    }
6582 
6583 /* If OK so far, obtain pointers to the primary Frames which underlie
6584    all target axes. Stop when a SkyFrame axis is found. */
6585    if ( match && astOK ) {
6586 
6587       match = 0;
6588       for( iaxis = 0; iaxis < target_naxes; iaxis++ ) {
6589          astPrimaryFrame( target, iaxis, &frame0, &iaxis0 );
6590          if( astIsASkyFrame( frame0 ) ) {
6591             target_axis0 = iaxis;
6592             match = 1;
6593             break;
6594          } else {
6595             frame0 = astAnnul( frame0 );
6596          }
6597       }
6598 
6599 /* Check at least one SkyFrame axis was found it the target. */
6600       if( match ) {
6601 
6602 /* If so, search the remaining target axes for another axis that is
6603    derived from the same SkyFrame. */
6604          match = 0;
6605          for( iaxis++ ; iaxis < target_naxes; iaxis++ ) {
6606             astPrimaryFrame( target, iaxis, &frame1, &iaxis1 );
6607             if( frame1 == frame0 ) {
6608                target_axis1 = iaxis;
6609                frame1 = astAnnul( frame1 );
6610                match = 1;
6611                break;
6612             } else {
6613                frame1 = astAnnul( frame1 );
6614             }
6615          }
6616 
6617 /* Annul the remaining Frame pointer used in the above tests. */
6618          frame0 = astAnnul( frame0 );
6619       }
6620 
6621 /* If this test is passed, we can now test that the underlying axis indices
6622    are 0 and 1, in either order. This then ensures that we have a
6623    single SkyFrame (not a compound Frame) with both axes present. */
6624       if ( match && astOK ) {
6625          match = ( ( ( iaxis0 == 0 ) && ( iaxis1 == 1 ) ) ||
6626                    ( ( iaxis1 == 0 ) && ( iaxis0 == 1 ) ) );
6627       }
6628 
6629    }
6630 
6631 /* If a possible match has been detected, we must now decide how the
6632    order of the axes in the result Frame relates to the order of axes
6633    in the target Frame. There are two factors involved. The first
6634    depends on whether the axis permutation array for the template
6635    SkyFrame (whose method we are executing) causes an axis
6636    reversal. Determine this by permuting axis index zero. */
6637    if ( astOK && match ) {
6638       swap1 = ( astValidateAxis( template, 0, 1, "astMatch" ) != 0 );
6639 
6640 /* The second factor depends on whether the axes of the underlying
6641    primary SkyFrame are reversed when seen in the target Frame. */
6642       swap2 = ( iaxis0 != 0 );
6643 
6644 /* Combine these to determine if an additional axis swap will be
6645    needed. */
6646       swap = ( swap1 != swap2 );
6647 
6648 /* Now check to see if this additional swap is permitted by the
6649    template's Permute attribute. */
6650       match = ( !swap || astGetPermute( template ) );
6651    }
6652 
6653 /* If the Frames still match, we next set up the axis association
6654    arrays. */
6655    if ( astOK && match ) {
6656 
6657 /* If the target axis order is to be preserved, then the target axis
6658    association involves no permutation but the template axis
6659    association may involve an axis swap. */
6660       if ( astGetPreserveAxes( template ) ) {
6661          (*template_axes)[ 0 ] = swap;
6662          (*template_axes)[ 1 ] = !swap;
6663          (*target_axes)[ 0 ] = target_axis0;
6664          (*target_axes)[ 1 ] = target_axis1;
6665 
6666 /* Otherwise, any swap applies to the target axis association
6667    instead. */
6668       } else {
6669          (*template_axes)[ 0 ] = 0;
6670          (*template_axes)[ 1 ] = 1;
6671          (*target_axes)[ 0 ] = swap ? target_axis1 : target_axis0;
6672          (*target_axes)[ 1 ] = swap ? target_axis0 : target_axis1;
6673       }
6674 
6675 /* Use the target's "astSubFrame" method to create a new Frame (the
6676    result Frame) with copies of the target axes in the required
6677    order. This process also overlays the template attributes on to the
6678    target Frame and returns a Mapping between the target and result
6679    Frames which effects the required coordinate conversion. */
6680       match = astSubFrame( target, template, 2, *target_axes, *template_axes,
6681                            map, result );
6682    }
6683 
6684 /* If an error occurred, or conversion to the result Frame's
6685    coordinate system was not possible, then free all memory, annul the
6686    returned objects, and reset the returned value. */
6687    if ( !astOK || !match ) {
6688       *template_axes = astFree( *template_axes );
6689       *target_axes = astFree( *target_axes );
6690       if( *map ) *map = astAnnul( *map );
6691       if( *result ) *result = astAnnul( *result );
6692       match = 0;
6693    }
6694 
6695 /* Return the result. */
6696    return match;
6697 }
6698 
MatchAxesX(AstFrame * frm2_frame,AstFrame * frm1,int * axes,int * status)6699 static void MatchAxesX( AstFrame *frm2_frame, AstFrame *frm1, int *axes,
6700                         int *status ) {
6701 /*
6702 *  Name:
6703 *     MatchAxesX
6704 
6705 *  Purpose:
6706 *     Find any corresponding axes in two Frames.
6707 
6708 *  Type:
6709 *     Private function.
6710 
6711 *  Synopsis:
6712 *     #include "skyframe.h"
6713 *     void MatchAxesX( AstFrame *frm2, AstFrame *frm1, int *axes )
6714 *                      int *status )
6715 
6716 *  Class Membership:
6717 *     SkyFrame member function (over-rides the protected astMatchAxesX
6718 *     method inherited from the Frame class).
6719 
6720 *     This function looks for corresponding axes within two supplied
6721 *     Frames. An array of integers is returned that contains an element
6722 *     for each axis in the second supplied Frame. An element in this array
6723 *     will be set to zero if the associated axis within the second Frame
6724 *     has no corresponding axis within the first Frame. Otherwise, it
6725 *     will be set to the index (a non-zero positive integer) of the
6726 *     corresponding axis within the first supplied Frame.
6727 
6728 *  Parameters:
6729 *     frm2
6730 *        Pointer to the second Frame.
6731 *     frm1
6732 *        Pointer to the first Frame.
6733 *     axes
6734 *        Pointer to an integer array in which to return the indices of
6735 *        the axes (within the first Frame) that correspond to each axis
6736 *        within the second Frame. Axis indices start at 1. A value of zero
6737 *        will be stored in the returned array for each axis in the second
6738 *        Frame that has no corresponding axis in the first Frame.
6739 *
6740 *        The number of elements in this array must be greater than or
6741 *        equal to the number of axes in the second Frame.
6742 *     status
6743 *        Pointer to inherited status value.
6744 
6745 *  Notes:
6746 *     -  Corresponding axes are identified by the fact that a Mapping
6747 *     can be found between them using astFindFrame or astConvert. Thus,
6748 *     "corresponding axes" are not necessarily identical. For instance,
6749 *     SkyFrame axes in two Frames will match even if they describe
6750 *     different celestial coordinate systems
6751 */
6752 
6753 /* Local Variables: */
6754    AstFrame *resfrm;
6755    AstMapping *resmap;
6756    AstSkyFrame *frm2;
6757    int *frm2_axes;
6758    int *frm1_axes;
6759    int max_axes;
6760    int min_axes;
6761    int preserve_axes;
6762 
6763 /* Check the global error status. */
6764    if ( !astOK ) return;
6765 
6766 /* Get a pointer to the SkyFrame. */
6767    frm2 = (AstSkyFrame *) frm2_frame;
6768 
6769 /* Temporarily ensure that the PreserveAxes attribute is non-zero in
6770    the first supplied Frame. This means thte result Frame returned by
6771    astMatch below will have the axis count and order of the target Frame
6772    (i.e. "pfrm"). */
6773    if( astTestPreserveAxes( frm1 ) ) {
6774       preserve_axes = astGetPreserveAxes( frm1 ) ? 1 : 0;
6775    } else {
6776       preserve_axes = -1;
6777    }
6778    astSetPreserveAxes( frm1, 1 );
6779 
6780 /* Temporarily ensure that the MaxAxes and MinAxes attributes in the
6781    first supplied Frame are set so the Frame can be used as a template
6782    in astMatch for matching any number of axes. */
6783    if( astTestMaxAxes( frm1 ) ) {
6784       max_axes = astGetMaxAxes( frm1 );
6785    } else {
6786       max_axes = -1;
6787    }
6788    astSetMaxAxes( frm1, 10000 );
6789 
6790    if( astTestMinAxes( frm1 ) ) {
6791       min_axes = astGetMinAxes( frm1 );
6792    } else {
6793       min_axes = -1;
6794    }
6795    astSetMinAxes( frm1, 1 );
6796 
6797 /* Attempt to find a sub-frame within the first supplied Frame that
6798    corresponds to the supplied SkyFrame. */
6799    if( astMatch( frm1, frm2, 1, &frm1_axes, &frm2_axes, &resmap, &resfrm ) ) {
6800 
6801 /* If successfull, Store the one-based index within "frm1" of the
6802    corresponding axes. */
6803       axes[ 0 ] = frm1_axes[ 0 ] + 1;
6804       axes[ 1 ] = frm1_axes[ 1 ] + 1;
6805 
6806 /* Free resources */
6807       frm1_axes = astFree( frm1_axes );
6808       frm2_axes = astFree( frm2_axes );
6809       resmap = astAnnul( resmap );
6810       resfrm = astAnnul( resfrm );
6811 
6812 /* If no corresponding SkyFrame was found store zeros in the returned array. */
6813    } else {
6814       axes[ 0 ] = 0;
6815       axes[ 1 ] = 0;
6816    }
6817 
6818 /* Re-instate the original attribute values in the first supplied Frame. */
6819    if( preserve_axes == -1 ) {
6820       astClearPreserveAxes( frm1 );
6821    } else {
6822       astSetPreserveAxes( frm1, preserve_axes );
6823    }
6824 
6825    if( max_axes == -1 ) {
6826       astClearMaxAxes( frm1 );
6827    } else {
6828       astSetMaxAxes( frm1, max_axes );
6829    }
6830 
6831    if( min_axes == -1 ) {
6832       astClearMinAxes( frm1 );
6833    } else {
6834       astSetMinAxes( frm1, min_axes );
6835    }
6836 }
6837 
Norm(AstFrame * this_frame,double value[],int * status)6838 static void Norm( AstFrame *this_frame, double value[], int *status ) {
6839 /*
6840 *  Name:
6841 *     Norm
6842 
6843 *  Purpose:
6844 *     Normalise a set of SkyFrame coordinates.
6845 
6846 *  Type:
6847 *     Private function.
6848 
6849 *  Synopsis:
6850 *     #include "skyframe.h"
6851 *     void Norm( AstAxis *this, double value[], int *status )
6852 
6853 *  Class Membership:
6854 *     SkyFrame member function (over-rides the astNorm method inherited
6855 *     from the Frame class).
6856 
6857 *  Description:
6858 *     This function converts a set of SkyFrame coordinate values,
6859 *     which might potentially be unsuitable for display to a user (for
6860 *     instance, may lie outside the expected range of values) into a
6861 *     set of acceptable alternative values suitable for display.
6862 *
6863 *     This is done by wrapping coordinates so that the latitude lies
6864 *     in the range (-pi/2.0) <= latitude <= (pi/2.0). If the NegLon
6865 *     attribute is zero (the default), then the wrapped longitude value
6866 *     lies in the range 0.0 <= longitude < (2.0*pi). Otherwise, it lies
6867 *     in the range -pi <= longitude < pi.
6868 
6869 *  Parameters:
6870 *     this
6871 *        Pointer to the SkyFrame.
6872 *     value
6873 *        An array of double, with one element for each SkyFrame axis.
6874 *        This should contain the initial set of coordinate values,
6875 *        which will be modified in place.
6876 *     status
6877 *        Pointer to the inherited status variable.
6878 */
6879 
6880 /* Local Variables: */
6881    AstSkyFrame *this;            /* Pointer to the SkyFrame structure */
6882    const int *perm;              /* Axis permutation array */
6883    double sky_lat;               /* Sky latitude value */
6884    double sky_long;              /* Sky longitude value */
6885    double v[ 2 ];                /* Permuted value coordinates */
6886 
6887 /* Check the global error status. */
6888    if ( !astOK ) return;
6889 
6890 /* Obtain a pointer to the SkyFrame structure. */
6891    this = (AstSkyFrame *) this_frame;
6892 
6893 /* Obtain a pointer to the SkyFrame's axis permutation array. */
6894    perm = astGetPerm( this );
6895    if ( astOK ) {
6896 
6897 /* Obtain the sky longitude and latitude values, allowing for any axis
6898    permutation. */
6899       v[ perm[ 0 ] ] = value[ 0 ];
6900       v[ perm[ 1 ] ] = value[ 1 ];
6901       sky_long = v[ 0 ];
6902       sky_lat = v[ 1 ];
6903 
6904 /* Test if both values are OK (i.e. not "bad"). */
6905       if ( ( sky_long != AST__BAD ) && ( sky_lat != AST__BAD ) ) {
6906 
6907 /* Fold the longitude value into the range 0 to 2*pi and the latitude into
6908    the range -pi to +pi. */
6909          sky_long = palDranrm( sky_long );
6910          sky_lat = palDrange( sky_lat );
6911 
6912 /* If the latitude now exceeds pi/2, shift the longitude by pi in whichever
6913    direction will keep it in the range 0 to 2*pi. */
6914          if ( sky_lat > ( pi / 2.0 ) ) {
6915             sky_long += ( sky_long < pi ) ? pi : -pi;
6916 
6917 /* Reflect the latitude value through the pole, so it lies in the range 0 to
6918    pi/2. */
6919             sky_lat = pi - sky_lat;
6920 
6921 /* If the latitude is less than -pi/2, shift the longitude in the same way
6922    as above. */
6923          } else if ( sky_lat < -( pi / 2.0 ) ) {
6924             sky_long += ( sky_long < pi ) ? pi : -pi;
6925 
6926 /* But reflect the latitude through the other pole, so it lies in the range
6927    -pi/2 to 0. */
6928             sky_lat = -pi - sky_lat;
6929          }
6930 
6931 /* If only the longitude value is valid, wrap it into the range 0 to 2*pi. */
6932       } else if ( sky_long != AST__BAD ) {
6933          sky_long = palDranrm( sky_long );
6934 
6935 /* If only the latitude value is valid, wrap it into the range -pi to +pi. */
6936       } else if ( sky_lat != AST__BAD ) {
6937          sky_lat = palDrange( sky_lat );
6938 
6939 /* Then refect through one of the poles (as above), if necessary, to move it
6940    into the range -pi/2 to +pi/2. */
6941          if ( sky_lat > ( pi / 2.0 ) ) {
6942             sky_lat = pi - sky_lat;
6943          } else if ( sky_lat < -( pi / 2.0 ) ) {
6944             sky_lat = -pi - sky_lat;
6945          }
6946       }
6947 
6948 /* Convert 2*pi longitude into zero. Allow for a small error. */
6949       if ( fabs( sky_long - ( 2.0 * pi ) ) <=
6950           ( 2.0 * pi ) * ( DBL_EPSILON * (double) FLT_RADIX ) ) sky_long = 0.0;
6951 
6952 /* If the NegLon attribute is set, and the longitude value is good,
6953    convert it into the range -pi to +pi. */
6954       if( sky_long != AST__BAD && astGetNegLon( this ) ) {
6955          sky_long = palDrange( sky_long );
6956       }
6957 
6958 /* Return the new values, allowing for any axis permutation. */
6959       v[ 0 ] = sky_long;
6960       v[ 1 ] = sky_lat;
6961       value[ 0 ] = v[ perm[ 0 ] ];
6962       value[ 1 ] = v[ perm[ 1 ] ];
6963    }
6964 }
6965 
NormBox(AstFrame * this_frame,double lbnd[],double ubnd[],AstMapping * reg,int * status)6966 static void NormBox( AstFrame *this_frame, double lbnd[], double ubnd[],
6967                      AstMapping *reg, int *status ) {
6968 /*
6969 *  Name:
6970 *     NormBox
6971 
6972 *  Purpose:
6973 *     Extend a box to include effect of any singularities in the Frame.
6974 
6975 *  Type:
6976 *     Private function.
6977 
6978 *  Synopsis:
6979 *     #include "skyframe.h"
6980 *     void astNormBox( AstFrame *this, double lbnd[], double ubnd[],
6981 *                      AstMapping *reg, int *status )
6982 
6983 *  Class Membership:
6984 *     SkyFrame member function (over-rides the astNormBox method inherited
6985 *     from the Frame class).
6986 
6987 *  Description:
6988 *     This function modifies a supplied box to include the effect of any
6989 *     singularities in the co-ordinate system represented by the Frame.
6990 *     For a normal Cartesian coordinate system, the box will be returned
6991 *     unchanged. Other classes of Frame may do other things. For instance,
6992 *     a SkyFrame will check to see if the box contains either the north
6993 *     or south pole and extend the box appropriately.
6994 
6995 *  Parameters:
6996 *     this
6997 *        Pointer to the Frame.
6998 *     lbnd
6999 *        An array of double, with one element for each Frame axis
7000 *        (Naxes attribute). Initially, this should contain a set of
7001 *        lower axis bounds for the box. They will be modified on exit
7002 *        to include the effect of any singularities within the box.
7003 *     ubnd
7004 *        An array of double, with one element for each Frame axis
7005 *        (Naxes attribute). Initially, this should contain a set of
7006 *        upper axis bounds for the box. They will be modified on exit
7007 *        to include the effect of any singularities within the box.
7008 *     reg
7009 *        A Mapping which should be used to test if any singular points are
7010 *        inside or outside the box. The Mapping should leave an input
7011 *        position unchanged if the point is inside the box, and should
7012 *        set all bad if the point is outside the box.
7013 *     status
7014 *        Pointer to the inherited status variable.
7015 */
7016 
7017 /* Local Variables: */
7018    AstSkyFrame *this;            /* Pointer to the SkyFrame structure */
7019    const int *perm;              /* Axis permutation array */
7020    double lb[ 2 ];               /* Permuted lower bounds */
7021    double t;                     /* Temporary storage */
7022    double t2;                    /* Temporary storage */
7023    double ub[ 2 ];               /* Permuted upper bounds */
7024    double x[2];                  /* 1st axis values at poles */
7025    double xo[2];                 /* Tested 1st axis values at poles */
7026    double y[2];                  /* 2nd axis values at poles */
7027    double yo[2];                 /* Tested 2nd axis values at poles */
7028 
7029 /* Check the global error status. */
7030    if ( !astOK ) return;
7031 
7032 /* Obtain a pointer to the SkyFrame structure. */
7033    this = (AstSkyFrame *) this_frame;
7034 
7035 /* Obtain a pointer to the SkyFrame's axis permutation array. */
7036    perm = astGetPerm( this );
7037    if( perm ) {
7038 
7039 /* Obtain the sky longitude and latitude limits, allowing for any axis
7040    permutation. */
7041       lb[ perm[ 0 ] ] = lbnd[ 0 ];
7042       lb[ perm[ 1 ] ] = lbnd[ 1 ];
7043       ub[ perm[ 0 ] ] = ubnd[ 0 ];
7044       ub[ perm[ 1 ] ] = ubnd[ 1 ];
7045 
7046 /* Use the supplied Mapping to test if box includes either pole. */
7047       if( perm[ 0 ] == 0 ) {
7048          x[ 0 ] = 0.0;
7049          y[ 0 ] = AST__DPIBY2;
7050          x[ 1 ] = 0.0;
7051          y[ 1 ] = -AST__DPIBY2;
7052       } else {
7053          x[ 0 ] = AST__DPIBY2;
7054          y[ 0 ] = 0.0;
7055          x[ 1 ] = -AST__DPIBY2;
7056          y[ 1 ] = 0.0;
7057       }
7058       astTran2( reg, 2, x, y, 1, xo, yo );
7059 
7060 /* If the box includes the north pole... */
7061       if( xo[ 0 ] != AST__BAD ) {
7062 
7063 /* Find the lowest latitude after normalisation. */
7064          if( ub[ 1 ] != AST__BAD &&  lb[ 1 ] != AST__BAD ){
7065             t = palDrange( ub[ 1 ] );
7066             t2 = palDrange( lb[ 1 ] );
7067             if( t2 < t ) t = t2;
7068          } else {
7069             t = AST__BAD;
7070          }
7071 
7072 /* Set the lower returned limit to this value and the upper returned limit
7073    to +90 degs */
7074          lb[ 1 ] = t;
7075          ub[ 1 ] = AST__DPIBY2;
7076 
7077 /* Set the longitude range to 0 to 2PI */
7078          lb[ 0 ] = 0;
7079          ub[ 0 ] = 2*AST__DPI;
7080 
7081       }
7082 
7083 /* If the box includes the south pole... */
7084       if( xo[ 1 ] != AST__BAD ) {
7085 
7086 /* Find the highest latitude after normalisation. */
7087          if( ub[ 1 ] != AST__BAD &&  lb[ 1 ] != AST__BAD ){
7088             t = palDrange( ub[ 1 ] );
7089             t2 = palDrange( lb[ 1 ] );
7090             if( t2 > t ) t = t2;
7091          } else {
7092             t = AST__BAD;
7093          }
7094 
7095 /* Set the upper returned limit to this value and the lower returned limit
7096    to -90 degs */
7097          lb[ 1 ] = -AST__DPIBY2;
7098          ub[ 1 ] = t;
7099 
7100 /* Set the longitude range to 0 to 2PI */
7101          lb[ 0 ] = 0;
7102          ub[ 0 ] = 2*AST__DPI;
7103       }
7104 
7105 /* Return the modified limits. */
7106       lbnd[ 0 ] = lb[ perm[ 0 ] ];
7107       lbnd[ 1 ] = lb[ perm[ 1 ] ];
7108       ubnd[ 0 ] = ub[ perm[ 0 ] ];
7109       ubnd[ 1 ] = ub[ perm[ 1 ] ];
7110    }
7111 }
7112 
Offset(AstFrame * this_frame,const double point1[],const double point2[],double offset,double point3[],int * status)7113 static void Offset( AstFrame *this_frame, const double point1[],
7114                     const double point2[], double offset, double point3[], int *status ) {
7115 /*
7116 *  Name:
7117 *     Offset
7118 
7119 *  Purpose:
7120 *     Calculate an offset along a geodesic curve.
7121 
7122 *  Type:
7123 *     Private function.
7124 
7125 *  Synopsis:
7126 *     #include "skyframe.h"
7127 *     void Offset( AstFrame *this,
7128 *                  const double point1[], const double point2[],
7129 *                  double offset, double point3[], int *status )
7130 
7131 *  Class Membership:
7132 *     SkyFrame member function (over-rides the astOffset method
7133 *     inherited from the Frame class).
7134 
7135 *  Description:
7136 *     This function finds the SkyFrame coordinate values of a point
7137 *     which is offset a specified distance along the geodesic curve
7138 *     (i.e. great circle) between two other points.
7139 
7140 *  Parameters:
7141 *     this
7142 *        Pointer to the SkyFrame.
7143 *     point1
7144 *        An array of double, with one element for each SkyFrame axis.
7145 *        This should contain the coordinates of the point marking the
7146 *        start of the geodesic curve.
7147 *     point2
7148 *        An array of double, with one element for each SkyFrame axis.
7149 *        This should contain the coordinates of the point marking the
7150 *        end of the geodesic curve.
7151 *     offset
7152 *        The required offset from the first point along the geodesic
7153 *        curve, in radians. If this is positive, it will be towards
7154 *        the second point. If it is negative, it will be in the
7155 *        opposite direction. This offset need not imply a position
7156 *        lying between the two points given, as the curve will be
7157 *        extrapolated if necessary.
7158 *     point3
7159 *        An array of double, with one element for each SkyFrame axis
7160 *        in which the coordinates of the required point will be
7161 *        returned.
7162 *     status
7163 *        Pointer to the inherited status variable.
7164 
7165 *  Notes:
7166 *     - The geodesic curve used by this function is the path of
7167 *     shortest distance between two points, as defined by the
7168 *     astDistance function.
7169 *     - This function will return "bad" coordinate values (AST__BAD)
7170 *     if any of the input coordinates has this value.
7171 *     - "Bad" coordinate values will also be returned if the two
7172 *     points supplied are coincident (or otherwise fail to uniquely
7173 *     specify a geodesic curve) but the requested offset is non-zero.
7174 */
7175 
7176 /* Local Variables: */
7177    AstSkyFrame *this;            /* Pointer to the SkyFrame structure */
7178    const int *perm;              /* Pointer to axis permutation array */
7179    double mrot[ 3 ][ 3 ];        /* Rotation matrix */
7180    double p1[ 2 ];               /* Permuted coordinates for point1 */
7181    double p2[ 2 ];               /* Permuted coordinates for point2 */
7182    double p3[ 2 ];               /* Permuted coordinates for point3 */
7183    double scale;                 /* Scale factor */
7184    double v1[ 3 ];               /* 3-vector for p1 */
7185    double v2[ 3 ];               /* 3-vector for p2 */
7186    double v3[ 3 ];               /* 3-vector for p3 */
7187    double vmod;                  /* Modulus of vector */
7188    double vrot[ 3 ];             /* Vector along rotation axis */
7189 
7190 /* Check the global error status. */
7191    if ( !astOK ) return;
7192 
7193 /* Obtain a pointer to the SkyFrame structure. */
7194    this = (AstSkyFrame *) this_frame;
7195 
7196 /* Obtain a pointer to the SkyFrame's axis permutation array. */
7197    perm = astGetPerm( this );
7198    if ( astOK ) {
7199 
7200 /* Check that all supplied coordinates are OK. If not, generate "bad"
7201    output coordinates. */
7202       if ( ( point1[ 0 ] == AST__BAD ) || ( point1[ 1 ] == AST__BAD ) ||
7203            ( point2[ 0 ] == AST__BAD ) || ( point2[ 1 ] == AST__BAD ) ) {
7204          point3[ 0 ] = AST__BAD;
7205          point3[ 1 ] = AST__BAD;
7206 
7207 /* Otherwise, apply the axis permutation array to obtain the
7208    coordinates of the two input points in the required
7209    (longitude,latitude) order. */
7210       } else {
7211          p1[ perm[ 0 ] ] = point1[ 0 ];
7212          p1[ perm[ 1 ] ] = point1[ 1 ];
7213          p2[ perm[ 0 ] ] = point2[ 0 ];
7214          p2[ perm[ 1 ] ] = point2[ 1 ];
7215 
7216 /* Convert each point into a 3-vector of unit length. */
7217          palDcs2c( p1[ 0 ], p1[ 1 ], v1 );
7218          palDcs2c( p2[ 0 ], p2[ 1 ], v2 );
7219 
7220 /* Find the cross product between these two vectors (the vector order
7221    is reversed here to compensate for the sense of rotation introduced
7222    by palDav2m and palDmxv below). */
7223          palDvxv( v2, v1, v3 );
7224 
7225 /* Normalise the cross product vector, also obtaining its original
7226    modulus. */
7227          palDvn( v3, vrot, &vmod );
7228 
7229 /* If the original modulus was zero, the input points are either
7230    coincident or diametrically opposite, so do not uniquely define a
7231    great circle. In either case, we can only generate output
7232    coordinates if the offset required is an exact multiple of pi. If
7233    it is, generate the 3-vector that results from rotating the first
7234    input point through this angle. */
7235          if ( vmod == 0.0 ) {
7236             if ( sin( offset ) == 0.0 ) {
7237                scale = cos( offset );
7238                v3[ 0 ] = v1[ 0 ] * scale;
7239                v3[ 1 ] = v1[ 1 ] * scale;
7240                v3[ 2 ] = v1[ 2 ] * scale;
7241 
7242 /* Convert the 3-vector back into spherical cooordinates and then
7243    constrain the longitude result to lie in the range 0 to 2*pi
7244    (palDcc2s doesn't do this itself). */
7245                palDcc2s( v3, &p3[ 0 ], &p3[ 1 ] );
7246                p3[ 0 ] = palDranrm( p3[ 0 ] );
7247 
7248 /* If the offset was not a multiple of pi, generate "bad" output
7249    coordinates. */
7250             } else {
7251                p3[ 0 ] = AST__BAD;
7252                p3[ 1 ] = AST__BAD;
7253             }
7254 
7255 /* If the two input points define a great circle, scale the normalised
7256    cross product vector to make its length equal to the required
7257    offset (angle) between the first input point and the result. */
7258          } else {
7259             vrot[ 0 ] *= offset;
7260             vrot[ 1 ] *= offset;
7261             vrot[ 2 ] *= offset;
7262 
7263 /* Generate the rotation matrix that implements this rotation and use
7264    it to rotate the first input point (3-vector) to give the required
7265    result (3-vector). */
7266             palDav2m( vrot, mrot );
7267             palDmxv( mrot, v1, v3 );
7268 
7269 /* Convert the 3-vector back into spherical cooordinates and then
7270    constrain the longitude result to lie in the range 0 to 2*pi. */
7271             palDcc2s( v3, &p3[ 0 ], &p3[ 1 ] );
7272             p3[ 0 ] = palDranrm( p3[ 0 ] );
7273          }
7274 
7275 /* Permute the result coordinates to undo the effect of the SkyFrame
7276    axis permutation array. */
7277          point3[ 0 ] = p3[ perm[ 0 ] ];
7278          point3[ 1 ] = p3[ perm[ 1 ] ];
7279       }
7280    }
7281 }
7282 
SkyOffsetMap(AstSkyFrame * this,int * status)7283 static AstMapping *SkyOffsetMap( AstSkyFrame *this, int *status ){
7284 /*
7285 *++
7286 *  Name:
7287 c     astSkyOffsetMap
7288 f     AST_SKYOFFSETMAP
7289 
7290 *  Purpose:
7291 *     Returns a Mapping which goes from absolute coordinates to offset
7292 *     coordinates.
7293 
7294 *  Type:
7295 *     Public virtual function.
7296 
7297 *  Synopsis:
7298 c     #include "skyframe.h"
7299 c     AstMapping *astSkyOffsetMap( AstSkyFrame *this )
7300 f     RESULT = AST_SKYOFFSETMAP( THIS, STATUS )
7301 
7302 *  Class Membership:
7303 *     SkyFrame method.
7304 
7305 *  Description:
7306 *     This function returns a Mapping in which the forward transformation
7307 *     transforms a position in the coordinate system given by the System
7308 *     attribute of the supplied SkyFrame, into the offset coordinate system
7309 *     specified by the SkyRef, SkyRefP and SkyRefIs attributes of the
7310 *     supplied SkyFrame.
7311 *
7312 *     A UnitMap is returned if the SkyFrame does not define an offset
7313 *     coordinate system.
7314 
7315 *  Parameters:
7316 c     this
7317 f     THIS = INTEGER (Given)
7318 *        Pointer to the SkyFrame.
7319 f     STATUS = INTEGER (Given and Returned)
7320 f        The global status.
7321 
7322 *  Returned Value:
7323 c     astSkyOffsetMap()
7324 f     AST_SKYOFFSETMAP = INTEGER
7325 *        Pointer to the returned Mapping.
7326 
7327 *  Notes:
7328 *     - A null Object pointer (AST__NULL) will be returned if this
7329 c     function is invoked with the AST error status set, or if it
7330 f     function is invoked with STATUS set to an error value, or if it
7331 *     should fail for any reason.
7332 *--
7333 */
7334 
7335 /* Local Variables: */
7336    AstCmpMap *map3;            /* Partial Mapping. */
7337    AstMapping *result;         /* The returned Mapping. */
7338    AstMatrixMap *map1;         /* Spherical rotation in 3D cartesian space */
7339    AstSphMap *map2;            /* 3D Cartesian to 2D spherical Mapping */
7340    double *vx;                 /* Pointer to x unit vector. */
7341    double *vy;                 /* Pointer to y unit vector. */
7342    double *vz;                 /* Pointer to z unit vector. */
7343    double mat[ 9 ];            /* Spherical rotation matrix */
7344    double vmod;                /* Length of vector (+ve) */
7345    double vp[ 3 ];             /* Unit vector representin SkyRefP position. */
7346    int lataxis;                /* Index of the latitude axis */
7347    int lonaxis;                /* Index of the longitude axis */
7348 
7349 /* Initialise. */
7350    result = NULL;
7351 
7352 /* Check the global error status. */
7353    if ( !astOK ) return result;
7354 
7355 /* Return a UnitMap if the offset coordinate system is not defined. */
7356    if( astGetSkyRefIs( this ) == AST__IGNORED_REF ||
7357        ( !astTestSkyRef( this, 0 ) && !astTestSkyRef( this, 1 ) ) ) {
7358       result = (AstMapping *) astUnitMap( 2, "", status );
7359 
7360 /* Otherwise... */
7361    } else {
7362 
7363 /* Get the longitude and latitude at the reference point and at a point
7364    on the primary meridian. */
7365       lataxis = astGetLatAxis( this );
7366       lonaxis = 1 - lataxis;
7367 
7368 /* Initialise pointers to the rows of the 3x3 matrix. Each row will be
7369    used to store a unit vector. */
7370       vx = mat;
7371       vy = mat + 3;
7372       vz = mat + 6;
7373 
7374 /* The following trig converts between (longitude,latitude) and (x,y,z)
7375    on a unit sphere, in which (0,0) is at (1,0,0), (0,pi/2) is (0,0,1)
7376    and (pi/2,0) is at (0,1,0). */
7377 
7378 /* First deal with cases where the SkyRef attribute holds the standard
7379    coords at the origin of the offset coordinate system. */
7380       if( astGetSkyRefIs( this ) == AST__ORIGIN_REF ) {
7381 
7382 /* Convert each point into a 3-vector of unit length. The SkyRef position
7383    defines the X axis in the offset coord system. */
7384          palDcs2c( astGetSkyRef( this, lonaxis ), astGetSkyRef( this, lataxis ), vx );
7385          palDcs2c( astGetSkyRefP( this, lonaxis ), astGetSkyRefP( this, lataxis ), vp );
7386 
7387 /* The Y axis is perpendicular to both the X axis and the skyrefp
7388    position. That is, it is parallel to the cross product of the 2 above
7389    vectors.*/
7390          palDvxv( vp, vx, vy );
7391 
7392 /* Normalize the y vector. */
7393          palDvn( vy, vy, &vmod );
7394 
7395 /* Report an error if the modulus of the vector is zero.*/
7396          if( vmod == 0.0 ) {
7397             astError( AST__BADOC, "astConvert(%s): The position specified by the SkyRefP "
7398                       "attribute is either coincident, with or opposite to, the "
7399                       "position specified by the SkyRef attribute.", status, astGetClass( this ) );
7400 
7401 /* If OK, form the Z axis as the cross product of the x and y axes. */
7402          } else {
7403             palDvxv( vx, vy, vz );
7404 
7405          }
7406 
7407 /* Now deal with cases where the SkyRef attribute holds the standard
7408    coords at the north pole of the offset coordinate system. */
7409       } else {
7410 
7411 /* Convert each point into a 3-vector of unit length. The SkyRef position
7412    defines the Z axis in the offset coord system. */
7413          palDcs2c( astGetSkyRef( this, lonaxis ), astGetSkyRef( this, lataxis ), vz );
7414          palDcs2c( astGetSkyRefP( this, lonaxis ), astGetSkyRefP( this, lataxis ), vp );
7415 
7416 /* The Y axis is perpendicular to both the Z axis and the skyrefp
7417    position. That is, it is parallel to the cross product of the 2 above
7418    vectors.*/
7419          palDvxv( vz, vp, vy );
7420 
7421 /* Normalize the y vector. */
7422          palDvn( vy, vy, &vmod );
7423 
7424 /* Report an error if the modulus of the vector is zero.*/
7425          if( vmod == 0.0 ) {
7426             astError( AST__BADOC, "astConvert(%s): The position specified by the SkyRefP "
7427                       "attribute is either coincident, with or opposite to, the "
7428                       "position specified by the SkyRef attribute.", status, astGetClass( this ) );
7429 
7430 /* If OK, form the X axis as the cross product of the y and z axes. */
7431          } else {
7432             palDvxv( vy, vz, vx );
7433          }
7434       }
7435 
7436 /* Create a MatrixMap which implements the above spherical rotation. Each
7437    row in this matrix represents one of the unit axis vectors found above. */
7438       map1 = astMatrixMap( 3, 3, 0, mat, "", status );
7439 
7440 /* Create a 3D cartesian to 2D spherical Mapping. */
7441       map2 = astSphMap( "UnitRadius=1", status );
7442 
7443 /* Form a series CmpMap which converts from 2D (long,lat) in the base
7444    System to 2D (long,lat) in the offset coordinate system. */
7445       map3 = astCmpMap( map1, map2, 1, "", status );
7446       astInvert( map2 );
7447       result = (AstMapping *) astCmpMap( map2, map3, 1, "", status );
7448 
7449 /* Free resources. */
7450       map1 = astAnnul( map1 );
7451       map2 = astAnnul( map2 );
7452       map3 = astAnnul( map3 );
7453    }
7454 
7455 /* Annul the returned Mapping if anything has gone wrong. */
7456    if( !astOK ) result = astAnnul( result );
7457 
7458 /* Return the result. */
7459    return result;
7460 
7461 }
7462 
Offset2(AstFrame * this_frame,const double point1[2],double angle,double offset,double point2[2],int * status)7463 static double Offset2( AstFrame *this_frame, const double point1[2],
7464                        double angle, double offset, double point2[2], int *status ) {
7465 /*
7466 *  Name:
7467 *     Offset2
7468 
7469 *  Purpose:
7470 *     Calculate an offset along a geodesic curve at a given bearing.
7471 
7472 *  Type:
7473 *     Private function.
7474 
7475 *  Synopsis:
7476 *     #include "skyframe.h"
7477 *     double Offset2( AstFrame *this_frame, const double point1[2],
7478 *                     double angle, double offset, double point2[2], int *status )
7479 
7480 *  Class Membership:
7481 *     SkyFrame member function (over-rides the astOffset2 method
7482 *     inherited from the Frame class).
7483 
7484 *  Description:
7485 *     This function finds the SkyFrame coordinate values of a point
7486 *     which is offset a specified distance along the geodesic curve
7487 *     (i.e. great circle) at a given angle from a given starting point.
7488 
7489 *  Parameters:
7490 *     this
7491 *        Pointer to the SkyFrame.
7492 *     point1
7493 *        An array of double, with one element for each SkyFrame axis.
7494 *        This should contain the coordinates of the point marking the
7495 *        start of the geodesic curve.
7496 *     angle
7497 *        The angle (in radians) from the positive direction of the second
7498 *        axis, to the direction of the required position, as seen from
7499 *        the starting position. Positive rotation is in the sense of
7500 *        rotation from the positive direction of axis 2 to the positive
7501 *        direction of axis 1.
7502 *     offset
7503 *        The required offset from the first point along the geodesic
7504 *        curve, in radians. If this is positive, it will be towards
7505 *        the given angle. If it is negative, it will be in the
7506 *        opposite direction.
7507 *     point2
7508 *        An array of double, with one element for each SkyFrame axis
7509 *        in which the coordinates of the required point will be
7510 *        returned.
7511 *     status
7512 *        Pointer to the inherited status variable.
7513 
7514 *  Returned Value:
7515 *     The direction of the geodesic curve at the end point. That is, the
7516 *     angle (in radians) between the positive direction of the second
7517 *     axis and the continuation of the geodesic curve at the requested
7518 *     end point. Positive rotation is in the sense of rotation from
7519 *     the positive direction of axis 2 to the positive direction of axis
7520 *     1.
7521 
7522 *  Notes:
7523 *     - The geodesic curve used by this function is the path of
7524 *     shortest distance between two points, as defined by the
7525 *     astDistance function.
7526 *     - This function will return "bad" coordinate values (AST__BAD)
7527 *     if any of the input coordinates has this value.
7528 */
7529 
7530 /* Local Variables: */
7531    AstSkyFrame *this;          /* Pointer to the SkyFrame structure */
7532    const int *perm;            /* Pointer to axis permutation array */
7533    double p1[ 2 ];             /* Permuted coordinates for point1 */
7534    double p2[ 2 ];             /* Permuted coordinates for point2 */
7535    double result;              /* The returned answer */
7536    double cosoff;              /* Cosine of offset */
7537    double cosa1;               /* Cosine of longitude at start */
7538    double cosb1;               /* Cosine of latitude at start */
7539    double pa;                  /* A position angle measured from north */
7540    double q1[ 3 ];             /* Vector PI/2 away from R4 in meridian of R4 */
7541    double q2[ 3 ];             /* Vector PI/2 away from R4 on equator */
7542    double q3[ 3 ];             /* Vector PI/2 away from R4 on great circle */
7543    double r0[ 3 ];             /* Reference position vector */
7544    double r3[ 3 ];             /* Vector PI/2 away from R0 on great circle */
7545    double sinoff;              /* Sine of offset */
7546    double sina1;               /* Sine of longitude at start */
7547    double sinb1;               /* Sine of latitude at start */
7548 
7549 /* Initialise. */
7550    result = AST__BAD;
7551 
7552 /* Check the global error status. */
7553    if ( !astOK ) return result;
7554 
7555 /* Obtain a pointer to the SkyFrame structure. */
7556    this = (AstSkyFrame *) this_frame;
7557 
7558 /* Obtain a pointer to the SkyFrame's axis permutation array. */
7559    perm = astGetPerm( this );
7560    if ( astOK ) {
7561 
7562 /* Check that all supplied values are OK. If not, generate "bad"
7563    output coordinates. */
7564       if ( ( point1[ 0 ] == AST__BAD ) || ( point1[ 1 ] == AST__BAD ) ||
7565            ( angle == AST__BAD ) || ( offset == AST__BAD ) ) {
7566          point2[ 0 ] = AST__BAD;
7567          point2[ 1 ] = AST__BAD;
7568 
7569 /* Otherwise, apply the axis permutation array to obtain the
7570    coordinates of the starting point in the required (longitude,latitude)
7571    order. */
7572       } else {
7573          p1[ perm[ 0 ] ] = point1[ 0 ];
7574          p1[ perm[ 1 ] ] = point1[ 1 ];
7575 
7576 /* If the axes are permuted, convert the supplied angle into a position
7577    angle. */
7578          pa = ( perm[ 0 ] == 0 )? angle: piby2 - angle;
7579 
7580 /* Use Shcal to calculate the required vectors R0 (representing
7581    the reference point) and R3 (representing the point which is 90
7582    degrees away from the reference point, along the required great
7583    circle). The XY plane defines zero latitude, Z is in the direction
7584    of increasing latitude, X is towards zero longitude, and Y is
7585    towards longitude 90 degrees. */
7586          Shcal( p1[ 0 ], p1[ 1 ], pa, r0, r3, status );
7587 
7588 /* Use Shapp to use R0 and R3 to calculate the new position. */
7589          Shapp( offset, r0, r3,  p1[ 0 ], p2, status );
7590 
7591 /* Normalize the result. */
7592          astNorm( this, p2 );
7593 
7594 /* Create the vector Q1 representing the point in the meridian of the
7595    required point which has latitude 90 degrees greater than the
7596    required point. */
7597          sina1 = sin( p2[ 0 ] );
7598          cosa1 = cos( p2[ 0 ] );
7599          sinb1 = sin( p2[ 1 ] );
7600          cosb1 = cos( p2[ 1 ] );
7601 
7602          q1[ 0 ] = -sinb1*cosa1;
7603          q1[ 1 ] = -sinb1*sina1;
7604          q1[ 2 ] = cosb1;
7605 
7606 /* Create the vector Q2 representing the point on the equator (i.e. a
7607    latitude of zero), which has a longitude 90 degrees to the west of
7608    the required point. */
7609          q2[ 0 ] = -sina1;
7610          q2[ 1 ] =  cosa1;
7611          q2[ 2 ] =  0.0;
7612 
7613 /* Create the vector Q3 representing the point which is 90 degrees away
7614    from the required point, along the required great circle. */
7615          cosoff = cos( offset );
7616          sinoff = sin( offset );
7617 
7618          q3[ 0 ] = -sinoff*r0[ 0 ] + cosoff*r3[ 0 ];
7619          q3[ 1 ] = -sinoff*r0[ 1 ] + cosoff*r3[ 1 ];
7620          q3[ 2 ] = -sinoff*r0[ 2 ] + cosoff*r3[ 2 ];
7621 
7622 /* Calculate the position angle of the great circle at the required
7623    point. */
7624          pa = atan2( palDvdv( q3, q2 ), palDvdv( q3, q1 ) );
7625 
7626 /* Convert this from a pa into the required angle. */
7627          result = ( perm[ 0 ] == 0 )? pa: piby2 - pa;
7628 
7629 /* Ensure that the end angle is in the range 0 to 2*pi. */
7630          result = palDranrm( result );
7631 
7632 /* Permute the result coordinates to undo the effect of the SkyFrame
7633    axis permutation array. */
7634          point2[ 0 ] = p2[ perm[ 0 ] ];
7635          point2[ 1 ] = p2[ perm[ 1 ] ];
7636       }
7637    }
7638 
7639 /* Return the result. */
7640    return result;
7641 
7642 }
7643 
Overlay(AstFrame * template,const int * template_axes,AstFrame * result,int * status)7644 static void Overlay( AstFrame *template, const int *template_axes,
7645                      AstFrame *result, int *status ) {
7646 /*
7647 *  Name:
7648 *     Overlay
7649 
7650 *  Purpose:
7651 *     Overlay the attributes of a template SkyFrame on to another Frame.
7652 
7653 *  Type:
7654 *     Private function.
7655 
7656 *  Synopsis:
7657 *     #include "skyframe.h"
7658 *     void Overlay( AstFrame *template, const int *template_axes,
7659 *                   AstFrame *result, int *status )
7660 
7661 *  Class Membership:
7662 *     SkyFrame member function (over-rides the protected astOverlay method
7663 *     inherited from the Frame class).
7664 
7665 *  Description:
7666 *     This function overlays attributes of a SkyFrame (the "template") on to
7667 *     another Frame, so as to over-ride selected attributes of that second
7668 *     Frame. Normally only those attributes which have been specifically set
7669 *     in the template will be transferred. This implements a form of
7670 *     defaulting, in which a Frame acquires attributes from the template, but
7671 *     retains its original attributes (as the default) if new values have not
7672 *     previously been explicitly set in the template.
7673 *
7674 *     Note that if the result Frame is a SkyFrame and a change of sky
7675 *     coordinate system occurs as a result of overlaying its System
7676 *     attribute, then some of its original attribute values may no
7677 *     longer be appropriate (e.g. the Title, or attributes describing
7678 *     its axes). In this case, these will be cleared before overlaying
7679 *     any new values.
7680 
7681 *  Parameters:
7682 *     template
7683 *        Pointer to the template SkyFrame, for which values should have been
7684 *        explicitly set for any attribute which is to be transferred.
7685 *     template_axes
7686 *        Pointer to an array of int, with one element for each axis of the
7687 *        "result" Frame (see below). For each axis in the result frame, the
7688 *        corresponding element of this array should contain the (zero-based)
7689 *        index of the template axis to which it corresponds. This array is used
7690 *        to establish from which template axis any axis-dependent attributes
7691 *        should be obtained.
7692 *
7693 *        If any axis in the result Frame is not associated with a template
7694 *        axis, the corresponding element of this array should be set to -1.
7695 *
7696 *        If a NULL pointer is supplied, the template and result axis
7697 *        indicies are assumed to be identical.
7698 *     result
7699 *        Pointer to the Frame which is to receive the new attribute values.
7700 *     status
7701 *        Pointer to the inherited status variable.
7702 
7703 *  Returned Value:
7704 *     void
7705 
7706 *  Notes:
7707 *     -  In general, if the result Frame is not from the same class as the
7708 *     template SkyFrame, or from a class derived from it, then attributes may
7709 *     exist in the template SkyFrame which do not exist in the result Frame. In
7710 *     this case, these attributes will not be transferred.
7711 */
7712 
7713 
7714 /* Local Variables: */
7715    AstSystemType new_alignsystem;/* Code identifying new alignment coords */
7716    AstSystemType new_system;     /* Code identifying new sky cordinates */
7717    AstSystemType old_system;     /* Code identifying old sky coordinates */
7718    int axis;                     /* Loop counter for result SkyFrame axes */
7719    int skyref_changed;           /* Has the SkyRef attribute changed? */
7720    int reset_system;             /* Was the template System value cleared? */
7721    int skyframe;                 /* Result Frame is a SkyFrame? */
7722    int tax0;                     /* Template axis for result axis 0 */
7723    int tax1;                     /* Template axis for result axis 1 */
7724 
7725 /* Check the global error status. */
7726    if ( !astOK ) return;
7727 
7728 /* Indicate that we do not need to reset the System attribute of the
7729    template. */
7730    reset_system = 0;
7731    new_system = AST__UNKNOWN;
7732 
7733 /* If the result Frame is a SkyFrame, we must test to see if overlaying its
7734    System attribute will change the type of sky coordinate system it
7735    describes. Determine the value of this attribute for the result and template
7736    SkyFrames. We also need to do this if either SkyRef attribute would
7737    change. */
7738    skyframe = astIsASkyFrame( result );
7739    if ( skyframe ) {
7740       old_system = astGetSystem( result );
7741       new_system = astGetSystem( template );
7742       skyref_changed = ( astGetSkyRef( result, 0 ) !=
7743                          astGetSkyRef( template, 0 ) ) ||
7744                        ( astGetSkyRef( result, 1 ) !=
7745                          astGetSkyRef( template, 1 ) );
7746 
7747 /* If the coordinate system will change, any value already set for the result
7748    SkyFrame's Title will no longer be appropriate, so clear it. */
7749       if ( new_system != old_system || skyref_changed ) {
7750          astClearTitle( result );
7751 
7752 /* Test if the old and new sky coordinate systems are similar enough to make
7753    use of the same axis attribute values (e.g. if they are both equatorial
7754    systems, then they can both use the same axis labels, etc.,so long as
7755    the SKyRefIs value has not changed). */
7756          if ( IsEquatorial( new_system, status ) != IsEquatorial( old_system, status ) ||
7757               skyref_changed ) {
7758 
7759 /* If necessary, clear inappropriate values for all those axis attributes
7760    whose access functions are over-ridden by this class (these access functions
7761    will then provide suitable defaults appropriate to the new coordinate system
7762    instead). */
7763             for ( axis = 0; axis < 2; axis++ ) {
7764                astClearAsTime( result, axis );
7765                astClearDirection( result, axis );
7766                astClearFormat( result, axis );
7767                astClearLabel( result, axis );
7768                astClearSymbol( result, axis );
7769                astClearUnit( result, axis );
7770             }
7771          }
7772       }
7773 
7774 /* If the result Frame is not a SkyFrame, we must temporarily clear the
7775    System and AlignSystem values since the values used by this class are only
7776    appropriate to this class. */
7777    } else {
7778       if( astTestSystem( template ) ) {
7779          new_system = astGetSystem( template );
7780          astClearSystem( template );
7781          new_alignsystem = astGetAlignSystem( template );
7782          astClearAlignSystem( template );
7783          reset_system = 1;
7784       }
7785    }
7786 
7787 /* Invoke the parent class astOverlay method to transfer attributes inherited
7788    from the parent class. */
7789    (*parent_overlay)( template, template_axes, result, status );
7790 
7791 /* Reset the System and AlignSystem values if necessary */
7792    if( reset_system ) {
7793       astSetSystem( template, new_system );
7794       astSetAlignSystem( template, new_alignsystem );
7795    }
7796 
7797 /* Check if the result Frame is a SkyFrame or from a class derived from
7798    SkyFrame. If not, we cannot transfer SkyFrame attributes to it as it is
7799    insufficiently specialised. In this case simply omit these attributes. */
7800    if ( skyframe && astOK ) {
7801 
7802 /* Define a macro that tests whether an attribute is set in the template and,
7803    if so, transfers its value to the result. */
7804 #define OVERLAY(attr) \
7805    if ( astTest##attr( template ) ) { \
7806       astSet##attr( result, astGet##attr( template ) ); \
7807    }
7808 
7809 /* Store template axis indices */
7810    if( template_axes ) {
7811       tax0 = template_axes[ 0 ];
7812       tax1 = template_axes[ 1 ];
7813    } else {
7814       tax0 = 0;
7815       tax1 = 1;
7816    }
7817 
7818 /* Define a similar macro that does the same for SkyFrame specific axis
7819    attributes. */
7820 #define OVERLAY2(attr) \
7821    if( astTest##attr( template, tax0 ) ) { \
7822       astSet##attr( result, 0, astGet##attr( template, tax0 ) ); \
7823    } \
7824    if( astTest##attr( template, tax1 ) ) { \
7825       astSet##attr( result, 1, astGet##attr( template, tax1 ) ); \
7826    }
7827 
7828 /* Use the macro to transfer each SkyFrame attribute in turn. */
7829       OVERLAY(Equinox);
7830       OVERLAY(Projection);
7831       OVERLAY(NegLon);
7832       OVERLAY(AlignOffset);
7833       OVERLAY(SkyRefIs);
7834       OVERLAY2(SkyRef);
7835       OVERLAY2(SkyRefP);
7836    }
7837 
7838 /* Undefine macros local to this function. */
7839 #undef OVERLAY
7840 #undef OVERLAY2
7841 }
7842 
Resolve(AstFrame * this_frame,const double point1[],const double point2[],const double point3[],double point4[],double * d1,double * d2,int * status)7843 static void Resolve( AstFrame *this_frame, const double point1[],
7844                      const double point2[], const double point3[],
7845                      double point4[], double *d1, double *d2, int *status ){
7846 /*
7847 *  Name:
7848 *     Resolve
7849 
7850 *  Purpose:
7851 *     Resolve a vector into two orthogonal components
7852 
7853 *  Type:
7854 *     Private function.
7855 
7856 *  Synopsis:
7857 *     #include "skyframe.h"
7858 *     void Resolve( AstFrame *this, const double point1[],
7859 *                   const double point2[], const double point3[],
7860 *                   double point4[], double *d1, double *d2, int *status )
7861 
7862 *  Class Membership:
7863 *     SkyFrame member function (over-rides the astResolve method
7864 *     inherited from the Frame class).
7865 
7866 *  Description:
7867 *     This function resolves a vector into two perpendicular components.
7868 *     The vector from point 1 to point 2 is used as the basis vector.
7869 *     The vector from point 1 to point 3 is resolved into components
7870 *     parallel and perpendicular to this basis vector. The lengths of the
7871 *     two components are returned, together with the position of closest
7872 *     aproach of the basis vector to point 3.
7873 *
7874 *     Each vector is a geodesic curve. For a SkyFrame, these are great
7875 *     circles on the celestial sphere.
7876 
7877 *  Parameters:
7878 *     this
7879 *        Pointer to the Frame.
7880 *     point1
7881 *        An array of double, with one element for each Frame axis
7882 *        (Naxes attribute). This marks the start of the basis vector,
7883 *        and of the vector to be resolved.
7884 *     point2
7885 *        An array of double, with one element for each Frame axis
7886 *        (Naxes attribute). This marks the end of the basis vector.
7887 *     point3
7888 *        An array of double, with one element for each Frame axis
7889 *        (Naxes attribute). This marks the end of the vector to be
7890 *        resolved.
7891 *     point4
7892 *        An array of double, with one element for each Frame axis
7893 *        in which the coordinates of the point of closest approach of the
7894 *        basis vector to point 3 will be returned.
7895 *     d1
7896 *        The address of a location at which to return the distance from
7897 *        point 1 to point 4 (that is, the length of the component parallel
7898 *        to the basis vector). Positive values are in the same sense as
7899 *        movement from point 1 to point 2.
7900 *     d2
7901 *        The address of a location at which to return the distance from
7902 *        point 4 to point 3 (that is, the length of the component
7903 *        perpendicular to the basis vector). The returned value is always
7904 *        positive.
7905 *     status
7906 *        Pointer to the inherited status variable.
7907 
7908 *  Notes:
7909 *     - This function will return "bad" coordinate values (AST__BAD)
7910 *     if any of the input coordinates has this value, or if the required
7911 *     output values are undefined.
7912 */
7913 
7914 /* Local Variables: */
7915    AstSkyFrame *this;            /* Pointer to the SkyFrame structure */
7916    const int *perm;              /* Pointer to axis permutation array */
7917    double n1[ 3 ];               /* Unit normal to grt crcl thru p1 and p2 */
7918    double n2[ 3 ];               /* Unit normal to grt crcl thru p3 and p4 */
7919    double p1[ 2 ];               /* Permuted coordinates for point1 */
7920    double p2[ 2 ];               /* Permuted coordinates for point2 */
7921    double p3[ 2 ];               /* Permuted coordinates for point3 */
7922    double p4[ 2 ];               /* Permuted coordinates for point4 */
7923    double v1[ 3 ];               /* 3-vector for p1 */
7924    double v2[ 3 ];               /* 3-vector for p2 */
7925    double v3[ 3 ];               /* 3-vector for p3 */
7926    double v4[ 3 ];               /* 3-vector for p4 */
7927    double v5[ 3 ];               /* 3-vector 90 degs away from p1 */
7928    double vmod;                  /* Modulus of vector */
7929    double vtemp[ 3 ];            /* Temporary vector workspace */
7930 
7931 /* Check the global error status. */
7932    if ( !astOK ) return;
7933 
7934 /* Obtain a pointer to the SkyFrame structure. */
7935    this = (AstSkyFrame *) this_frame;
7936 
7937 /* Store initial bad output values. */
7938    point4[ 0 ] = AST__BAD;
7939    point4[ 1 ] = AST__BAD;
7940    *d1 = AST__BAD;
7941    *d2 = AST__BAD;
7942 
7943 /* Check that all supplied values are OK. */
7944    if ( ( point1[ 0 ] != AST__BAD ) && ( point1[ 1 ] != AST__BAD ) &&
7945         ( point2[ 0 ] != AST__BAD ) && ( point2[ 1 ] != AST__BAD ) &&
7946         ( point3[ 0 ] != AST__BAD ) && ( point3[ 1 ] != AST__BAD ) ) {
7947 
7948 /* If so, obtain a pointer to the SkyFrame's axis permutation array. */
7949       perm = astGetPerm( this );
7950       if ( astOK ) {
7951 
7952 /* Apply the axis permutation array to obtain the coordinates of the
7953    three supplied point in the required (longitude,latitude) order. */
7954          p1[ perm[ 0 ] ] = point1[ 0 ];
7955          p1[ perm[ 1 ] ] = point1[ 1 ];
7956          p2[ perm[ 0 ] ] = point2[ 0 ];
7957          p2[ perm[ 1 ] ] = point2[ 1 ];
7958          p3[ perm[ 0 ] ] = point3[ 0 ];
7959          p3[ perm[ 1 ] ] = point3[ 1 ];
7960 
7961 /* Convert each point into a 3-vector of unit length. */
7962          palDcs2c( p1[ 0 ], p1[ 1 ], v1 );
7963          palDcs2c( p2[ 0 ], p2[ 1 ], v2 );
7964          palDcs2c( p3[ 0 ], p3[ 1 ], v3 );
7965 
7966 /* Find the cross product between the first two vectors, and normalize is.
7967    This is the unit normal to the great circle plane defining parallel
7968    distance. */
7969          palDvxv( v2, v1, vtemp );
7970          palDvn( vtemp, n1, &vmod );
7971 
7972 /* Return with bad values if the normal is undefined (i.e. if the first two
7973    vectors are identical or diametrically opposite). */
7974          if( vmod > 0.0 ) {
7975 
7976 /* Now take the cross product of the normal vector and v1. This gives a
7977    point, v5, on the great circle which is 90 degrees away from v1, in the
7978    direction of v2. */
7979             palDvxv( v1, n1, v5 );
7980 
7981 /* Find the cross product of the outlying point (point 3), and the vector
7982    n1 found above, and normalize it. This is the unit normal to the great
7983    circle plane defining perpendicular distance. */
7984             palDvxv( v3, n1, vtemp );
7985             palDvn( vtemp, n2, &vmod );
7986 
7987 /* Return with bad values if the normal is undefined (i.e. if the
7988    outlying point is normal to the great circle defining the basis
7989    vector). */
7990             if( vmod > 0.0 ) {
7991 
7992 /* The point of closest approach, point 4, is the point which is normal
7993    to both normal vectors (i.e. the intersection of the two great circles).
7994    This is the cross product of n1 and n2. No need to normalize this time
7995    since both n1 and n2 are unit vectors, and so v4 will already be a
7996    unit vector. */
7997                palDvxv( n1, n2, v4 );
7998 
7999 /* The dot product of v4 and v1 is the cos of the parallel distance,
8000    d1, whilst the dot product of v4 and v5 is the sin of the parallel
8001    distance. Use these to get the parallel distance with the correct
8002    sign, in the range -PI to +PI. */
8003                *d1 = atan2( palDvdv( v4, v5 ), palDvdv( v4, v1 ) );
8004 
8005 /* The dot product of v4 and v3 is the cos of the perpendicular distance,
8006    d2, whilst the dot product of n1 and v3 is the sin of the perpendicular
8007    distance. Use these to get the perpendicular distance. */
8008                *d2 = fabs( atan2( palDvdv( v3, n1 ), palDvdv( v3, v4 ) ) );
8009 
8010 /* Convert the 3-vector representing the intersection of the two planes
8011    back into spherical cooordinates and then constrain the longitude result
8012    to lie in the range 0 to 2*pi. */
8013                palDcc2s( v4, &p4[ 0 ], &p4[ 1 ] );
8014                p4[ 0 ] = palDranrm( p4[ 0 ] );
8015 
8016 /* Permute the result coordinates to undo the effect of the SkyFrame
8017    axis permutation array. */
8018                point4[ 0 ] = p4[ perm[ 0 ] ];
8019                point4[ 1 ] = p4[ perm[ 1 ] ];
8020             }
8021          }
8022       }
8023    }
8024 
8025    return;
8026 
8027 }
8028 
ResolvePoints(AstFrame * this_frame,const double point1[],const double point2[],AstPointSet * in,AstPointSet * out,int * status)8029 static AstPointSet *ResolvePoints( AstFrame *this_frame, const double point1[],
8030                                    const double point2[], AstPointSet *in,
8031                                    AstPointSet *out, int *status ) {
8032 /*
8033 *  Name:
8034 *     ResolvePoints
8035 
8036 *  Purpose:
8037 *     Resolve a set of vectors into orthogonal components
8038 
8039 *  Type:
8040 *     Private function.
8041 
8042 *  Synopsis:
8043 *     #include "frame.h"
8044 *     AstPointSet *astResolvePoints( AstFrame *this, const double point1[],
8045 *                                    const double point2[], AstPointSet *in,
8046 *                                    AstPointSet *out )
8047 
8048 *  Class Membership:
8049 *     SkyFrame member function (over-rides the astResolvePoints method
8050 *     inherited from the Frame class).
8051 
8052 *  Description:
8053 *     This function takes a Frame and a set of vectors encapsulated
8054 *     in a PointSet, and resolves each one into two orthogonal components,
8055 *     returning these two components in another PointSet.
8056 *
8057 *     This is exactly the same as the public astResolve method, except
8058 *     that this method allows many vectors to be processed in a single call,
8059 *     thus reducing the computational cost of overheads of many
8060 *     individual calls to astResolve.
8061 
8062 *  Parameters:
8063 *     this
8064 *        Pointer to the Frame.
8065 *     point1
8066 *        An array of double, with one element for each Frame axis
8067 *        (Naxes attribute). This marks the start of the basis vector,
8068 *        and of the vectors to be resolved.
8069 *     point2
8070 *        An array of double, with one element for each Frame axis
8071 *        (Naxes attribute). This marks the end of the basis vector.
8072 *     in
8073 *        Pointer to the PointSet holding the ends of the vectors to be
8074 *        resolved.
8075 *     out
8076 *        Pointer to a PointSet which will hold the length of the two
8077 *        resolved components. A NULL value may also be given, in which
8078 *        case a new PointSet will be created by this function.
8079 
8080 *  Returned Value:
8081 *     Pointer to the output (possibly new) PointSet. The first axis will
8082 *     hold the lengths of the vector components parallel to the basis vector.
8083 *     These values will be signed (positive values are in the same sense as
8084 *     movement from point 1 to point 2. The second axis will hold the lengths
8085 *     of the vector components perpendicular to the basis vector. These
8086 *     values will be signed only if the Frame is 2-dimensional, in which
8087 *     case a positive value indicates that rotation from the basis vector
8088 *     to the tested vector is in the same sense as rotation from the first
8089 *     to the second axis of the Frame.
8090 
8091 *  Notes:
8092 *     - The number of coordinate values per point in the input
8093 *     PointSet must match the number of axes in the supplied Frame.
8094 *     - If an output PointSet is supplied, it must have space for
8095 *     sufficient number of points and 2 coordinate values per point.
8096 *     - A null pointer will be returned if this function is invoked
8097 *     with the global error status set, or if it should fail for any
8098 *     reason.
8099 *     - We assume spherical geometry throughout this function.
8100 */
8101 
8102 /* Local Variables: */
8103    AstPointSet *result;          /* Pointer to output PointSet */
8104    AstSkyFrame *this;            /* Pointer to SkyFrame structure */
8105    const int *perm;              /* Pointer to axis permutation array */
8106    double **ptr_in;              /* Pointers to input axis values */
8107    double **ptr_out;             /* Pointers to returned axis values */
8108    double *d1;                   /* Pointer to next parallel component value */
8109    double *d2;                   /* Pointer to next perpendicular component value */
8110    double *point3x;              /* Pointer to next first axis value */
8111    double *point3y;              /* Pointer to next second axis value */
8112    double n1[ 3 ];               /* Unit normal to grt crcl thru p1 and p2 */
8113    double n2[ 3 ];               /* Unit normal to grt crcl thru p3 and p4 */
8114    double p1[ 2 ];               /* Permuted coordinates for point1 */
8115    double p2[ 2 ];               /* Permuted coordinates for point2 */
8116    double p3[ 2 ];               /* Permuted coordinates for point3 */
8117    double sign;                  /* Sign for perpendicular distances */
8118    double v1[ 3 ];               /* 3-vector for p1 */
8119    double v2[ 3 ];               /* 3-vector for p2 */
8120    double v3[ 3 ];               /* 3-vector for p3 */
8121    double v4[ 3 ];               /* 3-vector for p4 */
8122    double v5[ 3 ];               /* 3-vector 90 degs away from p1 */
8123    double vmod;                  /* Modulus of vector */
8124    double vtemp[ 3 ];            /* Temporary vector workspace */
8125    int ipoint;                   /* Index of next point */
8126    int ncoord_in;                /* Number of input PointSet coordinates */
8127    int ncoord_out;               /* Number of coordinates in output PointSet */
8128    int npoint;                   /* Number of points to transform */
8129    int npoint_out;               /* Number of points in output PointSet */
8130    int ok;                       /* OK to proceed? */
8131 
8132 /* Initialise. */
8133    result = NULL;
8134 
8135 /* Check the global error status. */
8136    if ( !astOK ) return result;
8137 
8138 /* Get a pointer to the SkyFrame structure. */
8139    this = (AstSkyFrame *) this_frame;
8140 
8141 /* Obtain the number of input vectors to resolve and the number of coordinate
8142    values per vector. */
8143    npoint = astGetNpoint( in );
8144    ncoord_in = astGetNcoord( in );
8145 
8146 /* If OK, check that the number of input coordinates matches the number
8147    required by the Frame. Report an error if these numbers do not match. */
8148    if ( astOK && ( ncoord_in != 2 ) ) {
8149       astError( AST__NCPIN, "astResolvePoints(%s): Bad number of coordinate "
8150                 "values (%d) in input %s.", status, astGetClass( this ), ncoord_in,
8151                 astGetClass( in ) );
8152       astError( AST__NCPIN, "The %s given requires 2 coordinate values for "
8153                 "each input point.", status, astGetClass( this ) );
8154    }
8155 
8156 /* If still OK, and a non-NULL pointer has been given for the output PointSet,
8157    then obtain the number of points and number of coordinates per point for
8158    this PointSet. */
8159    if ( astOK && out ) {
8160       npoint_out = astGetNpoint( out );
8161       ncoord_out = astGetNcoord( out );
8162 
8163 /* Check that the dimensions of this PointSet are adequate to accommodate the
8164    output coordinate values and report an error if they are not. */
8165       if ( astOK ) {
8166          if ( npoint_out < npoint ) {
8167             astError( AST__NOPTS, "astResolvePoints(%s): Too few points (%d) in "
8168                       "output %s.", status, astGetClass( this ), npoint_out,
8169                       astGetClass( out ) );
8170             astError( AST__NOPTS, "The %s needs space to hold %d transformed "
8171                       "point(s).", status, astGetClass( this ), npoint );
8172          } else if ( ncoord_out < 2 ) {
8173             astError( AST__NOCTS, "astResolvePoints(%s): Too few coordinate "
8174                       "values per point (%d) in output %s.", status,
8175                       astGetClass( this ), ncoord_out, astGetClass( out ) );
8176             astError( AST__NOCTS, "The %s supplied needs space to store 2 "
8177                       "coordinate value(s) per transformed point.", status,
8178                       astGetClass( this ) );
8179          }
8180       }
8181    }
8182 
8183 /* If all the validation stages are passed successfully, and a NULL output
8184    pointer was given, then create a new PointSet to encapsulate the output
8185    coordinate data. */
8186    if ( astOK ) {
8187       if ( !out ) {
8188          result = astPointSet( npoint, 2, "", status );
8189 
8190 /* Otherwise, use the PointSet supplied. */
8191       } else {
8192          result = out;
8193       }
8194    }
8195 
8196 /* Get pointers to the input and output axis values */
8197    ptr_in = astGetPoints( in );
8198    ptr_out = astGetPoints( result );
8199 
8200 /* Obtain a pointer to the SkyFrame's axis permutation array. */
8201    perm = astGetPerm( this );
8202 
8203 /* If the axes have been swapped we need to swap the sign of the returned
8204    perpendicular distances. */
8205    sign = ( perm[ 0 ] == 0 ) ? -1.0 : 1.0;
8206 
8207 /* Check pointers can be used safely */
8208    if( astOK ) {
8209 
8210 /* Apply the axis permutation array to obtain the coordinates of the
8211    two supplied points in the required (longitude,latitude) order. */
8212       p1[ perm[ 0 ] ] = point1[ 0 ];
8213       p1[ perm[ 1 ] ] = point1[ 1 ];
8214       p2[ perm[ 0 ] ] = point2[ 0 ];
8215       p2[ perm[ 1 ] ] = point2[ 1 ];
8216 
8217 /* Convert these points into 3-vectors of unit length. */
8218       palDcs2c( p1[ 0 ], p1[ 1 ], v1 );
8219       palDcs2c( p2[ 0 ], p2[ 1 ], v2 );
8220 
8221 /* Find the cross product between the vectors, and normalize it. This is the
8222    unit normal to the great circle plane defining parallel distance. */
8223       palDvxv( v2, v1, vtemp );
8224       palDvn( vtemp, n1, &vmod );
8225 
8226 /* Return with bad values if the normal is undefined (i.e. if the first two
8227    vectors are identical or diametrically opposite). */
8228       ok = 0;
8229       if( vmod > 0.0 ) {
8230          ok = 1;
8231 
8232 /* Now take the cross product of the normal vector and v1. This gives a
8233    point, v5, on the great circle which is 90 degrees away from v1, in the
8234    direction of v2. */
8235          palDvxv( v1, n1, v5 );
8236       }
8237 
8238 /* Store pointers to the first two axis arrays in the returned PointSet. */
8239       d1 = ptr_out[ 0 ];
8240       d2 = ptr_out[ 1 ];
8241 
8242 /* Store pointers to the axis values in the supplied PointSet. */
8243       point3x = ptr_in[ 0 ];
8244       point3y = ptr_in[ 1 ];
8245 
8246 /* Check supplied values can be used */
8247       if( ok ) {
8248 
8249 /* Loop round each supplied vector. */
8250          for( ipoint = 0; ipoint < npoint; ipoint++, d1++, d2++,
8251                                            point3x++, point3y++ ) {
8252 
8253 /* Store bad output values if either input axis value is bad. */
8254             if( *point3x == AST__BAD || *point3y == AST__BAD ){
8255                *d1 = AST__BAD;
8256                *d2 = AST__BAD;
8257 
8258 /* If both are good... */
8259             } else {
8260 
8261 /* Apply the axis permutation array to obtain the coordinates in the
8262    required (longitude,latitude) order. */
8263                p3[ perm[ 0 ] ] = *point3x;
8264                p3[ perm[ 1 ] ] = *point3y;
8265 
8266 /* Convert into a 3-vector of unit length. */
8267                palDcs2c( p3[ 0 ], p3[ 1 ], v3 );
8268 
8269 /* Find the cross product of the outlying point (point 3), and the vector
8270    n1 found above, and normalize it. This is the unit normal to the great
8271    circle plane defining perpendicular distance. */
8272                palDvxv( v3, n1, vtemp );
8273                palDvn( vtemp, n2, &vmod );
8274 
8275 /* Return with bad values if the normal is undefined (i.e. if the
8276    outlying point is normal to the great circle defining the basis
8277    vector). */
8278                if( vmod <= 0.0 ) {
8279                   *d1 = AST__BAD;
8280                   *d2 = AST__BAD;
8281                } else {
8282 
8283 /* The point of closest approach, point 4, is the point which is normal
8284    to both normal vectors (i.e. the intersection of the two great circles).
8285    This is the cross product of n1 and n2. No need to normalize this time
8286    since both n1 and n2 are unit vectors, and so v4 will already be a
8287    unit vector. */
8288                   palDvxv( n1, n2, v4 );
8289 
8290 /* The dot product of v4 and v1 is the cos of the parallel distance,
8291    d1, whilst the dot product of v4 and v5 is the sin of the parallel
8292    distance. Use these to get the parallel distance with the correct
8293    sign, in the range -PI to +PI. */
8294                   *d1 = atan2( palDvdv( v4, v5 ), palDvdv( v4, v1 ) );
8295 
8296 /* The dot product of v4 and v3 is the cos of the perpendicular distance,
8297    d2, whilst the dot product of n1 and v3 is the sin of the perpendicular
8298    distance. Use these to get the perpendicular distance. */
8299                   *d2 = sign*atan2( palDvdv( v3, n1 ), palDvdv( v3, v4 ) );
8300                }
8301             }
8302          }
8303 
8304 /* If supplied values cannot be used, fill the returned PointSet with bad
8305    values */
8306       } else {
8307          for( ipoint = 0; ipoint < npoint; ipoint++, d1++, d2++ ) {
8308             *d1 = AST__BAD;
8309             *d2 = AST__BAD;
8310          }
8311       }
8312    }
8313 
8314 /* Annul the returned PointSet if an error occurred. */
8315    if( !astOK ) result = astAnnul( result );
8316 
8317 /* Return a pointer to the output PointSet. */
8318    return result;
8319 }
8320 
SetAsTime(AstSkyFrame * this,int axis,int value,int * status)8321 static void SetAsTime( AstSkyFrame *this, int axis, int value, int *status ) {
8322 /*
8323 *  Name:
8324 *     SetAsTime
8325 
8326 *  Purpose:
8327 *     Set a value for the AsTime attribute for a SkyFrame's axis.
8328 
8329 *  Type:
8330 *     Private function.
8331 
8332 *  Synopsis:
8333 *     #include "skyframe.h"
8334 *     void SetAsTime( AstSkyFrame *this, int axis, int value, int *status )
8335 
8336 *  Class Membership:
8337 *     SkyFrame member function.
8338 
8339 *  Description:
8340 *     This function sets the boolean value of the AsTime attribute for a
8341 *     specified axis of a SkyFrame. This value indicates whether axis values
8342 *     should be formatted as times (as opposed to angles) by default.
8343 
8344 *  Parameters:
8345 *     this
8346 *        Pointer to the SkyFrame.
8347 *     axis
8348 *        Index of the axis for which a value is to be set (zero based).
8349 *     value
8350 *        The boolean value to be set.
8351 *     status
8352 *        Pointer to the inherited status variable.
8353 
8354 *  Returned Value:
8355 *     void.
8356 */
8357 
8358 /* Local Variables: */
8359    AstAxis *ax;                  /* Pointer to Axis object */
8360    AstSkyAxis *new_ax;           /* Pointer to new SkyAxis object */
8361 
8362 /* Check the global error status. */
8363    if ( !astOK ) return;
8364 
8365 /* Validate the axis index. */
8366    (void) astValidateAxis( this, axis, 1, "astSetAsTime" );
8367 
8368 /* Obtain a pointer to the Axis object. */
8369    ax = astGetAxis( this, axis );
8370 
8371 /* Check if the Axis object is a SkyAxis. If not, we will replace it with
8372    one. */
8373    if ( !astIsASkyAxis( ax ) ) {
8374 
8375 /* Create a new SkyAxis and overlay the attributes of the original Axis. */
8376       new_ax = astSkyAxis( "", status );
8377       astAxisOverlay( ax, new_ax );
8378 
8379 /* Modify the SkyFrame to use the new Skyaxis and annul the original Axis
8380    pointer. Retain a pointer to the new SkyAxis. */
8381       astSetAxis( this, axis, new_ax );
8382       ax = astAnnul( ax );
8383       ax = (AstAxis *) new_ax;
8384    }
8385 
8386 /* Set a value for the Axis AsTime attribute. */
8387    astSetAxisAsTime( ax, value );
8388 
8389 /* Annul the Axis pointer. */
8390    ax = astAnnul( ax );
8391 }
8392 
SetAttrib(AstObject * this_object,const char * setting,int * status)8393 static void SetAttrib( AstObject *this_object, const char *setting, int *status ) {
8394 /*
8395 *  Name:
8396 *     SetAttrib
8397 
8398 *  Purpose:
8399 *     Set an attribute value for a SkyFrame.
8400 
8401 *  Type:
8402 *     Private function.
8403 
8404 *  Synopsis:
8405 *     #include "skyframe.h"
8406 *     void SetAttrib( AstObject *this, const char *setting, int *status )
8407 
8408 *  Class Membership:
8409 *     SkyFrame member function (extends the astSetAttrib method inherited from
8410 *     the Mapping class).
8411 
8412 *  Description:
8413 *     This function assigns an attribute value for a SkyFrame, the attribute
8414 *     and its value being specified by means of a string of the form:
8415 *
8416 *        "attribute= value "
8417 *
8418 *     Here, "attribute" specifies the attribute name and should be in lower
8419 *     case with no white space present. The value to the right of the "="
8420 *     should be a suitable textual representation of the value to be assigned
8421 *     and this will be interpreted according to the attribute's data type.
8422 *     White space surrounding the value is only significant for string
8423 *     attributes.
8424 
8425 *  Parameters:
8426 *     this
8427 *        Pointer to the SkyFrame.
8428 *     setting
8429 *        Pointer to a null terminated string specifying the new attribute
8430 *        value.
8431 *     status
8432 *        Pointer to the inherited status variable.
8433 
8434 *  Returned Value:
8435 *     void
8436 
8437 *  Attributes:
8438 *     As well as those attributes inherited from the parent class, this
8439 *     function also accepts values for the following additional attributes:
8440 *
8441 *        Equinox (double, read as a string)
8442 
8443 *  Notes:
8444 *     This protected method is intended to be invoked by the Object astSet
8445 *     method and makes additional attributes accessible to it.
8446 */
8447 
8448 /* Local Vaiables: */
8449    AstSkyFrame *this;            /* Pointer to the SkyFrame structure */
8450    double dval;                  /* Floating point attribute value */
8451    double dval1;                 /* Floating point attribute value */
8452    double dval2;                 /* Floating point attribute value */
8453    double mjd;                   /* Modified Julian Date */
8454    int astime;                   /* Value of AsTime attribute */
8455    int axis;                     /* Axis index */
8456    int equinox;                  /* Offset of Equinox attribute value */
8457    int ival;                     /* Integer attribute value */
8458    int len;                      /* Length of setting string */
8459    int nc;                       /* Number of characters read by astSscanf */
8460    int neglon;                   /* Display -ve longitudes? */
8461    int ok;                       /* Can string be used? */
8462    int offset;                   /* Offset of start of attribute value */
8463    int projection;               /* Offset of projection attribute value */
8464 
8465 /* Check the global error status. */
8466    if ( !astOK ) return;
8467 
8468 /* Obtain a pointer to the SkyFrame structure. */
8469    this = (AstSkyFrame *) this_object;
8470 
8471 /* Obtain the length of the setting string. */
8472    len = strlen( setting );
8473 
8474 /* Test for each recognised attribute in turn, using "astSscanf" to parse the
8475    setting string and extract the attribute value (or an offset to it in the
8476    case of string values). In each case, use the value set in "nc" to check
8477    that the entire string was matched. Once a value has been obtained, use the
8478    appropriate method to set it. */
8479 
8480 /* AsTime(axis). */
8481 /* ------------- */
8482    if ( nc = 0,
8483         ( 2 == astSscanf( setting, "astime(%d)= %d %n", &axis, &astime, &nc ) )
8484         && ( nc >= len ) ) {
8485       astSetAsTime( this, axis - 1, astime );
8486 
8487 /* Equinox. */
8488 /* -------- */
8489    } else if ( nc = 0,
8490                ( 0 == astSscanf( setting, "equinox=%n%*[^\n]%n",
8491                               &equinox, &nc ) ) && ( nc >= len ) ) {
8492 
8493 /* Convert the Equinox value to a Modified Julian Date before use. */
8494       mjd = astReadDateTime( setting + equinox );
8495       if ( astOK ) {
8496          astSetEquinox( this, mjd );
8497 
8498 /* Report contextual information if the conversion failed. */
8499       } else {
8500          astError( AST__ATTIN, "astSetAttrib(%s): Invalid equinox value "
8501                    "\"%s\" given for sky coordinate system.", status,
8502                    astGetClass( this ), setting + equinox );
8503       }
8504 
8505 /* NegLon. */
8506 /* ------- */
8507    } else if ( nc = 0,
8508              ( 1 == astSscanf( setting, "neglon= %d %n", &neglon, &nc ) )
8509                && ( nc >= len ) ) {
8510       astSetNegLon( this, neglon );
8511 
8512 /* Projection. */
8513 /* ----------- */
8514    } else if ( nc = 0,
8515                ( 0 == astSscanf( setting, "projection=%n%*[^\n]%n",
8516                               &projection, &nc ) )
8517                && ( nc >= len ) ) {
8518       astSetProjection( this, setting + projection );
8519 
8520 /* SkyRef. */
8521 /* ------- */
8522    } else if ( nc = 0,
8523                ( 0 == astSscanf( setting, "skyref=%n%*[^\n]%n",
8524                                  &offset, &nc ) )
8525                && ( nc >= len ) ) {
8526       ok = 0;
8527       nc = astUnformat( this, 0, setting + offset, &dval1 );
8528       if( setting[ offset + nc ] == ',' ) {
8529          nc++;
8530          nc += astUnformat( this, 1, setting + offset + nc, &dval2 );
8531          if( nc == strlen( setting + offset ) ) {
8532             astSetSkyRef( this, 0, dval1 );
8533             astSetSkyRef( this, 1, dval2 );
8534             ok = 1;
8535          }
8536       }
8537 
8538       if( !ok && astOK ) {
8539          astError( AST__BADOC, "astSetAttrib(%s): Invalid axis values string "
8540                    "\"%.*s\" given for SkyRef attribute.", status, astGetClass( this ),
8541                    (int) astChrLen( setting + offset ), setting + offset );
8542       }
8543 
8544 /* SkyRef(axis). */
8545 /* ------------- */
8546    } else if ( nc = 0,
8547                ( 2 == astSscanf( setting, "skyref(%d)= %lg %n",
8548                                  &axis, &dval, &nc ) )
8549                && ( nc >= len ) ) {
8550       astSetSkyRef( this, axis - 1, dval );
8551 
8552 /* SkyRefIs. */
8553 /* --------- */
8554    } else if ( nc = 0,
8555                ( 0 == astSscanf( setting, "skyrefis=%n%*[^\n]%n",
8556                               &offset, &nc ) )
8557                && ( nc >= len ) ) {
8558 
8559       if( astChrMatch( setting + offset, POLE_STRING ) ) {
8560          astSetSkyRefIs( this, AST__POLE_REF );
8561 
8562       } else if( astChrMatch( setting + offset, ORIGIN_STRING ) ) {
8563          astSetSkyRefIs( this, AST__ORIGIN_REF );
8564 
8565       } else if( astChrMatch( setting + offset, IGNORED_STRING ) ) {
8566          astSetSkyRefIs( this, AST__IGNORED_REF );
8567 
8568       } else if( astOK ) {
8569          astError( AST__OPT, "astSet(%s): option '%s' is unknown in '%s'.", status,
8570                    astGetClass( this ), setting+offset, setting );
8571       }
8572 
8573 /* SkyRefP. */
8574 /* -------- */
8575    } else if ( nc = 0,
8576                ( 0 == astSscanf( setting, "skyrefp=%n%*[^\n]%n",
8577                                  &offset, &nc ) )
8578                && ( nc >= len ) ) {
8579 
8580       ok = 0;
8581       nc = astUnformat( this, 0, setting + offset, &dval1 );
8582       if( setting[ offset + nc ] == ',' ) {
8583          nc++;
8584          nc += astUnformat( this, 1, setting + offset + nc, &dval2 );
8585          if( nc == strlen( setting + offset ) ) {
8586             astSetSkyRefP( this, 0, dval1 );
8587             astSetSkyRefP( this, 1, dval2 );
8588             ok = 1;
8589          }
8590       }
8591 
8592       if( !ok && astOK ) {
8593          astError( AST__BADOC, "astSetAttrib(%s): Invalid axis values string "
8594                    "\"%.*s\" given for SkyRefP attribute.", status, astGetClass( this ),
8595                    (int) astChrLen( setting + offset ), setting + offset );
8596       }
8597 
8598 
8599 /* SkyRefP(axis). */
8600 /* -------------- */
8601    } else if ( nc = 0,
8602                ( 2 == astSscanf( setting, "skyrefp(%d)= %lg %n",
8603                                  &axis, &dval, &nc ) )
8604                && ( nc >= len ) ) {
8605       astSetSkyRefP( this, axis - 1, dval );
8606 
8607 /* AlignOffset. */
8608 /* ------------ */
8609    } else if ( nc = 0,
8610              ( 1 == astSscanf( setting, "alignoffset= %d %n", &ival, &nc ) )
8611                && ( nc >= len ) ) {
8612       astSetAlignOffset( this, ival );
8613 
8614 /* Define a macro to see if the setting string matches any of the
8615    read-only attributes of this class. */
8616 #define MATCH(attrib) \
8617         ( nc = 0, ( 0 == astSscanf( setting, attrib "=%*[^\n]%n", &nc ) ) && \
8618                   ( nc >= len ) )
8619 
8620 /* If the attribute was not recognised, use this macro to report an error
8621    if a read-only attribute has been specified. */
8622    } else if ( !strncmp( setting, "islataxis", 9 ) ||
8623                !strncmp( setting, "islonaxis", 9 ) ||
8624                MATCH( "lataxis" ) ||
8625                MATCH( "lonaxis" ) ) {
8626       astError( AST__NOWRT, "astSet: The setting \"%s\" is invalid for a %s.", status,
8627                 setting, astGetClass( this ) );
8628       astError( AST__NOWRT, "This is a read-only attribute." , status);
8629 
8630 /* Pass any unrecognised setting to the parent method for further
8631    interpretation. */
8632    } else {
8633       (*parent_setattrib)( this_object, setting, status );
8634    }
8635 }
8636 
SetCachedLAST(AstSkyFrame * this,double last,double epoch,double obslon,double obslat,double obsalt,double dut1,int * status)8637 static void SetCachedLAST( AstSkyFrame *this, double last, double epoch,
8638                            double obslon, double obslat, double obsalt,
8639                            double dut1, int *status ) {
8640 /*
8641 *  Name:
8642 *     SetCachedLAST
8643 
8644 *  Purpose:
8645 *     Store a LAST value in the cache in the SkyFrame vtab.
8646 
8647 *  Type:
8648 *     Private function.
8649 
8650 *  Synopsis:
8651 *     #include "skyframe.h"
8652 *     void SetCachedLAST( AstSkyFrame *this, double last, double epoch,
8653 *                         double obslon, double obslat, double obsalt,
8654 *                         double dut1, int *status )
8655 
8656 *  Class Membership:
8657 *     SkyFrame member function.
8658 
8659 *  Description:
8660 *     This function stores the supplied LAST value in a cache in the
8661 *     SkyFrame virtual function table for later use by GetCachedLAST.
8662 
8663 *  Parameters:
8664 *     this
8665 *        Pointer to the SkyFrame.
8666 *     last
8667 *        The Local Apparent Sidereal Time (radians).
8668 *     epoch
8669 *        The epoch (MJD).
8670 *     obslon
8671 *        Observatory geodetic longitude (radians)
8672 *     obslat
8673 *        Observatory geodetic latitude (radians)
8674 *     obsalt
8675 *        Observatory geodetic altitude (metres)
8676 *     dut1
8677 *        The UT1-UTC correction, in seconds.
8678 *     status
8679 *        Pointer to the inherited status variable.
8680 
8681 */
8682 
8683 /* Local Variables: */
8684    astDECLARE_GLOBALS
8685    AstSkyLastTable *table;
8686    double *ep;
8687    double *lp;
8688    double lp_ref;
8689    int i;
8690    int itable;
8691 
8692 /* Get a pointer to the structure holding thread-specific global data. */
8693    astGET_GLOBALS(this);
8694 
8695 /* Initialise */
8696    table = NULL;
8697 
8698 /* Check the global error status. */
8699    if ( !astOK ) return;
8700 
8701 /* Ensure no threads are allowed to read the table whilst we are writing
8702    to it. */
8703    LOCK_WLOCK1
8704 
8705 /* Loop round every LAST table held in the vtab. Each table refers to a
8706    different observatory position and/or DUT1 value. */
8707    for( itable = 0; itable < nlast_tables; itable++ ) {
8708       table = last_tables[ itable ];
8709 
8710 /* See if the table refers to the given position and dut1 value, allowing
8711    some small tolerance. If it does, leave the loop. */
8712       if( fabs( table->obslat - obslat ) < 2.0E-7 &&
8713           fabs( table->obslon - obslon ) < 2.0E-7 &&
8714           fabs( table->obsalt - obsalt ) < 1.0 &&
8715           fabs( table->dut1 - dut1 ) < 1.0E-5 ) break;
8716 
8717 /* Ensure "table" ends up NULL if no suitable table is found. */
8718       table = NULL;
8719    }
8720 
8721 /* If no table was found, create one now, and add it into the vtab cache. */
8722    if( !table ) {
8723 
8724       astBeginPM;
8725       table = astMalloc( sizeof( AstSkyLastTable ) );
8726       itable = nlast_tables++;
8727       last_tables = astGrow( last_tables, nlast_tables,
8728                                    sizeof( AstSkyLastTable * ) );
8729       astEndPM;
8730 
8731       if( astOK ) {
8732          last_tables[ itable ] = table;
8733          table->obslat = obslat;
8734          table->obslon = obslon;
8735          table->obsalt = obsalt;
8736          table->dut1 = dut1;
8737          table->nentry = 1;
8738 
8739          astBeginPM;
8740          table->epoch = astMalloc( sizeof( double ) );
8741          table->last = astMalloc( sizeof( double ) );
8742          astEndPM;
8743 
8744          if( astOK ) {
8745             table->epoch[ 0 ] = epoch;
8746             table->last[ 0 ] = last;
8747          }
8748       }
8749 
8750 
8751 /* If we have a table, add the new point into it. */
8752    } else {
8753 
8754 /* Extend the epoch and last arrays. */
8755       astBeginPM;
8756       table->epoch = astGrow( table->epoch, ++(table->nentry), sizeof( double ) );
8757       table->last = astGrow( table->last, table->nentry, sizeof( double ) );
8758       astEndPM;
8759 
8760 /* Check memory allocation was successful. */
8761       if( astOK ) {
8762 
8763 /* Get pointers to the last original elements in the arrays of epoch and
8764    corresponding LAST values in the table. */
8765          ep = table->epoch + table->nentry - 2;
8766          lp = table->last + table->nentry - 2;
8767 
8768 /* Starting from the end of the arrays, shuffle all entries up one
8769    element until an element is found which is less than the supplied epoch
8770    value. This maintains the epoch array in monotonic increasing order. */
8771          for( i = table->nentry - 2; i >= 0; i--,ep--,lp-- ) {
8772             if( *ep <= epoch ) break;
8773             ep[ 1 ] = *ep;
8774             lp[ 1 ] = *lp;
8775          }
8776 
8777 /* Store the new epoch and LAST value. Add or subtract 2.PI as needed
8778    from the new LAST value to ensure it is continuous with an adjacent
8779    LAST value. This is needed for interpolation between the two values
8780    to be meaningful.  */
8781          ep[ 1 ] = epoch;
8782 
8783 /* For most cases, compare with the previous LAST value. If the new epoch
8784    value is smaller than any epoch already in the table, there will be no
8785    previous LAST value. So compare with the next value instead. */
8786          if( i >= 0 ) {
8787             lp_ref = lp[ 0 ];
8788          } else {
8789             lp_ref = lp[ 2 ];
8790          }
8791 
8792          if( last > lp_ref + AST__DPI ) {
8793             lp[ 1 ] = last - 2*AST__DPI;
8794 
8795          } else if( last < lp_ref - AST__DPI ) {
8796             lp[ 1 ] = last + 2*AST__DPI;
8797 
8798          } else {
8799             lp[ 1 ] = last;
8800          }
8801       }
8802    }
8803 
8804 /* Indicate other threads are now allowed to read the table. */
8805    UNLOCK_RWLOCK1
8806 
8807 }
8808 
SetDut1(AstFrame * this_frame,double val,int * status)8809 static void SetDut1( AstFrame *this_frame, double val, int *status ) {
8810 /*
8811 *  Name:
8812 *     SetDut1
8813 
8814 *  Purpose:
8815 *     Set the value of the Dut1 attribute for a SkyFrame.
8816 
8817 *  Type:
8818 *     Private function.
8819 
8820 *  Synopsis:
8821 *     #include "skyframe.h"
8822 *     void SetDut1( AstFrame *this, double val, int *status )
8823 
8824 *  Class Membership:
8825 *     SkyFrame member function (over-rides the astSetDut1 method
8826 *     inherited from the Frame class).
8827 
8828 *  Description:
8829 *     This function clears the Dut1 value and updates the LAST value
8830 *     stored in the SkyFrame.
8831 
8832 *  Parameters:
8833 *     this
8834 *        Pointer to the SkyFrame.
8835 *     val
8836 *        New Dut1 value.
8837 *     status
8838 *        Pointer to the inherited status variable.
8839 
8840 */
8841 
8842 /* Local Variables: */
8843    AstSkyFrame *this;
8844    double orig;
8845 
8846 /* Check the global error status. */
8847    if ( !astOK ) return;
8848 
8849 /* Obtain a pointer to the SkyFrame structure. */
8850    this = (AstSkyFrame *) this_frame;
8851 
8852 /* Note the original Dut1 value. */
8853    orig = astGetDut1( this );
8854 
8855 /* Invoke the parent method to set the Frame Dut1 value. */
8856    (*parent_setdut1)( this_frame, val, status );
8857 
8858 /* If the DUT1 value has changed significantly, indicate that the LAST value
8859    will need to be re-calculated when it is next needed. */
8860    if( fabs( orig - val ) > 1.0E-6 ) {
8861       this->last = AST__BAD;
8862       this->eplast = AST__BAD;
8863       this->klast = AST__BAD;
8864    }
8865 }
8866 
SetLast(AstSkyFrame * this,int * status)8867 static void SetLast( AstSkyFrame *this, int *status ) {
8868 /*
8869 *  Name:
8870 *     SetLast
8871 
8872 *  Purpose:
8873 *     Set the Local Appearent Sidereal Time for a SkyFrame.
8874 
8875 *  Type:
8876 *     Private function.
8877 
8878 *  Synopsis:
8879 *     #include "skyframe.h"
8880 *     void SetLast( AstSkyFrame *this, int *status )
8881 
8882 *  Class Membership:
8883 *     SkyFrame member function.
8884 
8885 *  Description:
8886 *     This function sets the Local Apparent Sidereal Time at the epoch
8887 *     and geographical longitude given by the current values of the Epoch
8888 *     and ObsLon attributes associated with the supplied SkyFrame.
8889 
8890 *  Parameters:
8891 *     this
8892 *        Pointer to the SkyFrame.
8893 *     status
8894 *        Pointer to the inherited status variable.
8895 
8896 *  Notes:
8897 *     -  A value of AST__BAD will be returned if this function is invoked
8898 *     with the global error status set, or if it should fail for any reason.
8899 */
8900 
8901 /* Local Variables: */
8902    double epoch;      /* Epoch as a TDB MJD */
8903 
8904 /* Check the global error status. */
8905    if ( !astOK ) return;
8906 
8907 /* Get the SkyFrame Epoch as a TDB MJD. */
8908    epoch = astGetEpoch( this );
8909 
8910 /* Calculate the LAST value (in rads) and store in the SkyFrame structure. */
8911    this->last = CalcLAST( this, epoch, astGetObsLon( this ),
8912                           astGetObsLat( this ), astGetObsAlt( this ),
8913                           astGetDut1( this ), status );
8914 
8915 /* Save the TDB MJD to which this LAST corresponds. */
8916    this->eplast = epoch;
8917 
8918 /* The ratio between solar and sidereal time is a slowly varying function
8919    of epoch. The GetLAST function returns a fast approximation to LAST
8920    by using the ratio between solar and sidereal time. Indicate that
8921    GetLAST should re-calculate the ratio by setting the ratio value bad. */
8922    this->klast = AST__BAD;
8923 }
8924 
SetObsAlt(AstFrame * this,double val,int * status)8925 static void SetObsAlt( AstFrame *this, double val, int *status ) {
8926 /*
8927 *  Name:
8928 *     SetObsAlt
8929 
8930 *  Purpose:
8931 *     Set the value of the ObsAlt attribute for a SkyFrame.
8932 
8933 *  Type:
8934 *     Private function.
8935 
8936 *  Synopsis:
8937 *     #include "skyframe.h"
8938 *     void SetObsAlt( AstFrame *this, double val, int *status )
8939 
8940 *  Class Membership:
8941 *     SkyFrame member function (over-rides the astSetObsAlt method
8942 *     inherited from the Frame class).
8943 
8944 *  Description:
8945 *     This function sets the ObsAlt value.
8946 
8947 *  Parameters:
8948 *     this
8949 *        Pointer to the SkyFrame.
8950 *     val
8951 *        New ObsAlt value.
8952 *     status
8953 *        Pointer to the inherited status variable.
8954 
8955 */
8956 
8957 /* Local Variables: */
8958    double orig;
8959 
8960 /* Check the global error status. */
8961    if ( !astOK ) return;
8962 
8963 /* Note the original ObsAlt value. */
8964    orig = astGetObsAlt( this );
8965 
8966 /* Invoke the parent method to set the Frame ObsAlt. */
8967    (*parent_setobsalt)( this, val, status );
8968 
8969 /* If the altitude has changed significantly, indicate that the LAST value
8970    and magnitude of the diurnal aberration vector will need to be
8971    re-calculated when next needed. */
8972    if( fabs( orig - val ) > 0.001 ) {
8973       ( (AstSkyFrame *) this )->last = AST__BAD;
8974       ( (AstSkyFrame *) this )->eplast = AST__BAD;
8975       ( (AstSkyFrame *) this )->klast = AST__BAD;
8976       ( (AstSkyFrame *) this )->diurab = AST__BAD;
8977    }
8978 }
8979 
SetObsLat(AstFrame * this,double val,int * status)8980 static void SetObsLat( AstFrame *this, double val, int *status ) {
8981 /*
8982 *  Name:
8983 *     SetObsLat
8984 
8985 *  Purpose:
8986 *     Set the value of the ObsLat attribute for a SkyFrame.
8987 
8988 *  Type:
8989 *     Private function.
8990 
8991 *  Synopsis:
8992 *     #include "skyframe.h"
8993 *     void SetObsLat( AstFrame *this, double val, int *status )
8994 
8995 *  Class Membership:
8996 *     SkyFrame member function (over-rides the astSetObsLat method
8997 *     inherited from the Frame class).
8998 
8999 *  Description:
9000 *     This function sets the ObsLat value.
9001 
9002 *  Parameters:
9003 *     this
9004 *        Pointer to the SkyFrame.
9005 *     val
9006 *        New ObsLat value.
9007 *     status
9008 *        Pointer to the inherited status variable.
9009 
9010 */
9011 
9012 /* Local Variables: */
9013    double orig;
9014 
9015 /* Check the global error status. */
9016    if ( !astOK ) return;
9017 
9018 /* Note the original ObsLat value. */
9019    orig = astGetObsLat( this );
9020 
9021 /* Invoke the parent method to set the Frame ObsLat. */
9022    (*parent_setobslat)( this, val, status );
9023 
9024 /* If the altitude has changed significantly, indicate that the LAST value
9025    and magnitude of the diurnal aberration vector will need to be
9026    re-calculated when next needed. */
9027    if( fabs( orig - val ) > 1.0E-8 ) {
9028       ( (AstSkyFrame *) this )->last = AST__BAD;
9029       ( (AstSkyFrame *) this )->eplast = AST__BAD;
9030       ( (AstSkyFrame *) this )->klast = AST__BAD;
9031       ( (AstSkyFrame *) this )->diurab = AST__BAD;
9032    }
9033 }
9034 
SetObsLon(AstFrame * this,double val,int * status)9035 static void SetObsLon( AstFrame *this, double val, int *status ) {
9036 /*
9037 *  Name:
9038 *     SetObsLon
9039 
9040 *  Purpose:
9041 *     Set the value of the ObsLon attribute for a SkyFrame.
9042 
9043 *  Type:
9044 *     Private function.
9045 
9046 *  Synopsis:
9047 *     #include "skyframe.h"
9048 *     void SetObsLon( AstFrame *this, double val, int *status )
9049 
9050 *  Class Membership:
9051 *     SkyFrame member function (over-rides the astSetObsLon method
9052 *     inherited from the Frame class).
9053 
9054 *  Description:
9055 *     This function sets the ObsLon value.
9056 
9057 *  Parameters:
9058 *     this
9059 *        Pointer to the SkyFrame.
9060 *     val
9061 *        New ObsLon value.
9062 *     status
9063 *        Pointer to the inherited status variable.
9064 
9065 */
9066 
9067 /* Local Variables: */
9068    double orig;
9069 
9070 /* Check the global error status. */
9071    if ( !astOK ) return;
9072 
9073 /* Note the original ObsLon value. */
9074    orig = astGetObsLon( this );
9075 
9076 /* Invoke the parent method to set the Frame ObsLon. */
9077    (*parent_setobslon)( this, val, status );
9078 
9079 /* If the longitude has changed significantly, indicate that the LAST value
9080    will need to be re-calculated when it is next needed. */
9081    if( fabs( orig - val ) > 1.0E-8 ) {
9082       ( (AstSkyFrame *) this )->last = AST__BAD;
9083       ( (AstSkyFrame *) this )->eplast = AST__BAD;
9084       ( (AstSkyFrame *) this )->klast = AST__BAD;
9085    }
9086 }
9087 
SetSystem(AstFrame * this_frame,AstSystemType system,int * status)9088 static void SetSystem( AstFrame *this_frame, AstSystemType system, int *status ) {
9089 /*
9090 *  Name:
9091 *     SetSystem
9092 
9093 *  Purpose:
9094 *     Set the System attribute for a SkyFrame.
9095 
9096 *  Type:
9097 *     Private function.
9098 
9099 *  Synopsis:
9100 *     #include "skyframe.h"
9101 *     void SetSystem( AstFrame *this_frame, AstSystemType system, int *status )
9102 
9103 *  Class Membership:
9104 *     SkyFrame member function (over-rides the astSetSystem protected
9105 *     method inherited from the Frame class).
9106 
9107 *  Description:
9108 *     This function assigns a new value to the System attribute for a SkyFrame.
9109 
9110 *  Parameters:
9111 *     this
9112 *        Pointer to the SkyFrame.
9113 *     system
9114 *        The new System value.
9115 *     status
9116 *        Pointer to the inherited status variable.
9117 
9118 */
9119 
9120 /* Local Variables: */
9121    AstFrameSet *fs;              /* FrameSet to be used as the Mapping */
9122    AstSkyFrame *sfrm;            /* Copy of original SkyFrame */
9123    AstSkyFrame *this;            /* Pointer to SkyFrame structure */
9124    double xin[ 2 ];              /* Axis 0 values */
9125    double xout[ 2 ];             /* Axis 0 values */
9126    double yin[ 2 ];              /* Axis 1 values */
9127    double yout[ 2 ];             /* Axis 1 values */
9128    int aloff;                    /* The AlignOffset attribute value */
9129    int aloff_set;                /* Is the AlignOffset attribute set? */
9130    int skyref_set;               /* Is either SkyRef attribute set? */
9131    int skyrefis;                 /* The SkyRefIs attribute value */
9132    int skyrefis_set;             /* Is the SkyRefIs attribute set? */
9133    int skyrefp_set;              /* Is either SkyRefP attribute set? */
9134 
9135 /* Check the global error status. */
9136    if ( !astOK ) return;
9137 
9138 /* Obtain a pointer to the SkyFrame structure. */
9139    this = (AstSkyFrame *) this_frame;
9140 
9141 /* See if either the SkyRef or SkyRefP attribute is set. */
9142    skyref_set = astTestSkyRef( this, 0 ) || astTestSkyRef( this, 1 );
9143    skyrefp_set = astTestSkyRefP( this, 0 ) || astTestSkyRefP( this, 1 );
9144 
9145 /* If so, we will need to transform their values into the new coordinate
9146    system. Save a copy of the SkyFrame with its original System value. */
9147    sfrm = ( skyref_set || skyrefp_set )?astCopy( this ):NULL;
9148 
9149 /* Use the parent method to set the new System value. */
9150    (*parent_setsystem)( this_frame, system, status );
9151 
9152 /* Now modify the SkyRef and SkyRefP attributes if necessary. */
9153    if( sfrm ) {
9154 
9155 /* Save the AlignOffset, SkyRefIs, SkyRef and SkyRefP values. */
9156       aloff_set = astTestAlignOffset( sfrm );
9157       aloff = astGetAlignOffset( sfrm );
9158       skyrefis_set = astTestSkyRefIs( sfrm );
9159       skyrefis = astGetSkyRefIs( sfrm );
9160 
9161       xin[ 0 ] = astGetSkyRef( sfrm, 0 );
9162       xin[ 1 ] = astGetSkyRefP( sfrm, 0 );
9163       yin[ 0 ] = astGetSkyRef( sfrm, 1 );
9164       yin[ 1 ] = astGetSkyRefP( sfrm, 1 );
9165 
9166 /* Clear the SkyRef and SkyRefP values to avoid infinite recursion in the
9167    following call to astConvert. */
9168       if( skyref_set ) {
9169          astClearSkyRef( sfrm, 0 );
9170          astClearSkyRef( sfrm, 1 );
9171          astClearSkyRef( this, 0 );
9172          astClearSkyRef( this, 1 );
9173       }
9174 
9175       if( skyrefp_set ) {
9176          astClearSkyRefP( sfrm, 0 );
9177          astClearSkyRefP( sfrm, 1 );
9178          astClearSkyRefP( this, 0 );
9179          astClearSkyRefP( this, 1 );
9180       }
9181 
9182 /* Also set AlignOffset and SkyRefIs so that the following call to
9183    astConvert does not align in offset coords. */
9184       astSetAlignOffset( sfrm, 0 );
9185       astSetSkyRefIs( sfrm, AST__IGNORED_REF );
9186 
9187 /* Get the Mapping from the original System to the new System. Invoking
9188    astConvert will recursively invoke SetSystem again. This is why we need
9189    to be careful to ensure that SkyRef and SKyRefP are cleared above - doing
9190    so ensure we do not end up with infinite recursion. */
9191       fs = astConvert( sfrm, this, "" );
9192 
9193 /* If the conversion is not possible, clear the SkyRef and SkyRefP
9194    values. */
9195       if( !fs ) {
9196          if( skyref_set ) {
9197             astClearSkyRef( this, 0 );
9198             astClearSkyRef( this, 1 );
9199          }
9200          if( skyrefp_set ) {
9201             astClearSkyRefP( this, 0 );
9202             astClearSkyRefP( this, 1 );
9203          }
9204 
9205 /* Use the Mapping to find the SkyRef and SkyRefP positions in the new
9206    coordinate system. */
9207       } else {
9208          astTran2( fs, 2, xin, yin, 1, xout, yout );
9209 
9210 /* Store the values as required. */
9211          if( skyref_set ) {
9212             astSetSkyRef( this, 0, xout[ 0 ] );
9213             astSetSkyRef( this, 1, yout[ 0 ] );
9214          }
9215 
9216          if( skyrefp_set ) {
9217             astSetSkyRefP( this, 0, xout[ 1 ] );
9218             astSetSkyRefP( this, 1, yout[ 1 ] );
9219          }
9220 
9221 /* Restore the original SkyRefIs and AlignOffset values. */
9222          if( aloff_set ) {
9223             astSetAlignOffset( this, aloff );
9224          } else {
9225             astClearAlignOffset( this );
9226          }
9227 
9228          if( skyrefis_set ) {
9229             astSetSkyRefIs( this, skyrefis );
9230          } else {
9231             astClearSkyRefIs( this );
9232          }
9233 
9234 /* Free resources. */
9235          fs = astAnnul( fs );
9236       }
9237       sfrm = astAnnul( sfrm );
9238    }
9239 }
9240 
Shapp(double dist,double * r0,double * r3,double a0,double * p4,int * status)9241 static void Shapp( double dist, double *r0, double *r3, double a0,
9242                    double *p4, int *status ){
9243 /*
9244 *  Name:
9245 *     Shapp
9246 
9247 *  Purpose:
9248 *     Use the vectors calculated by Shcal to find a sky position
9249 *     which is offset along a given position angle.
9250 
9251 *  Type:
9252 *     Private function.
9253 
9254 *  Synopsis:
9255 *     #include "skyframe.h"
9256 *     void Shapp( double dist, double *r0, double *r3, double a0,
9257 *                 double *p4, int *status )
9258 
9259 *  Class Membership:
9260 *     SkyFrame member function.
9261 
9262 *  Description:
9263 *     This function uses the vectors R0 and R3 calculated previously by
9264 *     Shcal to find the sky position which is offset away from the
9265 *     "reference" position (see function Offset2) by a given arc
9266 *     distance, along a given great circle.
9267 *
9268 *     No checks are made for AST__BAD values.
9269 
9270 *  Parameters:
9271 *     dist
9272 *        The arc distance to move away from the reference position
9273 *        in the given direction, in radians.
9274 *     r0
9275 *        Pointer to an array holding the 3-vector representing the reference
9276 *        position.
9277 *     r3
9278 *        Pointer to an array holding the 3-vector representing the
9279 *        point which is 90 degrees away from the reference point, along
9280 *        the required great circle.
9281 *     a0
9282 *        The sky longitude of the reference position, in radians.
9283 *     p4
9284 *        Pointer to an array of 2 doubles in which to put the sky longitude
9285 *        and latitude of the required point, in radians.
9286 *     status
9287 *        Pointer to the inherited status variable.
9288 
9289 */
9290 
9291 /* Local Variables: */
9292    double cosdst;            /* Cosine of DIST */
9293    double r4[ 3 ];           /* Required position vector */
9294    double sindst;            /* Sine of DIST */
9295 
9296 /* Check the global error status. */
9297    if ( !astOK ) return;
9298 
9299 /* Store commonly used values. */
9300    sindst = sin( dist );
9301    cosdst = cos( dist );
9302 
9303 /* The vector R4 representing the required point is produced as a
9304    linear sum of R0 and R3. */
9305    r4[ 0 ] = cosdst*r0[ 0 ] + sindst*r3[ 0 ];
9306    r4[ 1 ] = cosdst*r0[ 1 ] + sindst*r3[ 1 ];
9307    r4[ 2 ] = cosdst*r0[ 2 ] + sindst*r3[ 2 ];
9308 
9309 /* Create the longitude of the required point. If this point is at
9310    a pole it is assigned the same longitude as the reference point. */
9311    if( r4[ 0 ] != 0.0 || r4[ 1 ] != 0.0 ) {
9312       p4[ 0 ] = atan2( r4[ 1 ], r4[ 0 ] );
9313    } else {
9314       p4[ 0 ] = a0;
9315    }
9316 
9317 /* Create the latitude of the required point. */
9318    if( r4[ 2 ] > 1.0 ) {
9319       r4[ 2 ] = 1.0;
9320    } else if( r4[ 2 ] < -1.0 ) {
9321       r4[ 2 ] = -1.0;
9322    }
9323    p4[ 1 ] = asin( r4[ 2 ] );
9324 
9325 }
9326 
Shcal(double a0,double b0,double angle,double * r0,double * r3,int * status)9327 static void Shcal( double a0, double b0, double angle, double *r0,
9328                    double *r3, int *status ) {
9329 /*
9330 *  Name:
9331 *     Shcal
9332 
9333 *  Purpose:
9334 *     Calculate vectors required by Offset2.
9335 
9336 *  Type:
9337 *     Private function.
9338 
9339 *  Synopsis:
9340 *     #include "skyframe.h"
9341 *     void Shcal( double a0, double b0, double angle, double *r0,
9342 *                 double *r3, int *status )
9343 
9344 *  Class Membership:
9345 *     SkyFrame member function.
9346 
9347 *  Description:
9348 *     This function calculates the 3-vector R0, representing the given
9349 *     sky position (A0,B0), and the 3-vector R3, representing the sky
9350 *     position which is 90 degrees away from R0, along a great circle
9351 *     passing through R0 at a position angle given by ANGLE. Each
9352 *     3-vector holds Cartesian (X,Y,Z) values with origin at the centre
9353 *     of the celestial sphere. The XY plane is the "equator", the Z
9354 *     axis is in the direction of the "north pole", X is towards zero
9355 *     longitude (A=0), and Y is towards longitude 90 degrees.
9356 *
9357 *     No checks are made for AST__BAD input values.
9358 
9359 *  Parameters:
9360 *     a0
9361 *        The sky longitude of the given position, in radians.
9362 *     b0
9363 *        The sky latitude of the given position, in radians.
9364 *     angle
9365 *        The position angle of a great circle passing through the given
9366 *        position.  That is, the angle from north to the required
9367 *        direction, in radians. Positive angles are in the sense of
9368 *        rotation from north to east.
9369 *     r0
9370 *        A pointer to an array to receive 3-vector R0. See above.
9371 *     r3
9372 *        A pointer to an array to receive 3-vector R3. See above.
9373 *     status
9374 *        Pointer to the inherited status variable.
9375 
9376 */
9377 
9378 /* Local Variables: */
9379    double cosa0;         /* Cosine of A0 */
9380    double cosb0;         /* Cosine of B0 */
9381    double cospa;         /* Cosine of ANGLE */
9382    double r1[ 3 ];       /* Vector PI/2 away from R0 in meridian of R0 */
9383    double r2[ 3 ];       /* Vector PI/2 away from R0 on equator */
9384    double sinpa;         /* Sine of ANGLE */
9385    double sina0;         /* Sine of A0 */
9386    double sinb0;         /* Sine of B0 */
9387 
9388 /* Check the global error status. */
9389    if ( !astOK ) return;
9390 
9391 /* Store commonly used values. */
9392    sina0 = sin( a0 );
9393    cosa0 = cos( a0 );
9394    sinb0 = sin( b0 );
9395    cosb0 = cos( b0 );
9396    sinpa = sin( angle );
9397    cospa = cos( angle );
9398 
9399 /* Create the vector R0 representing the given point. The XY plane
9400    defines zero latitude, Z is in the direction of increasing latitude,
9401    X is towards zero longitude, and Y is towards longitude 90 degrees. */
9402    r0[ 0 ] =  cosb0*cosa0;
9403    r0[ 1 ] =  cosb0*sina0;
9404    r0[ 2 ] =  sinb0;
9405 
9406 /* Create the vector R1 representing the point in the meridian of the
9407    given point which has latitude 90 degrees greater than the
9408    given point. */
9409    r1[ 0 ] = -sinb0*cosa0;
9410    r1[ 1 ] = -sinb0*sina0;
9411    r1[ 2 ] =  cosb0;
9412 
9413 /* Create the vector R2 representing the point on the equator (i.e. a
9414    latitude of zero), which has a longitude 90 degrees to the west of
9415    the given point. */
9416    r2[ 0 ] = -sina0;
9417    r2[ 1 ] =  cosa0;
9418    r2[ 2 ] =  0.0;
9419 
9420 /* Create the vector R3 representing the point which is 90 degrees away
9421    from the given point, along the required great circle. */
9422    r3[ 0 ] =  cospa*r1[ 0 ] + sinpa*r2[ 0 ];
9423    r3[ 1 ] =  cospa*r1[ 1 ] + sinpa*r2[ 1 ];
9424    r3[ 2 ] =  cospa*r1[ 2 ] + sinpa*r2[ 2 ];
9425 
9426 /* Return */
9427    return;
9428 }
9429 
SubFrame(AstFrame * target_frame,AstFrame * template,int result_naxes,const int * target_axes,const int * template_axes,AstMapping ** map,AstFrame ** result,int * status)9430 static int SubFrame( AstFrame *target_frame, AstFrame *template,
9431                      int result_naxes, const int *target_axes,
9432                      const int *template_axes, AstMapping **map,
9433                      AstFrame **result, int *status ) {
9434 /*
9435 *  Name:
9436 *     SubFrame
9437 
9438 *  Purpose:
9439 *     Select axes from a SkyFrame and convert to the new coordinate system.
9440 
9441 *  Type:
9442 *     Private function.
9443 
9444 *  Synopsis:
9445 *     #include "skyframe.h"
9446 *     int SubFrame( AstFrame *target, AstFrame *template,
9447 *                   int result_naxes, const int *target_axes,
9448 *                   const int *template_axes, AstMapping **map,
9449 *                   AstFrame **result, int *status )
9450 
9451 *  Class Membership:
9452 *     SkyFrame member function (over-rides the protected astSubFrame method
9453 *     inherited from the Frame class).
9454 
9455 *  Description:
9456 *     This function selects a requested sub-set (or super-set) of the axes from
9457 *     a "target" SkyFrame and creates a new Frame with copies of the selected
9458 *     axes assembled in the requested order. It then optionally overlays the
9459 *     attributes of a "template" Frame on to the result. It returns both the
9460 *     resulting Frame and a Mapping that describes how to convert between the
9461 *     coordinate systems described by the target and result Frames. If
9462 *     necessary, this Mapping takes account of any differences in the Frames'
9463 *     attributes due to the influence of the template.
9464 
9465 *  Parameters:
9466 *     target
9467 *        Pointer to the target SkyFrame, from which axes are to be selected.
9468 *     template
9469 *        Pointer to the template Frame, from which new attributes for the
9470 *        result Frame are to be obtained. Optionally, this may be NULL, in
9471 *        which case no overlaying of template attributes will be performed.
9472 *     result_naxes
9473 *        Number of axes to be selected from the target Frame. This number may
9474 *        be greater than or less than the number of axes in this Frame (or
9475 *        equal).
9476 *     target_axes
9477 *        Pointer to an array of int with result_naxes elements, giving a list
9478 *        of the (zero-based) axis indices of the axes to be selected from the
9479 *        target SkyFrame. The order in which these are given determines the
9480 *        order in which the axes appear in the result Frame. If any of the
9481 *        values in this array is set to -1, the corresponding result axis will
9482 *        not be derived from the target Frame, but will be assigned default
9483 *        attributes instead.
9484 *     template_axes
9485 *        Pointer to an array of int with result_naxes elements. This should
9486 *        contain a list of the template axes (given as zero-based axis indices)
9487 *        with which the axes of the result Frame are to be associated. This
9488 *        array determines which axes are used when overlaying axis-dependent
9489 *        attributes of the template on to the result. If any element of this
9490 *        array is set to -1, the corresponding result axis will not receive any
9491 *        template attributes.
9492 *
9493 *        If the template argument is given as NULL, this array is not used and
9494 *        a NULL pointer may also be supplied here.
9495 *     map
9496 *        Address of a location to receive a pointer to the returned Mapping.
9497 *        The forward transformation of this Mapping will describe how to
9498 *        convert coordinates from the coordinate system described by the target
9499 *        SkyFrame to that described by the result Frame. The inverse
9500 *        transformation will convert in the opposite direction.
9501 *     result
9502 *        Address of a location to receive a pointer to the result Frame.
9503 *     status
9504 *        Pointer to the inherited status variable.
9505 
9506 *  Returned Value:
9507 *     A non-zero value is returned if coordinate conversion is possible
9508 *     between the target and the result Frame. Otherwise zero is returned and
9509 *     *map and *result are returned as NULL (but this will not in itself
9510 *     result in an error condition). In general, coordinate conversion should
9511 *     always be possible if no template Frame is supplied but may not always
9512 *     be possible otherwise.
9513 
9514 *  Notes:
9515 *     -  A value of zero will be returned if this function is invoked with the
9516 *     global error status set, or if it should fail for any reason.
9517 
9518 *  Implementation Notes:
9519 *     -  This implementation addresses the selection of axes from a SkyFrame
9520 *     object. This results in another object of the same class only if both
9521 *     axes of the SkyFrame are selected, once each. Otherwise, the result is a
9522 *     Frame class object which inherits the SkyFrame's axis information (if
9523 *     appropriate) but none of the other properties of a SkyFrame.
9524 *     -  In the event that a SkyFrame results, the returned Mapping will take
9525 *     proper account of the relationship between the target and result sky
9526 *     coordinate systems.
9527 *     -  In the event that a Frame class object results, the returned Mapping
9528 *     will only represent a selection/permutation of axes.
9529 
9530 *  Implementation Deficiencies:
9531 *     -  Any axis selection is currently permitted. Probably this should be
9532 *     restricted so that each axis can only be selected once. The
9533 *     astValidateAxisSelection method will do this but currently there are bugs
9534 *     in the CmpFrame class that cause axis selections which will not pass this
9535 *     test. Install the validation when these are fixed.
9536 */
9537 
9538 /* Local Variables: */
9539    AstAxis *ax;                  /* Pointer to result Frame Axis object */
9540    AstMapping *tmpmap;           /* Temporary Mapping pointer */
9541    AstPermMap *permmap;          /* Pointer to PermMap */
9542    AstSkyFrame *target;          /* Pointer to the SkyFrame structure */
9543    AstSkyFrame *temp;            /* Pointer to copy of target SkyFrame */
9544    AstSystemType align_sys;      /* System in which to align the SkyFrames */
9545    int match;                    /* Coordinate conversion is possible? */
9546    int perm[ 2 ];                /* Permutation array for axis swap */
9547    int result_swap;              /* Swap result SkyFrame coordinates? */
9548    int set_usedefs;              /* Set the returned UseDefs attribute zero?*/
9549    int target_axis;              /* Target SkyFrame axis index */
9550    int target_swap;              /* Swap target SkyFrame coordinates? */
9551 
9552 /* Initialise the returned values. */
9553    *map = NULL;
9554    *result = NULL;
9555    match = 0;
9556 
9557 /* Check the global error status. */
9558    if ( !astOK ) return match;
9559 
9560 /* Obtain a pointer to the target SkyFrame structure. */
9561    target = (AstSkyFrame *) target_frame;
9562 
9563 /* Result is a SkyFrame. */
9564 /* --------------------- */
9565 /* Check if the result Frame is to have two axes obtained by selecting
9566    both of the target SkyFrame axes, in either order. If so, the
9567    result will also be a SkyFrame. */
9568    if ( ( result_naxes == 2 ) &&
9569         ( ( ( target_axes[ 0 ] == 0 ) && ( target_axes[ 1 ] == 1 ) ) ||
9570           ( ( target_axes[ 0 ] == 1 ) && ( target_axes[ 1 ] == 0 ) ) ) ) {
9571 
9572 /* If a template has not been supplied, or is the same object as the
9573    target, we are simply extracting axes from the supplied SkyFrame. In
9574    this case we temporarily force the UseDefs attribute to 1 so that (for
9575    instance) the astPickAxes method can function correctly. E.g. if you
9576    have a SkyFrame with no set Epoch and UseDefs set zero,  and you try to
9577    swap the axes, the attempt would fail because MakeSkyMapping would be
9578    unable to determine the Mapping from original to swapped SkyFrame,
9579    because of the lack of an Epoch value. */
9580       set_usedefs = 0;
9581       if( !template || template == target_frame ) {
9582          if( !astGetUseDefs( target ) ) {
9583             astClearUseDefs( target );
9584             set_usedefs = 1;
9585          }
9586       }
9587 
9588 /* Form the result from a copy of the target and then permute its axes
9589    into the order required. */
9590       *result = astCopy( target );
9591       astPermAxes( *result, target_axes );
9592 
9593 /* If required, overlay the template attributes on to the result SkyFrame.
9594    Also get the system in which to align the two SkyFrames. This is the
9595    value of the AlignSystem attribute from the template (if there is a
9596    template). */
9597       if ( template ) {
9598          astOverlay( template, template_axes, *result );
9599          align_sys = astGetAlignSystem( template );
9600 
9601       } else {
9602          align_sys = astGetAlignSystem( target );
9603       }
9604 
9605 /* See whether alignment occurs in offset coordinates or absolute
9606    coordinates. If the current call to this function is part of the
9607    process of restoring a FrameSet's integrity following changes to
9608    the FrameSet's current Frame, then we ignore the setting of the
9609    AlignOffset attributes and use 0. This ensures that when the System
9610    attribute (for instance) is changed via a FrameSet pointer, the
9611    Mappings within the FrameSet are modified to produce offsets in the
9612    new System. If we are not currently restoring a FrameSet's integrity,
9613    then we align in offsets if the template is a SkyFrame and both template
9614    and target want alignment to occur in the offset coordinate system. In
9615    this case we use a UnitMap to connect them. */
9616       if( ( astGetFrameFlags( target_frame ) & AST__INTFLAG ) == 0 ) {
9617          if( astGetAlignOffset( target ) &&
9618              astGetSkyRefIs( target ) != AST__IGNORED_REF &&
9619              template && astIsASkyFrame( template ) ){
9620             if( astGetAlignOffset( (AstSkyFrame *) template ) &&
9621                 astGetSkyRefIs( (AstSkyFrame *) template ) != AST__IGNORED_REF ) {
9622                match = 1;
9623                *map = (AstMapping *) astUnitMap( 2, "", status );
9624             }
9625          }
9626       }
9627 
9628 /* Otherwise, generate a Mapping that takes account of changes in the sky
9629    coordinate system (equinox, epoch, etc.) between the target SkyFrame and
9630    the result SkyFrame. If this Mapping can be generated, set "match" to
9631    indicate that coordinate conversion is possible. */
9632       if( ! *map ) {
9633          match = ( MakeSkyMapping( target, (AstSkyFrame *) *result,
9634                                    align_sys, map, status ) != 0 );
9635       }
9636 
9637 /* If required, re-instate the original zero value of UseDefs. */
9638       if( set_usedefs ) {
9639          astSetUseDefs( target, 0 );
9640          astSetUseDefs( *result, 0 );
9641       }
9642 
9643 /* If a Mapping has been obtained, it will expect coordinate values to be
9644    supplied in (longitude,latitude) pairs. Test whether we need to swap the
9645    order of the target SkyFrame coordinates to conform with this. */
9646       if ( astOK && match ) {
9647          target_swap = ( astValidateAxis( target, 0, 1, "astSubFrame" ) != 0 );
9648 
9649 /* Coordinates will also be delivered in (longitude,latitude) pairs, so check
9650    to see whether the result SkyFrame coordinate order should be swapped. */
9651          result_swap = ( target_swap != ( target_axes[ 0 ] != 0 ) );
9652 
9653 /* If either set of coordinates needs swapping, create a PermMap that
9654    will swap a pair of coordinates. */
9655          permmap = NULL;
9656          if ( target_swap || result_swap ) {
9657             perm[ 0 ] = 1;
9658             perm[ 1 ] = 0;
9659             permmap = astPermMap( 2, perm, 2, perm, NULL, "", status );
9660          }
9661 
9662 /* If necessary, prefix this PermMap to the main Mapping. */
9663          if ( target_swap ) {
9664             tmpmap = (AstMapping *) astCmpMap( permmap, *map, 1, "", status );
9665             *map = astAnnul( *map );
9666             *map = tmpmap;
9667          }
9668 
9669 /* Also, if necessary, append it to the main Mapping. */
9670          if ( result_swap ) {
9671             tmpmap = (AstMapping *) astCmpMap( *map, permmap, 1, "", status );
9672             *map = astAnnul( *map );
9673             *map = tmpmap;
9674          }
9675 
9676 /* Annul the pointer to the PermMap (if created). */
9677          if ( permmap ) permmap = astAnnul( permmap );
9678       }
9679 
9680 /* Result is not a SkyFrame. */
9681 /* ------------------------- */
9682 /* In this case, we select axes as if the target were from the Frame
9683    class.  However, since the resulting data will then be separated
9684    from their enclosing SkyFrame, default attribute values may differ
9685    if the methods for obtaining them were over-ridden by the SkyFrame
9686    class. To overcome this, we ensure that these values are explicitly
9687    set for the result Frame (rather than relying on their
9688    defaults). */
9689    } else {
9690 
9691 /* Make a temporary copy of the target SkyFrame. We will explicitly
9692    set the attribute values in this copy so as not to modify the
9693    original. */
9694       temp = astCopy( target );
9695 
9696 /* Define a macro to test if an attribute is set. If not, set it
9697    explicitly to its default value. */
9698 #define SET(attribute) \
9699    if ( !astTest##attribute( temp ) ) { \
9700       astSet##attribute( temp, astGet##attribute( temp ) ); \
9701    }
9702 
9703 /* Set attribute values which apply to the Frame as a whole and which
9704    we want to retain, but whose defaults are over-ridden by the
9705    SkyFrame class. */
9706       SET(Domain)
9707       SET(Title)
9708 
9709 /* Now loop to set explicit attribute values for each axis. */
9710       for ( target_axis = 0; target_axis < 2; target_axis++ ) {
9711 
9712 /* Define a macro to test if an axis attribute is set. If not, set it
9713    explicitly to its default value. */
9714 #define SET_AXIS(attribute) \
9715    if ( !astTest##attribute( temp, target_axis ) ) { \
9716       astSet##attribute( temp, target_axis, \
9717                          astGet##attribute( temp, target_axis ) ); \
9718    }
9719 
9720 /* Use this macro to set explicit values for all the axis attributes
9721    for which the SkyFrame class over-rides the default value. */
9722          SET_AXIS(AsTime)
9723          SET_AXIS(Format)
9724          SET_AXIS(Label)
9725          SET_AXIS(Symbol)
9726          SET_AXIS(Unit)
9727 
9728 /* Now handle axis attributes for which there are no SkyFrame access
9729    methods.  For these we require a pointer to the temporary
9730    SkyFrame's Axis object. */
9731          ax = astGetAxis( temp, target_axis );
9732 
9733 /* Set an explicit value for the IsLatitude and CentreZero attributes. */
9734          if( astValidateAxis( temp, target_axis, 1, "astSubFrame" ) == 1 ) {
9735             astSetAxisIsLatitude( ax, 1 );
9736             astSetAxisCentreZero( ax, 1 );
9737 
9738          } else {
9739             astSetAxisIsLatitude( ax, 0 );
9740             astSetAxisCentreZero( ax, astGetNegLon( temp ) );
9741          }
9742 
9743 /* Annul the Axis object pointer. */
9744          ax = astAnnul( ax );
9745       }
9746 
9747 /* Clear attributes which have an extended range of values allowed by
9748    this class. */
9749       astClearSystem( temp );
9750       astClearAlignSystem( temp );
9751 
9752 /* Invoke the astSubFrame method inherited from the Frame class to
9753    produce the result Frame by selecting the required set of axes and
9754    overlaying the template Frame's attributes. */
9755       match = (*parent_subframe)( (AstFrame *) temp, template,
9756                                   result_naxes, target_axes, template_axes,
9757                                   map, result, status );
9758 
9759 /* Delete the temporary copy of the target SkyFrame. */
9760       temp = astDelete( temp );
9761    }
9762 
9763 /* Ensure the returned Frame does not have active units. */
9764    astSetActiveUnit( *result, 0 );
9765 
9766 /* If an error occurred or no match was found, annul the returned
9767    objects and reset the returned result. */
9768    if ( !astOK || !match ) {
9769       if( *map ) *map = astAnnul( *map );
9770       if( *result ) *result = astAnnul( *result );
9771       match = 0;
9772    }
9773 
9774 /* Return the result. */
9775    return match;
9776 
9777 /* Undefine macros local to this function. */
9778 #undef SET
9779 #undef SET_AXIS
9780 }
9781 
SystemCode(AstFrame * this,const char * system,int * status)9782 static AstSystemType SystemCode( AstFrame *this, const char *system, int *status ) {
9783 /*
9784 *  Name:
9785 *     SystemCode
9786 
9787 *  Purpose:
9788 *     Convert a string into a coordinate system type code.
9789 
9790 *  Type:
9791 *     Private function.
9792 
9793 *  Synopsis:
9794 *     #include "skyframe.h"
9795 *     AstSystemType SystemCode( AstFrame *this, const char *system, int *status )
9796 
9797 *  Class Membership:
9798 *     SkyFrame member function (over-rides the astSystemCode method
9799 *     inherited from the Frame class).
9800 
9801 *  Description:
9802 *     This function converts a string used for the external
9803 *     description of a sky coordinate system into a SkyFrame
9804 *     coordinate system type code (System attribute value). It is the
9805 *     inverse of the astSystemString function.
9806 
9807 *  Parameters:
9808 *     this
9809 *        The Frame.
9810 *     system
9811 *        Pointer to a constant null-terminated string containing the
9812 *        external description of the sky coordinate system.
9813 *     status
9814 *        Pointer to the inherited status variable.
9815 
9816 *  Returned Value:
9817 *     The System type code.
9818 
9819 *  Notes:
9820 *     - A value of AST__BADSYSTEM is returned if the sky coordinate
9821 *     system description was not recognised. This does not produce an
9822 *     error.
9823 *     - A value of AST__BADSYSTEM is also returned if this function
9824 *     is invoked with the global error status set or if it should fail
9825 *     for any reason.
9826 */
9827 
9828 /* Local Variables: */
9829    AstSystemType result;      /* Result value to return */
9830 
9831 /* Initialise. */
9832    result = AST__BADSYSTEM;
9833 
9834 /* Check the global error status. */
9835    if ( !astOK ) return result;
9836 
9837 /* Match the "system" string against each possibility and assign the
9838    result. */
9839    if ( astChrMatch( "FK4", system ) ) {
9840       result = AST__FK4;
9841 
9842    } else if ( astChrMatch( "FK4_NO_E", system ) ||
9843                astChrMatch( "FK4-NO-E", system ) ) {
9844       result = AST__FK4_NO_E;
9845 
9846    } else if ( astChrMatch( "FK5", system ) ||
9847                astChrMatch( "Equatorial", system ) ) {
9848       result = AST__FK5;
9849 
9850    } else if ( astChrMatch( "J2000", system ) ) {
9851       result = AST__J2000;
9852 
9853    } else if ( astChrMatch( "ICRS", system ) ) {
9854       result = AST__ICRS;
9855 
9856    } else if ( astChrMatch( "AZEL", system ) ) {
9857       result = AST__AZEL;
9858 
9859    } else if ( astChrMatch( "GAPPT", system ) ||
9860                astChrMatch( "GEOCENTRIC", system ) ||
9861                astChrMatch( "APPARENT", system ) ) {
9862          result = AST__GAPPT;
9863 
9864    } else if ( astChrMatch( "ECLIPTIC", system ) ) {
9865       result = AST__ECLIPTIC;
9866 
9867    } else if ( astChrMatch( "HELIOECLIPTIC", system ) ) {
9868       result = AST__HELIOECLIPTIC;
9869 
9870    } else if ( astChrMatch( "GALACTIC", system ) ) {
9871       result = AST__GALACTIC;
9872 
9873    } else if ( astChrMatch( "SUPERGALACTIC", system ) ) {
9874       result = AST__SUPERGALACTIC;
9875 
9876    } else if ( astChrMatch( "UNKNOWN", system ) ) {
9877       result = AST__UNKNOWN;
9878    }
9879 
9880 /* Return the result. */
9881    return result;
9882 }
9883 
SystemString(AstFrame * this,AstSystemType system,int * status)9884 static const char *SystemString( AstFrame *this, AstSystemType system, int *status ) {
9885 /*
9886 *  Name:
9887 *     SystemString
9888 
9889 *  Purpose:
9890 *     Convert a coordinate system type code into a string.
9891 
9892 *  Type:
9893 *     Private function.
9894 
9895 *  Synopsis:
9896 *     #include "skyframe.h"
9897 *     const char *SystemString( AstFrame *this, AstSystemType system, int *status )
9898 
9899 *  Class Membership:
9900 *     SkyFrame member function (over-rides the astSystemString method
9901 *     inherited from the Frame class).
9902 
9903 *  Description:
9904 *     This function converts a SkyFrame coordinate system type code
9905 *     (System attribute value) into a string suitable for use as an
9906 *     external representation of the coordinate system type.
9907 
9908 *  Parameters:
9909 *     this
9910 *        The Frame.
9911 *     system
9912 *        The coordinate system type code.
9913 *     status
9914 *        Pointer to the inherited status variable.
9915 
9916 *  Returned Value:
9917 *     Pointer to a constant null-terminated string containing the
9918 *     textual equivalent of the type code supplied.
9919 
9920 *  Notes:
9921 *     - A NULL pointer value is returned if the sky coordinate system
9922 *     code was not recognised. This does not produce an error.
9923 *     - A NULL pointer value is also returned if this function is
9924 *     invoked with the global error status set or if it should fail
9925 *     for any reason.
9926 */
9927 
9928 /* Local Variables: */
9929    const char *result;           /* Pointer value to return */
9930 
9931 /* Initialise. */
9932    result = NULL;
9933 
9934 /* Check the global error status. */
9935    if ( !astOK ) return result;
9936 
9937 /* Match the "system" value against each possibility and convert to a
9938    string pointer. (Where possible, return the same string as would be
9939    used in the FITS WCS representation of the coordinate system). */
9940    switch ( system ) {
9941    case AST__FK4:
9942       result = "FK4";
9943       break;
9944 
9945    case AST__FK4_NO_E:
9946       result = "FK4-NO-E";
9947       break;
9948 
9949    case AST__FK5:
9950       result = "FK5";
9951       break;
9952 
9953    case AST__J2000:
9954       result = "J2000";
9955       break;
9956 
9957    case AST__ICRS:
9958       result = "ICRS";
9959       break;
9960 
9961    case AST__GAPPT:
9962       result = "GAPPT";
9963       break;
9964 
9965    case AST__AZEL:
9966       result = "AZEL";
9967       break;
9968 
9969    case AST__ECLIPTIC:
9970       result = "ECLIPTIC";
9971       break;
9972 
9973    case AST__HELIOECLIPTIC:
9974       result = "HELIOECLIPTIC";
9975       break;
9976 
9977    case AST__GALACTIC:
9978       result = "GALACTIC";
9979       break;
9980 
9981    case AST__SUPERGALACTIC:
9982       result = "SUPERGALACTIC";
9983       break;
9984 
9985    case AST__UNKNOWN:
9986       result = "Unknown";
9987       break;
9988    }
9989 
9990 /* Return the result pointer. */
9991    return result;
9992 }
9993 
TestActiveUnit(AstFrame * this_frame,int * status)9994 static int TestActiveUnit( AstFrame *this_frame, int *status ) {
9995 /*
9996 *  Name:
9997 *     TestActiveUnit
9998 
9999 *  Purpose:
10000 *     Test the ActiveUnit flag for a SkyFrame.
10001 
10002 *  Type:
10003 *     Private function.
10004 
10005 *  Synopsis:
10006 *     #include "skyframe.h"
10007 *     int TestActiveUnit( AstFrame *this_frame, int *status )
10008 
10009 *  Class Membership:
10010 *     SkyFrame member function (over-rides the astTestActiveUnit protected
10011 *     method inherited from the Frame class).
10012 
10013 *  Description:
10014 *    This function test the value of the ActiveUnit flag for a SkyFrame,
10015 *    which is always "unset".
10016 
10017 *  Parameters:
10018 *     this
10019 *        Pointer to the SkyFrame.
10020 *     status
10021 *        Pointer to the inherited status variable.
10022 
10023 *  Returned Value:
10024 *     The result of the test (0).
10025 
10026 */
10027    return 0;
10028 }
10029 
TestAsTime(AstSkyFrame * this,int axis,int * status)10030 static int TestAsTime( AstSkyFrame *this, int axis, int *status ) {
10031 /*
10032 *  Name:
10033 *     TestAsTime
10034 
10035 *  Purpose:
10036 *     Determine if a value has been set for a SkyFrame's AsTime attribute.
10037 
10038 *  Type:
10039 *     Private function.
10040 
10041 *  Synopsis:
10042 *     #include "skyframe.h"
10043 *     int TestAsTime( AstSkyFrame *this, int axis, int *status )
10044 
10045 *  Class Membership:
10046 *     SkyFrame member function.
10047 
10048 *  Description:
10049 *     This function returns a boolean value to indicate if a value has
10050 *     previously been set for the AsTime attribute for a specified axis of a
10051 *     SkyFrame. This attribute indicates whether axis values should be
10052 *     formatted as times (as opposed to angles) by default.
10053 
10054 *  Parameters:
10055 *     this
10056 *        Pointer to the SkyFrame.
10057 *     axis
10058 *        Index of the axis for which information is required (zero based).
10059 *     status
10060 *        Pointer to the inherited status variable.
10061 
10062 *  Returned Value:
10063 *     Zero or one, according to whether the AsTime attribute has been set.
10064 
10065 *  Notes:
10066 *     -  A value of zero will be returned if this function is invoked with the
10067 *     global error status set, or if it should fail for any reason.
10068 */
10069 
10070 /* Local Variables. */
10071    AstAxis *ax;                  /* Pointer to Axis object */
10072    int result;                   /* Result to be returned */
10073 
10074 /* Check the global error status. */
10075    if ( !astOK ) return 0;
10076 
10077 /* Validate the axis index. */
10078    (void) astValidateAxis( this, axis, 1, "astTestAsTime" );
10079 
10080 /* Obtain a pointer to the Axis object. */
10081    ax = astGetAxis( this, axis );
10082 
10083 /* Determine if the AsTime attribute has been set for it (it cannot have been
10084    set unless the object is a SkyAxis). */
10085    result = ( astIsASkyAxis( ax ) && astTestAxisAsTime( ax ) );
10086 
10087 /* Annul the Axis pointer. */
10088    ax = astAnnul( ax );
10089 
10090 /* Return the result. */
10091    return result;
10092 }
10093 
TestAttrib(AstObject * this_object,const char * attrib,int * status)10094 static int TestAttrib( AstObject *this_object, const char *attrib, int *status ) {
10095 /*
10096 *  Name:
10097 *     TestAttrib
10098 
10099 *  Purpose:
10100 *     Test if a specified attribute value is set for a SkyFrame.
10101 
10102 *  Type:
10103 *     Private function.
10104 
10105 *  Synopsis:
10106 *     #include "skyframe.h"
10107 *     int TestAttrib( AstObject *this, const char *attrib, int *status )
10108 
10109 *  Class Membership:
10110 *     SkyFrame member function (over-rides the astTestAttrib protected
10111 *     method inherited from the Frame class).
10112 
10113 *  Description:
10114 *     This function returns a boolean result (0 or 1) to indicate whether
10115 *     a value has been set for one of a SkyFrame's attributes.
10116 
10117 *  Parameters:
10118 *     this
10119 *        Pointer to the SkyFrame.
10120 *     attrib
10121 *        Pointer to a null terminated string specifying the attribute
10122 *        name.  This should be in lower case with no surrounding white
10123 *        space.
10124 *     status
10125 *        Pointer to the inherited status variable.
10126 
10127 *  Returned Value:
10128 *     One if a value has been set, otherwise zero.
10129 
10130 *  Notes:
10131 *     - This function uses one-based axis numbering so that it is
10132 *     suitable for external (public) use.
10133 *     - A value of zero will be returned if this function is invoked
10134 *     with the global status set, or if it should fail for any reason.
10135 */
10136 
10137 /* Local Variables: */
10138    AstSkyFrame *this;            /* Pointer to the SkyFrame structure */
10139    int axis;                     /* SkyFrame axis number */
10140    int len;                      /* Length of attrib string */
10141    int nc;                       /* No. characters read by astSscanf */
10142    int result;                   /* Result value to return */
10143 
10144 /* Initialise. */
10145    result = 0;
10146 
10147 /* Check the global error status. */
10148    if ( !astOK ) return result;
10149 
10150 /* Obtain a pointer to the SkyFrame structure. */
10151    this = (AstSkyFrame *) this_object;
10152 
10153 /* Obtain the length of the attrib string. */
10154    len = strlen( attrib );
10155 
10156 /* Check the attribute name and test the appropriate attribute. */
10157 
10158 /* AsTime(axis). */
10159 /* ------------- */
10160    if ( nc = 0,
10161         ( 1 == astSscanf( attrib, "astime(%d)%n", &axis, &nc ) )
10162         && ( nc >= len ) ) {
10163       result = astTestAsTime( this, axis - 1 );
10164 
10165 /* Equinox. */
10166 /* -------- */
10167    } else if ( !strcmp( attrib, "equinox" ) ) {
10168       result = astTestEquinox( this );
10169 
10170 /* NegLon. */
10171 /* ------- */
10172    } else if ( !strcmp( attrib, "neglon" ) ) {
10173       result = astTestNegLon( this );
10174 
10175 /* Projection. */
10176 /* ----------- */
10177    } else if ( !strcmp( attrib, "projection" ) ) {
10178       result = astTestProjection( this );
10179 
10180 /* SkyRefIs. */
10181 /* --------- */
10182    } else if ( !strcmp( attrib, "skyrefis" ) ) {
10183       result = astTestSkyRefIs( this );
10184 
10185 /* SkyRef. */
10186 /* ------- */
10187    } else if ( !strcmp( attrib, "skyref" ) ) {
10188       result = astTestSkyRef( this, 0 ) || astTestSkyRef( this, 1 );
10189 
10190 /* SkyRef(axis). */
10191 /* ------------- */
10192    } else if ( nc = 0,
10193         ( 1 == astSscanf( attrib, "skyref(%d)%n", &axis, &nc ) )
10194         && ( nc >= len ) ) {
10195       result = astTestSkyRef( this, axis - 1 );
10196 
10197 /* SkyRefP. */
10198 /* -------- */
10199    } else if ( !strcmp( attrib, "skyrefp" ) ) {
10200       result = astTestSkyRefP( this, 0 ) || astTestSkyRefP( this, 1 );
10201 
10202 /* SkyRefP(axis). */
10203 /* ------------- */
10204    } else if ( nc = 0,
10205         ( 1 == astSscanf( attrib, "skyrefp(%d)%n", &axis, &nc ) )
10206         && ( nc >= len ) ) {
10207       result = astTestSkyRefP( this, axis - 1 );
10208 
10209 /* AlignOffset */
10210 /* ----------- */
10211    } else if ( !strcmp( attrib, "alignoffset" ) ) {
10212       result = astTestAlignOffset( this );
10213 
10214 /* If the name is not recognised, test if it matches any of the
10215    read-only attributes of this class. If it does, then return
10216    zero. */
10217    } else if ( !strncmp( attrib, "islataxis", 9 ) ||
10218                !strncmp( attrib, "islonaxis", 9 ) ||
10219                !strcmp( attrib, "lataxis" ) ||
10220                !strcmp( attrib, "lonaxis" ) ) {
10221       result = 0;
10222 
10223 /* If the attribute is not recognised, pass it on to the parent method
10224    for further interpretation. */
10225    } else {
10226       result = (*parent_testattrib)( this_object, attrib, status );
10227    }
10228 
10229 /* Return the result, */
10230    return result;
10231 }
10232 
Unformat(AstFrame * this_frame,int axis,const char * string,double * value,int * status)10233 static int Unformat( AstFrame *this_frame, int axis, const char *string,
10234                      double *value, int *status ) {
10235 /*
10236 *  Name:
10237 *     Unformat
10238 
10239 *  Purpose:
10240 *     Read a formatted coordinate value for a SkyFrame axis.
10241 
10242 *  Type:
10243 *     Private function.
10244 
10245 *  Synopsis:
10246 *     #include "skyframe.h"
10247 *     int Unformat( AstFrame *this, int axis, const char *string,
10248 *                   double *value, int *status )
10249 
10250 *  Class Membership:
10251 *     SkyFrame member function (over-rides the public astUnformat
10252 *     method inherited from the Frame class).
10253 
10254 *  Description:
10255 *     This function reads a formatted coordinate value for a SkyFrame
10256 *     axis (supplied as a string) and returns the equivalent numerical
10257 *     value as a double. It also returns the number of characters read
10258 *     from the string.
10259 
10260 *  Parameters:
10261 *     this
10262 *        Pointer to the SkyFrame.
10263 *     axis
10264 *        The number of the SkyFrame axis for which the coordinate
10265 *        value is to be read (axis numbering starts at zero for the
10266 *        first axis).
10267 *     string
10268 *        Pointer to a constant null-terminated string containing the
10269 *        formatted coordinate value.
10270 *     value
10271 *        Pointer to a double in which the coordinate value read will
10272 *        be returned (in radians).
10273 *     status
10274 *        Pointer to the inherited status variable.
10275 
10276 *  Returned Value:
10277 *     The number of characters read from the string to obtain the
10278 *     coordinate value.
10279 
10280 *  Notes:
10281 *     - Any white space at the beginning of the string will be
10282 *     skipped, as also will any trailing white space following the
10283 *     coordinate value read. The function's return value will reflect
10284 *     this.
10285 *     - A function value of zero (and no coordinate value) will be
10286 *     returned, without error, if the string supplied does not contain
10287 *     a suitably formatted value.
10288 *     - The string "<bad>" is recognised as a special case and will
10289 *     generate the value AST__BAD, without error. The test for this
10290 *     string is case-insensitive and permits embedded white space.
10291 *     - A function result of zero will be returned and no coordinate
10292 *     value will be returned via the "value" pointer if this function
10293 *     is invoked with the global error status set, or if it should
10294 *     fail for any reason.
10295 */
10296 
10297 /* Local Variables: */
10298    AstSkyFrame *this;            /* Pointer to the SkyFrame structure */
10299    double coord;                 /* Coordinate value read */
10300    int format_set;               /* Format attribute set? */
10301    int nc;                       /* Number of characters read */
10302 
10303 /* Initialise. */
10304    nc = 0;
10305 
10306 /* Check the global error status. */
10307    if ( !astOK ) return nc;
10308 
10309 /* Obtain a pointer to the SkyFrame structure. */
10310    this = (AstSkyFrame *) this_frame;
10311 
10312 /* Validate the axis index. */
10313    (void) astValidateAxis( this, axis, 1, "astUnformat" );
10314 
10315 /* Determine if a Format value has been set for the axis and set a
10316    temporary value if it has not. Use the GetFormat member function
10317    for this class together with member functions inherited from the
10318    parent class (rather than using the object's methods directly)
10319    because if any of these methods have been over-ridden by a derived
10320    class the Format string syntax may no longer be compatible with
10321    this class. */
10322    format_set = (*parent_testformat)( this_frame, axis, status );
10323    if ( !format_set ) {
10324       (*parent_setformat)( this_frame, axis, GetFormat( this_frame, axis, status ), status );
10325    }
10326 
10327 /* Use the Unformat member function inherited from the parent class to
10328    read the coordinate value. */
10329    nc = (*parent_unformat)( this_frame, axis, string, &coord, status );
10330 
10331 /* If necessary, clear any temporary Format value that was set above. */
10332    if ( !format_set ) (*parent_clearformat)( this_frame, axis, status );
10333 
10334 /* If an error occurred, clear the number of characters read. */
10335    if ( !astOK ) {
10336       nc = 0;
10337 
10338 /* Otherwise, if characters were read, return the coordinate value. */
10339    } else if ( nc ) {
10340       *value = coord;
10341    }
10342 
10343 /* Return the number of characters read. */
10344    return nc;
10345 }
10346 
ValidateSystem(AstFrame * this,AstSystemType system,const char * method,int * status)10347 static int ValidateSystem( AstFrame *this, AstSystemType system, const char *method, int *status ) {
10348 /*
10349 *
10350 *  Name:
10351 *     ValidateSystem
10352 
10353 *  Purpose:
10354 *     Validate a value for a Frame's System attribute.
10355 
10356 *  Type:
10357 *     Protected virtual function.
10358 
10359 *  Synopsis:
10360 *     #include "frame.h"
10361 *     int ValidateSystem( AstFrame *this, AstSystemType system,
10362 *                         const char *method, int *status )
10363 
10364 *  Class Membership:
10365 *     SkyFrame member function (over-rides the astValidateSystem method
10366 *     inherited from the Frame class).
10367 
10368 *  Description:
10369 *     This function checks the validity of the supplied system value.
10370 *     If the value is valid, it is returned unchanged. Otherwise, an
10371 *     error is reported and a value of AST__BADSYSTEM is returned.
10372 
10373 *  Parameters:
10374 *     this
10375 *        Pointer to the Frame.
10376 *     system
10377 *        The system value to be checked.
10378 *     method
10379 *        Pointer to a constant null-terminated character string
10380 *        containing the name of the method that invoked this function
10381 *        to validate an axis index. This method name is used solely
10382 *        for constructing error messages.
10383 *     status
10384 *        Pointer to the inherited status variable.
10385 
10386 *  Returned Value:
10387 *     The validated system value.
10388 
10389 *  Notes:
10390 *     - A value of AST__BADSYSTEM will be returned if this function is invoked
10391 *     with the global error status set, or if it should fail for any
10392 *     reason.
10393 */
10394 
10395 /* Local Variables: */
10396    AstSystemType result;              /* Validated system value */
10397 
10398 /* Initialise. */
10399    result = AST__BADSYSTEM;
10400 
10401 /* Check the global error status. */
10402    if ( !astOK ) return result;
10403 
10404 /* If the value is out of bounds, report an error. */
10405    if ( system < FIRST_SYSTEM || system > LAST_SYSTEM ) {
10406          astError( AST__AXIIN, "%s(%s): Bad value (%d) given for the System "
10407                    "or AlignSystem attribute of a %s.", status, method,
10408                    astGetClass( this ), (int) system, astGetClass( this ) );
10409 
10410 /* Otherwise, return the supplied value. */
10411    } else {
10412       result = system;
10413    }
10414 
10415 /* Return the result. */
10416    return result;
10417 }
10418 
VerifyMSMAttrs(AstSkyFrame * target,AstSkyFrame * result,int which,const char * attrs,const char * method,int * status)10419 static void VerifyMSMAttrs( AstSkyFrame *target, AstSkyFrame *result,
10420                             int which, const char *attrs, const char *method, int *status ) {
10421 /*
10422 *  Name:
10423 *     VerifyMSMAttrs
10424 
10425 *  Purpose:
10426 *     Verify that usable attribute values are available.
10427 
10428 *  Type:
10429 *     Private function.
10430 
10431 *  Synopsis:
10432 *     #include "skyframe.h"
10433 *      void VerifyMSMAttrs( AstSkyFrame *target, AstSkyFrame *result,
10434 *                           int which, const char *attrs, const char *method, int *status )
10435 
10436 *  Class Membership:
10437 *     SkyFrame member function
10438 
10439 *  Description:
10440 *     This function tests each attribute listed in "attrs". It returns
10441 *     without action if 1) an explicit value has been set for each attribute
10442 *     in the SkyFrame indicated by "which" or 2) the UseDefs attribute of the
10443 *     "which" SkyFrame is non-zero.
10444 *
10445 *     If UseDefs is zero (indicating that default values should not be
10446 *     used for attributes), and any of the named attributes does not have
10447 *     an explicitly set value, then an error is reported.
10448 *
10449 *     The displayed error message assumes that tjis function was called
10450 *     as part of the process of producing a Mapping from "target" to "result".
10451 
10452 *  Parameters:
10453 *     target
10454 *        Pointer to the target SkyFrame.
10455 *     result
10456 *        Pointer to the result SkyFrame.
10457 *     which
10458 *        If 2, both the target and result SkyFrames are checked for the
10459 *        supplied attributes. If less than 2, only the target SkyFrame is
10460 *        checked. If greater than 2, only the result SkyFrame is checked.
10461 *     attrs
10462 *        A string holding a space separated list of attribute names.
10463 *     method
10464 *        A string holding the name of the calling method for use in error
10465 *        messages.
10466 *     status
10467 *        Pointer to the inherited status variable.
10468 
10469 */
10470 
10471 /* Local Variables: */
10472    const char *a;
10473    const char *p;
10474    const char *desc;
10475    int len;
10476    int set1;
10477    int set2;
10478    int state;
10479    int usedef1;
10480    int usedef2;
10481 
10482 /* Check inherited status */
10483    if( !astOK ) return;
10484 
10485 /* Get the UseDefs attributes of the two SkyFrames. */
10486    usedef1 = astGetUseDefs( target );
10487    usedef2 = astGetUseDefs( result );
10488 
10489 /* If both SkyFrames have a non-zero value for its UseDefs attribute, then
10490    all attributes are assumed to have usable values, since the defaults
10491    will be used if no explicit value has been set. So we only need to do
10492    any checks if UseDefs is zero for either SkyFrame. */
10493    if( !usedef1 || !usedef2 ) {
10494 
10495 /* Stop compiler warnings about uninitialised variables */
10496       a = NULL;
10497       desc = NULL;
10498       len = 0;
10499       set1 = 0;
10500       set2 = 0;
10501 
10502 /* Loop round the "attrs" string identifying the start and length of each
10503    non-blank word in the string. */
10504       state = 0;
10505       p = attrs;
10506       while( 1 ) {
10507          if( state == 0 ) {
10508             if( !isspace( *p ) ) {
10509                a = p;
10510                len = 1;
10511                state = 1;
10512             }
10513          } else {
10514             if( isspace( *p ) || !*p ) {
10515 
10516 /* The end of a word has just been reached. Compare it to each known
10517    attribute value. Get a flag indicating if the attribute has a set
10518    value, and a string describing the attribute.*/
10519                if( len > 0 ) {
10520 
10521                   if( !strncmp( "Equinox", a, len ) ) {
10522                      set1 = astTestEquinox( target );
10523                      set2 = astTestEquinox( result );
10524                      desc = "reference equinox";
10525 
10526                   } else if( !strncmp( "Dut1", a, len ) ) {
10527                      set1 = astTestDut1( target );
10528                      set2 = astTestDut1( result );
10529                      desc = "UT1-UTC correction";
10530 
10531                   } else if( !strncmp( "Epoch", a, len ) ) {
10532                      set1 = astTestEpoch( target );
10533                      set2 = astTestEpoch( result );
10534                      desc = "epoch of observation";
10535 
10536                   } else if( !strncmp( "ObsLon", a, len ) ) {
10537                      set1 = astTestObsLon( target );
10538                      set2 = astTestObsLon( result );
10539                      desc = "longitude of observer";
10540 
10541                   } else if( !strncmp( "ObsLat", a, len ) ) {
10542                      set1 = astTestObsLat( target );
10543                      set2 = astTestObsLat( result );
10544                      desc = "latitude of observer";
10545 
10546                   } else if( !strncmp( "ObsAlt", a, len ) ) {
10547                      set1 = astTestObsAlt( target );
10548                      set2 = astTestObsAlt( result );
10549                      desc = "altitude of observer";
10550 
10551                   } else {
10552                      astError( AST__INTER, "VerifyMSMAttrs(SkyFrame): "
10553                                "Unknown attribute name \"%.*s\" supplied (AST "
10554                                "internal programming error).", status, len, a );
10555                   }
10556 
10557 /* If the attribute is not set in the target but should be, report an
10558    error. */
10559                   if( !usedef1 && !set1 && which < 3 ) {
10560                      astClearTitle( target );
10561                      astClearTitle( result );
10562                      astError( AST__NOVAL, "%s(%s): Cannot convert "
10563                                "celestial coordinates from %s to %s.", status,
10564                                method, astGetClass( target ),
10565                                astGetC( target, "Title" ),
10566                                astGetC( result, "Title" ) );
10567                      astError( AST__NOVAL, "No value has been set for "
10568                                "the \"%.*s\" attribute (%s) in the input %s.", status,
10569                                len, a, desc, astGetClass( target ) );
10570                      break;
10571                   }
10572 
10573 /* If the attribute is not set in the result but should be, report an
10574    error. */
10575                   if( !usedef2 && !set2 && which > 1 ) {
10576                      astClearTitle( target );
10577                      astClearTitle( result );
10578                      astError( AST__NOVAL, "%s(%s): Cannot convert "
10579                                "celestial coordinates from %s to %s.", status,
10580                                method, astGetClass( result ),
10581                                astGetC( target, "Title" ),
10582                                astGetC( result, "Title" ) );
10583                      astError( AST__NOVAL, "No value has been set for "
10584                                "the \"%.*s\" attribute (%s) in the output %s.", status,
10585                                len, a, desc, astGetClass( result ) );
10586                      break;
10587                   }
10588 
10589 /* Continue the word search algorithm. */
10590                }
10591                len = 0;
10592                state = 0;
10593             } else {
10594                len++;
10595             }
10596          }
10597          if( !*(p++) ) break;
10598       }
10599    }
10600 }
10601 
10602 /* Functions which access class attributes. */
10603 /* ---------------------------------------- */
10604 /*
10605 *att++
10606 *  Name:
10607 *     AlignOffset
10608 
10609 *  Purpose:
10610 *     Align SkyFrames using the offset coordinate system?
10611 
10612 *  Type:
10613 *     Public attribute.
10614 
10615 *  Synopsis:
10616 *     Integer (boolean).
10617 
10618 *  Description:
10619 *     This attribute is a boolean value which controls how a SkyFrame
10620 *     behaves when it is used (by
10621 c     astFindFrame or astConvert) as a template to match another (target)
10622 f     AST_FINDFRAME or AST_CONVERT) as a template to match another (target)
10623 *     SkyFrame. It determines the coordinate system in which the two
10624 *     SkyFrames are aligned if a match occurs.
10625 *
10626 *     If the template and target SkyFrames both have defined offset coordinate
10627 *     systems (i.e. the SkyRefIs attribute is set to either "Origin" or "
10628 *     Pole"), and they both have a non-zero value for AlignOffset, then
10629 *     alignment occurs within the offset coordinate systems (that is, a
10630 *     UnitMap will always be used to align the two SkyFrames). If either
10631 *     the template or target SkyFrame has zero (the default value) for
10632 *     AlignOffset, or if either SkyFrame has SkyRefIs set to "Ignored", then
10633 *     alignment occurring within the coordinate system specified by the
10634 *     AlignSystem attribute.
10635 
10636 *  Applicability:
10637 *     SkyFrame
10638 *        All SkyFrames have this attribute.
10639 *att--
10640 */
10641 astMAKE_CLEAR(SkyFrame,AlignOffset,alignoffset,-INT_MAX)
10642 astMAKE_GET(SkyFrame,AlignOffset,int,0,( ( this->alignoffset != -INT_MAX ) ?
10643                                            this->alignoffset : 0 ))
10644 astMAKE_SET(SkyFrame,AlignOffset,int,alignoffset,( value != 0 ))
10645 astMAKE_TEST(SkyFrame,AlignOffset,( this->alignoffset != -INT_MAX ))
10646 
10647 /*
10648 *att++
10649 *  Name:
10650 *     AsTime(axis)
10651 
10652 *  Purpose:
10653 *     Format celestal coordinates as times?
10654 
10655 *  Type:
10656 *     Public attribute.
10657 
10658 *  Synopsis:
10659 *     Integer (boolean).
10660 
10661 *  Description:
10662 *     This attribute specifies the default style of formatting to be
10663 c     used (e.g. by astFormat) for the celestial coordinate values
10664 f     used (e.g. by AST_FORMAT) for the celestial coordinate values
10665 *     described by a SkyFrame. It takes a separate boolean value for
10666 *     each SkyFrame axis so that, for instance, the setting
10667 *     "AsTime(2)=0" specifies the default formatting style for
10668 *     celestial latitude values.
10669 *
10670 *     If the AsTime attribute for a SkyFrame axis is zero, then
10671 *     coordinates on that axis will be formatted as angles by default
10672 *     (using degrees, minutes and seconds), otherwise they will be
10673 *     formatted as times (using hours, minutes and seconds).
10674 *
10675 *     The default value of AsTime is chosen according to the sky
10676 *     coordinate system being represented, as determined by the
10677 *     SkyFrame's System attribute. This ensures, for example, that
10678 *     right ascension values will be formatted as times by default,
10679 *     following normal conventions.
10680 
10681 *  Applicability:
10682 *     SkyFrame
10683 *        All SkyFrames have this attribute.
10684 
10685 *  Notes:
10686 *     - The AsTime attribute operates by changing the default value of
10687 *     the corresponding Format(axis) attribute. This, in turn, may
10688 *     also affect the value of the Unit(axis) attribute.
10689 *     - Only the default style of formatting is affected by the AsTime
10690 *     value. If an explicit Format(axis) value is set, it will
10691 *     over-ride any effect from the AsTime attribute.
10692 *att--
10693 */
10694 
10695 /*
10696 *att++
10697 *  Name:
10698 *     Equinox
10699 
10700 *  Purpose:
10701 *     Epoch of the mean equinox.
10702 
10703 *  Type:
10704 *     Public attribute.
10705 
10706 *  Synopsis:
10707 *     Floating point.
10708 
10709 *  Description:
10710 *     This attribute is used to qualify those celestial coordinate
10711 *     systems described by a SkyFrame which are notionally based on
10712 *     the ecliptic (the plane of the Earth's orbit around the Sun)
10713 *     and/or the Earth's equator.
10714 *
10715 *     Both of these planes are in motion and their positions are
10716 *     difficult to specify precisely. In practice, therefore, a model
10717 *     ecliptic and/or equator are used instead. These, together with
10718 *     the point on the sky that defines the coordinate origin (the
10719 *     intersection of the two planes termed the "mean equinox") move
10720 *     with time according to some model which removes the more rapid
10721 *     fluctuations. The SkyFrame class supports both the FK4 and
10722 *     FK5 models.
10723 *
10724 *     The position of a fixed source expressed in any of these
10725 *     coordinate systems will appear to change with time due to
10726 *     movement of the coordinate system itself (rather than motion of
10727 *     the source).  Such coordinate systems must therefore be
10728 *     qualified by a moment in time (the "epoch of the mean equinox"
10729 *     or "equinox" for short) which allows the position of the model
10730 *     coordinate system on the sky to be determined. This is the role
10731 *     of the Equinox attribute.
10732 *
10733 *     The Equinox attribute is stored as a Modified Julian Date, but
10734 *     when setting or getting its value you may use the same formats
10735 *     as for the Epoch attribute (q.v.).
10736 *
10737 *     The default Equinox value is B1950.0 (Besselian) for the old
10738 *     FK4-based coordinate systems (see the System attribute) and
10739 *     J2000.0 (Julian) for all others.
10740 
10741 *  Applicability:
10742 *     SkyFrame
10743 *        All SkyFrames have this attribute.
10744 
10745 *  Notes:
10746 *     - Care must be taken to distinguish the Equinox value, which
10747 *     relates to the definition of a time-dependent coordinate system
10748 *     (based on solar system reference planes which are in motion),
10749 *     from the superficially similar Epoch value. The latter is used
10750 *     to qualify coordinate systems where the positions of sources
10751 *     change with time (or appear to do so) for a variety of other
10752 *     reasons, such as aberration of light caused by the observer's
10753 *     motion, etc.
10754 *     - See the description of the System attribute for details of
10755 *     which qualifying attributes apply to each celestial coordinate
10756 *     system.
10757 *att--
10758 */
10759 /* Clear the Equinox value by setting it to AST__BAD. */
astMAKE_CLEAR(SkyFrame,Equinox,equinox,AST__BAD)10760 astMAKE_CLEAR(SkyFrame,Equinox,equinox,AST__BAD)
10761 
10762 /* Provide a default value of B1950.0 or J2000.0 depending on the System
10763    setting. */
10764 astMAKE_GET(SkyFrame,Equinox,double,AST__BAD,(
10765             ( this->equinox != AST__BAD ) ? this->equinox :
10766                ( ( ( astGetSystem( this ) == AST__FK4 ) ||
10767                    ( astGetSystem( this ) == AST__FK4_NO_E ) ) ?
10768                     palEpb2d( 1950.0 ) : palEpj2d( 2000.0 ) ) ))
10769 
10770 /* Allow any Equinox value to be set, unless the System is Helio-ecliptic
10771    (in which case clear the value so that J2000 is used). */
10772 astMAKE_SET(SkyFrame,Equinox,double,equinox,((astGetSystem(this)!=AST__HELIOECLIPTIC)?value:AST__BAD))
10773 
10774 /* An Equinox value is set if it is not equal to AST__BAD. */
10775 astMAKE_TEST(SkyFrame,Equinox,( this->equinox != AST__BAD ))
10776 
10777 
10778 /*
10779 *att++
10780 *  Name:
10781 *     IsLatAxis(axis)
10782 
10783 *  Purpose:
10784 *     Is the specified celestial axis a latitude axis?
10785 
10786 *  Type:
10787 *     Public attribute.
10788 
10789 *  Synopsis:
10790 *     Integer (boolean), read-only.
10791 
10792 *  Description:
10793 *     This is a read-only boolean attribute that indicates the nature of
10794 *     the specified axis. The attribute has a non-zero value if the
10795 *     specified axis is a celestial latitude axis (Declination, Galactic
10796 *     latitude, etc), and is zero otherwise.
10797 
10798 *  Applicability:
10799 *     SkyFrame
10800 *        All SkyFrames have this attribute.
10801 
10802 *  Notes:
10803 *     - When specifying this attribute by name, it should be
10804 *     subscripted with the number of the SkyFrame axis to be tested.
10805 *att--
10806 */
10807 
10808 /*
10809 *att++
10810 *  Name:
10811 *     IsLonAxis(axis)
10812 
10813 *  Purpose:
10814 *     Is the specified celestial axis a longitude axis?
10815 
10816 *  Type:
10817 *     Public attribute.
10818 
10819 *  Synopsis:
10820 *     Integer (boolean), read-only.
10821 
10822 *  Description:
10823 *     This is a read-only boolean attribute that indicates the nature of
10824 *     the specified axis. The attribute has a non-zero value if the
10825 *     specified axis is a celestial longitude axis (Right Ascension, Galactic
10826 *     longitude, etc), and is zero otherwise.
10827 
10828 *  Applicability:
10829 *     SkyFrame
10830 *        All SkyFrames have this attribute.
10831 
10832 *  Notes:
10833 *     - When specifying this attribute by name, it should be
10834 *     subscripted with the number of the SkyFrame axis to be tested.
10835 *att--
10836 */
10837 
10838 /*
10839 *att++
10840 *  Name:
10841 *     LatAxis
10842 
10843 *  Purpose:
10844 *     Index of the latitude axis.
10845 
10846 *  Type:
10847 *     Public attribute.
10848 
10849 *  Synopsis:
10850 *     Integer.
10851 
10852 *  Description:
10853 *     This read-only attribute gives the index (1 or 2) of the latitude
10854 *     axis within the SkyFrame (taking into account any current axis
10855 *     permutations).
10856 
10857 *  Applicability:
10858 *     SkyFrame
10859 *        All SkyFrames have this attribute.
10860 
10861 *att--
10862 */
10863 
10864 /*
10865 *att++
10866 *  Name:
10867 *     LonAxis
10868 
10869 *  Purpose:
10870 *     Index of the longitude axis.
10871 
10872 *  Type:
10873 *     Public attribute.
10874 
10875 *  Synopsis:
10876 *     Integer.
10877 
10878 *  Description:
10879 *     This read-only attribute gives the index (1 or 2) of the longitude
10880 *     axis within the SkyFrame (taking into account any current axis
10881 *     permutations).
10882 
10883 *  Applicability:
10884 *     SkyFrame
10885 *        All SkyFrames have this attribute.
10886 
10887 *att--
10888 */
10889 
10890 /*
10891 *att++
10892 *  Name:
10893 *     NegLon
10894 
10895 *  Purpose:
10896 *     Display negative longitude values?
10897 
10898 *  Type:
10899 *     Public attribute.
10900 
10901 *  Synopsis:
10902 *     Integer (boolean).
10903 
10904 *  Description:
10905 *     This attribute is a boolean value which controls how longitude values
10906 c     are normalized for display by astNorm.
10907 f     are normalized for display by AST_NORM.
10908 *
10909 *     If the NegLon attribute is zero, then normalized
10910 *     longitude values will be in the range zero to 2.pi. If NegLon is
10911 *     non-zero, then normalized longitude values will be in the range -pi
10912 *     to pi.
10913 *
10914 *     The default value depends on the current value of the SkyRefIs
10915 *     attribute, If SkyRefIs has a value of "Origin", then the default for
10916 *     NegLon is one, otherwise the default is zero.
10917 
10918 *  Applicability:
10919 *     SkyFrame
10920 *        All SkyFrames have this attribute.
10921 *att--
10922 */
10923 /* Clear the NegLon value by setting it to -INT_MAX. */
10924 astMAKE_CLEAR(SkyFrame,NegLon,neglon,-INT_MAX)
10925 
10926 /* Supply a default of 0 for absolute coords and 1 for offset coords if
10927    no NegLon value has been set. */
10928 astMAKE_GET(SkyFrame,NegLon,int,0,( ( this->neglon != -INT_MAX ) ?
10929 this->neglon : (( astGetSkyRefIs( this ) == AST__ORIGIN_REF )? 1 : 0)))
10930 
10931 /* Set a NegLon value of 1 if any non-zero value is supplied. */
10932 astMAKE_SET(SkyFrame,NegLon,int,neglon,( value != 0 ))
10933 
10934 /* The NegLon value is set if it is not -INT_MAX. */
10935 astMAKE_TEST(SkyFrame,NegLon,( this->neglon != -INT_MAX ))
10936 
10937 /*
10938 *att++
10939 *  Name:
10940 *     Projection
10941 
10942 *  Purpose:
10943 *     Sky projection description.
10944 
10945 *  Type:
10946 *     Public attribute.
10947 
10948 *  Synopsis:
10949 *     String.
10950 
10951 *  Description:
10952 *     This attribute provides a place to store a description of the
10953 *     type of sky projection used when a SkyFrame is attached to a
10954 *     2-dimensional object, such as an image or plotting surface. For
10955 *     example, typical values might be "orthographic", "Hammer-Aitoff"
10956 *     or "cylindrical equal area".
10957 *
10958 *     The Projection value is purely descriptive and does not affect
10959 *     the celestial coordinate system represented by the SkyFrame in
10960 *     any way. If it is set to a non-blank string, the description
10961 *     provided may be used when forming the default value for the
10962 *     SkyFrame's Title attribute (so that typically it will appear in
10963 *     graphical output, for instance). The default value is an empty
10964 *     string.
10965 
10966 *  Applicability:
10967 *     SkyFrame
10968 *        All SkyFrames have this attribute.
10969 *att--
10970 */
10971 /* Clear the Projection value by freeing the allocated memory and
10972    assigning a NULL pointer. */
10973 astMAKE_CLEAR(SkyFrame,Projection,projection,astFree( this->projection ))
10974 
10975 /* If the Projection value is not set, return a pointer to an empty
10976    string. */
10977 astMAKE_GET(SkyFrame,Projection,const char *,NULL,( this->projection ?
10978                                                     this->projection : "" ))
10979 
10980 /* Set a Projection value by freeing any previously allocated memory,
10981    allocating new memory, storing the string and saving the pointer to
10982    the copy. */
10983 astMAKE_SET(SkyFrame,Projection,const char *,projection,astStore(
10984                      this->projection, value, strlen( value ) + (size_t) 1 ))
10985 
10986 /* The Projection value is set if the pointer to it is not NULL. */
10987 astMAKE_TEST(SkyFrame,Projection,( this->projection != NULL ))
10988 
10989 /*
10990 *att++
10991 *  Name:
10992 *     SkyRefIs
10993 
10994 *  Purpose:
10995 *     Selects the nature of the offset coordinate system.
10996 
10997 *  Type:
10998 *     Public attribute.
10999 
11000 *  Synopsis:
11001 *     String.
11002 
11003 *  Description:
11004 *     This attribute controls how the values supplied for the SkyRef and
11005 *     SkyRefP attributes are used. These three attributes together allow
11006 *     a SkyFrame to represent offsets relative to some specified origin
11007 *     or pole within the coordinate system specified by the System attribute,
11008 *     rather than absolute axis values. SkyRefIs can take one of the
11009 *     case-insensitive values "Origin", "Pole" or "Ignored".
11010 *
11011 *     If SkyRefIs is set to "Origin", then the coordinate system
11012 *     represented by the SkyFrame is modified to put the origin of longitude
11013 *     and latitude at the position specified by the SkyRef attribute.
11014 *
11015 *     If SkyRefIs is set to "Pole", then the coordinate system represented
11016 *     by the SkyFrame is modified to put the north pole at the position
11017 *     specified by the SkyRef attribute.
11018 *
11019 *     If SkyRefIs is set to "Ignored" (the default), then any value set for the
11020 *     SkyRef attribute is ignored, and the SkyFrame represents the coordinate
11021 *     system specified by the System attribute directly without any rotation.
11022 
11023 *  Applicability:
11024 *     SkyFrame
11025 *        All SkyFrames have this attribute.
11026 
11027 *att--
11028 */
11029 astMAKE_CLEAR(SkyFrame,SkyRefIs,skyrefis,AST__BAD_REF)
11030 astMAKE_SET(SkyFrame,SkyRefIs,int,skyrefis,value)
11031 astMAKE_TEST(SkyFrame,SkyRefIs,( this->skyrefis != AST__BAD_REF ))
11032 astMAKE_GET(SkyFrame,SkyRefIs,int,AST__IGNORED_REF,(this->skyrefis == AST__BAD_REF ? AST__IGNORED_REF : this->skyrefis))
11033 
11034 /*
11035 *att++
11036 *  Name:
11037 *     SkyRef(axis)
11038 
11039 *  Purpose:
11040 *     Position defining the offset coordinate system.
11041 
11042 *  Type:
11043 *     Public attribute.
11044 
11045 *  Synopsis:
11046 *     Floating point.
11047 
11048 *  Description:
11049 *     This attribute allows a SkyFrame to represent offsets, rather than
11050 *     absolute axis values, within the coordinate system specified by the
11051 *     System attribute. If supplied, SkyRef should be set to hold the
11052 *     longitude and latitude of a point within the coordinate system
11053 *     specified by the System attribute. The coordinate system represented
11054 *     by the SkyFrame will then be rotated in order to put the specified
11055 *     position at either the pole or the origin of the new coordinate system
11056 *     (as indicated by the SkyRefIs attribute). The orientation of the
11057 *     modified coordinate system is then controlled using the SkyRefP
11058 *     attribute.
11059 *
11060 *     If an integer axis index is included in the attribute name (e.g.
11061 *     "SkyRef(1)") then the attribute value should be supplied as a single
11062 *     floating point axis value, in radians, when setting a value for the
11063 *     attribute, and will be returned in the same form when getting the value
11064 *     of the attribute. In this case the integer axis index should be "1"
11065 *     or "2" (the values to use for longitude and latitude axes are
11066 *     given by the LonAxis and LatAxis attributes).
11067 *
11068 *     If no axis index is included in the attribute name (e.g. "SkyRef") then
11069 *     the attribute value should be supplied as a character string
11070 *     containing two formatted axis values (an axis 1 value followed by a
11071 *     comma, followed by an axis 2 value). The same form
11072 *     will be used when getting the value of the attribute.
11073 *
11074 *     The default values for SkyRef are zero longitude and zero latitude.
11075 
11076 *  Aligning SkyFrames with Offset Coordinate Systems:
11077 *     The offset coordinate system within a SkyFrame should normally be
11078 *     considered as a superficial "re-badging" of the axes of the coordinate
11079 *     system specified by the System attribute - it merely provides an
11080 *     alternative numerical "label" for each position in the System coordinate
11081 *     system. The SkyFrame retains full knowledge of the celestial coordinate
11082 *     system on which the offset coordinate system is based (given by the
11083 *     System attribute). For instance, the SkyFrame retains knowledge of the
11084 *     way that one celestial coordinate system may "drift" with respect to
11085 *     another over time. Normally, if you attempt to align two SkyFrames (e.g.
11086 f     using the AST_CONVERT or AST_FINDFRAME routine),
11087 c     using the astConvert or astFindFrame routine),
11088 *     the effect of any offset coordinate system defined in either SkyFrame
11089 *     will be removed, resulting in alignment being performed in the
11090 *     celestial coordinate system given by the AlignSystem attribute.
11091 *     However, by setting the AlignOffset attribute ot a non-zero value, it
11092 *     is possible to change this behaviour so that the effect of the offset
11093 *     coordinate system is not removed when aligning two SkyFrames.
11094 
11095 *  Applicability:
11096 *     SkyFrame
11097 *        All SkyFrames have this attribute.
11098 
11099 *  Notes:
11100 *     - If the System attribute of the SkyFrame is changed, any position
11101 *     given for SkyRef is transformed into the new System.
11102 *     - If a value has been assigned to SkyRef attribute, then
11103 *     the default values for certain attributes are changed as follows:
11104 *     the default axis Labels for the SkyFrame are modified by appending
11105 *     " offset" to the end, the default axis Symbols for the SkyFrame are
11106 *     modified by prepending the character "D" to the start, and the
11107 *     default title is modified by replacing the projection information by the
11108 *     origin information.
11109 
11110 *att--
11111 */
11112 MAKE_CLEAR(SkyRef,skyref,AST__BAD,2)
11113 MAKE_SET(SkyRef,double,skyref,value,2)
11114 MAKE_TEST(SkyRef,( this->skyref[axis_p] != AST__BAD ),2)
11115 MAKE_GET(SkyRef,double,0.0,((this->skyref[axis_p]!=AST__BAD)?this->skyref[axis_p]:0.0),2)
11116 
11117 /*
11118 *att++
11119 *  Name:
11120 *     SkyRefP(axis)
11121 
11122 *  Purpose:
11123 *     Position on primary meridian of offset coordinate system.
11124 
11125 *  Type:
11126 *     Public attribute.
11127 
11128 *  Synopsis:
11129 *     Floating point.
11130 
11131 *  Description:
11132 *     This attribute is used to control the orientation of the offset
11133 *     coordinate system defined by attributes SkyRef and SkyRefIs. If used,
11134 *     it should be set to hold the longitude and latitude of a point within
11135 *     the coordinate system specified by the System attribute. The offset
11136 *     coordinate system represented by the SkyFrame will then be rotated in
11137 *     order to put the position supplied for SkyRefP on the zero longitude
11138 *     meridian. This rotation is about an axis from the centre of the
11139 *     celestial sphere to the point specified by the SkyRef attribute.
11140 *     The default value for SkyRefP is usually the north pole (that is, a
11141 *     latitude of +90 degrees in the coordinate system specified by the System
11142 *     attribute). The exception to this is if the SkyRef attribute is
11143 *     itself set to either the north or south pole. In these cases the
11144 *     default for SkyRefP is the origin (that is, a (0,0) in the coordinate
11145 *     system specified by the System attribute).
11146 *
11147 *     If an integer axis index is included in the attribute name (e.g.
11148 *     "SkyRefP(1)") then the attribute value should be supplied as a single
11149 *     floating point axis value, in radians, when setting a value for the
11150 *     attribute, and will be returned in the same form when getting the value
11151 *     of the attribute. In this case the integer axis index should be "1"
11152 *     or "2" (the values to use for longitude and latitude axes are
11153 *     given by the LonAxis and LatAxis attributes).
11154 *
11155 *     If no axis index is included in the attribute name (e.g. "SkyRefP") then
11156 *     the attribute value should be supplied as a character string
11157 *     containing two formatted axis values (an axis 1 value followed by a
11158 *     comma, followed by an axis 2 value). The same form
11159 *     will be used when getting the value of the attribute.
11160 
11161 *  Applicability:
11162 *     SkyFrame
11163 *        All SkyFrames have this attribute.
11164 
11165 *  Notes:
11166 *     - If the position given by the SkyRef attribute defines the origin
11167 *     of the offset coordinate system (that is, if the SkyRefIs attribute
11168 *     is set to "origin"), then there will in general be two orientations
11169 *     which will put the supplied SkyRefP position on the zero longitude
11170 *     meridian. The orientation which is actually used is the one which
11171 *     gives the SkyRefP position a positive latitude in the offset coordinate
11172 *     system (the other possible orientation would give the SkyRefP position
11173 *     a negative latitude).
11174 *     - An error will be reported if an attempt is made to use a
11175 *     SkyRefP value which is co-incident with SkyRef or with the point
11176 *     diametrically opposite to SkyRef on the celestial sphere. The
11177 *     reporting of this error is deferred until the SkyRef and SkyRefP
11178 *     attribute values are used within a calculation.
11179 *     - If the System attribute of the SkyFrame is changed, any position
11180 *     given for SkyRefP is transformed into the new System.
11181 
11182 *att--
11183 */
11184 MAKE_CLEAR(SkyRefP,skyrefp,AST__BAD,2)
11185 MAKE_SET(SkyRefP,double,skyrefp,value,2)
11186 MAKE_TEST(SkyRefP,( this->skyrefp[axis_p] != AST__BAD ),2)
11187 
11188 /* Copy constructor. */
11189 /* ----------------- */
11190 static void Copy( const AstObject *objin, AstObject *objout, int *status ) {
11191 /*
11192 *  Name:
11193 *     Copy
11194 
11195 *  Purpose:
11196 *     Copy constructor for SkyFrame objects.
11197 
11198 *  Type:
11199 *     Private function.
11200 
11201 *  Synopsis:
11202 *     void Copy( const AstObject *objin, AstObject *objout, int *status )
11203 
11204 *  Description:
11205 *     This function implements the copy constructor for SkyFrame objects.
11206 
11207 *  Parameters:
11208 *     objin
11209 *        Pointer to the object to be copied.
11210 *     objout
11211 *        Pointer to the object being constructed.
11212 *     status
11213 *        Pointer to the inherited status variable.
11214 
11215 *  Notes:
11216 *     -  This constructor makes a deep copy.
11217 */
11218 
11219 /* Local Variables: */
11220    AstSkyFrame *in;              /* Pointer to input SkyFrame */
11221    AstSkyFrame *out;             /* Pointer to output SkyFrame */
11222 
11223 /* Check the global error status. */
11224    if ( !astOK ) return;
11225 
11226 /* Obtain pointers to the input and output SkyFrames. */
11227    in = (AstSkyFrame *) objin;
11228    out = (AstSkyFrame *) objout;
11229 
11230 /* For safety, first clear any references to the input memory from
11231    the output SkyFrame. */
11232    out->projection = NULL;
11233 
11234 /* If necessary, allocate memory in the output SkyFrame and store a
11235    copy of the input Projection string. */
11236    if ( in->projection ) out->projection = astStore( NULL, in->projection,
11237                                       strlen( in->projection ) + (size_t) 1 );
11238 
11239 /* If an error occurred, free any allocated memory. */
11240    if ( !astOK ) {
11241       out->projection = astFree( out->projection );
11242    }
11243 }
11244 
11245 /* Destructor. */
11246 /* ----------- */
Delete(AstObject * obj,int * status)11247 static void Delete( AstObject *obj, int *status ) {
11248 /*
11249 *  Name:
11250 *     Delete
11251 
11252 *  Purpose:
11253 *     Destructor for SkyFrame objects.
11254 
11255 *  Type:
11256 *     Private function.
11257 
11258 *  Synopsis:
11259 *     void Delete( AstObject *obj, int *status )
11260 
11261 *  Description:
11262 *     This function implements the destructor for SkyFrame objects.
11263 
11264 *  Parameters:
11265 *     obj
11266 *        Pointer to the object to be deleted.
11267 *     status
11268 *        Pointer to the inherited status variable.
11269 
11270 *  Notes:
11271 *     This function attempts to execute even if the global error status is
11272 *     set.
11273 */
11274 
11275 /* Local Variables: */
11276    AstSkyFrame *this;            /* Pointer to SkyFrame */
11277 
11278 /* Obtain a pointer to the SkyFrame structure. */
11279    this = (AstSkyFrame *) obj;
11280 
11281 /* Free the memory used for the Projection string if necessary. */
11282    this->projection = astFree( this->projection );
11283 }
11284 
11285 /* Dump function. */
11286 /* -------------- */
Dump(AstObject * this_object,AstChannel * channel,int * status)11287 static void Dump( AstObject *this_object, AstChannel *channel, int *status ) {
11288 /*
11289 *  Name:
11290 *     Dump
11291 
11292 *  Purpose:
11293 *     Dump function for SkyFrame objects.
11294 
11295 *  Type:
11296 *     Private function.
11297 
11298 *  Synopsis:
11299 *     void Dump( AstObject *this, AstChannel *channel, int *status )
11300 
11301 *  Description:
11302 *     This function implements the Dump function which writes out data
11303 *     for the SkyFrame class to an output Channel.
11304 
11305 *  Parameters:
11306 *     this
11307 *        Pointer to the SkyFrame whose data are being written.
11308 *     channel
11309 *        Pointer to the Channel to which the data are being written.
11310 *     status
11311 *        Pointer to the inherited status variable.
11312 */
11313 
11314 /* Local Variables: */
11315    AstSkyFrame *this;            /* Pointer to the SkyFrame structure */
11316    AstSystemType system;         /* System attribute value */
11317    char buf[ 100 ];              /* Comment buffer */
11318    char key[ 10 ];               /* Buffer for keywords */
11319    const char *sval;             /* Pointer to string value */
11320    const int *perm;              /* Pointer to axis permutation array */
11321    double dval;                  /* Double value */
11322    int bessyr;                   /* Use a Besselian year value ?*/
11323    int helpful;                  /* Helpful to display un-set value? */
11324    int invperm[ 2 ];             /* Inverse permutation array */
11325    int ival;                     /* Integer value */
11326    int set;                      /* Attribute value set? */
11327    int axis;                     /* External (i.e. permuted) zero-based axis index */
11328    int axis_p;                   /* Internal zero-based axis index */
11329 
11330 /* Check the global error status. */
11331    if ( !astOK ) return;
11332 
11333 /* Obtain a pointer to the SkyFrame structure. */
11334    this = (AstSkyFrame *) this_object;
11335 
11336 /* Get a pointer to the SkyFrame's axis permutation array (using a method,
11337    to allow for any over-ride by a derived class). */
11338    perm = astGetPerm( this );
11339 
11340 /* Generate an inverse axis permutation array from the forward permutation
11341    values. This will be used to determine which axis should be enquired
11342    about (using possibly over-ridden methods) to obtain data to
11343    correspond with a particular internal value (i.e. instance variable)
11344    relating to an axis. This step is needed so that the effect of any
11345    axis permutation can be un-done before values are written out, as
11346    output values are written by this function in un-permuted order. */
11347    for ( axis = 0; axis < 2; axis++ ) invperm[ perm[ axis ] ] = axis;
11348 
11349 /* Write out values representing the instance variables for the
11350    SkyFrame class.  Accompany these with appropriate comment strings,
11351    possibly depending on the values being written.*/
11352 
11353 /* In the case of attributes, we first use the appropriate (private)
11354    Test...  member function to see if they are set. If so, we then use
11355    the (private) Get... function to obtain the value to be written
11356    out.
11357 
11358    For attributes which are not set, we use the astGet... method to
11359    obtain the value instead. This will supply a default value
11360    (possibly provided by a derived class which over-rides this method)
11361    which is more useful to a human reader as it corresponds to the
11362    actual default attribute value.  Since "set" will be zero, these
11363    values are for information only and will not be read back. */
11364 
11365 /* Projection. */
11366 /* ----------- */
11367    set = TestProjection( this, status );
11368    sval = set ? GetProjection( this, status ) : astGetProjection( this );
11369    astWriteString( channel, "Proj", set, 0, sval,
11370                    "Description of sky projection" );
11371 
11372 /* NegLon. */
11373 /* ------- */
11374    set = TestNegLon( this, status );
11375    ival = set ? GetNegLon( this, status ) : astGetNegLon( this );
11376    astWriteInt( channel, "NegLon", set, 0, ival,
11377                 ival ? "Display negative longitude values" :
11378                        "Display positive longitude values" );
11379 
11380 /* Equinox. */
11381 /* -------- */
11382    set = TestEquinox( this, status );
11383    dval = set ? GetEquinox( this, status ) : astGetEquinox( this );
11384 
11385 /* Decide whether the Equinox value is relevant to the current
11386    coordinate system. */
11387    system = astGetSystem( this );
11388    helpful = ( ( system == AST__FK4 ) ||
11389                ( system == AST__FK4_NO_E ) ||
11390                ( system == AST__FK5 ) ||
11391                ( system == AST__ECLIPTIC ) );
11392 
11393 /* Convert MJD to Besselian or Julian years, depending on the value. */
11394    bessyr = ( dval < palEpj2d( 1984.0 ) );
11395    dval = bessyr ? palEpb( dval ) : palEpj( dval );
11396    astWriteDouble( channel, "Eqnox", set, helpful, dval,
11397                    bessyr ? "Besselian epoch of mean equinox" :
11398                             "Julian epoch of mean equinox" );
11399 
11400 /* SkyRefIs. */
11401 /* --------- */
11402    set = TestSkyRefIs( this, status );
11403    ival = set ? GetSkyRefIs( this, status ) : astGetSkyRefIs( this );
11404    if( ival == AST__POLE_REF ) {
11405       astWriteString( channel, "SRefIs", set, 0, POLE_STRING,
11406                       "Rotated to put pole at ref. pos." );
11407 
11408    } else if( ival == AST__IGNORED_REF ) {
11409       astWriteString( channel, "SRefIs", set, 0, IGNORED_STRING,
11410                       "Not rotated (ref. pos. is ignored)" );
11411 
11412    } else {
11413       astWriteString( channel, "SRefIs", set, 0, ORIGIN_STRING,
11414                       "Rotated to put origin at ref. pos." );
11415    }
11416 
11417 /* SkyRef. */
11418 /* ------- */
11419 /* The inverse axis permutation array is used to obtain the axis index
11420    to use when accessing the SkyRef attribute. This reverses the effect
11421    of the SkyFrame's axis permutation array and yields a value appropriate
11422    to the axis with internal index "axis". */
11423    for ( axis_p = 0; axis_p < 2; axis_p++ ) {
11424       axis = invperm[ axis_p ];
11425 
11426       set = TestSkyRef( this, axis, status );
11427       dval = set ? GetSkyRef( this, axis, status ) : astGetSkyRef( this, axis );
11428       sprintf( buf, "Ref. pos. %s %s", astGetSymbol( this, axis ),
11429                astFormat( this, axis, dval ) );
11430       sprintf( key, "SRef%d", axis_p + 1 );
11431       astWriteDouble( channel, key, set, 0, dval, buf );
11432    }
11433 
11434 /* SkyRefP. */
11435 /* -------- */
11436    for ( axis_p = 0; axis_p < 2; axis_p++ ) {
11437       axis = invperm[ axis_p ];
11438 
11439       set = TestSkyRefP( this, axis, status );
11440       dval = set ? GetSkyRefP( this, axis, status ) : astGetSkyRefP( this, axis );
11441       sprintf( buf, "Ref. north %s %s", astGetSymbol( this, axis ),
11442                astFormat( this, axis, dval ) );
11443       sprintf( key, "SRefP%d", axis_p + 1 );
11444       astWriteDouble( channel, key, set, 0, dval, buf );
11445    }
11446 
11447 /* AlignOffset. */
11448 /* ------------ */
11449    set = TestAlignOffset( this, status );
11450    ival = set ? GetAlignOffset( this, status ) : astGetAlignOffset( this );
11451    astWriteInt( channel, "AlOff", set, 0, ival,
11452                 ival ? "Align in offset coords" :
11453                        "Align in system coords" );
11454 }
11455 
11456 /* Standard class functions. */
11457 /* ========================= */
11458 /* Implement the astIsASkyFrame and astCheckSkyFrame functions using the macros
11459    defined for this purpose in the "object.h" header file. */
astMAKE_ISA(SkyFrame,Frame)11460 astMAKE_ISA(SkyFrame,Frame)
11461 astMAKE_CHECK(SkyFrame)
11462 
11463 AstSkyFrame *astSkyFrame_( const char *options, int *status, ...) {
11464 /*
11465 *+
11466 *  Name:
11467 *     astSkyFrame
11468 
11469 *  Purpose:
11470 *     Create a SkyFrame.
11471 
11472 *  Type:
11473 *     Protected function.
11474 
11475 *  Synopsis:
11476 *     #include "skyframe.h"
11477 *     AstSkyFrame *astSkyFrame( const char *options, int *status, ... )
11478 
11479 *  Class Membership:
11480 *     SkyFrame constructor.
11481 
11482 *  Description:
11483 *     This function creates a new SkyFrame and optionally initialises its
11484 *     attributes.
11485 
11486 *  Parameters:
11487 *     options
11488 *        Pointer to a null terminated string containing an optional
11489 *        comma-separated list of attribute assignments to be used for
11490 *        initialising the new SkyFrame. The syntax used is the same as for the
11491 *        astSet method and may include "printf" format specifiers identified
11492 *        by "%" symbols in the normal way.
11493 *     status
11494 *        Pointer to the inherited status variable.
11495 *     ...
11496 *        If the "options" string contains "%" format specifiers, then an
11497 *        optional list of arguments may follow it in order to supply values to
11498 *        be substituted for these specifiers. The rules for supplying these
11499 *        are identical to those for the astSet method (and for the C "printf"
11500 *        function).
11501 
11502 *  Returned Value:
11503 *     A pointer to the new SkyFrame.
11504 
11505 *  Notes:
11506 *     -  A NULL pointer will be returned if this function is invoked with the
11507 *     global error status set, or if it should fail for any reason.
11508 *-
11509 
11510 *  Implementation Notes:
11511 *     - This function implements the basic SkyFrame constructor which
11512 *     is available via the protected interface to the SkyFrame class.
11513 *     A public interface is provided by the astSkyFrameId_ function.
11514 */
11515 
11516 /* Local Variables: */
11517    astDECLARE_GLOBALS            /* Pointer to thread-specific global data */
11518    AstSkyFrame *new;             /* Pointer to new SkyFrame */
11519    va_list args;                 /* Variable argument list */
11520 
11521 /* Get a pointer to the thread specific global data structure. */
11522    astGET_GLOBALS(NULL);
11523 
11524 /* Check the global status. */
11525    if ( !astOK ) return NULL;
11526 
11527 /* Initialise the SkyFrame, allocating memory and initialising the virtual
11528    function table as well if necessary. */
11529    new = astInitSkyFrame( NULL, sizeof( AstSkyFrame ), !class_init, &class_vtab,
11530                           "SkyFrame" );
11531 
11532 /* If successful, note that the virtual function table has been initialised. */
11533    if ( astOK ) {
11534       class_init = 1;
11535 
11536 /* Obtain the variable argument list and pass it along with the options string
11537    to the astVSet method to initialise the new SkyFrame's attributes. */
11538       va_start( args, status );
11539       astVSet( new, options, NULL, args );
11540       va_end( args );
11541 
11542 /* If an error occurred, clean up by deleting the new object. */
11543       if ( !astOK ) new = astDelete( new );
11544    }
11545 
11546 /* Return a pointer to the new SkyFrame. */
11547    return new;
11548 }
11549 
astInitSkyFrame_(void * mem,size_t size,int init,AstSkyFrameVtab * vtab,const char * name,int * status)11550 AstSkyFrame *astInitSkyFrame_( void *mem, size_t size, int init,
11551                                AstSkyFrameVtab *vtab, const char *name, int *status ) {
11552 /*
11553 *+
11554 *  Name:
11555 *     astInitSkyFrame
11556 
11557 *  Purpose:
11558 *     Initialise a SkyFrame.
11559 
11560 *  Type:
11561 *     Protected function.
11562 
11563 *  Synopsis:
11564 *     #include "skyframe.h"
11565 *     AstSkyFrame *astInitSkyFrame( void *mem, size_t size, int init,
11566 *                                   AstFrameVtab *vtab, const char *name )
11567 
11568 *  Class Membership:
11569 *     SkyFrame initialiser.
11570 
11571 *  Description:
11572 *     This function is provided for use by class implementations to initialise
11573 *     a new SkyFrame object. It allocates memory (if necessary) to accommodate
11574 *     the SkyFrame plus any additional data associated with the derived class.
11575 *     It then initialises a SkyFrame structure at the start of this memory. If
11576 *     the "init" flag is set, it also initialises the contents of a virtual
11577 *     function table for a SkyFrame at the start of the memory passed via the
11578 *     "vtab" parameter.
11579 
11580 *  Parameters:
11581 *     mem
11582 *        A pointer to the memory in which the SkyFrame is to be created. This
11583 *        must be of sufficient size to accommodate the SkyFrame data
11584 *        (sizeof(SkyFrame)) plus any data used by the derived class. If a value
11585 *        of NULL is given, this function will allocate the memory itself using
11586 *        the "size" parameter to determine its size.
11587 *     size
11588 *        The amount of memory used by the SkyFrame (plus derived class data).
11589 *        This will be used to allocate memory if a value of NULL is given for
11590 *        the "mem" parameter. This value is also stored in the SkyFrame
11591 *        structure, so a valid value must be supplied even if not required for
11592 *        allocating memory.
11593 *     init
11594 *        A logical flag indicating if the SkyFrame's virtual function table is
11595 *        to be initialised. If this value is non-zero, the virtual function
11596 *        table will be initialised by this function.
11597 *     vtab
11598 *        Pointer to the start of the virtual function table to be associated
11599 *        with the new SkyFrame.
11600 *     name
11601 *        Pointer to a constant null-terminated character string which contains
11602 *        the name of the class to which the new object belongs (it is this
11603 *        pointer value that will subsequently be returned by the astGetClass
11604 *        method).
11605 
11606 *  Returned Value:
11607 *     A pointer to the new SkyFrame.
11608 
11609 *  Notes:
11610 *     -  A null pointer will be returned if this function is invoked with the
11611 *     global error status set, or if it should fail for any reason.
11612 *-
11613 */
11614 
11615 /* Local Variables: */
11616    AstSkyAxis *ax;               /* Pointer to SkyAxis object */
11617    AstSkyFrame *new;             /* Pointer to the new SkyFrame */
11618    int axis;                     /* Loop counter for axes */
11619 
11620 /* Check the global status. */
11621    if ( !astOK ) return NULL;
11622 
11623 /* If necessary, initialise the virtual function table. */
11624    if ( init ) astInitSkyFrameVtab( vtab, name );
11625 
11626 /* Initialise a Frame structure (the parent class) as the first component
11627    within the SkyFrame structure, allocating memory if necessary. */
11628    new = (AstSkyFrame *) astInitFrame( mem, size, 0,
11629                                        (AstFrameVtab *) vtab, name, 2 );
11630 
11631       if ( astOK ) {
11632 
11633 /* Initialise the SkyFrame data. */
11634 /* ----------------------------- */
11635 /* Initialise all attributes to their "undefined" values. */
11636       new->equinox = AST__BAD;
11637       new->projection = NULL;
11638       new->neglon = -INT_MAX;
11639       new->alignoffset = -INT_MAX;
11640       new->skyrefis = AST__BAD_REF;
11641       new->skyref[ 0 ] = AST__BAD;
11642       new->skyref[ 1 ] = AST__BAD;
11643       new->skyrefp[ 0 ] = AST__BAD;
11644       new->skyrefp[ 1 ] = AST__BAD;
11645       new->last = AST__BAD;
11646       new->eplast = AST__BAD;
11647       new->klast = AST__BAD;
11648       new->diurab = AST__BAD;
11649 
11650 /* Loop to replace the Axis object associated with each SkyFrame axis with
11651    a SkyAxis object instead. */
11652       for ( axis = 0; axis < 2; axis++ ) {
11653 
11654 /* Create the new SkyAxis, assign it to the required SkyFrame axis and then
11655    annul the SkyAxis pointer. */
11656          ax = astSkyAxis( "", status );
11657          astSetAxis( new, axis, ax );
11658          ax = astAnnul( ax );
11659       }
11660 
11661 /* If an error occurred, clean up by deleting the new object. */
11662       if ( !astOK ) new = astDelete( new );
11663    }
11664 
11665 /* Return a pointer to the new object. */
11666    return new;
11667 }
11668 
astLoadSkyFrame_(void * mem,size_t size,AstSkyFrameVtab * vtab,const char * name,AstChannel * channel,int * status)11669 AstSkyFrame *astLoadSkyFrame_( void *mem, size_t size,
11670                                AstSkyFrameVtab *vtab, const char *name,
11671                                AstChannel *channel, int *status ) {
11672 /*
11673 *+
11674 *  Name:
11675 *     astLoadSkyFrame
11676 
11677 *  Purpose:
11678 *     Load a SkyFrame.
11679 
11680 *  Type:
11681 *     Protected function.
11682 
11683 *  Synopsis:
11684 *     #include "skyframe.h"
11685 *     AstSkyFrame *astLoadSkyFrame( void *mem, size_t size,
11686 *                                    AstSkyFrameVtab *vtab, const char *name,
11687 *                                    AstChannel *channel )
11688 
11689 *  Class Membership:
11690 *     SkyFrame loader.
11691 
11692 *  Description:
11693 *     This function is provided to load a new SkyFrame using data read
11694 *     from a Channel. It first loads the data used by the parent class
11695 *     (which allocates memory if necessary) and then initialises a
11696 *     SkyFrame structure in this memory, using data read from the
11697 *     input Channel.
11698 
11699 *  Parameters:
11700 *     mem
11701 *        A pointer to the memory into which the SkyFrame is to be
11702 *        loaded.  This must be of sufficient size to accommodate the
11703 *        SkyFrame data (sizeof(SkyFrame)) plus any data used by
11704 *        derived classes. If a value of NULL is given, this function
11705 *        will allocate the memory itself using the "size" parameter to
11706 *        determine its size.
11707 *     size
11708 *        The amount of memory used by the SkyFrame (plus derived class
11709 *        data).  This will be used to allocate memory if a value of
11710 *        NULL is given for the "mem" parameter. This value is also
11711 *        stored in the SkyFrame structure, so a valid value must be
11712 *        supplied even if not required for allocating memory.
11713 *
11714 *        If the "vtab" parameter is NULL, the "size" value is ignored
11715 *        and sizeof(AstSkyFrame) is used instead.
11716 *     vtab
11717 *        Pointer to the start of the virtual function table to be
11718 *        associated with the new SkyFrame. If this is NULL, a pointer
11719 *        to the (static) virtual function table for the SkyFrame class
11720 *        is used instead.
11721 *     name
11722 *        Pointer to a constant null-terminated character string which
11723 *        contains the name of the class to which the new object
11724 *        belongs (it is this pointer value that will subsequently be
11725 *        returned by the astGetClass method).
11726 *
11727 *        If the "vtab" parameter is NULL, the "name" value is ignored
11728 *        and a pointer to the string "SkyFrame" is used instead.
11729 
11730 *  Returned Value:
11731 *     A pointer to the new SkyFrame.
11732 
11733 *  Notes:
11734 *     - A null pointer will be returned if this function is invoked
11735 *     with the global error status set, or if it should fail for any
11736 *     reason.
11737 *-
11738 */
11739 
11740 /* Local Variables: */
11741    AstSkyFrame *new;             /* Pointer to the new SkyFrame */
11742    astDECLARE_GLOBALS            /* Pointer to thread-specific global data */
11743    char *sval;                   /* Pointer to string value */
11744    const int *perm;              /* Pointer to axis permutation array */
11745    double dval;                  /* Floating point attribute value */
11746    int axis;                     /* External axis index */
11747    int invperm[ 2 ];             /* Inverse permutation array */
11748 
11749 /* Initialise. */
11750    new = NULL;
11751 
11752 /* Get a pointer to the thread specific global data structure. */
11753    astGET_GLOBALS(channel);
11754 
11755 /* Check the global error status. */
11756    if ( !astOK ) return new;
11757 
11758 /* If a NULL virtual function table has been supplied, then this is
11759    the first loader to be invoked for this SkyFrame. In this case the
11760    SkyFrame belongs to this class, so supply appropriate values to be
11761    passed to the parent class loader (and its parent, etc.). */
11762    if ( !vtab ) {
11763       size = sizeof( AstSkyFrame );
11764       vtab = &class_vtab;
11765       name = "SkyFrame";
11766 
11767 /* If required, initialise the virtual function table for this class. */
11768       if ( !class_init ) {
11769          astInitSkyFrameVtab( vtab, name );
11770          class_init = 1;
11771       }
11772    }
11773 
11774 /* Invoke the parent class loader to load data for all the ancestral
11775    classes of the current one, returning a pointer to the resulting
11776    partly-built SkyFrame. */
11777    new = astLoadFrame( mem, size, (AstFrameVtab *) vtab, name,
11778                        channel );
11779 
11780    if ( astOK ) {
11781 
11782 /* Get a pointer to the SkyFrame's axis permutation array (using a method,
11783    to allow for any over-ride by a derived class). */
11784       perm = astGetPerm( new );
11785 
11786 /* Generate an inverse axis permutation array from the forward permutation
11787    values. This will be used to determine which axis should be enquired
11788    about (using possibly over-ridden methods) to obtain data to
11789    correspond with a particular internal value (i.e. instance variable)
11790    relating to an axis. This step is needed so that the effect of any
11791    axis permutation can be un-done before values are written out, as
11792    output values are written by this function in un-permuted order. */
11793       for( axis = 0; axis < 2; axis++ ) invperm[ perm[ axis ] ] = axis;
11794 
11795 /* Read input data. */
11796 /* ================ */
11797 /* Request the input Channel to read all the input data appropriate to
11798    this class into the internal "values list". */
11799       astReadClassData( channel, "SkyFrame" );
11800 
11801 /* Now read each individual data item from this list and use it to
11802    initialise the appropriate instance variable(s) for this class. */
11803 
11804 /* In the case of attributes, we first read the "raw" input value,
11805    supplying the "unset" value as the default. If a "set" value is
11806    obtained, we then use the appropriate (private) Set... member
11807    function to validate and set the value properly. */
11808 
11809 /* The attributes defining the offset coordinate system must be loaded
11810    before the System attrivbute, since SetSystem uses them. */
11811 
11812 /* AlignOffset */
11813 /* ----------- */
11814       new->alignoffset = astReadInt( channel, "aloff", -INT_MAX );
11815       if ( TestAlignOffset( new, status ) ) SetAlignOffset( new, new->alignoffset, status );
11816 
11817 /* SkyRefIs. */
11818 /* --------- */
11819       sval = astReadString( channel, "srefis", " " );
11820       if( sval ){
11821          new->skyrefis = AST__BAD_REF;
11822          if( astChrMatch( sval, POLE_STRING ) ) {
11823             new->skyrefis = AST__POLE_REF;
11824          } else if( astChrMatch( sval, ORIGIN_STRING ) ) {
11825             new->skyrefis = AST__ORIGIN_REF;
11826          } else if( astChrMatch( sval, IGNORED_STRING ) ) {
11827             new->skyrefis = AST__IGNORED_REF;
11828          } else if( !astChrMatch( sval, " " ) && astOK ){
11829 	    astError( AST__INTER, "astRead(SkyFrame): Corrupt SkyFrame contains "
11830 		      "invalid SkyRefIs attribute value (%s).", status, sval );
11831          }
11832          if( TestSkyRefIs( new, status ) ) SetSkyRefIs( new, new->skyrefis, status );
11833          sval = astFree( sval );
11834       }
11835 
11836 /* SkyRef. */
11837 /* ------- */
11838       new->skyref[ 0 ] = astReadDouble( channel, "sref1", AST__BAD );
11839       axis = invperm[ 0 ];
11840       if ( TestSkyRef( new, axis, status ) ) SetSkyRef( new, axis, new->skyref[ 0 ], status );
11841 
11842       new->skyref[ 1 ] = astReadDouble( channel, "sref2", AST__BAD );
11843       axis = invperm[ 1 ];
11844       if ( TestSkyRef( new, axis, status ) ) SetSkyRef( new, axis, new->skyref[ 1 ], status );
11845 
11846 /* SkyRefP. */
11847 /* -------- */
11848       new->skyrefp[ 0 ] = astReadDouble( channel, "srefp1", AST__BAD );
11849       axis = invperm[ 0 ];
11850       if ( TestSkyRefP( new, axis, status ) ) SetSkyRefP( new, axis, new->skyrefp[ 0 ], status );
11851 
11852       new->skyrefp[ 1 ] = astReadDouble( channel, "srefp2", AST__BAD );
11853       axis = invperm[ 1 ];
11854       if ( TestSkyRefP( new, axis, status ) ) SetSkyRefP( new, axis, new->skyrefp[ 1 ], status );
11855 
11856 /* System. */
11857 /* ------- */
11858 /* The System attribute is now part of the Frame class, but this code is
11859    retained to allow this version of AST to read SkyFrames dumped by
11860    previous versions.  */
11861 
11862 /* Check a value has not already been assigned to the Frames System
11863    attribute.  */
11864       if( !astTestSystem( new ) ){
11865 
11866 /* Read the external representation as a string. */
11867          sval = astReadString( channel, "system", NULL );
11868 
11869 /* If a value was read, use the SetAttrib method to validate and store the
11870    new value in the correct place, then free the string. */
11871          if ( sval ) {
11872             astSet( new, "System=%s", status, sval);
11873             sval = astFree( sval );
11874          }
11875       }
11876 
11877 /* Epoch. */
11878 /* ------ */
11879 /* The Epoch attribute is now part of the Frame class, but this code is
11880    retained to allow this version of AST to read SkyFrames dumped by
11881    previous versions.  */
11882 
11883 /* Check a value has not already been assigned to the Frames Epoch
11884    attribute.  */
11885       if( !astTestEpoch( new ) ){
11886 
11887 /* Get the value. */
11888          dval = astReadDouble( channel, "epoch", AST__BAD );
11889 
11890 /* If a value was read, use the SetAttrib method to validate and store the
11891    new value in the correct place. */
11892          if( dval != AST__BAD ) {
11893             if( dval < 1984.0 ) {
11894                astSet( new, "Epoch=B%.*g", status, DBL_DIG, dval);
11895             } else {
11896                astSet( new, "Epoch=J%.*g", status, DBL_DIG, dval);
11897             }
11898          }
11899       }
11900 
11901 /* Projection. */
11902 /* ----------- */
11903       new->projection = astReadString( channel, "proj", NULL );
11904 
11905 /* Equinox. */
11906 /* -------- */
11907 /* Interpret this as Besselian or Julian depending on its value. */
11908       new->equinox = astReadDouble( channel, "eqnox", AST__BAD );
11909       if ( TestEquinox( new, status ) ) {
11910          SetEquinox( new, ( new->equinox < 1984.0 ) ? palEpb2d( new->equinox ) :
11911                                                       palEpj2d( new->equinox ), status );
11912       }
11913 
11914 /* NegLon. */
11915 /* ------- */
11916       new->neglon = astReadInt( channel, "neglon", -INT_MAX );
11917       if ( TestNegLon( new, status ) ) SetNegLon( new, new->neglon, status );
11918 
11919 /* Other values */
11920 /* ------------ */
11921       new->last = AST__BAD;
11922       new->eplast = AST__BAD;
11923       new->klast = AST__BAD;
11924       new->diurab = AST__BAD;
11925 
11926 /* If an error occurred, clean up by deleting the new SkyFrame. */
11927       if ( !astOK ) new = astDelete( new );
11928    }
11929 
11930 /* Return the new SkyFrame pointer. */
11931    return new;
11932 }
11933 
11934 /* Virtual function interfaces. */
11935 /* ============================ */
11936 /* These provide the external interface to the virtual functions defined by
11937    this class. Each simply checks the global error status and then locates and
11938    executes the appropriate member function, using the function pointer stored
11939    in the object's virtual function table (this pointer is located using the
11940    astMEMBER macro defined in "object.h").
11941 
11942    Note that the member function may not be the one defined here, as it may
11943    have been over-ridden by a derived class. However, it should still have the
11944    same interface. */
astClearAsTime_(AstSkyFrame * this,int axis,int * status)11945 void astClearAsTime_( AstSkyFrame *this, int axis, int *status ) {
11946    if ( !astOK ) return;
11947    (**astMEMBER(this,SkyFrame,ClearAsTime))( this, axis, status );
11948 }
astGetAsTime_(AstSkyFrame * this,int axis,int * status)11949 int astGetAsTime_( AstSkyFrame *this, int axis, int *status ) {
11950    if ( !astOK ) return 0;
11951    return (**astMEMBER(this,SkyFrame,GetAsTime))( this, axis, status );
11952 }
astSetAsTime_(AstSkyFrame * this,int axis,int value,int * status)11953 void astSetAsTime_( AstSkyFrame *this, int axis, int value, int *status ) {
11954    if ( !astOK ) return;
11955    (**astMEMBER(this,SkyFrame,SetAsTime))( this, axis, value, status );
11956 }
astTestAsTime_(AstSkyFrame * this,int axis,int * status)11957 int astTestAsTime_( AstSkyFrame *this, int axis, int *status ) {
11958    if ( !astOK ) return 0;
11959    return (**astMEMBER(this,SkyFrame,TestAsTime))( this, axis, status );
11960 }
astGetIsLatAxis_(AstSkyFrame * this,int axis,int * status)11961 int astGetIsLatAxis_( AstSkyFrame *this, int axis, int *status ) {
11962    if ( !astOK ) return 0;
11963    return (**astMEMBER(this,SkyFrame,GetIsLatAxis))( this, axis, status );
11964 }
astGetIsLonAxis_(AstSkyFrame * this,int axis,int * status)11965 int astGetIsLonAxis_( AstSkyFrame *this, int axis, int *status ) {
11966    if ( !astOK ) return 0;
11967    return (**astMEMBER(this,SkyFrame,GetIsLonAxis))( this, axis, status );
11968 }
astGetLatAxis_(AstSkyFrame * this,int * status)11969 int astGetLatAxis_( AstSkyFrame *this, int *status ) {
11970    if ( !astOK ) return 1;
11971    return (**astMEMBER(this,SkyFrame,GetLatAxis))( this, status );
11972 }
astGetLonAxis_(AstSkyFrame * this,int * status)11973 int astGetLonAxis_( AstSkyFrame *this, int *status ) {
11974    if ( !astOK ) return 0;
11975    return (**astMEMBER(this,SkyFrame,GetLonAxis))( this, status );
11976 }
astGetSkyRefP_(AstSkyFrame * this,int axis,int * status)11977 double astGetSkyRefP_( AstSkyFrame *this, int axis, int *status ) {
11978    if ( !astOK ) return 0.0;
11979    return (**astMEMBER(this,SkyFrame,GetSkyRefP))( this, axis, status );
11980 }
astSkyOffsetMap_(AstSkyFrame * this,int * status)11981 AstMapping *astSkyOffsetMap_( AstSkyFrame *this, int *status ) {
11982    if ( !astOK ) return NULL;
11983    return (**astMEMBER(this,SkyFrame,SkyOffsetMap))( this, status );
11984 }
11985 
11986 /* Special public interface functions. */
11987 /* =================================== */
11988 /* These provide the public interface to certain special functions
11989    whose public interface cannot be handled using macros (such as
11990    astINVOKE) alone. In general, they are named after the
11991    corresponding protected version of the function, but with "Id"
11992    appended to the name. */
11993 
11994 /* Public Interface Function Prototypes. */
11995 /* ------------------------------------- */
11996 /* The following functions have public prototypes only (i.e. no
11997    protected prototypes), so we must provide local prototypes for use
11998    within this module. */
11999 AstSkyFrame *astSkyFrameId_( const char *, ... );
12000 
12001 /* Special interface function implementations. */
12002 /* ------------------------------------------- */
astSkyFrameId_(const char * options,...)12003 AstSkyFrame *astSkyFrameId_( const char *options, ... ) {
12004 /*
12005 *++
12006 *  Name:
12007 c     astSkyFrame
12008 f     AST_SKYFRAME
12009 
12010 *  Purpose:
12011 *     Create a SkyFrame.
12012 
12013 *  Type:
12014 *     Public function.
12015 
12016 *  Synopsis:
12017 c     #include "skyframe.h"
12018 c     AstSkyFrame *astSkyFrame( const char *options, ... )
12019 f     RESULT = AST_SKYFRAME( OPTIONS, STATUS )
12020 
12021 *  Class Membership:
12022 *     SkyFrame constructor.
12023 
12024 *  Description:
12025 *     This function creates a new SkyFrame and optionally initialises
12026 *     its attributes.
12027 *
12028 *     A SkyFrame is a specialised form of Frame which describes
12029 *     celestial longitude/latitude coordinate systems. The particular
12030 *     celestial coordinate system to be represented is specified by
12031 *     setting the SkyFrame's System attribute (currently, the default
12032 *     is ICRS) qualified, as necessary, by a mean Equinox value and/or
12033 *     an Epoch.
12034 *
12035 *     For each of the supported celestial coordinate systems, a SkyFrame
12036 *     can apply an optional shift of origin to create a coordinate system
12037 *     representing offsets within the celestial coordinate system from some
12038 *     specified point. This offset coordinate system can also be rotated to
12039 *     define new longitude and latitude axes. See attributes SkyRef, SkyRefIs
12040 *     and SkyRefP
12041 *
12042 *     All the coordinate values used by a SkyFrame are in
12043 *     radians. These may be formatted in more conventional ways for
12044 c     display by using astFormat.
12045 f     display by using AST_FORMAT.
12046 
12047 *  Parameters:
12048 c     options
12049 f     OPTIONS = CHARACTER * ( * ) (Given)
12050 c        Pointer to a null-terminated string containing an optional
12051 c        comma-separated list of attribute assignments to be used for
12052 c        initialising the new SkyFrame. The syntax used is identical to
12053 c        that for the astSet function and may include "printf" format
12054 c        specifiers identified by "%" symbols in the normal way.
12055 c        If no initialisation is required, a zero-length string may be
12056 c        supplied.
12057 f        A character string containing an optional comma-separated
12058 f        list of attribute assignments to be used for initialising the
12059 f        new SkyFrame. The syntax used is identical to that for the
12060 f        AST_SET routine. If no initialisation is required, a blank
12061 f        value may be supplied.
12062 c     ...
12063 c        If the "options" string contains "%" format specifiers, then
12064 c        an optional list of additional arguments may follow it in
12065 c        order to supply values to be substituted for these
12066 c        specifiers. The rules for supplying these are identical to
12067 c        those for the astSet function (and for the C "printf"
12068 c        function).
12069 f     STATUS = INTEGER (Given and Returned)
12070 f        The global status.
12071 
12072 *  Returned Value:
12073 c     astSkyFrame()
12074 f     AST_SKYFRAME = INTEGER
12075 *        A pointer to the new SkyFrame.
12076 
12077 *  Examples:
12078 c     frame = astSkyFrame( "" );
12079 c        Creates a SkyFrame to describe the default ICRS celestial
12080 c        coordinate system.
12081 c     frame = astSkyFrame( "System = FK5, Equinox = J2005, Digits = 10" );
12082 c        Creates a SkyFrame to describe the FK5 celestial
12083 c        coordinate system, with a mean Equinox of J2005.0.
12084 c        Because especially accurate coordinates will be used,
12085 c        additional precision (10 digits) has been requested. This will
12086 c        be used when coordinate values are formatted for display.
12087 c     frame = astSkyFrame( "System = FK4, Equinox = 1955-sep-2" );
12088 c        Creates a SkyFrame to describe the old FK4 celestial
12089 c        coordinate system.  A default Epoch value (B1950.0) is used,
12090 c        but the mean Equinox value is given explicitly as "1955-sep-2".
12091 c     frame = astSkyFrame( "System = GAPPT, Epoch = %s", date );
12092 c        Creates a SkyFrame to describe the Geocentric Apparent
12093 c        celestial coordinate system. The Epoch value, which specifies
12094 c        the date of observation, is obtained from a date/time string
12095 c        supplied via the string pointer "date".
12096 f     FRAME = AST_SKYFRAME( ' ', STATUS )
12097 f        Creates a SkyFrame to describe the default ICRS celestial
12098 f        coordinate system.
12099 f     FRAME = AST_SKYFRAME( 'System = FK5, Equinox = J2005, Digits = 10', STATUS )
12100 f        Creates a SkyFrame to describe the FK5 celestial
12101 f        coordinate system, with a mean Equinox of J2005.0.
12102 f        Because especially accurate coordinates will be used,
12103 f        additional precision (10 digits) has been requested. This will
12104 f        be used when coordinate values are formatted for display.
12105 f     FRAME = AST_SKYFRAME( 'System = FK4, Equinox = 1955-SEP-2', STATUS )
12106 f        Creates a SkyFrame to describe the old FK4 celestial
12107 f        coordinate system.  A default Epoch value (B1950.0) is used,
12108 f        but the mean Equinox value is given explicitly as "1955-SEP-2".
12109 f     FRAME = AST_SKYFRAME( 'System = GAPPT, Epoch = ' // DATE, STATUS )
12110 f        Creates a SkyFrame to describe the Geocentric Apparent
12111 f        celestial coordinate system. The Epoch value, which specifies
12112 f        the date of observation, is obtained from a date/time string
12113 f        contained in the character variable DATE.
12114 
12115 *  Notes:
12116 *     - Currently, the default celestial coordinate system is
12117 *     ICRS. However, this default may change in future as new
12118 *     astrometric standards evolve. The intention is to track the most
12119 *     modern appropriate standard. For this reason, you should use the
12120 *     default only if this is what you intend (and can tolerate any
12121 *     associated slight change in behaviour with future versions of
12122 *     this function). If you intend to use the ICRS system
12123 *     indefinitely, then you should specify it explicitly using an
12124 c     "options" value of "System=ICRS".
12125 f     OPTIONS value of "System=ICRS".
12126 *     - Whichever celestial coordinate system is represented, it will
12127 *     have two axes.  The first of these will be the longitude axis
12128 *     and the second will be the latitude axis. This order can be
12129 c     changed using astPermAxes if required.
12130 f     changed using AST_PERMAXES if required.
12131 *     - When conversion between two SkyFrames is requested (as when
12132 c     supplying SkyFrames to astConvert),
12133 f     supplying SkyFrames AST_CONVERT),
12134 *     account will be taken of the nature of the celestial coordinate
12135 *     systems they represent, together with any qualifying mean Equinox or
12136 *     Epoch values, etc. The AlignSystem attribute will also be taken into
12137 *     account. The results will therefore fully reflect the
12138 *     relationship between positions on the sky measured in the two
12139 *     systems.
12140 *     - A null Object pointer (AST__NULL) will be returned if this
12141 c     function is invoked with the AST error status set, or if it
12142 f     function is invoked with STATUS set to an error value, or if it
12143 *     should fail for any reason.
12144 *--
12145 
12146 *  Implementation Notes:
12147 *     - This function implements the external (public) interface to
12148 *     the astSkyFrame constructor function. It returns an ID value
12149 *     (instead of a true C pointer) to external users, and must be
12150 *     provided because astSkyFrame_ has a variable argument list which
12151 *     cannot be encapsulated in a macro (where this conversion would
12152 *     otherwise occur).
12153 *     - The variable argument list also prevents this function from
12154 *     invoking astSkyFrame_ directly, so it must be a
12155 *     re-implementation of it in all respects, except for the final
12156 *     conversion of the result to an ID value.
12157 */
12158 
12159 /* Local Variables: */
12160    astDECLARE_GLOBALS            /* Pointer to thread-specific global data */
12161    AstSkyFrame *new;             /* Pointer to new SkyFrame */
12162    va_list args;                 /* Variable argument list */
12163 
12164    int *status;                  /* Pointer to inherited status value */
12165 
12166 /* Get a pointer to the inherited status value. */
12167    status = astGetStatusPtr;
12168 
12169 /* Get a pointer to the thread specific global data structure. */
12170    astGET_GLOBALS(NULL);
12171 
12172 /* Check the global status. */
12173    if ( !astOK ) return NULL;
12174 
12175 /* Initialise the SkyFrame, allocating memory and initialising the virtual
12176    function table as well if necessary. */
12177    new = astInitSkyFrame( NULL, sizeof( AstSkyFrame ), !class_init, &class_vtab,
12178                           "SkyFrame" );
12179 
12180 /* If successful, note that the virtual function table has been initialised. */
12181    if ( astOK ) {
12182       class_init = 1;
12183 
12184 /* Obtain the variable argument list and pass it along with the options string
12185    to the astVSet method to initialise the new SkyFrame's attributes. */
12186       va_start( args, options );
12187       astVSet( new, options, NULL, args );
12188       va_end( args );
12189 
12190 /* If an error occurred, clean up by deleting the new object. */
12191       if ( !astOK ) new = astDelete( new );
12192    }
12193 
12194 /* Return an ID value for the new SkyFrame. */
12195    return astMakeId( new );
12196 }
12197 
12198 
12199 
12200 
12201 
12202 
12203 
12204