diff options
author | William Joye <wjoye@cfa.harvard.edu> | 2019-05-10 18:45:23 (GMT) |
---|---|---|
committer | William Joye <wjoye@cfa.harvard.edu> | 2019-05-10 18:45:23 (GMT) |
commit | 0b1addf5643c828955a6add53f2a4f7a7a813e41 (patch) | |
tree | 94fe32a76399be787746c3c403cca2076a253e29 /ast/skyframe.c | |
parent | 75fab9d80911f74aaab738fa9ab8a4f9b0f57a6b (diff) | |
download | blt-0b1addf5643c828955a6add53f2a4f7a7a813e41.zip blt-0b1addf5643c828955a6add53f2a4f7a7a813e41.tar.gz blt-0b1addf5643c828955a6add53f2a4f7a7a813e41.tar.bz2 |
upgrade ast 8.7.1
Diffstat (limited to 'ast/skyframe.c')
-rw-r--r-- | ast/skyframe.c | 12592 |
1 files changed, 12592 insertions, 0 deletions
diff --git a/ast/skyframe.c b/ast/skyframe.c new file mode 100644 index 0000000..1c20839 --- /dev/null +++ b/ast/skyframe.c @@ -0,0 +1,12592 @@ +/* +*class++ +* Name: +* SkyFrame + +* Purpose: +* Celestial coordinate system description. + +* Constructor Function: +c astSkyFrame +f AST_SKYFRAME + +* Description: +* A SkyFrame is a specialised form of Frame which describes +* celestial longitude/latitude coordinate systems. The particular +* celestial coordinate system to be represented is specified by +* setting the SkyFrame's System attribute (currently, the default +* is ICRS) qualified, as necessary, by a mean Equinox value and/or +* an Epoch. +* +* For each of the supported celestial coordinate systems, a SkyFrame +* can apply an optional shift of origin to create a coordinate system +* representing offsets within the celestial coordinate system from some +* specified reference point. This offset coordinate system can also be +* rotated to define new longitude and latitude axes. See attributes +* SkyRef, SkyRefIs, SkyRefP and AlignOffset. +* +* All the coordinate values used by a SkyFrame are in +* radians. These may be formatted in more conventional ways for +c display by using astFormat. +f display by using AST_FORMAT. +* For a SkyFrame, the Unit attribute describes the formatted value of +* a SkyFrame axis, and may for instance be "h:m:s", indicating that a +* formatted axis value contains colon-separated fields for hours, minutes +* and seconds. On the other hand, the InternalUnit attribute for a +* SkyFrame is always set to "rad" (i.e. radians), indicating that the +* unformatted (i.e. floating point) axis values used by application code +* are always in units of radians + +* Inheritance: +* The SkyFrame class inherits from the Frame class. + +* Attributes: +* In addition to those attributes common to all Frames, every +* SkyFrame also has the following attributes: +* +* - AlignOffset: Align SkyFrames using the offset coordinate system? +* - AsTime(axis): Format celestial coordinates as times? +* - Equinox: Epoch of the mean equinox +* - IsLatAxis: Is the specified axis the latitude axis? +* - IsLonAxis: Is the specified axis the longitude axis? +* - LatAxis: Index of the latitude axis +* - LonAxis: Index of the longitude axis +* - NegLon: Display longitude values in the range [-pi,pi]? +* - Projection: Sky projection description. +* - SkyRef: Position defining location of the offset coordinate system +* - SkyRefIs: Selects the nature of the offset coordinate system +* - SkyRefP: Position defining orientation of the offset coordinate system +* - SkyTol: Smallest significant shift in sky coordinates + +* Functions: +* In addition to those +c functions +f routines +* applicable to all Frames, the following +c functions +f routines +* may also be applied to all SkyFrames: +* +c - astSkyOffsetMap: Obtain a Mapping from absolute to offset coordinates +f - AST_SKYOFFSETMAP: Obtain a Mapping from absolute to offset coordinates + +* Copyright: +* Copyright (C) 1997-2006 Council for the Central Laboratory of the +* Research Councils +* Copyright (C) 2010 Science & Technology Facilities Council. +* All Rights Reserved. + +* Licence: +* This program is free software: you can redistribute it and/or +* modify it under the terms of the GNU Lesser General Public +* License as published by the Free Software Foundation, either +* version 3 of the License, or (at your option) any later +* version. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU Lesser General Public License for more details. +* +* You should have received a copy of the GNU Lesser General +* License along with this program. If not, see +* <http://www.gnu.org/licenses/>. + +* Authors: +* RFWS: R.F. Warren-Smith (Starlink) +* DSB: David S. Berry (Starlink) +* BEC: Brad Cavanagh (JAC, Hawaii) + +* History: +* 4-MAR-1996 (RFWS): +* Original version. +* 17-MAY-1996 (RFWS): +* Tidied up, etc. +* 31-JUL-1996 (RFWS): +* Added support for attributes and a public interface. +* 11-SEP-1996 (RFWS): +* Added Gap (written by DSB). +* 24-SEP-1996 (RFWS): +* Added I/O facilities. +* 27-FEB-1997 (RFWS): +* Improved the public prologues. +* 27-MAY-1997 (RFWS): +* Modified to use a new public interface to the SlaMap class +* and to use the astSimplify method to remove redundant +* conversions. +* 16-JUN-1997 (RFWS): +* Fixed bug in axis associations returned by astMatch if axes +* were swapped. +* 16-JUL-1997 (RFWS): +* Added Projection attribute. +* 14-NOV-1997 (RFWS): +* Corrected the omission of axis permutations from astNorm. +* 21-JAN-1998 (RFWS): +* Ensure that Title and Domain values appropriate to a SkyFrame +* are preserved if a Frame result is generated by SubFrame. +* 26-FEB-1998 (RFWS): +* Over-ride the astUnformat method. +* 3-APR-2001 (DSB): +* Added "Unknown" option for the System attribute. Added read-only +* attributes LatAxis and LonAxis. +* 21-JUN-2001 (DSB): +* Added astAngle and astOffset2. +* 4-SEP-2001 (DSB): +* Added NegLon attribute, and astResolve method. +* 9-SEP-2001 (DSB): +* Added astBear method. +* 21-SEP-2001 (DSB): +* Removed astBear method. +* 10-OCT-2002 (DSB): +* Moved definitions of macros for SkyFrame system values from +* this file into skyframe.h. +* 24-OCT-2002 (DSB): +* Modified MakeSkyMapping so that any two SkyFrames with system=unknown +* are assumed to be related by a UnitMap. previously, they were +* considered to be unrelated, resulting in no ability to convert from +* one to the other. This could result for instance in astConvert +* being unable to find a maping from a SkyFrame to itself. +* 15-NOV-2002 (DSB): +* Moved System and Epoch attributes to the Frame class. +* 8-JAN-2003 (DSB): +* Changed private InitVtab method to protected astInitSkyFrameVtab +* method. +* 11-JUN-2003 (DSB): +* Added ICRS option for System attribute, and made it the default +* in place of FK5. +* 27-SEP-2003 (DSB): +* Added HELIOECLIPTIC option for System attribute. +* 19-APR-2004 (DSB): +* Added SkyRef, SkyRefIs, SkyRefP and AlignOffset attributes. +* 8-SEP-2004 (DSB): +* Added astResolvePoints method. +* 2-DEC-2004 (DSB): +* Added System "J2000" +* 27-JAN-2005 (DSB): +* Fix memory leaks in astLoadSkyFrame_ and Match. +* 7-APR-2005 (DSB): +* Allow SkyRefIs to be set to "Ignored". +* 12-MAY-2005 (DSB): +* Override astNormBox method. +* 15-AUG-2005 (DSB): +* Added AZEL system. +* 13-SEP-2005 (DSB): +* Override astClearSystem so that SkyRef/SkyRefPcan be converted +* from the original System to the default System. +* 19-SEP-2005 (DSB): +* Changed default for SkyRefIs from ORIGIN to IGNORED. +* 14-FEB-2006 (DSB): +* Override astGetObjSize. +* 22-FEB-2006 (DSB): +* Store the Local Apparent Sidereal Time in the SkyFrame structure +* in order to avoid expensive re-computations. +* 22-AUG-2006 (DSB): +* Ensure the cached Local Apparent Siderial Time is initialised +* when initialising or loading a SkyFrame. +* 22-SEP-2006 (DSB): +* Report an error in SetSystem if it is not possible to convert +* from old to new systems. +* 3-OCT-2006 (DSB): +* Added Equation of Equinoxes to the SkyFrame structure. +* 6-OCT-2006 (DSB): +* - Guard against annulling null pointers in subFrame. +* - Add Dut1 attribute +* - Use linear approximation for LAST over short periods (less +* than 0.001 of a day) +* - Remove Equation of Equinoxes from the SkyFrame structure. +* 10-OCT-2006 (DSB): +* Use "AlOff" instead of "AlignOffset" as the external channel name +* for the AlignOffset attribute. The longer form exceeded the +* limit that can be used by the Channel class. +* 14-OCT-2006 (DSB): +* - Move Dut1 attribute to the Frame class. +* - Use the TimeFrame class to do the TDB->LAST conversions. +* 17-JAN-2007 (DSB): +* - Use a UnitMap to align offset coordinate systems in two +* SkyFrames, regardless of other attribute values. +* - Only align in offset coordinates if both target and template +* have a non-zero value for AlignOffset. +* 23-JAN-2007 (DSB): +* Modified so that a SkyFrame can be used as a template to find a +* SkyFrame contained within a CmpFrame. This involves changes in +* Match and the removal of the local versions of SetMaxAxes and +* SetMinAxes. +* 4-JUL-2007 (DSB): +* Modified GetLast to use the correct solar to sidereal conversion +* factor. As a consequence the largest acceptable epoch gap before +* the LAST needs to be recalculated has been increased. +* 11-JUL-2007 (DSB): +* Override astSetEpoch and astClearEpoch by implementations which +* update the LAST value stored in the SkyFrame. +* 7-AUG-2007 (DSB): +* - Set a value for the CentreZero attribute when extracting a +* SkyAxis from a SkyFrame in function SubFrame. +* - In SubFrame, clear extended attributes such as System after +* all axis attributes have been "fixated. +* 30-AUG-2007 (DSB): +* Override astSetDut1 and astClearDut1 by implementations which +* update the LAST value stored in the SkyFrame. +* 31-AUG-2007 (DSB): +* - Cache the magnitude of the diurnal aberration vector in the +* SkyFrame structure for use when correcting for diurnal aberration. +* - Modify the azel conversions to include correction for diurnal +* aberration. +* - Override astClearObsLat and astSetObsLat by implementations which +* reset the magnitude of the diurnal aberration vector. +* 3-SEP-2007 (DSB): +* In SubFrame, since AlignSystem is extended by the SkyFrame class +* it needs to be cleared before invoking the parent SubFrame +* method in cases where the result Frame is not a SkyFrame. +* 2-OCT-2007 (DSB): +* In Overlay, clear AlignSystem as well as System before calling +* the parent overlay method. +* 10-OCT-2007 (DSB): +* In MakeSkyMapping, correct the usage of variables "system" and +* "align_sys" when aligning in AZEL. +* 18-OCT-2007 (DSB): +* Compare target and template AlignSystem values in Match, rather +* than comparing target and result AlignSystem values in MakeSkyMapping +* (since result is basically a copy of target). +* 27-NOV-2007 (DSB): +* - Modify SetSystem to ensure that SkyRef and SkyRefP position are +* always transformed as absolute values, rather than as offset +* values. +* - Modify SubMatch so that a value of zero is assumed for +* AlignOffset when restoring thre integrity of a FrameSet. +* 15-DEC-2008 (DSB): +* Improve calculation of approximate Local Apparent Sidereal time +* by finding and using the ratio of solar to sidereal time +* independently for each approximation period. +* 14-JAN-2009 (DSB): +* Override the astIntersect method. +* 21-JAN-2009 (DSB): +* Fix mis-use of results buffers for GetFormat and GetAttrib. +* 16-JUN-2009 (DSB): +* All sky coordinate systems currently supported by SkyFrame are +* left handed. So fix GetDirection method to return zero for all +* longitude axes and 1 for all latitude axes. +* 18-JUN-2009 (DSB): +* Incorporate the new ObsAlt attribute. +* 23-SEP-2009 (DSB): +* Allow some rounding error when checking for changes in SetObsLon +* and SetDut1. This reduces the number of times the expensive +* calculation of LAST is performed. +* 24-SEP-2009 (DSB); +* Create a static cache of LAST values stored in the class virtual +* function table. These are used in preference to calculating a new +* value from scratch. +* 25-SEP-2009 (DSB); +* Do not calculate LAST until it is needed. +* 12-OCT-2009 (DSB); +* - Handle 2.PI->0 discontinuity in cached LAST values. +* 12-OCT-2009 (BEC); +* - Fix bug in caching LAST value. +* 31-OCT-2009 (DSB); +* Correct SetCachedLAST to handle cases where the epoch to be +* stored is smaller than any epoch already in the table. +* 24-NOV-2009 (DSB): +* - In CalcLast, only use end values form the table of stored +* LAST values if the corresponding epochs are within 0.001 of +* a second of the required epoch (this tolerance used to be +* 0.1 seconds). +* - Do not clear the cached LAST value in SetEpoch and ClearEpoch. +* 8-MAR-2010 (DSB): +* Add astSkyOffsetMap method. +* 7-APR-2010 (DSB): +* Add IsLatAxis and IsLonAxis attributes. +* 11-MAY-2010 (DSB): +* In SetSystem, clear SkyRefP as well as SkyRef. +* 22-MAR-2011 (DSB): +* Override astFrameGrid method. +* 29-APR-2011 (DSB): +* Prevent astFindFrame from matching a subclass template against a +* superclass target. +* 23-MAY-2011 (DSB): +* Truncate returned PointSet in function FrameGrid to exclude unused points. +* 24-MAY-2011 (DSB): +* When clearing or setting the System attribute, clear SkyRef rather +* than reporting an error if the Mapping from the old System to the +* new System is unknown. +* 30-NOV-2011 (DSB): +* When aligning two SkyFrames in the system specified by AlignSystem, +* do not assume inappropriate default equinox values for systems +* that are not referred to the equinox specified by the Equinox attribute. +* 26-APR-2012 (DSB): +* - Correct Dump function so that any axis permutation is taken into +* account when dumping SkyFrame attributes that have a separate value +* for each axis (e.g. SkyRef and SkyRefP). +* - Take axis permutation into account when setting a new value +* for attributes that have a separate value for each axis (e.g. +* SkyRef and SkyRefP). +* - Remove the code that overrides ClearEpoch and SetEpoch (these +* overrides have not been needed since the changes made on +* 24/11/2009). +* 27-APR-2012 (DSB): +* - Correct astLoadSkyFrame function so that any axis permutation is +* taken into account when loading SkyFrame attributes that have a +* separate value for each axis. +* 25-JUL-2013 (DSB): +* Use a single table of cached LAST values for all threads, rather +* than a separate table for each thread. The problem with a table per +* thread is that if you have N threads, each table contains only +* one N'th of the total number of cached values, resulting in +* poorer accuracy, and small variations in interpolated LAST value +* depending on the way the cached values are distributed amongst the +* N threads. +* 6-AST-2013 (DSB): +* Fix the use of the read-write lock that is used to serialise +* access to the table of cached LAST values. This bug could +* cause occasional problems where an AST pointer would became +* invalid for no apparent reason. +* 21-FEB-2014 (DSB): +* Rounding errors in the SkyLineDef constructor could result in the line +* between coincident points being given a non-zero length. +* 6-JUL-2015 (DSB): +* Added SkyTol attribute. +* 3-FEB-2017 (GSB): +* Override astSetDtai and astClearDtai. +* 6-APR-2017 (GSB): +* Added dtai to AstSkyLastTable. +* 10-APR-2017 (GSB): +* Added macro to test floating point equality and used it for Dtai. +*class-- +*/ + +/* Module Macros. */ +/* ============== */ +/* Set the name of the class we are implementing. This indicates to + the header files that define class interfaces that they should make + "protected" symbols available. */ +#define astCLASS SkyFrame + +/* Define the first and last acceptable System values. */ +#define FIRST_SYSTEM AST__FK4 +#define LAST_SYSTEM AST__AZEL + +/* Speed of light (AU per day) (from SLA_AOPPA) */ +#define C 173.14463331 + +/* Ratio between solar and sidereal time (from SLA_AOPPA) */ +#define SOLSID 1.00273790935 + +/* Define values for the different values of the SkyRefIs attribute. */ +#define POLE_STRING "Pole" +#define ORIGIN_STRING "Origin" +#define IGNORED_STRING "Ignored" + +/* Define other numerical constants for use in this module. */ +#define GETATTRIB_BUFF_LEN 200 +#define GETFORMAT_BUFF_LEN 50 +#define GETLABEL_BUFF_LEN 40 +#define GETSYMBOL_BUFF_LEN 20 +#define GETTITLE_BUFF_LEN 200 + +/* A macro which returns a flag indicating if the supplied system is + references to the equinox specified by the Equinox attribute. */ +#define EQREF(system) \ +((system==AST__FK4||system==AST__FK4_NO_E||system==AST__FK5||system==AST__ECLIPTIC)?1:0) + +/* Check for floating point equality (within the given tolerance), taking + bad values into account. */ +#define EQUAL(aa,bb,tol) (((aa)==AST__BAD)?(((bb)==AST__BAD)?1:0):(((bb)==AST__BAD)?0:(fabs((aa)-(bb))<=(tol)))) + +/* +* +* Name: +* MAKE_CLEAR + +* Purpose: +* Implement a method to clear a single value in a multi-valued attribute. + +* Type: +* Private macro. + +* Synopsis: +* #include "skyframe.h" +* MAKE_CLEAR(attr,component,assign,nval) + +* Class Membership: +* Defined by the SkyFrame class. + +* Description: +* This macro expands to an implementation of a private member function of +* the form: +* +* static void Clear<Attribute>( AstSkyFrame *this, int axis ) +* +* and an external interface function of the form: +* +* void astClear<Attribute>_( AstSkyFrame *this, int axis ) +* +* which implement a method for clearing a single value in a specified +* multi-valued attribute for an axis of a SkyFrame. + +* Parameters: +* attr +* The name of the attribute to be cleared, as it appears in the function +* name (e.g. Label in "astClearLabelAt"). +* component +* The name of the class structure component that holds the attribute +* value. +* assign +* An expression that evaluates to the value to assign to the component +* to clear its value. +* nval +* Specifies the number of values in the multi-valued attribute. The +* "axis" values supplied to the created function should be in the +* range zero to (nval - 1). + +* Notes: +* - To avoid problems with some compilers, you should not leave any white +* space around the macro arguments. +* +*/ + +/* Define the macro. */ +#define MAKE_CLEAR(attr,component,assign,nval) \ +\ +/* Private member function. */ \ +/* ------------------------ */ \ +static void Clear##attr( AstSkyFrame *this, int axis, int *status ) { \ +\ + int axis_p; \ +\ +/* Check the global error status. */ \ + if ( !astOK ) return; \ +\ +/* Validate and permute the axis index. */ \ + axis_p = astValidateAxis( this, axis, 1, "astClear" #attr ); \ +\ +/* Assign the "clear" value. */ \ + if( astOK ) { \ + this->component[ axis_p ] = (assign); \ + } \ +} \ +\ +/* External interface. */ \ +/* ------------------- */ \ +void astClear##attr##_( AstSkyFrame *this, int axis, int *status ) { \ +\ +/* Check the global error status. */ \ + if ( !astOK ) return; \ +\ +/* Invoke the required method via the virtual function table. */ \ + (**astMEMBER(this,SkyFrame,Clear##attr))( this, axis, status ); \ +} + + +/* +* +* Name: +* MAKE_GET + +* Purpose: +* Implement a method to get a single value in a multi-valued attribute. + +* Type: +* Private macro. + +* Synopsis: +* #include "skyframe.h" +* MAKE_GET(attr,type,bad_value,assign,nval) + +* Class Membership: +* Defined by the SkyFrame class. + +* Description: +* This macro expands to an implementation of a private member function of +* the form: +* +* static <Type> Get<Attribute>( AstSkyFrame *this, int axis ) +* +* and an external interface function of the form: +* +* <Type> astGet<Attribute>_( AstSkyFrame *this, int axis ) +* +* which implement a method for getting a single value from a specified +* multi-valued attribute for an axis of a SkyFrame. + +* Parameters: +* attr +* The name of the attribute whose value is to be obtained, as it +* appears in the function name (e.g. Label in "astGetLabel"). +* type +* The C type of the attribute. +* bad_value +* A constant value to return if the global error status is set, or if +* the function fails. +* assign +* An expression that evaluates to the value to be returned. This can +* use the string "axis" to represent the zero-based value index. +* nval +* Specifies the number of values in the multi-valued attribute. The +* "axis" values supplied to the created function should be in the +* range zero to (nval - 1). + +* Notes: +* - To avoid problems with some compilers, you should not leave any white +* space around the macro arguments. +* +*/ + +/* Define the macro. */ +#define MAKE_GET(attr,type,bad_value,assign,nval) \ +\ +/* Private member function. */ \ +/* ------------------------ */ \ +static type Get##attr( AstSkyFrame *this, int axis, int *status ) { \ + int axis_p; /* Permuted axis index */ \ + type result; /* Result to be returned */ \ +\ +/* Initialise */\ + result = (bad_value); \ +\ +/* Check the global error status. */ \ + if ( !astOK ) return result; \ +\ +/* Validate and permute the axis index. */ \ + axis_p = astValidateAxis( this, axis, 1, "astGet" #attr ); \ +\ +/* Assign the result value. */ \ + if( astOK ) { \ + result = (assign); \ + } \ +\ +/* Check for errors and clear the result if necessary. */ \ + if ( !astOK ) result = (bad_value); \ +\ +/* Return the result. */ \ + return result; \ +} \ +/* External interface. */ \ +/* ------------------- */ \ +type astGet##attr##_( AstSkyFrame *this, int axis, int *status ) { \ +\ +/* Check the global error status. */ \ + if ( !astOK ) return (bad_value); \ +\ +/* Invoke the required method via the virtual function table. */ \ + return (**astMEMBER(this,SkyFrame,Get##attr))( this, axis, status ); \ +} + +/* +* +* Name: +* MAKE_SET + +* Purpose: +* Implement a method to set a single value in a multi-valued attribute +* for a SkyFrame. + +* Type: +* Private macro. + +* Synopsis: +* #include "skyframe.h" +* MAKE_SET(attr,type,component,assign,nval) + +* Class Membership: +* Defined by the SkyFrame class. + +* Description: +* This macro expands to an implementation of a private member function of +* the form: +* +* static void Set<Attribute>( AstSkyFrame *this, int axis, <Type> value ) +* +* and an external interface function of the form: +* +* void astSet<Attribute>_( AstSkyFrame *this, int axis, <Type> value ) +* +* which implement a method for setting a single value in a specified +* multi-valued attribute for a SkyFrame. + +* Parameters: +* attr +* The name of the attribute to be set, as it appears in the function +* name (e.g. Label in "astSetLabelAt"). +* type +* The C type of the attribute. +* component +* The name of the class structure component that holds the attribute +* value. +* assign +* An expression that evaluates to the value to be assigned to the +* component. +* nval +* Specifies the number of values in the multi-valued attribute. The +* "axis" values supplied to the created function should be in the +* range zero to (nval - 1). + +* Notes: +* - To avoid problems with some compilers, you should not leave any white +* space around the macro arguments. +*- +*/ + +/* Define the macro. */ +#define MAKE_SET(attr,type,component,assign,nval) \ +\ +/* Private member function. */ \ +/* ------------------------ */ \ +static void Set##attr( AstSkyFrame *this, int axis, type value, int *status ) { \ +\ + int axis_p; \ +\ +/* Check the global error status. */ \ + if ( !astOK ) return; \ +\ +/* Validate and permute the axis index. */ \ + axis_p = astValidateAxis( this, axis, 1, "astSet" #attr ); \ +\ +/* Store the new value in the structure component. */ \ + if( astOK ) { \ + this->component[ axis_p ] = (assign); \ + } \ +} \ +\ +/* External interface. */ \ +/* ------------------- */ \ +void astSet##attr##_( AstSkyFrame *this, int axis, type value, int *status ) { \ +\ +/* Check the global error status. */ \ + if ( !astOK ) return; \ +\ +/* Invoke the required method via the virtual function table. */ \ + (**astMEMBER(this,SkyFrame,Set##attr))( this, axis, value, status ); \ +} + +/* +* +* Name: +* MAKE_TEST + +* Purpose: +* Implement a method to test if a single value has been set in a +* multi-valued attribute for a class. + +* Type: +* Private macro. + +* Synopsis: +* #include "skyframe.h" +* MAKE_TEST(attr,assign,nval) + +* Class Membership: +* Defined by the SkyFrame class. + +* Description: +* This macro expands to an implementation of a private member function of +* the form: +* +* static int Test<Attribute>( AstSkyFrame *this, int axis ) +* +* and an external interface function of the form: +* +* int astTest<Attribute>_( AstSkyFrame *this, int axis ) +* +* which implement a method for testing if a single value in a specified +* multi-valued attribute has been set for a class. + +* Parameters: +* attr +* The name of the attribute to be tested, as it appears in the function +* name (e.g. Label in "astTestLabelAt"). +* assign +* An expression that evaluates to 0 or 1, to be used as the returned +* value. This can use the string "axis" to represent the zero-based +* index of the value within the attribute. +* nval +* Specifies the number of values in the multi-valued attribute. The +* "axis" values supplied to the created function should be in the +* range zero to (nval - 1). + +* Notes: +* - To avoid problems with some compilers, you should not leave any white +* space around the macro arguments. +*- +*/ + +/* Define the macro. */ +#define MAKE_TEST(attr,assign,nval) \ +\ +/* Private member function. */ \ +/* ------------------------ */ \ +static int Test##attr( AstSkyFrame *this, int axis, int *status ) { \ + int result; /* Value to return */ \ + int axis_p; /* Permuted axis index */ \ +\ +/* Initialise */ \ + result =0; \ +\ +/* Check the global error status. */ \ + if ( !astOK ) return result; \ +\ +/* Validate and permute the axis index. */ \ + axis_p = astValidateAxis( this, axis, 1, "astTest" #attr ); \ +\ +/* Assign the result value. */ \ + if( astOK ) { \ + result = (assign); \ + } \ +\ +/* Check for errors and clear the result if necessary. */ \ + if ( !astOK ) result = 0; \ +\ +/* Return the result. */ \ + return result; \ +} \ +/* External interface. */ \ +/* ------------------- */ \ +int astTest##attr##_( AstSkyFrame *this, int axis, int *status ) { \ +\ +/* Check the global error status. */ \ + if ( !astOK ) return 0; \ +\ +/* Invoke the required method via the virtual function table. */ \ + return (**astMEMBER(this,SkyFrame,Test##attr))( this, axis, status ); \ +} + + +/* Header files. */ +/* ============= */ +/* Interface definitions. */ +/* ---------------------- */ + +#include "globals.h" /* Thread-safe global data access */ +#include "error.h" /* Error reporting facilities */ +#include "memory.h" /* Memory allocation facilities */ +#include "globals.h" /* Thread-safe global data access */ +#include "object.h" /* Base Object class */ +#include "pointset.h" /* Sets of points (for AST__BAD) */ +#include "unitmap.h" /* Unit Mappings */ +#include "permmap.h" /* Coordinate permutations */ +#include "cmpmap.h" /* Compound Mappings */ +#include "slamap.h" /* SLALIB sky coordinate Mappings */ +#include "timemap.h" /* Time conversions */ +#include "skyaxis.h" /* Sky axes */ +#include "frame.h" /* Parent Frame class */ +#include "matrixmap.h" /* Matrix multiplication */ +#include "sphmap.h" /* Cartesian<->Spherical transformations */ +#include "skyframe.h" /* Interface definition for this class */ +#include "pal.h" /* SLALIB library interface */ +#include "wcsmap.h" /* Factors of PI */ +#include "timeframe.h" /* Time system transformations */ + +/* Error code definitions. */ +/* ----------------------- */ +#include "ast_err.h" /* AST error codes */ + +/* C header files. */ +/* --------------- */ +#include <ctype.h> +#include <float.h> +#include <limits.h> +#include <math.h> +#include <stdarg.h> +#include <stddef.h> +#include <stdio.h> +#include <string.h> + +/* Type Definitions. */ +/* ================= */ + +/* Cached Line structure. */ +/* ---------------------- */ +/* This structure contains information describing a line segment within a + SkyFrame. It differs from the AstLineDef defined in frame.h because + positions are represented by 3D (x,y,z) cartesian coords rather than + 2D (long,lat) coords. */ + +typedef struct SkyLineDef { + AstFrame *frame; /* Pointer to Frame in which the line is defined */ + double length; /* Line length */ + int infinite; /* Disregard the start and end of the line? */ + double start[3]; /* Unit vector defining start of line */ + double end[3]; /* Unit vector defining end of line */ + double dir[3]; /* Unit vector defining line direction */ + double q[3]; /* Unit vector perpendicular to line */ + double start_2d[2]; + double end_2d[2]; +} SkyLineDef; + +/* Module Variables. */ +/* ================= */ + +/* Address of this static variable is used as a unique identifier for + member of this class. */ +static int class_check; + +/* Pointers to parent class methods which are used or extended by this + class. */ +static AstSystemType (* parent_getalignsystem)( AstFrame *, int * ); +static AstSystemType (* parent_getsystem)( AstFrame *, int * ); +static const char *(* parent_format)( AstFrame *, int, double, int * ); +static const char *(* parent_getattrib)( AstObject *, const char *, int * ); +static const char *(* parent_getdomain)( AstFrame *, int * ); +static const char *(* parent_getformat)( AstFrame *, int, int * ); +static const char *(* parent_getlabel)( AstFrame *, int, int * ); +static const char *(* parent_getsymbol)( AstFrame *, int, int * ); +static const char *(* parent_gettitle)( AstFrame *, int * ); +static const char *(* parent_getunit)( AstFrame *, int, int * ); +static double (* parent_gap)( AstFrame *, int, double, int *, int * ); +static double (* parent_getbottom)( AstFrame *, int, int * ); +static double (* parent_getepoch)( AstFrame *, int * ); +static double (* parent_gettop)( AstFrame *, int, int * ); +static int (* parent_getdirection)( AstFrame *, int, int * ); +static int (* parent_getobjsize)( AstObject *, int * ); +static int (* parent_match)( AstFrame *, AstFrame *, int, int **, int **, AstMapping **, AstFrame **, int * ); +static int (* parent_subframe)( AstFrame *, AstFrame *, int, const int *, const int *, AstMapping **, AstFrame **, int * ); +static int (* parent_testattrib)( AstObject *, const char *, int * ); +static int (* parent_testformat)( AstFrame *, int, int * ); +static int (* parent_unformat)( AstFrame *, int, const char *, double *, int * ); +static void (* parent_clearattrib)( AstObject *, const char *, int * ); +static void (* parent_cleardtai)( AstFrame *, int * ); +static void (* parent_cleardut1)( AstFrame *, int * ); +static void (* parent_clearformat)( AstFrame *, int, int * ); +static void (* parent_clearobsalt)( AstFrame *, int * ); +static void (* parent_clearobslat)( AstFrame *, int * ); +static void (* parent_clearobslon)( AstFrame *, int * ); +static void (* parent_clearsystem)( AstFrame *, int * ); +static void (* parent_overlay)( AstFrame *, const int *, AstFrame *, int * ); +static void (* parent_setattrib)( AstObject *, const char *, int * ); +static void (* parent_setdtai)( AstFrame *, double, int * ); +static void (* parent_setdut1)( AstFrame *, double, int * ); +static void (* parent_setformat)( AstFrame *, int, const char *, int * ); +static void (* parent_setobsalt)( AstFrame *, double, int * ); +static void (* parent_setobslat)( AstFrame *, double, int * ); +static void (* parent_setobslon)( AstFrame *, double, int * ); +static void (* parent_setsystem)( AstFrame *, AstSystemType, int * ); + +/* Factors for converting between hours, degrees and radians. */ +static double hr2rad; +static double deg2rad; +static double pi; +static double piby2; + +/* Table of cached Local Apparent Sidereal Time values and corresponding + epochs. */ +static int nlast_tables = 0; +static AstSkyLastTable **last_tables = NULL; + + +/* Define macros for accessing each item of thread specific global data. */ +#ifdef THREAD_SAFE + +/* Define how to initialise thread-specific globals. */ +#define GLOBAL_inits \ + globals->Class_Init = 0; \ + globals->GetAttrib_Buff[ 0 ] = 0; \ + globals->GetFormat_Buff[ 0 ] = 0; \ + globals->GetLabel_Buff[ 0 ] = 0; \ + globals->GetSymbol_Buff[ 0 ] = 0; \ + globals->GetTitle_Buff[ 0 ] = 0; \ + globals->GetTitle_Buff2[ 0 ] = 0; \ + globals->TDBFrame = NULL; \ + globals->LASTFrame = NULL; \ + +/* Create the function that initialises global data for this module. */ +astMAKE_INITGLOBALS(SkyFrame) + +/* Define macros for accessing each item of thread specific global data. */ +#define class_init astGLOBAL(SkyFrame,Class_Init) +#define class_vtab astGLOBAL(SkyFrame,Class_Vtab) +#define getattrib_buff astGLOBAL(SkyFrame,GetAttrib_Buff) +#define getformat_buff astGLOBAL(SkyFrame,GetFormat_Buff) +#define getlabel_buff astGLOBAL(SkyFrame,GetLabel_Buff) +#define getsymbol_buff astGLOBAL(SkyFrame,GetSymbol_Buff) +#define gettitle_buff astGLOBAL(SkyFrame,GetTitle_Buff) +#define gettitle_buff2 astGLOBAL(SkyFrame,GetTitle_Buff2) +#define tdbframe astGLOBAL(SkyFrame,TDBFrame) +#define lastframe astGLOBAL(SkyFrame,LASTFrame) + + + +static pthread_mutex_t mutex2 = PTHREAD_MUTEX_INITIALIZER; +#define LOCK_MUTEX2 pthread_mutex_lock( &mutex2 ); +#define UNLOCK_MUTEX2 pthread_mutex_unlock( &mutex2 ); + +/* A read-write lock used to protect the table of cached LAST values so + that multiple threads can read simultaneously so long as no threads are + writing to the table. */ +static pthread_rwlock_t rwlock1=PTHREAD_RWLOCK_INITIALIZER; +#define LOCK_WLOCK1 pthread_rwlock_wrlock( &rwlock1 ); +#define LOCK_RLOCK1 pthread_rwlock_rdlock( &rwlock1 ); +#define UNLOCK_RWLOCK1 pthread_rwlock_unlock( &rwlock1 ); + +/* If thread safety is not needed, declare and initialise globals at static + variables. */ +#else + +/* Buffer returned by GetAttrib. */ +static char getattrib_buff[ GETATTRIB_BUFF_LEN + 1 ]; + +/* Buffer returned by GetFormat. */ +static char getformat_buff[ GETFORMAT_BUFF_LEN + 1 ]; + +/* Default GetLabel string buffer */ +static char getlabel_buff[ GETLABEL_BUFF_LEN + 1 ]; + +/* Default GetSymbol buffer */ +static char getsymbol_buff[ GETSYMBOL_BUFF_LEN + 1 ]; + +/* Default Title string buffer */ +static char gettitle_buff[ AST__SKYFRAME_GETTITLE_BUFF_LEN + 1 ]; +static char gettitle_buff2[ AST__SKYFRAME_GETTITLE_BUFF_LEN + 1 ]; + +/* TimeFrames for doing TDB<->LAST conversions. */ +static AstTimeFrame *tdbframe = NULL; +static AstTimeFrame *lastframe = NULL; + + +/* Define the class virtual function table and its initialisation flag + as static variables. */ +static AstSkyFrameVtab class_vtab; /* Virtual function table */ +static int class_init = 0; /* Virtual function table initialised? */ + +#define LOCK_MUTEX2 +#define UNLOCK_MUTEX2 + +#define LOCK_WLOCK1 +#define LOCK_RLOCK1 +#define UNLOCK_RWLOCK1 + +#endif + + +/* Prototypes for Private Member Functions. */ +/* ======================================== */ +static AstLineDef *LineDef( AstFrame *, const double[2], const double[2], int * ); +static AstMapping *SkyOffsetMap( AstSkyFrame *, int * ); +static AstPointSet *FrameGrid( AstFrame *, int, const double *, const double *, int * ); +static AstPointSet *ResolvePoints( AstFrame *, const double [], const double [], AstPointSet *, AstPointSet *, int * ); +static AstSystemType GetAlignSystem( AstFrame *, int * ); +static AstSystemType GetSystem( AstFrame *, int * ); +static AstSystemType SystemCode( AstFrame *, const char *, int * ); +static AstSystemType ValidateSystem( AstFrame *, AstSystemType, const char *, int * ); +static const char *Format( AstFrame *, int, double, int * ); +static const char *GetAttrib( AstObject *, const char *, int * ); +static const char *GetDomain( AstFrame *, int * ); +static const char *GetFormat( AstFrame *, int, int * ); +static const char *GetLabel( AstFrame *, int, int * ); +static const char *GetProjection( AstSkyFrame *, int * ); +static const char *GetSymbol( AstFrame *, int, int * ); +static const char *GetTitle( AstFrame *, int * ); +static const char *GetUnit( AstFrame *, int, int * ); +static const char *SystemString( AstFrame *, AstSystemType, int * ); +static double Angle( AstFrame *, const double[], const double[], const double[], int * ); +static double CalcLAST( AstSkyFrame *, double, double, double, double, double, double, int * ); +static double Distance( AstFrame *, const double[], const double[], int * ); +static double Gap( AstFrame *, int, double, int *, int * ); +static double GetBottom( AstFrame *, int, int * ); +static double GetCachedLAST( AstSkyFrame *, double, double, double, double, double, double, int * ); +static double GetEpoch( AstFrame *, int * ); +static double GetEquinox( AstSkyFrame *, int * ); +static void SetCachedLAST( AstSkyFrame *, double, double, double, double, double, double, double, int * ); +static void SetLast( AstSkyFrame *, int * ); +static double GetTop( AstFrame *, int, int * ); +static double Offset2( AstFrame *, const double[2], double, double, double[2], int * ); +static double GetDiurab( AstSkyFrame *, int * ); +static double GetLAST( AstSkyFrame *, int * ); +static int GetActiveUnit( AstFrame *, int * ); +static int GetAsTime( AstSkyFrame *, int, int * ); +static int GetDirection( AstFrame *, int, int * ); +static int GetIsLatAxis( AstSkyFrame *, int, int * ); +static int GetIsLonAxis( AstSkyFrame *, int, int * ); +static int GetLatAxis( AstSkyFrame *, int * ); +static int GetLonAxis( AstSkyFrame *, int * ); +static int GetNegLon( AstSkyFrame *, int * ); +static int GetObjSize( AstObject *, int * ); +static int IsEquatorial( AstSystemType, int * ); +static int LineContains( AstFrame *, AstLineDef *, int, double *, int * ); +static int LineCrossing( AstFrame *, AstLineDef *, AstLineDef *, double **, int * ); +static int LineIncludes( SkyLineDef *, double[3], int * ); +static int MakeSkyMapping( AstSkyFrame *, AstSkyFrame *, AstSystemType, AstMapping **, int * ); +static int Match( AstFrame *, AstFrame *, int, int **, int **, AstMapping **, AstFrame **, int * ); +static int SubFrame( AstFrame *, AstFrame *, int, const int *, const int *, AstMapping **, AstFrame **, int * ); +static int TestActiveUnit( AstFrame *, int * ); +static int TestAsTime( AstSkyFrame *, int, int * ); +static int TestAttrib( AstObject *, const char *, int * ); +static int TestEquinox( AstSkyFrame *, int * ); +static int TestNegLon( AstSkyFrame *, int * ); +static int TestProjection( AstSkyFrame *, int * ); +static int TestSlaUnit( AstSkyFrame *, AstSkyFrame *, AstSlaMap *, int * ); +static int Unformat( AstFrame *, int, const char *, double *, int * ); +static void ClearAsTime( AstSkyFrame *, int, int * ); +static void ClearAttrib( AstObject *, const char *, int * ); +static void ClearDtai( AstFrame *, int * ); +static void ClearDut1( AstFrame *, int * ); +static void ClearEquinox( AstSkyFrame *, int * ); +static void ClearNegLon( AstSkyFrame *, int * ); +static void ClearObsAlt( AstFrame *, int * ); +static void ClearObsLat( AstFrame *, int * ); +static void ClearObsLon( AstFrame *, int * ); +static void ClearProjection( AstSkyFrame *, int * ); +static void ClearSystem( AstFrame *, int * ); +static void Copy( const AstObject *, AstObject *, int * ); +static void Delete( AstObject *, int * ); +static void Dump( AstObject *, AstChannel *, int * ); +static void Intersect( AstFrame *, const double[2], const double[2], const double[2], const double[2], double[2], int * ); +static void LineOffset( AstFrame *, AstLineDef *, double, double, double[2], int * ); +static void MatchAxesX( AstFrame *, AstFrame *, int *, int * ); +static void Norm( AstFrame *, double[], int * ); +static void NormBox( AstFrame *, double[], double[], AstMapping *, int * ); +static void Offset( AstFrame *, const double[], const double[], double, double[], int * ); +static void Overlay( AstFrame *, const int *, AstFrame *, int * ); +static void Resolve( AstFrame *, const double [], const double [], const double [], double [], double *, double *, int * ); +static void SetAsTime( AstSkyFrame *, int, int, int * ); +static void SetAttrib( AstObject *, const char *, int * ); +static void SetDtai( AstFrame *, double, int * ); +static void SetDut1( AstFrame *, double, int * ); +static void SetEquinox( AstSkyFrame *, double, int * ); +static void SetNegLon( AstSkyFrame *, int, int * ); +static void SetObsAlt( AstFrame *, double, int * ); +static void SetObsLat( AstFrame *, double, int * ); +static void SetObsLon( AstFrame *, double, int * ); +static void SetProjection( AstSkyFrame *, const char *, int * ); +static void SetSystem( AstFrame *, AstSystemType, int * ); +static void Shapp( double, double *, double *, double, double *, int * ); +static void Shcal( double, double, double, double *, double *, int * ); +static void VerifyMSMAttrs( AstSkyFrame *, AstSkyFrame *, int, const char *, const char *, int * ); + +static double GetSkyRef( AstSkyFrame *, int, int * ); +static int TestSkyRef( AstSkyFrame *, int, int * ); +static void SetSkyRef( AstSkyFrame *, int, double, int * ); +static void ClearSkyRef( AstSkyFrame *, int, int * ); + +static double GetSkyRefP( AstSkyFrame *, int, int * ); +static int TestSkyRefP( AstSkyFrame *, int, int * ); +static void SetSkyRefP( AstSkyFrame *, int, double, int * ); +static void ClearSkyRefP( AstSkyFrame *, int, int * ); + +static int GetSkyRefIs( AstSkyFrame *, int * ); +static int TestSkyRefIs( AstSkyFrame *, int * ); +static void SetSkyRefIs( AstSkyFrame *, int, int * ); +static void ClearSkyRefIs( AstSkyFrame *, int * ); + +static int GetAlignOffset( AstSkyFrame *, int * ); +static int TestAlignOffset( AstSkyFrame *, int * ); +static void SetAlignOffset( AstSkyFrame *, int, int * ); +static void ClearAlignOffset( AstSkyFrame *, int * ); + +static double GetSkyTol( AstSkyFrame *, int * ); +static int TestSkyTol( AstSkyFrame *, int * ); +static void SetSkyTol( AstSkyFrame *, double, int * ); +static void ClearSkyTol( AstSkyFrame *, int * ); + +/* Member functions. */ +/* ================= */ +static double Angle( AstFrame *this_frame, const double a[], + const double b[], const double c[], int *status ) { +/* +* Name: +* Angle + +* Purpose: +* Calculate the angle subtended by two points at a third point. + +* Type: +* Private function. + +* Synopsis: +* #include "skyframe.h" +* double Angle( AstFrame *this_frame, const double a[], +* const double b[], const double c[], int *status ) + +* Class Membership: +* SkyFrame member function (over-rides the astAngle method +* inherited from the Frame class). + +* Description: +* This function finds the angle at point B between the line +* joining points A and B, and the line joining points C +* and B. These lines will in fact be geodesic curves (great circles). + +* Parameters: +* this +* Pointer to the SkyFrame. +* a +* An array of double, with one element for each SkyFrame axis, +* containing the coordinates of the first point. +* b +* An array of double, with one element for each SkyFrame axis, +* containing the coordinates of the second point. +* c +* An array of double, with one element for each SkyFrame axis, +* containing the coordinates of the third point. +* status +* Pointer to the inherited status variable. + +* Returned Value: +* The angle in radians, from the line AB to the line CB, in +* the range $\pm \pi$ with positive rotation is in the same sense +* as rotation from axis 2 to axis 1. + +* Notes: +* - This function will return a "bad" result value (AST__BAD) if +* any of the input coordinates has this value. +* - A "bad" value will also be returned if points A and B are +* co-incident, or if points B and C are co-incident. +* - A "bad" value will also be returned if this function is +* invoked with the AST error status set, or if it should fail for +* any reason. +*/ + + AstSkyFrame *this; /* Pointer to SkyFrame structure */ + const int *perm; /* Axis permutation array */ + double aa[ 2 ]; /* Permuted a coordinates */ + double anga; /* Angle from north to the line BA */ + double angc; /* Angle from north to the line BC */ + double bb[ 2 ]; /* Permuted b coordinates */ + double cc[ 2 ]; /* Permuted c coordinates */ + double result; /* Value to return */ + +/* Initialise. */ + result = AST__BAD; + +/* Check the global error status. */ + if ( !astOK ) return result; + +/* Obtain a pointer to the SkyFrame structure. */ + this = (AstSkyFrame *) this_frame; + +/* Obtain a pointer to the SkyFrame's axis permutation array. */ + perm = astGetPerm( this ); + if ( astOK ) { + +/* Check that all supplied coordinates are OK. */ + if ( ( a[ 0 ] != AST__BAD ) && ( a[ 1 ] != AST__BAD ) && + ( b[ 0 ] != AST__BAD ) && ( b[ 1 ] != AST__BAD ) && + ( c[ 0 ] != AST__BAD ) && ( c[ 1 ] != AST__BAD ) ) { + +/* Apply the axis permutation array to obtain the coordinates of the + three points in the required (longitude,latitude) order. */ + aa[ perm[ 0 ] ] = a[ 0 ]; + aa[ perm[ 1 ] ] = a[ 1 ]; + bb[ perm[ 0 ] ] = b[ 0 ]; + bb[ perm[ 1 ] ] = b[ 1 ]; + cc[ perm[ 0 ] ] = c[ 0 ]; + cc[ perm[ 1 ] ] = c[ 1 ]; + +/* Check that A and B are not co-incident. */ + if( aa[ 0 ] != bb[ 0 ] || aa[ 1 ] != bb[ 1 ] ) { + +/* Check that C and B are not co-incident. */ + if( cc[ 0 ] != bb[ 0 ] || cc[ 1 ] != bb[ 1 ] ) { + +/* Find the angle from north to the line BA. */ + anga = palDbear( bb[ 0 ], bb[ 1 ], aa[ 0 ], aa[ 1 ] ); + +/* Find the angle from north to the line BC. */ + angc = palDbear( bb[ 0 ], bb[ 1 ], cc[ 0 ], cc[ 1 ] ); + +/* Find the difference. */ + result = angc - anga; + +/* This value is the angle from north, but we want the angle from axis 2. + If the axes have been swapped so that axis 2 is actually the longitude + axis, then we need to correct this result. */ + if( perm[ 0 ] != 0 ) result = piby2 - result; + +/* Fold the result into the range +/- PI. */ + result = palDrange( result ); + } + } + } + } + +/* Return the result. */ + return result; +} + +static double CalcLAST( AstSkyFrame *this, double epoch, double obslon, + double obslat, double obsalt, double dut1, double dtai, + int *status ) { +/* +* Name: +* CalcLAST + +* Purpose: +* Calculate the Local Appearent Sidereal Time for a SkyFrame. + +* Type: +* Private function. + +* Synopsis: +* #include "skyframe.h" +* double CalcLAST( AstSkyFrame *this, double epoch, double obslon, +* double obslat, double obsalt, double dut1, double dtai, +* int *status ) + +* Class Membership: +* SkyFrame member function. + +* Description: +* This function calculates and returns the Local Apparent Sidereal Time +* at the given epoch, etc. + +* Parameters: +* this +* Pointer to the SkyFrame. +* epoch +* The epoch (MJD). +* obslon +* Observatory geodetic longitude (radians) +* obslat +* Observatory geodetic latitude (radians) +* obsalt +* Observatory geodetic altitude (metres) +* dut1 +* The UT1-UTC correction, in seconds. +* dtai +* The TAI-UTC correction, in seconds. +* status +* Pointer to the inherited status variable. + +* Returned Value: +* The Local Apparent Sidereal Time, in radians. + +* Notes: +* - A value of AST__BAD will be returned if this function is invoked +* with the global error status set, or if it should fail for any reason. +*/ + +/* Local Variables: */ + astDECLARE_GLOBALS /* Declare the thread specific global data */ + AstFrameSet *fs; /* Mapping from TDB offset to LAST offset */ + double epoch0; /* Supplied epoch value */ + double result; /* Returned LAST value */ + +/* Get a pointer to the structure holding thread-specific global data. */ + astGET_GLOBALS(this); + +/* Check the global error status. */ + if ( !astOK ) return AST__BAD; + +/* See if the required LAST value can be determined from the cached LAST + values in the SkyFrame virtual function table. */ + result = GetCachedLAST( this, epoch, obslon, obslat, obsalt, dut1, dtai, + status ); + +/* If not, we do an exact calculation from scratch. */ + if( result == AST__BAD ) { + +/* If not yet done, create two TimeFrames. Note, this is done here + rather than in astInitSkyFrameVtab in order to avoid infinite vtab + initialisation loops (caused by the TimeFrame class containing a + static SkyFrame). */ + if( ! tdbframe ) { + astBeginPM; + tdbframe = astTimeFrame( "system=mjd,timescale=tdb", status ); + lastframe = astTimeFrame( "system=mjd,timescale=last", status ); + astEndPM; + } + +/* For better accuracy, use this integer part of the epoch as the origin of + the two TimeFrames. */ + astSetTimeOrigin( tdbframe, (int) epoch ); + astSetTimeOrigin( lastframe, (int) epoch ); + +/* Convert the absolute Epoch value to an offset from the above origin. */ + epoch0 = epoch; + epoch -= (int) epoch; + +/* Store the observers position in the two TimeFrames. */ + astSetObsLon( tdbframe, obslon ); + astSetObsLon( lastframe, obslon ); + + astSetObsLat( tdbframe, obslat ); + astSetObsLat( lastframe, obslat ); + + astSetObsAlt( tdbframe, obsalt ); + astSetObsAlt( lastframe, obsalt ); + +/* Store the DUT1 value. */ + astSetDut1( tdbframe, dut1 ); + astSetDut1( lastframe, dut1 ); + +/* Store the DTAI value. */ + if ( dtai == AST__BAD ) { + astClearDtai( tdbframe ); + astClearDtai( lastframe ); + } + else { + astSetDtai( tdbframe, dtai ); + astSetDtai( lastframe, dtai ); + } + +/* Get the conversion from tdb mjd offset to last mjd offset. */ + fs = astConvert( tdbframe, lastframe, "" ); + +/* Use it to transform the SkyFrame Epoch from TDB offset to LAST offset. */ + astTran1( fs, 1, &epoch, 1, &epoch ); + fs = astAnnul( fs ); + +/* Convert the LAST offset from days to radians. */ + result = ( epoch - (int) epoch )*2*AST__DPI; + +/* Cache the new LAST value in the SkyFrame virtual function table. */ + SetCachedLAST( this, result, epoch0, obslon, obslat, obsalt, dut1, dtai, + status ); + } + +/* Return the required LAST value. */ + return result; +} + +static void ClearAsTime( AstSkyFrame *this, int axis, int *status ) { +/* +* Name: +* ClearAsTime + +* Purpose: +* Clear the value of the AsTime attribute for a SkyFrame's axis. + +* Type: +* Private function. + +* Synopsis: +* #include "skyframe.h" +* void ClearAsTime( AstSkyFrame *this, int axis, int *status ) + +* Class Membership: +* SkyFrame member function. + +* Description: +* This function clears any value that has been set for the AsTime +* attribute for a specified axis of a SkyFrame. This attribute indicates +* whether axis values should be formatted as times (as opposed to angles) +* by default. + +* Parameters: +* this +* Pointer to the SkyFrame. +* axis +* Index of the axis for which the value is to be cleared (zero based). +* status +* Pointer to the inherited status variable. + +* Returned Value: +* void. +*/ + +/* Local Variables: */ + AstAxis *ax; /* Pointer to Axis object */ + +/* Check the global error status. */ + if ( !astOK ) return; + +/* Validate the axis index. */ + (void) astValidateAxis( this, axis, 1, "astClearAsTime" ); + +/* Obtain a pointer to the Axis object. */ + ax = astGetAxis( this, axis ); + +/* If the Axis is a SkyAxis, clear the AsTime attribute (if it is not a + SkyAxis, it will not have this attribute anyway). */ + if ( astIsASkyAxis( ax ) ) astClearAxisAsTime( ax ); + +/* Annul the Axis pointer. */ + ax = astAnnul( ax ); +} + +static void ClearAttrib( AstObject *this_object, const char *attrib, int *status ) { +/* +* Name: +* ClearAttrib + +* Purpose: +* Clear an attribute value for a SkyFrame. + +* Type: +* Private function. + +* Synopsis: +* #include "skyframe.h" +* void ClearAttrib( AstObject *this, const char *attrib, int *status ) + +* Class Membership: +* SkyFrame member function (over-rides the astClearAttrib protected +* method inherited from the Frame class). + +* Description: +* This function clears the value of a specified attribute for a +* SkyFrame, so that the default value will subsequently be used. + +* Parameters: +* this +* Pointer to the SkyFrame. +* attrib +* Pointer to a null terminated string specifying the attribute +* name. This should be in lower case with no surrounding white +* space. +* status +* Pointer to the inherited status variable. + +* Notes: +* - This function uses one-based axis numbering so that it is +* suitable for external (public) use. +*/ + +/* Local Variables: */ + AstSkyFrame *this; /* Pointer to the SkyFrame structure */ + int axis; /* SkyFrame axis number */ + int len; /* Length of attrib string */ + int nc; /* No. characters read by astSscanf */ + +/* Check the global error status. */ + if ( !astOK ) return; + +/* Obtain a pointer to the SkyFrame structure. */ + this = (AstSkyFrame *) this_object; + +/* Obtain the length of the "attrib" string. */ + len = strlen( attrib ); + +/* Check the attribute name and clear the appropriate attribute. */ + +/* AsTime(axis). */ +/* ------------- */ + if ( nc = 0, + ( 1 == astSscanf( attrib, "astime(%d)%n", &axis, &nc ) ) + && ( nc >= len ) ) { + astClearAsTime( this, axis - 1 ); + +/* Equinox. */ +/* -------- */ + } else if ( !strcmp( attrib, "equinox" ) ) { + astClearEquinox( this ); + +/* NegLon. */ +/* ------- */ + } else if ( !strcmp( attrib, "neglon" ) ) { + astClearNegLon( this ); + +/* Projection. */ +/* ----------- */ + } else if ( !strcmp( attrib, "projection" ) ) { + astClearProjection( this ); + +/* SkyRef. */ +/* ------- */ + } else if ( !strcmp( attrib, "skyref" ) ) { + astClearSkyRef( this, 0 ); + astClearSkyRef( this, 1 ); + +/* SkyTol. */ +/* ------- */ + } else if ( !strcmp( attrib, "skytol" ) ) { + astClearSkyTol( this ); + +/* SkyRef(axis). */ +/* ------------- */ + } else if ( nc = 0, + ( 1 == astSscanf( attrib, "skyref(%d)%n", &axis, &nc ) ) + && ( nc >= len ) ) { + astClearSkyRef( this, axis - 1 ); + +/* SkyRefP. */ +/* -------- */ + } else if ( !strcmp( attrib, "skyrefp" ) ) { + astClearSkyRefP( this, 0 ); + astClearSkyRefP( this, 1 ); + +/* SkyRefP(axis). */ +/* ------------- */ + } else if ( nc = 0, + ( 1 == astSscanf( attrib, "skyrefp(%d)%n", &axis, &nc ) ) + && ( nc >= len ) ) { + astClearSkyRefP( this, axis - 1 ); + +/* SkyRefIs. */ +/* --------- */ + } else if ( !strcmp( attrib, "skyrefis" ) ) { + astClearSkyRefIs( this ); + +/* AlignOffset. */ +/* ------------ */ + } else if ( !strcmp( attrib, "alignoffset" ) ) { + astClearAlignOffset( this ); + +/* If the name was not recognised, test if it matches any of the + read-only attributes of this class. If it does, then report an + error. */ + } else if ( !strncmp( attrib, "islataxis", 9 ) || + !strncmp( attrib, "islonaxis", 9 ) || + !strcmp( attrib, "lataxis" ) || + !strcmp( attrib, "lonaxis" ) ) { + astError( AST__NOWRT, "astClear: Invalid attempt to clear the \"%s\" " + "value for a %s.", status, attrib, astGetClass( this ) ); + astError( AST__NOWRT, "This is a read-only attribute." , status); + +/* If the attribute is not recognised, pass it on to the parent method + for further interpretation. */ + } else { + (*parent_clearattrib)( this_object, attrib, status ); + } +} + +static void ClearDtai( AstFrame *this, int *status ) { +/* +* Name: +* ClearDtai + +* Purpose: +* Clear the value of the Dtai attribute for a SkyFrame. + +* Type: +* Private function. + +* Synopsis: +* #include "skyframe.h" +* void ClearDtai( AstFrame *this, int *status ) + +* Class Membership: +* SkyFrame member function (over-rides the astClearDtai method +* inherited from the Frame class). + +* Description: +* This function clears the Dtai value and updates the LAST value +* stored in the SkyFrame. + +* Parameters: +* this +* Pointer to the SkyFrame. +* status +* Pointer to the inherited status variable. + +*/ + +/* Local Variables: */ + double orig; + +/* Check the global error status. */ + if ( !astOK ) return; + +/* Note the original value */ + orig = astGetDtai( this ); + +/* Invoke the parent method to clear the Frame Dtai */ + (*parent_cleardtai)( this, status ); + +/* If the DTAI value has changed significantly, indicate that the LAST value + will need to be re-calculated when it is next needed. */ + if( ! EQUAL( orig, astGetDtai( this ), 1.0E-6 ) ) { + ( (AstSkyFrame *) this )->last = AST__BAD; + ( (AstSkyFrame *) this )->eplast = AST__BAD; + ( (AstSkyFrame *) this )->klast = AST__BAD; + } +} + +static void ClearDut1( AstFrame *this, int *status ) { +/* +* Name: +* ClearDut1 + +* Purpose: +* Clear the value of the Dut1 attribute for a SkyFrame. + +* Type: +* Private function. + +* Synopsis: +* #include "skyframe.h" +* void ClearDut1( AstFrame *this, int *status ) + +* Class Membership: +* SkyFrame member function (over-rides the astClearDut1 method +* inherited from the Frame class). + +* Description: +* This function clears the Dut1 value and updates the LAST value +* stored in the SkyFrame. + +* Parameters: +* this +* Pointer to the SkyFrame. +* status +* Pointer to the inherited status variable. + +*/ + +/* Local Variables: */ + double orig; + +/* Check the global error status. */ + if ( !astOK ) return; + +/* Note the original value */ + orig = astGetDut1( this ); + +/* Invoke the parent method to clear the Frame Dut1 */ + (*parent_cleardut1)( this, status ); + +/* If the DUT1 value has changed significantly, indicate that the LAST value + will need to be re-calculated when it is next needed. */ + if( fabs( orig - astGetDut1( this ) ) > 1.0E-6 ) { + ( (AstSkyFrame *) this )->last = AST__BAD; + ( (AstSkyFrame *) this )->eplast = AST__BAD; + ( (AstSkyFrame *) this )->klast = AST__BAD; + } +} + +static void ClearObsAlt( AstFrame *this, int *status ) { +/* +* Name: +* ClearObsAlt + +* Purpose: +* Clear the value of the ObsAlt attribute for a SkyFrame. + +* Type: +* Private function. + +* Synopsis: +* #include "skyframe.h" +* void ClearObsAlt( AstFrame *this, int *status ) + +* Class Membership: +* SkyFrame member function (over-rides the astClearObsAlt method +* inherited from the Frame class). + +* Description: +* This function clears the ObsAlt value. + +* Parameters: +* this +* Pointer to the SkyFrame. +* status +* Pointer to the inherited status variable. + +*/ + +/* Local Variables: */ + double orig; + +/* Check the global error status. */ + if ( !astOK ) return; + +/* Note the original value */ + orig = astGetObsAlt( this ); + +/* Invoke the parent method to clear the Frame ObsAlt. */ + (*parent_clearobsalt)( this, status ); + +/* If the altitude has changed significantly, indicate that the LAST value + and magnitude of the diurnal aberration vector will need to be + re-calculated when next needed. */ + if( fabs( orig - astGetObsAlt( this ) ) > 0.001 ) { + ( (AstSkyFrame *) this )->last = AST__BAD; + ( (AstSkyFrame *) this )->eplast = AST__BAD; + ( (AstSkyFrame *) this )->klast = AST__BAD; + ( (AstSkyFrame *) this )->diurab = AST__BAD; + } +} + +static void ClearObsLat( AstFrame *this, int *status ) { +/* +* Name: +* ClearObsLat + +* Purpose: +* Clear the value of the ObsLat attribute for a SkyFrame. + +* Type: +* Private function. + +* Synopsis: +* #include "skyframe.h" +* void ClearObsLat( AstFrame *this, int *status ) + +* Class Membership: +* SkyFrame member function (over-rides the astClearObsLat method +* inherited from the Frame class). + +* Description: +* This function clears the ObsLat value. + +* Parameters: +* this +* Pointer to the SkyFrame. +* status +* Pointer to the inherited status variable. + +*/ + +/* Local Variables: */ + double orig; + +/* Check the global error status. */ + if ( !astOK ) return; + +/* Note the original value */ + orig = astGetObsLat( this ); + +/* Invoke the parent method to clear the Frame ObsLat. */ + (*parent_clearobslat)( this, status ); + +/* If the altitude has changed significantly, indicate that the LAST value + and magnitude of the diurnal aberration vector will need to be + re-calculated when next needed. */ + if( fabs( orig - astGetObsLat( this ) ) > 1.0E-8 ) { + ( (AstSkyFrame *) this )->last = AST__BAD; + ( (AstSkyFrame *) this )->eplast = AST__BAD; + ( (AstSkyFrame *) this )->klast = AST__BAD; + ( (AstSkyFrame *) this )->diurab = AST__BAD; + } +} + +static void ClearObsLon( AstFrame *this, int *status ) { +/* +* Name: +* ClearObsLon + +* Purpose: +* Clear the value of the ObsLon attribute for a SkyFrame. + +* Type: +* Private function. + +* Synopsis: +* #include "skyframe.h" +* void ClearObsLon( AstFrame *this, int *status ) + +* Class Membership: +* SkyFrame member function (over-rides the astClearObsLon method +* inherited from the Frame class). + +* Description: +* This function clears the ObsLon value. + +* Parameters: +* this +* Pointer to the SkyFrame. +* status +* Pointer to the inherited status variable. + +*/ + +/* Local Variables: */ + double orig; + +/* Check the global error status. */ + if ( !astOK ) return; + +/* Note the original value */ + orig = astGetObsLon( this ); + +/* Invoke the parent method to clear the Frame ObsLon. */ + (*parent_clearobslon)( this, status ); + +/* If the longitude has changed significantly, indicate that the LAST value + will need to be re-calculated when it is next needed. */ + if( fabs( orig - astGetObsLon( this ) ) > 1.0E-8 ) { + ( (AstSkyFrame *) this )->last = AST__BAD; + ( (AstSkyFrame *) this )->eplast = AST__BAD; + ( (AstSkyFrame *) this )->klast = AST__BAD; + } +} + +static void ClearSystem( AstFrame *this_frame, int *status ) { +/* +* Name: +* ClearSystem + +* Purpose: +* Clear the System attribute for a SkyFrame. + +* Type: +* Private function. + +* Synopsis: +* #include "skyframe.h" +* void ClearSystem( AstFrame *this_frame, int *status ) + +* Class Membership: +* SkyFrame member function (over-rides the astClearSystem protected +* method inherited from the Frame class). + +* Description: +* This function clears the System attribute for a SkyFrame. + +* Parameters: +* this +* Pointer to the SkyFrame. +* status +* Pointer to the inherited status variable. + +*/ + +/* Local Variables: */ + AstFrameSet *fs; /* FrameSet to be used as the Mapping */ + AstSkyFrame *sfrm; /* Copy of original SkyFrame */ + AstSkyFrame *this; /* Pointer to SkyFrame structure */ + double xin[ 2 ]; /* Axis 0 values */ + double yin[ 2 ]; /* Axis 1 values */ + double xout[ 2 ]; /* Axis 0 values */ + double yout[ 2 ]; /* Axis 1 values */ + int skyref_set; /* Is either SkyRef attribute set? */ + int skyrefp_set; /* Is either SkyRefP attribute set? */ + +/* Check the global error status. */ + if ( !astOK ) return; + +/* Obtain a pointer to the SkyFrame structure. */ + this = (AstSkyFrame *) this_frame; + +/* See if either the SkyRef or SkyRefP attribute is set. */ + skyref_set = astTestSkyRef( this, 0 ) || astTestSkyRef( this, 1 ); + skyrefp_set = astTestSkyRefP( this, 0 ) || astTestSkyRefP( this, 1 ); + +/* If so, we will need to transform their values into the new coordinate + system. Save a copy of the SkyFrame with its original System value. */ + sfrm = ( skyref_set || skyrefp_set )?astCopy( this ):NULL; + +/* Use the parent method to clear the System value. */ + (*parent_clearsystem)( this_frame, status ); + +/* Now modify the SkyRef and SkyRefP attributes if necessary. */ + if( sfrm ) { + +/* Save the SkyRef and SkyRefP values. */ + xin[ 0 ] = astGetSkyRef( sfrm, 0 ); + xin[ 1 ] = astGetSkyRefP( sfrm, 0 ); + yin[ 0 ] = astGetSkyRef( sfrm, 1 ); + yin[ 1 ] = astGetSkyRefP( sfrm, 1 ); + +/* Clear the SkyRef values to avoid infinite recursion in the following + call to astConvert. */ + if( skyref_set ) { + astClearSkyRef( sfrm, 0 ); + astClearSkyRef( sfrm, 1 ); + astClearSkyRef( this, 0 ); + astClearSkyRef( this, 1 ); + } + +/* Get the Mapping from the original System to the default System. Invoking + astConvert will recursively invoke ClearSystem again. This is why we need + to be careful to ensure that SkyRef is cleared above - doing so ensure + we do not end up with infinite recursion. */ + fs = astConvert( sfrm, this, "" ); + +/* Check the Mapping was found. */ + if( fs ) { + +/* Use the Mapping to find the SkyRef and SkyRefP positions in the default + coordinate system. */ + astTran2( fs, 2, xin, yin, 1, xout, yout ); + +/* Store the values as required. */ + if( skyref_set ) { + astSetSkyRef( this, 0, xout[ 0 ] ); + astSetSkyRef( this, 1, yout[ 0 ] ); + } + + if( skyrefp_set ) { + astSetSkyRefP( this, 0, xout[ 1 ] ); + astSetSkyRefP( this, 1, yout[ 1 ] ); + } + +/* Free resources. */ + fs = astAnnul( fs ); + +/* If the Mapping is not defined, we cannot convert the SkyRef or SkyRefP + positions in the new Frame so clear them. */ + } else { + if( skyref_set ) { + astClearSkyRef( this, 0 ); + astClearSkyRef( this, 1 ); + } + if( skyrefp_set ) { + astClearSkyRefP( this, 0 ); + astClearSkyRefP( this, 1 ); + } + } + +/* Free resources. */ + sfrm = astAnnul( sfrm ); + } +} + +static double Distance( AstFrame *this_frame, + const double point1[], const double point2[], int *status ) { +/* +* Name: +* Distance + +* Purpose: +* Calculate the distance between two points. + +* Type: +* Private function. + +* Synopsis: +* #include "skyframe.h" +* double Distance( AstFrame *this, +* const double point1[], const double point2[], int *status ) + +* Class Membership: +* SkyFrame member function (over-rides the astDistance method +* inherited from the Frame class). + +* Description: +* This function finds the distance between two points whose +* SkyFrame coordinates are given. The distance calculated is that +* along the geodesic curve (i.e. great circle) that joins the two +* points. + +* Parameters: +* this +* Pointer to the SkyFrame. +* point1 +* An array of double, with one element for each SkyFrame axis, +* containing the coordinates of the first point. +* point2 +* An array of double, with one element for each SkyFrame axis, +* containing the coordinates of the second point. +* status +* Pointer to the inherited status variable. + +* Returned Value: +* The distance between the two points, in radians. + +* Notes: +* - This function will return a "bad" result value (AST__BAD) if +* any of the input coordinates has this value. +* - A "bad" value will also be returned if this function is +* invoked with the AST error status set or if it should fail for +* any reason. +*/ + +/* Local Variables: */ + AstSkyFrame *this; /* Pointer to SkyFrame structure */ + const int *perm; /* Axis permutation array */ + double p1[ 2 ]; /* Permuted point1 coordinates */ + double p2[ 2 ]; /* Permuted point2 coordinates */ + double result; /* Value to return */ + +/* Initialise. */ + result = AST__BAD; + +/* Check the global error status. */ + if ( !astOK ) return result; + +/* Obtain a pointer to the SkyFrame structure. */ + this = (AstSkyFrame *) this_frame; + +/* Obtain a pointer to the SkyFrame's axis permutation array. */ + perm = astGetPerm( this ); + if ( astOK ) { + +/* Check that all supplied coordinates are OK. */ + if ( ( point1[ 0 ] != AST__BAD ) && ( point1[ 1 ] != AST__BAD ) && + ( point2[ 0 ] != AST__BAD ) && ( point2[ 1 ] != AST__BAD ) ) { + +/* Apply the axis permutation array to obtain the coordinates of the + two points in the required (longitude,latitude) order. */ + p1[ perm[ 0 ] ] = point1[ 0 ]; + p1[ perm[ 1 ] ] = point1[ 1 ]; + p2[ perm[ 0 ] ] = point2[ 0 ]; + p2[ perm[ 1 ] ] = point2[ 1 ]; + +/* Calculate the great circle distance between the points in radians. */ + result = palDsep( p1[ 0 ], p1[ 1 ], p2[ 0 ], p2[ 1 ] ); + } + } + +/* Return the result. */ + return result; +} + +static const char *Format( AstFrame *this_frame, int axis, double value, int *status ) { +/* +* Name: +* Format + +* Purpose: +* Format a coordinate value for a SkyFrame axis. + +* Type: +* Private function. + +* Synopsis: +* #include "skyframe.h" +* const char *Format( AstFrame *this, int axis, double value, int *status ) + +* Class Membership: +* SkyFrame member function (over-rides the astFormat method inherited +* from the Frame class). + +* Description: +* This function returns a pointer to a string containing the formatted +* (character) version of a coordinate value for a SkyFrame axis. The +* formatting applied is that specified by a previous invocation of the +* astSetFormat method. A suitable default format is applied if necessary, +* and this may depend on which sky coordinate system the SkyFrame +* describes. + +* Parameters: +* this +* Pointer to the SkyFrame. +* axis +* The number of the axis (zero-based) for which formatting is to be +* performed. +* value +* The coordinate value to be formatted, in radians. +* status +* Pointer to the inherited status variable. + +* Returned Value: +* A pointer to a null-terminated string containing the formatted value. + +* Notes: +* - A NULL pointer will be returned if this function is invoked with the +* global error status set, or if it should fail for any reason. +*/ + +/* Local Variables: */ + AstSkyFrame *this; /* Pointer to the SkyFrame structure */ + const char *result; /* Pointer value to return */ + int format_set; /* Format attribute set? */ + +/* Check the global error status. */ + if ( !astOK ) return NULL; + +/* Obtain a pointer to the SkyFrame structure. */ + this = (AstSkyFrame *) this_frame; + +/* Validate the axis index. */ + (void) astValidateAxis( this, axis, 1, "astFormat" ); + +/* Determine if a Format value has been set for the axis and set a temporary + value if it has not. Use the GetFormat member function for this class + together with member functions inherited from the parent class (rather than + using the object's methods directly) because if any of these methods have + been over-ridden by a derived class the Format string syntax may no longer + be compatible with this class. */ + format_set = (*parent_testformat)( this_frame, axis, status ); + if ( !format_set ) { + (*parent_setformat)( this_frame, axis, GetFormat( this_frame, axis, status ), status ); + } + +/* Use the Format member function inherited from the parent class to format the + value and return a pointer to the resulting string. */ + result = (*parent_format)( this_frame, axis, value, status ); + +/* If necessary, clear any temporary Format value that was set above. */ + if ( !format_set ) (*parent_clearformat)( this_frame, axis, status ); + +/* If an error occurred, clear the returned value. */ + if ( !astOK ) result = NULL; + +/* Return the result. */ + return result; +} + +static AstPointSet *FrameGrid( AstFrame *this_object, int size, const double *lbnd, + const double *ubnd, int *status ){ +/* +* Name: +* FrameGrid + +* Purpose: +* Return a grid of points covering a rectangular area of a Frame. + +* Type: +* Private function. + +* Synopsis: +* #include "skyframe.h" +* AstPointSet *FrameGrid( AstFrame *this_frame, int size, +* const double *lbnd, const double *ubnd, +* int *status ) + +* Class Membership: +* SkyFrame member function (over-rides the protected astFrameGrid +* method inherited from the Frame class). + +* Description: +* This function returns a PointSet containing positions spread +* approximately evenly throughtout a specified rectangular area of +* the Frame. + +* Parameters: +* this +* Pointer to the Frame. +* size +* The preferred number of points in the returned PointSet. The +* actual number of points in the returned PointSet may be +* different, but an attempt is made to stick reasonably closely to +* the supplied value. +* lbnd +* Pointer to an array holding the lower bound of the rectangular +* area on each Frame axis. The array should have one element for +* each Frame axis. +* ubnd +* Pointer to an array holding the upper bound of the rectangular +* area on each Frame axis. The array should have one element for +* each Frame axis. + +* Returned Value: +* A pointer to a new PointSet holding the grid of points. + +* Notes: +* - A NULL pointer is returned if an error occurs. +*/ + +/* Local Variables: */ + AstPointSet *result; + AstSkyFrame *this; + double **ptr; + double box_area; + double cl; + double dlon; + double hilat; + double hilon; + double inclon; + double lat_size; + double lat; + double lon; + double lolon; + double lon_size; + double lolat; + double totlen; + int ilat; + int ilon; + int imer; + int ip; + int ipar; + int ipmax; + int nmer; + int npar; + +/* Initialise. */ + result = NULL; + +/* Check the global error status. */ + if ( !astOK ) return result; + +/* Obtain a pointer to the SkyFrame structure. */ + this = (AstSkyFrame *) this_object; + +/* Get the zero-based indices of the longitude and latitude axes. */ + ilon = astGetLonAxis( this ); + ilat = 1 - ilon; + +/* The latitude bounds may not be the right way round so check for it. */ + if( lbnd[ ilat ] <= ubnd[ ilat ] ) { + lolat = lbnd[ ilat ]; + hilat = ubnd[ ilat ]; + } else { + lolat = ubnd[ ilat ]; + hilat = lbnd[ ilat ]; + } + +/* Check all bounds are good. Also check the size is positive. */ + lolon = lbnd[ ilon ]; + hilon = ubnd[ ilon ]; + if( size > 0 && lolat != AST__BAD && hilat != AST__BAD && + lolon != AST__BAD && hilon != AST__BAD ) { + +/* Ensure the longitude bounds are in the range 0-2PI. */ + lolon = palDranrm( lolon ); + hilon = palDranrm( hilon ); + +/* If the upper longitude limit is less than the lower limit, add 2.PI */ + if( hilon <= lolon && + ubnd[ ilon ] != lbnd[ ilon ] ) hilon += 2*AST__DPI; + +/* Get the total area of the box in steradians. */ + dlon = hilon - lolon; + box_area = fabs( dlon*( sin( hilat ) - sin( lolat ) ) ); + +/* Get the nominal size of a square grid cell, in radians. */ + lat_size = sqrt( box_area/size ); + +/* How many parallels should we use to cover the box? Ensure we use at + least two. These parallels pass through the centre of the grid cells. */ + npar = (int)( 0.5 + ( hilat - lolat )/lat_size ); + if( npar < 2 ) npar = 2; + +/* Find the actual sample size implied by this number of parallels. */ + lat_size = ( hilat - lolat )/npar; + +/* Find the total arc length of the parallels. */ + totlen = 0.0; + lat = lolat + 0.5*lat_size; + for( ipar = 0; ipar < npar; ipar++ ) { + totlen += dlon*cos( lat ); + lat += lat_size; + } + +/* If we space "size" samples evenly over this total arc-length, what is + the arc-distance between samples? */ + lon_size = totlen/size; + +/* Create a PointSet in which to store the grid. Make it bigger than + necessary in order to leave room for extra samples caused by integer + truncation. */ + ipmax = 2*size; + result = astPointSet( ipmax, 2, " ", status ); + ptr = astGetPoints( result ); + if( astOK ) { + +/* Loop over all the parallels. */ + ip = 0; + lat = lolat + 0.5*lat_size; + for( ipar = 0; ipar < npar; ipar++ ) { + +/* Get the longitude increment between samples on this parallel. */ + cl = cos( lat ); + inclon = ( cl != 0.0 ) ? lon_size/cl : 0.0; + +/* Get the number of longitude samples for this parallel. Reduce it if + it would extend beyond the end of the PointSet. */ + nmer = dlon/inclon; + if( ip + nmer >= ipmax ) nmer = ipmax - ip; + +/* Adjust the longitude increment to take up any slack caused by the + above integer division. */ + inclon = dlon/nmer; + +/* Produce the samples for the current parallel. */ + lon = lolon + 0.5*inclon; + for( imer = 0; imer < nmer; imer++ ) { + ptr[ ilon ][ ip ] = lon; + ptr[ ilat ][ ip ] = lat; + + lon += inclon; + ip++; + } + +/* Get the latitude on the next parallel. */ + lat += lat_size; + } + +/* Truncate the PointSet to exclude unused elements at the end. */ + astSetNpoint( result, ip ); + } + +/* Report error if supplied values were bad. */ + } else if( astOK ) { + if( size < 1 ) { + astError( AST__ATTIN, "astFrameGrid(%s): The supplied grid " + "size (%d) is invalid (programming error).", + status, astGetClass( this ), size ); + } else { + astError( AST__ATTIN, "astFrameGrid(%s): One of more of the " + "supplied bounds is AST__BAD (programming error).", + status, astGetClass( this ) ); + } + } + +/* Annul the returned PointSet if an error has occurred. */ + if( !astOK ) result = astAnnul( result ); + +/* Return the PointSet holding the grid. */ + return result; +} + +static double Gap( AstFrame *this_frame, int axis, double gap, int *ntick, int *status ) { +/* +* Name: +* Gap + +* Purpose: +* Find a "nice" gap for tabulating SkyFrame axis values. + +* Type: +* Private function. + +* Synopsis: +* #include "skyframe.h" +* double Gap( AstFrame *this, int axis, double gap, int *ntick, int *status ) + +* Class Membership: +* SkyFrame member function (over-rides the protected astGap method +* inherited from the Frame class). + +* Description: +* This function returns a gap size which produces a nicely spaced +* series of formatted values for a SkyFrame axis, the returned gap +* size being as close as possible to the supplied target gap +* size. It also returns a convenient number of divisions into +* which the gap can be divided. + +* Parameters: +* this +* Pointer to the SkyFrame. +* axis +* The number of the axis (zero-based) for which a gap is to be found. +* gap +* The target gap size. +* ntick +* Address of an int in which to return a convenient number of +* divisions into which the gap can be divided. +* status +* Pointer to the inherited status variable. + +* Returned Value: +* The nice gap size. + +* Notes: +* - A value of zero is returned if the target gap size is zero. +* - A negative gap size is returned if the supplied gap size is negative. +* - A value of zero will be returned if this function is invoked +* with the global error status set, or if it should fail for any +* reason. +*/ + +/* Local Variables: */ + AstSkyFrame *this; /* Pointer to the SkyFrame structure */ + double result; /* Gap value to return */ + int format_set; /* Format attribute set? */ + +/* Check the global error status. */ + if ( !astOK ) return 0.0; + +/* Obtain a pointer to the SkyFrame structure. */ + this = (AstSkyFrame *) this_frame; + +/* Validate the axis index. */ + (void) astValidateAxis( this, axis, 1, "astGap" ); + +/* Determine if a Format value has been set for the axis and set a + temporary value if it has not. Use the GetFormat member function + for this class together with member functions inherited from the + parent class (rather than using the object's methods directly) + because if any of these methods have been over-ridden by a derived + class the Format string syntax may no longer be compatible with + this class. */ + format_set = (*parent_testformat)( this_frame, axis, status ); + if ( !format_set ) { + (*parent_setformat)( this_frame, axis, GetFormat( this_frame, axis, status ), status ); + } + +/* Use the Gap member function inherited from the parent class to find + the gap size. */ + result = (*parent_gap)( this_frame, axis, gap, ntick, status ); + +/* If necessary, clear any temporary Format value that was set above. */ + if ( !format_set ) (*parent_clearformat)( this_frame, axis, status ); + +/* If an error occurred, clear the returned value. */ + if ( !astOK ) result = 0.0; + +/* Return the result. */ + return result; +} + +static int GetObjSize( AstObject *this_object, int *status ) { +/* +* Name: +* GetObjSize + +* Purpose: +* Return the in-memory size of an Object. + +* Type: +* Private function. + +* Synopsis: +* #include "skyframe.h" +* int GetObjSize( AstObject *this, int *status ) + +* Class Membership: +* SkyFrame member function (over-rides the astGetObjSize protected +* method inherited from the parent class). + +* Description: +* This function returns the in-memory size of the supplied SkyFrame, +* in bytes. + +* Parameters: +* this +* Pointer to the SkyFrame. +* status +* Pointer to the inherited status variable. + +* Returned Value: +* The Object size, in bytes. + +* Notes: +* - A value of zero will be returned if this function is invoked +* with the global status set, or if it should fail for any reason. +*/ + +/* Local Variables: */ + AstSkyFrame *this; /* Pointer to SkyFrame structure */ + int result; /* Result value to return */ + +/* Initialise. */ + result = 0; + +/* Check the global error status. */ + if ( !astOK ) return result; + +/* Obtain a pointers to the SkyFrame structure. */ + this = (AstSkyFrame *) this_object; + +/* Invoke the GetObjSize method inherited from the parent class, and then + add on any components of the class structure defined by thsi class + which are stored in dynamically allocated memory. */ + result = (*parent_getobjsize)( this_object, status ); + result += astTSizeOf( this->projection ); + +/* If an error occurred, clear the result value. */ + if ( !astOK ) result = 0; + +/* Return the result, */ + return result; +} + +static int GetActiveUnit( AstFrame *this_frame, int *status ) { +/* +* Name: +* GetActiveUnit + +* Purpose: +* Obtain the value of the ActiveUnit flag for a SkyFrame. + +* Type: +* Private function. + +* Synopsis: +* #include "skyframe.h" +* int GetActiveUnit( AstFrame *this_frame, int *status ) + +* Class Membership: +* SkyFrame member function (over-rides the astGetActiveUnit protected +* method inherited from the Frame class). + +* Description: +* This function returns the value of the ActiveUnit flag for a +* SkyFrame, which is always 0. + +* Parameters: +* this +* Pointer to the SkyFrame. +* status +* Pointer to the inherited status variable. + +* Returned Value: +* The value to use for the ActiveUnit flag (0). + +*/ + return 0; +} + +static int GetAsTime( AstSkyFrame *this, int axis, int *status ) { +/* +* Name: +* GetAsTime + +* Purpose: +* Obtain the value of the AsTime attribute for a SkyFrame's axis. + +* Type: +* Private function. + +* Synopsis: +* #include "skyframe.h" +* int GetAsTime( AstSkyFrame *this, int axis, int *status ) + +* Class Membership: +* SkyFrame member function. + +* Description: +* This function returns the boolean value of the AsTime attribute for a +* specified axis of a SkyFrame. This value indicates whether axis values +* should be formatted as times (as opposed to angles) by default. + +* Parameters: +* this +* Pointer to the SkyFrame. +* axis +* Index of the axis for which information is required (zero based). +* status +* Pointer to the inherited status variable. + +* Returned Value: +* Zero or one, according to the setting of the AsTime attribute (if no +* value has previously been set, a suitable default is returned). + +* Notes: +* - A value of zero will be returned if this function is invoked with the +* global error status set, or if it should fail for any reason. +*/ + +/* Local Variables: */ + AstAxis *ax; /* Pointer to Axis object */ + int axis_p; /* Permuted axis index */ + int result; /* Result to be returned */ + +/* Check the global error status. */ + if ( !astOK ) return 0; + +/* Initialise. */ + result = 0; + +/* Validate and permute the axis index. */ + axis_p = astValidateAxis( this, axis, 1, "astGetAsTime" ); + +/* Obtain a pointer to the required Axis object. */ + ax = astGetAxis( this, axis ); + +/* Determine if the AsTime attribute has been set for the axis (this can only + be the case if the object is a SkyAxis). If the attribute is set, obtain its + value. */ + if ( astIsASkyAxis( ax ) && astTestAxisAsTime( ax ) ) { + result = astGetAxisAsTime( ax ); + +/* Otherwise, check which (permuted) axis is involved. Only the first + (longitude) axis may be displayed as a time by default. */ + } else if ( axis_p == 0 ) { + +/* Test for those coordinate systems which normally have their longitude axes + displayed as times (basically, those that involve the Earth's equator) and + set the returned value appropriately. */ + result = IsEquatorial( astGetSystem( this ), status ); + } + +/* Annul the Axis object pointer. */ + ax = astAnnul( ax ); + +/* Return the result. */ + return result; +} + +static const char *GetAttrib( AstObject *this_object, const char *attrib, int *status ) { +/* +* Name: +* GetAttrib + +* Purpose: +* Get the value of a specified attribute for a SkyFrame. + +* Type: +* Private function. + +* Synopsis: +* #include "skyframe.h" +* const char *GetAttrib( AstObject *this, const char *attrib, int *status ) + +* Class Membership: +* SkyFrame member function (over-rides the protected astGetAttrib +* method inherited from the Frame class). + +* Description: +* This function returns a pointer to the value of a specified +* attribute for a SkyFrame, formatted as a character string. + +* Parameters: +* this +* Pointer to the SkyFrame. +* attrib +* Pointer to a null-terminated string containing the name of +* the attribute whose value is required. This name should be in +* lower case, with all white space removed. +* status +* Pointer to the inherited status variable. + +* Returned Value: +* - Pointer to a null-terminated string containing the attribute +* value. + +* Notes: +* - This function uses one-based axis numbering so that it is +* suitable for external (public) use. +* - The returned string pointer may point at memory allocated +* within the SkyFrame, or at static memory. The contents of the +* string may be over-written or the pointer may become invalid +* following a further invocation of the same function or any +* modification of the SkyFrame. A copy of the string should +* therefore be made if necessary. +* - A NULL pointer will be returned if this function is invoked +* with the global error status set, or if it should fail for any +* reason. +*/ + +/* Local Variables: */ + astDECLARE_GLOBALS /* Declare the thread specific global data */ + AstSkyFrame *this; /* Pointer to the SkyFrame structure */ + const char *cval; /* Pointer to character attribute value */ + const char *result; /* Pointer value to return */ + double dval; /* Floating point attribute value */ + double equinox; /* Equinox attribute value (as MJD) */ + int as_time; /* AsTime attribute value */ + int axis; /* SkyFrame axis number */ + int ival; /* Integer attribute value */ + int len; /* Length of attrib string */ + int nc; /* No. characters read by astSscanf */ + int neglon; /* Display long. values as [-pi,pi]? */ + +/* Initialise. */ + result = NULL; + +/* Check the global error status. */ + if ( !astOK ) return result; + +/* Get a pointer to the structure holding thread-specific global data. */ + astGET_GLOBALS(this_object); + +/* Obtain a pointer to the SkyFrame structure. */ + this = (AstSkyFrame *) this_object; + +/* Obtain the length of the attrib string. */ + len = strlen( attrib ); + +/* Compare "attrib" with each recognised attribute name in turn, + obtaining the value of the required attribute. If necessary, write + the value into "getattrib_buff" as a null-terminated string in an appropriate + format. Set "result" to point at the result string. */ + +/* AsTime(axis). */ +/* ------------- */ + if ( nc = 0, + ( 1 == astSscanf( attrib, "astime(%d)%n", &axis, &nc ) ) + && ( nc >= len ) ) { + as_time = astGetAsTime( this, axis - 1 ); + if ( astOK ) { + (void) sprintf( getattrib_buff, "%d", as_time ); + result = getattrib_buff; + } + +/* Equinox. */ +/* -------- */ + } else if ( !strcmp( attrib, "equinox" ) ) { + equinox = astGetEquinox( this ); + if ( astOK ) { + +/* Format the Equinox as decimal years. Use a Besselian epoch if it + will be less than 1984.0, otherwise use a Julian epoch. */ + result = astFmtDecimalYr( ( equinox < palEpj2d( 1984.0 ) ) ? + palEpb( equinox ) : palEpj( equinox ), + AST__DBL_DIG ); + } + +/* IsLatAxis(axis) */ +/* --------------- */ + } else if ( nc = 0, + ( 1 == astSscanf( attrib, "islataxis(%d)%n", &axis, &nc ) ) + && ( nc >= len ) ) { + ival = astGetIsLatAxis( this, axis - 1 ); + if ( astOK ) { + (void) sprintf( getattrib_buff, "%d", ival ); + result = getattrib_buff; + } + +/* IsLonAxis(axis) */ +/* --------------- */ + } else if ( nc = 0, + ( 1 == astSscanf( attrib, "islonaxis(%d)%n", &axis, &nc ) ) + && ( nc >= len ) ) { + ival = astGetIsLonAxis( this, axis - 1 ); + if ( astOK ) { + (void) sprintf( getattrib_buff, "%d", ival ); + result = getattrib_buff; + } + +/* LatAxis */ +/* -------- */ + } else if ( !strcmp( attrib, "lataxis" ) ) { + axis = astGetLatAxis( this ); + if ( astOK ) { + (void) sprintf( getattrib_buff, "%d", axis + 1 ); + result = getattrib_buff; + } + +/* LonAxis */ +/* -------- */ + } else if ( !strcmp( attrib, "lonaxis" ) ) { + axis = astGetLonAxis( this ); + if ( astOK ) { + (void) sprintf( getattrib_buff, "%d", axis + 1 ); + result = getattrib_buff; + } + +/* NegLon */ +/* ------ */ + } else if ( !strcmp( attrib, "neglon" ) ) { + neglon = astGetNegLon( this ); + if ( astOK ) { + (void) sprintf( getattrib_buff, "%d", neglon ); + result = getattrib_buff; + } + +/* SkyTol */ +/* ------ */ + } else if ( !strcmp( attrib, "skytol" ) ) { + dval = astGetSkyTol( this ); + if ( astOK ) { + (void) sprintf( getattrib_buff, "%.*g", AST__DBL_DIG, dval ); + result = getattrib_buff; + } + +/* Projection. */ +/* ----------- */ + } else if ( !strcmp( attrib, "projection" ) ) { + result = astGetProjection( this ); + +/* SkyRef. */ +/* ------- */ + } else if ( !strcmp( attrib, "skyref" ) ) { + cval = astFormat( this, 0, astGetSkyRef( this, 0 ) ); + if ( astOK ) { + nc = sprintf( getattrib_buff, "%s, ", cval ); + cval = astFormat( this, 1, astGetSkyRef( this, 1 ) ); + if ( astOK ) { + (void) sprintf( getattrib_buff + nc, "%s", cval ); + result = getattrib_buff; + } + } + +/* SkyRef(axis). */ +/* ------------- */ + } else if ( nc = 0, + ( 1 == astSscanf( attrib, "skyref(%d)%n", &axis, &nc ) ) + && ( nc >= len ) ) { + dval = astGetSkyRef( this, axis - 1 ); + if ( astOK ) { + (void) sprintf( getattrib_buff, "%.*g", AST__DBL_DIG, dval ); + result = getattrib_buff; + } + +/* SkyRefP. */ +/* -------- */ + } else if ( !strcmp( attrib, "skyrefp" ) ) { + cval = astFormat( this, 0, astGetSkyRefP( this, 0 ) ); + if ( astOK ) { + nc = sprintf( getattrib_buff, "%s, ", cval ); + cval = astFormat( this, 1, astGetSkyRefP( this, 1 ) ); + if ( astOK ) { + (void) sprintf( getattrib_buff + nc, "%s", cval ); + result = getattrib_buff; + } + } + +/* SkyRefP(axis). */ +/* -------------- */ + } else if ( nc = 0, + ( 1 == astSscanf( attrib, "skyrefp(%d)%n", &axis, &nc ) ) + && ( nc >= len ) ) { + dval = astGetSkyRefP( this, axis - 1 ); + if ( astOK ) { + (void) sprintf( getattrib_buff, "%.*g", AST__DBL_DIG, dval ); + result = getattrib_buff; + } + +/* SkyRefIs. */ +/* --------- */ + } else if ( !strcmp( attrib, "skyrefis" ) ) { + ival = astGetSkyRefIs( this ); + if ( astOK ) { + if( ival == AST__POLE_REF ){ + result = POLE_STRING; + } else if( ival == AST__IGNORED_REF ){ + result = IGNORED_STRING; + } else { + result = ORIGIN_STRING; + } + } + +/* AlignOffset */ +/* ----------- */ + } else if ( !strcmp( attrib, "alignoffset" ) ) { + ival = astGetAlignOffset( this ); + if ( astOK ) { + (void) sprintf( getattrib_buff, "%d", ival ); + result = getattrib_buff; + } + +/* If the attribute name was not recognised, pass it on to the parent + method for further interpretation. */ + } else { + result = (*parent_getattrib)( this_object, attrib, status ); + } + +/* Return the result. */ + return result; +} + +static int GetDirection( AstFrame *this_frame, int axis, int *status ) { +/* +* Name: +* GetDirection + +* Purpose: +* Obtain the value of the Direction attribute for a SkyFrame axis. + +* Type: +* Private function. + +* Synopsis: +* #include "skyframe.h" +* int GetDirection( AstFrame *this_frame, int axis, int *status ) + +* Class Membership: +* SkyFrame member function (over-rides the astGetDirection method inherited +* from the Frame class). + +* Description: +* This function returns the value of the Direction attribute for a +* specified axis of a SkyFrame. A suitable default value is returned if no +* Direction value has previously been set. + +* Parameters: +* this +* Pointer to the SkyFrame. +* axis +* Axis index (zero-based) identifying the axis for which information is +* required. +* status +* Pointer to the inherited status variable. + +* Returned Value: +* Zero or one, depending on the Direction attribute value. + +* Notes: +* - A value of zero will be returned if this function is invoked with the +* global error status set, or if it should fail for any reason. +*/ + +/* Local Variables: */ + AstSkyFrame *this; /* Pointer to the SkyFrame structure */ + int axis_p; /* Permuted axis index */ + int result; /* Result to be returned */ + +/* Check the global error status. */ + if ( !astOK ) return 0; + +/* Initialise. */ + result = 0; + +/* Obtain a pointer to the SkyFrame structure. */ + this = (AstSkyFrame *) this_frame; + +/* Validate and permute the axis index. */ + axis_p = astValidateAxis( this, axis, 1, "astGetDirection" ); + +/* Check if a value has been set for the axis Direction attribute. If so, + obtain its value. */ + if ( astTestDirection( this, axis ) ) { + result = (*parent_getdirection)( this_frame, axis, status ); + +/* Otherwise, we will generate a default Direction value. Currently all + systems supported by SkyFrame are left handed, so all longitude axes + are reversed and all latitude axes are not reversed. */ + } else if( axis_p == 0 ) { + result = 0; + } else { + result = 1; + } + +/* If an error occurred, clear the result value. */ + if ( !astOK ) result = 0; + +/* Return the result. */ + return result; +} + +static double GetBottom( AstFrame *this_frame, int axis, int *status ) { +/* +* Name: +* GetBottom + +* Purpose: +* Obtain the value of the Bottom attribute for a SkyFrame axis. + +* Type: +* Private function. + +* Synopsis: +* #include "skyframe.h" +* double GetBottom( AstFrame *this_frame, int axis, int *status ) + +* Class Membership: +* SkyFrame member function (over-rides the astGetBottom method inherited +* from the Frame class). + +* Description: +* This function returns the value of the Bottom attribute for a +* specified axis of a SkyFrame. A suitable default value is returned if no +* value has previously been set. + +* Parameters: +* this +* Pointer to the SkyFrame. +* axis +* Axis index (zero-based) identifying the axis for which information is +* required. +* status +* Pointer to the inherited status variable. + +* Returned Value: +* The Bottom value to use. + +* Notes: +* - A value of -DBL_MAX will be returned if this function is invoked +* with the global error status set, or if it should fail for any reason. +*/ + +/* Local Variables: */ + AstSkyFrame *this; /* Pointer to the SkyFrame structure */ + int axis_p; /* Permuted axis index */ + double result; /* Result to be returned */ + +/* Check the global error status. */ + if ( !astOK ) return -DBL_MAX; + +/* Initialise. */ + result = -DBL_MAX; + +/* Obtain a pointer to the SkyFrame structure. */ + this = (AstSkyFrame *) this_frame; + +/* Validate and permute the axis index. */ + axis_p = astValidateAxis( this, axis, 1, "astGetBottom" ); + +/* Check if a value has been set for the axis Bottom attribute. If so, + obtain its value. */ + if ( astTestBottom( this, axis ) ) { + result = (*parent_getbottom)( this_frame, axis, status ); + +/* Otherwise, we will return a default Bottom value appropriate to the + SkyFrame class. */ + } else { + +/* If it is a latitude axis return -pi/2. */ + if( axis_p == 1 ) { + result = -piby2; + +/* If it is a longitude value return -DBL_MAX (i.e. no lower limit). */ + } else { + result = -DBL_MAX; + } + } + +/* If an error occurred, clear the result value. */ + if ( !astOK ) result = -DBL_MAX; + +/* Return the result. */ + return result; +} + +static double GetCachedLAST( AstSkyFrame *this, double epoch, double obslon, + double obslat, double obsalt, double dut1, + double dtai, int *status ) { +/* +* Name: +* GetCachedLAST + +* Purpose: +* Attempt to get a LAST value from the cache in the SkyFrame vtab. + +* Type: +* Private function. + +* Synopsis: +* #include "skyframe.h" +* double GetCachedLAST( AstSkyFrame *this, double epoch, double obslon, +* double obslat, double obsalt, double dut1, +* double dtai, int *status ) + +* Class Membership: +* SkyFrame member function. + +* Description: +* This function searches the static cache of LAST values held in the +* SkyFrame virtual function table for a value that corresponds to the +* supplied parameter values. If one is found, it is returned. +* Otherwise AST__BAD is found. + +* Parameters: +* this +* Pointer to the SkyFrame. +* epoch +* The epoch (MJD). +* obslon +* Observatory geodetic longitude (radians) +* obslat +* Observatory geodetic latitude (radians) +* obsalt +* Observatory geodetic altitude (metres) +* dut1 +* The UT1-UTC correction, in seconds. +* dtai +* The TAI-UTC correction, in seconds. +* status +* Pointer to the inherited status variable. + +* Returned Value: +* The Local Apparent Sidereal Time, in radians. + +* Notes: +* - A value of AST__BAD will be returned if this function is invoked +* with the global error status set, or if it should fail for any reason. +*/ + +/* Local Variables: */ + astDECLARE_GLOBALS + AstSkyLastTable *table; + double *ep; + double *lp; + double dep; + double result; + int ihi; + int ilo; + int itable; + int itest; + +/* Get a pointer to the structure holding thread-specific global data. */ + astGET_GLOBALS(this); + +/* Initialise */ + result = AST__BAD; + +/* Check the global error status. */ + if ( !astOK ) return result; + +/* Wait until the table is not being written to by any thread. This also + prevents a thread from writing to the table whilst we are reading it. */ + LOCK_RLOCK1 + +/* Loop round every LAST table held in the vtab. Each table refers to a + different observatory position and/or DUT1 and/or DTAI value. */ + for( itable = 0; itable < nlast_tables; itable++ ) { + table = last_tables[ itable ]; + +/* See if the table refers to the given position, dut1 and dtai value, allowing + some small tolerance. */ + if( fabs( table->obslat - obslat ) < 2.0E-7 && + fabs( table->obslon - obslon ) < 2.0E-7 && + fabs( table->obsalt - obsalt ) < 1.0 && + fabs( table->dut1 - dut1 ) < 1.0E-5 && + EQUAL( table->dtai, dtai, 1.0E-5 ) ) { + +/* Get pointers to the array of epoch and corresponding LAST values in + the table. */ + ep = table->epoch; + lp = table->last; + +/* The values in the epoch array are monotonic increasing. Do a binary chop + within the table's epoch array to find the earliest entry that has a + value equal to or greater than the supplied epoch value. */ + ilo = 0; + ihi = table->nentry - 1; + while( ihi > ilo ) { + itest = ( ilo + ihi )/2; + if( ep[ itest ] >= epoch ) { + ihi = itest; + } else { + ilo = itest + 1; + } + } + +/* Get the difference between the epoch at the entry selected above and + the requested epoch. */ + dep = ep[ ilo ] - epoch; + +/* If the entry selected above is the first entry in the table, it can + only be used if it is within 0.001 second of the requested epoch. */ + if( ilo == 0 ) { + if( fabs( dep ) < 0.001/86400.0 ) { + result = lp[ 0 ]; + } + +/* If the list of epoch values contained no value that was greater than + the supplied epoch value, then we can use the last entry if + it is no more than 0.001 second away from the requested epoch. */ + } else if( dep <= 0.0 ) { + if( fabs( dep ) < 0.001/86400.0 ) { + result = lp[ ilo ]; + } + + +/* Otherwise, see if the entry selected above is sufficiently close to + its lower neighbour (i.e. closer than 0.4 days) to allow a reasonably + accurate LAST value to be determined by interpolation. */ + } else if( ep[ ilo ] - ep[ ilo - 1 ] < 0.4 ) { + ep += ilo - 1; + lp += ilo - 1; + result = *lp + ( epoch - *ep )*( lp[ 1 ] - *lp )/( ep[ 1 ] - *ep ); + +/* If the neighbouring point is too far away for interpolation to be + reliable, then we can only use the point if it is within 0.001 seconds of + the requested epoch. */ + } else if( fabs( dep ) < 0.001/86400.0 ) { + result = lp[ ilo ]; + } + +/* If we have found the right table, we do not need to look at any other + tables, so leave the table loop. */ + break; + } + } + +/* Indicate that threads may now write to the table. */ + UNLOCK_RWLOCK1 + +/* Ensure the returned value is within the range 0 - 2.PI. */ + if( result != AST__BAD ) { + while( result > 2*AST__DPI ) result -= 2*AST__DPI; + while( result < 0.0 ) result += 2*AST__DPI; + } + +/* Return the required LAST value. */ + return result; +} + +static double GetEpoch( AstFrame *this_frame, int *status ) { +/* +* Name: +* GetEpoch + +* Purpose: +* Obtain the value of the Epoch attribute for a SkyFrame axis. + +* Type: +* Private function. + +* Synopsis: +* #include "skyframe.h" +* double GetEpoch( AstFrame *this_frame, int *status ) + +* Class Membership: +* SkyFrame member function (over-rides the astGetEpoch method inherited +* from the Frame class). + +* Description: +* This function returns the value of the Epoch attribute for a +* SkyFrame. A suitable default value is returned if no value has +* previously been set. + +* Parameters: +* this +* Pointer to the SkyFrame. +* status +* Pointer to the inherited status variable. + +* Returned Value: +* The Epoch value to use. + +* Notes: +* - A value of AST__BAD will be returned if this function is invoked +* with the global error status set, or if it should fail for any reason. +*/ + +/* Local Variables: */ + AstSkyFrame *this; /* Pointer to the SkyFrame structure */ + AstSystemType system; /* System attribute */ + double result; /* Result to be returned */ + +/* Check the global error status. */ + if ( !astOK ) return AST__BAD; + +/* Initialise. */ + result = AST__BAD; + +/* Obtain a pointer to the SkyFrame structure. */ + this = (AstSkyFrame *) this_frame; + +/* Check if a value has been set for the Epoch attribute. If so, obtain its + value. */ + if ( astTestEpoch( this ) ) { + result = (*parent_getepoch)( this_frame, status ); + +/* Otherwise, we will return a default Epoch value appropriate to the + SkyFrame class. */ + } else { + +/* Provide a default value of B1950.0 or J2000.0 depending on the System + setting. */ + system = astGetSystem( this ); + if( system == AST__FK4 || system == AST__FK4_NO_E ) { + result = palEpb2d( 1950.0 ); + } else { + result = palEpj2d( 2000.0 ); + } + } + +/* If an error occurred, clear the result value. */ + if ( !astOK ) result = AST__BAD; + +/* Return the result. */ + return result; +} + +static double GetTop( AstFrame *this_frame, int axis, int *status ) { +/* +* Name: +* GetTop + +* Purpose: +* Obtain the value of the Top attribute for a SkyFrame axis. + +* Type: +* Private function. + +* Synopsis: +* #include "skyframe.h" +* double GetTop( AstFrame *this_frame, int axis, int *status ) + +* Class Membership: +* SkyFrame member function (over-rides the astGetTop method inherited +* from the Frame class). + +* Description: +* This function returns the value of the Top attribute for a +* specified axis of a SkyFrame. A suitable default value is returned if no +* value has previously been set. + +* Parameters: +* this +* Pointer to the SkyFrame. +* axis +* Axis index (zero-based) identifying the axis for which information is +* required. +* status +* Pointer to the inherited status variable. + +* Returned Value: +* The Top value to use. + +* Notes: +* - A value of DBL_MAX will be returned if this function is invoked +* with the global error status set, or if it should fail for any reason. +*/ + +/* Local Variables: */ + AstSkyFrame *this; /* Pointer to the SkyFrame structure */ + int axis_p; /* Permuted axis index */ + double result; /* Result to be returned */ + +/* Check the global error status. */ + if ( !astOK ) return DBL_MAX; + +/* Initialise. */ + result = DBL_MAX; + +/* Obtain a pointer to the SkyFrame structure. */ + this = (AstSkyFrame *) this_frame; + +/* Validate and permute the axis index. */ + axis_p = astValidateAxis( this, axis, 1, "astGetTop" ); + +/* Check if a value has been set for the axis Top attribute. If so, + obtain its value. */ + if ( astTestTop( this, axis ) ) { + result = (*parent_gettop)( this_frame, axis, status ); + +/* Otherwise, we will return a default Top value appropriate to the + SkyFrame class. */ + } else { + +/* If this is a latitude axis return pi/2. */ + if( axis_p == 1 ) { + result = piby2; + +/* If it is a longitude value return DBL_MAX (i.e. no upper limit). */ + } else { + result = DBL_MAX; + } + } + +/* If an error occurred, clear the result value. */ + if ( !astOK ) result = DBL_MAX; + +/* Return the result. */ + return result; +} + +static const char *GetDomain( AstFrame *this_frame, int *status ) { +/* +* Name: +* GetDomain + +* Purpose: +* Obtain a pointer to the Domain attribute string for a SkyFrame. + +* Type: +* Private function. + +* Synopsis: +* #include "skyframe.h" +* const char *GetDomain( AstFrame *this, int *status ) + +* Class Membership: +* SkyFrame member function (over-rides the astGetDomain protected +* method inherited from the Frame class). + +* Description: +* This function returns a pointer to the Domain attribute string +* for a SkyFrame. + +* Parameters: +* this +* Pointer to the SkyFrame. +* status +* Pointer to the inherited status variable. + +* Returned Value: +* A pointer to a constant null-terminated string containing the +* Domain value. + +* Notes: +* - The returned pointer or the string it refers to may become +* invalid following further invocation of this function or +* modification of the SkyFrame. +* - A NULL pointer is returned if this function is invoked with +* the global error status set or if it should fail for any reason. +*/ + +/* Local Variables: */ + AstSkyFrame *this; /* Pointer to SkyFrame structure */ + const char *result; /* Pointer value to return */ + +/* Initialise. */ + result = NULL; + +/* Check the global error status. */ + if ( !astOK ) return result; + +/* Obtain a pointer to the SkyFrame structure. */ + this = (AstSkyFrame *) this_frame; + +/* If a Domain attribute string has been set, invoke the parent method + to obtain a pointer to it. */ + if ( astTestDomain( this ) ) { + result = (*parent_getdomain)( this_frame, status ); + +/* Otherwise, provide a pointer to a suitable default string. */ + } else { + result = "SKY"; + } + +/* Return the result. */ + return result; +} + +static const char *GetFormat( AstFrame *this_frame, int axis, int *status ) { +/* +* Name: +* GetFormat + +* Purpose: +* Access the Format string for a SkyFrame axis. + +* Type: +* Private function. + +* Synopsis: +* #include "skyframe.h" +* const char *GetFormat( AstFrame *this, int axis ) + +* Class Membership: +* SkyFrame member function (over-rides the astGetFormat method inherited +* from the Frame class). + +* Description: +* This function returns a pointer to the Format string for a specified axis +* of a SkyFrame. A pointer to a suitable default string is returned if no +* Format value has previously been set. + +* Parameters: +* this +* Pointer to the SkyFrame. +* axis +* Axis index (zero-based) identifying the axis for which information is +* required. + +* Returned Value: +* Pointer to a null-terminated character string containing the requested +* information. + +* Notes: +* - A NULL pointer will be returned if this function is invoked with the +* global error status set, or if it should fail for any reason. +*/ + +/* Local Variables: */ + astDECLARE_GLOBALS /* Declare the thread specific global data */ + AstAxis *ax; /* Pointer to Axis object */ + AstSkyFrame *this; /* Pointer to the SkyFrame structure */ + const char *result; /* Pointer value to return */ + int as_time; /* Value of AsTime attribute */ + int as_time_set; /* AsTime attribute set? */ + int axis_p; /* Permuted axis index */ + int digits; /* Number of digits of precision */ + int is_latitude; /* Value of IsLatitude attribute */ + int is_latitude_set; /* IsLatitude attribute set? */ + int parent; /* Use parent method? */ + int skyaxis; /* Is the Axis a SkyAxis? */ + +/* Check the global error status. */ + if ( !astOK ) return NULL; + +/* Get a pointer to the structure holding thread-specific global data. */ + astGET_GLOBALS(this_frame); + +/* Initialise. */ + result = NULL; + as_time_set = 0; + is_latitude = 0; + is_latitude_set = 0; + +/* Obtain a pointer to the SkyFrame structure. */ + this = (AstSkyFrame *) this_frame; + +/* Validate and permute the axis index. */ + axis_p = astValidateAxis( this, axis, 1, "astGetFormat" ); + +/* Obtain a pointer to the Axis structure. */ + ax = astGetAxis( this, axis ); + +/* Decide whether the parent astGetFormat method is able to provide the format + string we require. We must use the parent method if the Axis is not a + SkyAxis, because the syntax of the Format string would become unsuitable + for use with the Axis astFormat method if it was over-ridden here. We also + use the parent method to return a Format pointer if an explicit Format + string has already been set. */ + skyaxis = astIsASkyAxis( ax ); + parent = ( !skyaxis || (*parent_testformat)( this_frame, axis, status ) ); + +/* If neither of the above conditions apply, we may still be able to use the + parent method if the Axis (actually a SkyAxis) is required to behave as a + normal RA or DEC axis, as this is the standard behaviour provided by the + SkyAxis class. Examine the SkyFrame's System attribute to determine if its + axes should behave in this way. */ + if ( !parent ) parent = IsEquatorial( astGetSystem( this ), status ); + +/* If using the parent method and dealing with a SkyAxis, determine the + settings of any attributes that may affect the Format string. */ + if ( astOK ) { + if ( parent ) { + if ( skyaxis ) { + as_time_set = astTestAsTime( this, axis ); + is_latitude_set = astTestAxisIsLatitude( ax ); + is_latitude = astGetAxisIsLatitude( ax ); + +/* If no AsTime value is set for the axis, set a temporary value as determined + by the astGetAsTime method, which supplies suitable defaults for the axes of + a SkyFrame. */ + if ( !as_time_set ) { + astSetAsTime( this, axis, astGetAsTime( this, axis ) ); + } + +/* Temporarly over-ride the SkyAxis IsLatitude attribute, regardless of its + setting, as the second axis of a SkyFrame is always the latitude axis. */ + astSetAxisIsLatitude( ax, axis_p == 1 ); + } + +/* Invoke the parent method to obtain a pointer to the Format string. */ + result = (*parent_getformat)( this_frame, axis, status ); + +/* Now restore the attributes that were temporarily over-ridden above to their + previous states. */ + if ( skyaxis ) { + if ( !as_time_set ) astClearAsTime( this, axis ); + if ( !is_latitude_set ) { + astClearAxisIsLatitude( ax ); + } else { + astSetAxisIsLatitude( ax, is_latitude ); + } + } + +/* If the parent method is unsuitable, we must construct a new Format string + here. This affects only those coordinate systems whose axes do not behave + like standard RA/DEC axes (e.g. typically ecliptic, galactic and + supergalactic coordinates). For these, we format values as decimal degrees + (or decimal hours if the AsTime attribute is set). Obtain the AsTime + value. */ + } else { + as_time = astGetAsTime( this, axis ); + +/* Determine how many digits of precision to use. This is obtained from the + SkyAxis Digits attribute (if set), otherwise from the Digits attribute of + the enclosing SkyFrame. */ + if ( astTestAxisDigits( ax ) ) { + digits = astGetAxisDigits( ax ); + } else { + digits = astGetDigits( this ); + } + +/* If a time format is required, generate a Format string using decimal + hours. */ + if ( astOK ) { + if ( as_time ) { + if ( digits <= 2 ) { + result = "h"; + } else { + (void) sprintf( getformat_buff, "h.%d", digits - 2 ); + result = getformat_buff; + } + +/* Otherwise use decimal degrees. */ + } else { + if ( digits <= 3 ) { + result = "d"; + } else { + (void) sprintf( getformat_buff, "d.%d", digits - 3 ); + result = getformat_buff; + } + } + } + } + } + +/* Annul the Axis pointer. */ + ax = astAnnul( ax ); + +/* If an error occurred, clear the returned value. */ + if ( !astOK ) result = NULL; + +/* Return the result. */ + return result; +} + +static const char *GetLabel( AstFrame *this, int axis, int *status ) { +/* +* Name: +* GetLabel + +* Purpose: +* Access the Label string for a SkyFrame axis. + +* Type: +* Private function. + +* Synopsis: +* #include "skyframe.h" +* const char *GetLabel( AstFrame *this, int axis, int *status ) + +* Class Membership: +* SkyFrame member function (over-rides the astGetLabel method inherited +* from the Frame class). + +* Description: +* This function returns a pointer to the Label string for a specified axis +* of a SkyFrame. + +* Parameters: +* this +* Pointer to the SkyFrame. +* axis +* Axis index (zero-based) identifying the axis for which information is +* required. +* status +* Pointer to the inherited status variable. + +* Returned Value: +* Pointer to a constant null-terminated character string containing the +* requested information. + +* Notes: +* - A NULL pointer will be returned if this function is invoked with the +* global error status set, or if it should fail for any reason. +*/ + +/* Local Variables: */ + astDECLARE_GLOBALS /* Declare the thread specific global data */ + AstSystemType system; /* Code identifying type of sky coordinates */ + const char *result; /* Pointer to label string */ + int axis_p; /* Permuted axis index */ + +/* Check the global error status. */ + if ( !astOK ) return NULL; + +/* Get a pointer to the structure holding thread-specific global data. */ + astGET_GLOBALS(this); + +/* Initialise. */ + result = NULL; + +/* Validate and permute the axis index. */ + axis_p = astValidateAxis( this, axis, 1, "astGetLabel" ); + +/* Check if a value has been set for the required axis label string. If so, + invoke the parent astGetLabel method to obtain a pointer to it. */ + if ( astTestLabel( this, axis ) ) { + result = (*parent_getlabel)( this, axis, status ); + +/* Otherwise, identify the sky coordinate system described by the SkyFrame. */ + } else { + system = astGetSystem( this ); + +/* If OK, supply a pointer to a suitable default label string. */ + if ( astOK ) { + +/* Equatorial coordinate systems. */ + if ( IsEquatorial( system, status ) ) { + result = ( axis_p == 0 ) ? "Right ascension" : + "Declination"; + +/* Ecliptic coordinates. */ + } else if ( system == AST__ECLIPTIC ) { + result = ( axis_p == 0 ) ? "Ecliptic longitude" : + "Ecliptic latitude"; + +/* Helio-ecliptic coordinates. */ + } else if ( system == AST__HELIOECLIPTIC ) { + result = ( axis_p == 0 ) ? "Helio-ecliptic longitude" : + "Helio-ecliptic latitude"; + +/* AzEl coordinates. */ + } else if ( system == AST__AZEL ) { + result = ( axis_p == 0 ) ? "Azimuth" : + "Elevation"; + +/* Galactic coordinates. */ + } else if ( system == AST__GALACTIC ) { + result = ( axis_p == 0 ) ? "Galactic longitude" : + "Galactic latitude"; + +/* Supergalactic coordinates. */ + } else if ( system == AST__SUPERGALACTIC ) { + result = ( axis_p == 0 ) ? "Supergalactic longitude" : + "Supergalactic latitude"; + +/* Unknown spherical coordinates. */ + } else if ( system == AST__UNKNOWN ) { + result = ( axis_p == 0 ) ? "Longitude" : + "Latitude"; + +/* Report an error if the coordinate system was not recognised. */ + } else { + astError( AST__SCSIN, "astGetLabel(%s): Corrupt %s contains " + "invalid sky coordinate system identification code " + "(%d).", status, astGetClass( this ), astGetClass( this ), + (int) system ); + } + +/* If the SkyRef attribute has a set value, append " offset" to the label. */ + if( astGetSkyRefIs( this ) != AST__IGNORED_REF && + ( astTestSkyRef( this, 0 ) || astTestSkyRef( this, 1 ) ) ) { + sprintf( getlabel_buff, "%s offset", result ); + result = getlabel_buff; + } + } + } + +/* Return the result. */ + return result; +} + +static double GetDiurab( AstSkyFrame *this, int *status ) { +/* +* Name: +* GetDiurab + +* Purpose: +* Return the magnitude of the diurnal aberration vector. + +* Type: +* Private function. + +* Synopsis: +* #include "skyframe.h" +* double GetDiurab( AstSkyFrame *this, int *status ) + +* Class Membership: +* SkyFrame member function + +* Description: +* This function returns the magnitude of the diurnal aberration +* vector. + +* Parameters: +* this +* Pointer to the SkyFrame. +* status +* Pointer to the inherited status variable. + +* Returned Value: +* The magnitude of the diurnal aberration vector. + +*/ + +/* Local Variables: */ + double uau; + double vau; + +/* Check the global error status. */ + if ( !astOK ) return AST__BAD; + +/* If the magnitude of the diurnal aberration vector has not yet been + found, find it now, and cache it in the SkyFrame structure. The cached + value will be reset to AST__BAD if the ObsLat attribute value is + changed. This code is transliterated from SLA_AOPPA. */ + if( this->diurab == AST__BAD ) { + palGeoc( astGetObsLat( this ), astGetObsAlt( this ), &uau, &vau ); + this->diurab = 2*AST__DPI*uau*SOLSID/C; + } + +/* Return the result, */ + return this->diurab; +} + +static double GetLAST( AstSkyFrame *this, int *status ) { +/* +* Name: +* GetLAST + +* Purpose: +* Return the Local Apparent Sidereal Time for the SkyFrame. + +* Type: +* Private function. + +* Synopsis: +* #include "skyframe.h" +* double GetLAST( AstSkyFrame *this, int *status ) + +* Class Membership: +* SkyFrame member function + +* Description: +* This function returns the Local Apparent Sidereal Time (LAST) +* at the moment intime given by the Epoch attribute of the SkyFrame. + +* Parameters: +* this +* Pointer to the SkyFrame. +* status +* Pointer to the inherited status variable. + +* Returned Value: +* The LAST value. + +*/ + +/* Local Variables: */ + double dlast; /* Change in LAST */ + double epoch; /* Epoch (TDB MJD) */ + double last1; /* LAST at end of current interval */ + double result; /* Result value to return */ + double delta_epoch; /* Change in Epoch */ + +/* Initialise. */ + result = 0; + +/* Check the global error status. */ + if ( !astOK ) return result; + +/* The "last" component of the SkyFrame structure holds the accurate + LAST at the moment in time given by the "eplast" (a TDB MJD) component + of the SkyFrame structure. If the current value of the SkyFrame's + Epoch attribute is not much different to "eplast" (within 0.4 of a day), + then the returned LAST value is the "last" value plus the difference + between Epoch and "eplast", converted from solar to sidereal time, + then converted to radians. This approximation seems to be good to less + than a tenth of an arcsecond. If this approximation cannot be used, + invoke SetLast to recalculate the accurate LAST and update the "eplast" + and "last" values. */ + if( this->eplast != AST__BAD ) { + epoch = astGetEpoch( this ); + delta_epoch = epoch - this->eplast; + +/* Return the current LAST value if the epoch has not changed. */ + if( delta_epoch == 0.0 ) { + result = this->last; + +/* If the previous full calculation of LAST was less than 0.4 days ago, + use a linear approximation to LAST. */ + } else if( fabs( delta_epoch ) < 0.4 ) { + +/* If we do not know the ratio of sidereal to solar time at the current + epoch, calculate it now. This involves a full calculation of LAST at + the end of the current linear approximation period. */ + if( this->klast == AST__BAD ) { + last1 = CalcLAST( this, this->eplast + 0.4, astGetObsLon( this ), + astGetObsLat( this ), astGetObsAlt( this ), + astGetDut1( this ), astGetDtai( this ), status ); + +/* Ensure the change in LAST is positive so that we get a positive ratio. */ + dlast = last1 - this->last; + if( dlast < 0.0 ) dlast += 2*AST__DPI; + this->klast = 2*AST__DPI*0.4/dlast; + } + +/* Now use the ratio of solar to sidereal time to calculate the linear + approximation to LAST. */ + result = this->last + 2*AST__DPI*delta_epoch/this->klast; + +/* If the last accurate calculation of LAST was more than 0.4 days ago, + do a full accurate calculation. */ + } else { + SetLast( this, status ); + result = this->last; + } + +/* If we have not yet done an accurate calculation of LAST, do one now. */ + } else { + SetLast( this, status ); + result = this->last; + } + +/* Return the result, */ + return result; +} + +static int GetIsLatAxis( AstSkyFrame *this, int axis, int *status ) { +/* +* Name: +* GetIsLatAxis + +* Purpose: +* Test an axis to see if it is a latitude axis. + +* Type: +* Private function. + +* Synopsis: +* #include "skyframe.h" +* int GetIsLatAxis( AstSkyFrame *this, int axis, int *status ) + +* Class Membership: +* SkyFrame member function. + +* Description: +* This function tests if a SkyFrame axis is a celestial latitude axis. + +* Parameters: +* this +* Pointer to the SkyFrame. +* axis +* Zero based axis index. +* status +* Pointer to the inherited status variable. + +* Returned Value: +* One if the supplied axis is a celestial latitude axis, and zero +* otherwise. + +* Notes: +* - A value of zero will be returned if this function is invoked with the +* global error status set, or if it should fail for any reason. +*/ + +/* Local Variables: */ + int result; /* Result to be returned */ + +/* Check the global error status. */ + if ( !astOK ) return 0; + +/* Get the index of the latitude axis and compare to the supplied axis + index. */ + result = ( axis == astGetLatAxis( this ) ); + +/* Return the result. */ + return astOK ? result : 0; + +} + +static int GetIsLonAxis( AstSkyFrame *this, int axis, int *status ) { +/* +* Name: +* GetIsLonAxis + +* Purpose: +* Test an axis to see if it is a longitude axis. + +* Type: +* Private function. + +* Synopsis: +* #include "skyframe.h" +* int GetIsLonAxis( AstSkyFrame *this, int axis, int *status ) + +* Class Membership: +* SkyFrame member function. + +* Description: +* This function tests if a SkyFrame axis is a celestial longitude axis. + +* Parameters: +* this +* Pointer to the SkyFrame. +* axis +* Zero based axis index. +* status +* Pointer to the inherited status variable. + +* Returned Value: +* One if the supplied axis is a celestial longitude axis, and zero +* otherwise. + +* Notes: +* - A value of zero will be returned if this function is invoked with the +* global error status set, or if it should fail for any reason. +*/ + +/* Local Variables: */ + int result; /* Result to be returned */ + +/* Check the global error status. */ + if ( !astOK ) return 0; + +/* Get the index of the longitude axis and compare to the supplied axis + index. */ + result = ( axis == astGetLonAxis( this ) ); + +/* Return the result. */ + return astOK ? result : 0; + +} + +static int GetLatAxis( AstSkyFrame *this, int *status ) { +/* +* Name: +* GetLatAxis + +* Purpose: +* Obtain the index of the latitude axis of a SkyFrame. + +* Type: +* Private function. + +* Synopsis: +* #include "skyframe.h" +* int GetLatAxis( AstSkyFrame *this, int *status ) + +* Class Membership: +* SkyFrame member function. + +* Description: +* This function returns the zero-based index of the latitude axis of +* a SkyFrame, taking into account any current axis permutation. + +* Parameters: +* this +* Pointer to the SkyFrame. +* status +* Pointer to the inherited status variable. + +* Returned Value: +* The zero based axis index (0 or 1) of the latitude axis. + +* Notes: +* - A value of one will be returned if this function is invoked with the +* global error status set, or if it should fail for any reason. +*/ + +/* Local Variables: */ + int result; /* Result to be returned */ + const int *perm; /* Axis permutation array */ + +/* Check the global error status. */ + if ( !astOK ) return 1; + +/* Initialise. */ + result = 1; + +/* Obtain a pointer to the SkyFrame's axis permutation array. */ + perm = astGetPerm( this ); + if ( astOK ) { + +/* Identify the latitude axis. */ + if( perm[ 0 ] == 1 ) { + result = 0; + } else { + result = 1; + } + + } + +/* Return the result. */ + return result; + +} + +static int GetLonAxis( AstSkyFrame *this, int *status ) { +/* +* Name: +* GetLonAxis + +* Purpose: +* Obtain the index of the longitude axis of a SkyFrame. + +* Type: +* Private function. + +* Synopsis: +* #include "skyframe.h" +* int GetLonAxis( AstSkyFrame *this, int *status ) + +* Class Membership: +* SkyFrame member function. + +* Description: +* This function returns the zero-based index of the longitude axis of +* a SkyFrame, taking into account any current axis permutation. + +* Parameters: +* this +* Pointer to the SkyFrame. +* status +* Pointer to the inherited status variable. + +* Returned Value: +* The zero based axis index (0 or 1) of the longitude axis. + +* Notes: +* - A value of zero will be returned if this function is invoked with the +* global error status set, or if it should fail for any reason. +*/ + +/* Local Variables: */ + int result; /* Result to be returned */ + const int *perm; /* Axis permutation array */ + +/* Check the global error status. */ + if ( !astOK ) return 0; + +/* Initialise. */ + result = 0; + +/* Obtain a pointer to the SkyFrame's axis permutation array. */ + perm = astGetPerm( this ); + if ( astOK ) { + +/* Identify the longitude axis. */ + if( perm[ 0 ] == 0 ) { + result = 0; + } else { + result = 1; + } + + } + +/* Return the result. */ + return result; + +} + +static double GetSkyRefP( AstSkyFrame *this, int axis, int *status ) { +/* +* Name: +* GetSkyRefP + +* Purpose: +* Obtain the value of the SkyRefP attribute for a SkyFrame axis. + +* Type: +* Private function. + +* Synopsis: +* #include "skyframe.h" +* double GetSkyRefP( AstSkyFrame *this, int axis, int *status ) + +* Class Membership: +* SkyFrame member function. + +* Description: +* This function returns the value of the SkyRefP attribute for a +* SkyFrame axis, providing suitable defaults. + +* Parameters: +* this +* Pointer to the SkyFrame. +* axis +* Axis index (zero-based) identifying the axis for which information is +* required. +* status +* Pointer to the inherited status variable. + +* Returned Value: +* The SkyRefP value to be used. + +* Notes: +* - A value of zero will be returned if this function is invoked with the +* global error status set, or if it should fail for any reason. +*/ + +/* Local Variables: */ + double result; /* Returned value */ + int axis_p; /* Permuted axis index */ + +/* Initialise. */ + result = 0.0; + +/* Check the global error status. */ + if ( !astOK ) return result; + +/* Validate and permute the axis index. */ + axis_p = astValidateAxis( this, axis, 1, "astGetSkyRefP" ); + +/* Check if a value has been set for the required axis. If so, return it. */ + if( this->skyrefp[ axis_p ] != AST__BAD ) { + result = this->skyrefp[ axis_p ]; + +/* Otherwise, return the default value */ + } else { + +/* The default longitude value is always zero. */ + if( axis_p == 0 ) { + result= 0.0; + +/* The default latitude value depends on SkyRef. The usual default is the + north pole. The exception to this is if the SkyRef attribute identifies + either the north or the south pole, in which case the origin is used as + the default. Allow some tolerance. */ + } else if( fabs( cos( this->skyref[ 1 ] ) ) > 1.0E-10 ) { + result = pi/2; + + } else { + result = 0.0; + } + } + +/* Return the result. */ + return result; +} + +static const char *GetSymbol( AstFrame *this, int axis, int *status ) { +/* +* Name: +* GetSymbol + +* Purpose: +* Obtain a pointer to the Symbol string for a SkyFrame axis. + +* Type: +* Private function. + +* Synopsis: +* #include "skyframe.h" +* const char *GetSymbol( AstFrame *this, int axis, int *status ) + +* Class Membership: +* SkyFrame member function (over-rides the astGetSymbol method inherited +* from the Frame class). + +* Description: +* This function returns a pointer to the Symbol string for a specified axis +* of a SkyFrame. + +* Parameters: +* this +* Pointer to the SkyFrame. +* axis +* Axis index (zero-based) identifying the axis for which information is +* required. +* status +* Pointer to the inherited status variable. + +* Returned Value: +* Pointer to a constant null-terminated character string containing the +* requested information. + +* Notes: +* - A NULL pointer will be returned if this function is invoked with the +* global error status set, or if it should fail for any reason. +*/ + +/* Local Variables: */ + astDECLARE_GLOBALS /* Declare the thread specific global data */ + AstSystemType system; /* Code identifying type of sky coordinates */ + const char *result; /* Pointer to symbol string */ + int axis_p; /* Permuted axis index */ + +/* Check the global error status. */ + if ( !astOK ) return NULL; + +/* Get a pointer to the structure holding thread-specific global data. */ + astGET_GLOBALS(this); + +/* Initialise. */ + result = NULL; + +/* Validate and permute the axis index. */ + axis_p = astValidateAxis( this, axis, 1, "astGetSymbol" ); + +/* Check if a value has been set for the required axis symbol string. If so, + invoke the parent astGetSymbol method to obtain a pointer to it. */ + if ( astTestSymbol( this, axis ) ) { + result = (*parent_getsymbol)( this, axis, status ); + +/* Otherwise, identify the sky coordinate system described by the SkyFrame. */ + } else { + system = astGetSystem( this ); + +/* If OK, supply a pointer to a suitable default Symbol string. */ + if ( astOK ) { + +/* Equatorial coordinate systems. */ + if ( IsEquatorial( system, status ) ) { + result = ( axis_p == 0 ) ? "RA" : "Dec"; + +/* Ecliptic coordinates. */ + } else if ( system == AST__ECLIPTIC ) { + result = ( axis_p == 0 ) ? "Lambda" : "Beta"; + +/* Helio-ecliptic coordinates. */ + } else if ( system == AST__HELIOECLIPTIC ) { + result = ( axis_p == 0 ) ? "Lambda" : "Beta"; + +/* AzEl coordinates. */ + } else if ( system == AST__AZEL ) { + result = ( axis_p == 0 ) ? "Az" : "El"; + +/* Galactic coordinates. */ + } else if ( system == AST__GALACTIC ) { + result = ( axis_p == 0 ) ? "l" : "b"; + +/* Supergalactic coordinates. */ + } else if ( system == AST__SUPERGALACTIC ) { + result = ( axis_p == 0 ) ? "SGL" : "SGB"; + +/* Unknown spherical coordinates. */ + } else if ( system == AST__UNKNOWN ) { + result = ( axis_p == 0 ) ? "Lon" : "Lat"; + +/* Report an error if the coordinate system was not recognised. */ + } else { + astError( AST__SCSIN, "astGetSymbol(%s): Corrupt %s contains " + "invalid sky coordinate system identification code " + "(%d).", status, astGetClass( this ), astGetClass( this ), + (int) system ); + } + +/* If the SkyRef attribute had a set value, prepend "D" (for "delta") to the + Symbol. */ + if( astGetSkyRefIs( this ) != AST__IGNORED_REF && + ( astTestSkyRef( this, 0 ) || astTestSkyRef( this, 1 ) ) ) { + sprintf( getsymbol_buff, "D%s", result ); + result = getsymbol_buff; + } + } + } + +/* Return the result. */ + return result; +} + +static AstSystemType GetAlignSystem( AstFrame *this_frame, int *status ) { +/* +* Name: +* GetAlignSystem + +* Purpose: +* Obtain the AlignSystem attribute for a SkyFrame. + +* Type: +* Private function. + +* Synopsis: +* #include "skyframe.h" +* AstSystemType GetAlignSystem( AstFrame *this_frame, int *status ) + +* Class Membership: +* SkyFrame member function (over-rides the astGetAlignSystem protected +* method inherited from the Frame class). + +* Description: +* This function returns the AlignSystem attribute for a SkyFrame. + +* Parameters: +* this +* Pointer to the SkyFrame. +* status +* Pointer to the inherited status variable. + +* Returned Value: +* The AlignSystem value. + +*/ + +/* Local Variables: */ + AstSkyFrame *this; /* Pointer to SkyFrame structure */ + AstSystemType result; /* Value to return */ + +/* Initialise. */ + result = AST__BADSYSTEM; + +/* Check the global error status. */ + if ( !astOK ) return result; + +/* Obtain a pointer to the SkyFrame structure. */ + this = (AstSkyFrame *) this_frame; + +/* If a AlignSystem attribute has been set, invoke the parent method to obtain + it. */ + if ( astTestAlignSystem( this ) ) { + result = (*parent_getalignsystem)( this_frame, status ); + +/* Otherwise, provide a suitable default. */ + } else { + result = AST__ICRS; + } + +/* Return the result. */ + return result; +} + +static AstSystemType GetSystem( AstFrame *this_frame, int *status ) { +/* +* Name: +* GetSystem + +* Purpose: +* Obtain the System attribute for a SkyFrame. + +* Type: +* Private function. + +* Synopsis: +* #include "skyframe.h" +* AstSystemType GetSystem( AstFrame *this_frame, int *status ) + +* Class Membership: +* SkyFrame member function (over-rides the astGetSystem protected +* method inherited from the Frame class). + +* Description: +* This function returns the System attribute for a SkyFrame. + +* Parameters: +* this +* Pointer to the SkyFrame. +* status +* Pointer to the inherited status variable. + +* Returned Value: +* The System value. + +* Notes: +* - AST__BADSYSTEM is returned if this function is invoked with +* the global error status set or if it should fail for any reason. +*/ + +/* Local Variables: */ + AstSkyFrame *this; /* Pointer to SkyFrame structure */ + AstSystemType result; /* Value to return */ + +/* Initialise. */ + result = AST__BADSYSTEM; + +/* Check the global error status. */ + if ( !astOK ) return result; + +/* Obtain a pointer to the SkyFrame structure. */ + this = (AstSkyFrame *) this_frame; + +/* If a System attribute has been set, invoke the parent method to obtain + it. */ + if ( astTestSystem( this ) ) { + result = (*parent_getsystem)( this_frame, status ); + +/* Otherwise, provide a suitable default. */ + } else { + result = AST__ICRS; + } + +/* Return the result. */ + return result; +} + +static const char *GetTitle( AstFrame *this_frame, int *status ) { +/* +* Name: +* GetTitle + +* Purpose: +* Obtain a pointer to the Title string for a SkyFrame. + +* Type: +* Private function. + +* Synopsis: +* #include "skyframe.h" +* const char *GetTitle( AstFrame *this_frame, int *status ) + +* Class Membership: +* SkyFrame member function (over-rides the astGetTitle method inherited +* from the Frame class). + +* Description: +* This function returns a pointer to the Title string for a SkyFrame. +* A pointer to a suitable default string is returned if no Title value has +* previously been set. + +* Parameters: +* this +* Pointer to the SkyFrame. +* status +* Pointer to the inherited status variable. + +* Returned Value: +* Pointer to a null-terminated character string containing the requested +* information. + +* Notes: +* - A NULL pointer will be returned if this function is invoked with the +* global error status set, or if it should fail for any reason. +*/ + +/* Local Variables: */ + astDECLARE_GLOBALS /* Declare the thread specific global data */ + AstSkyFrame *this; /* Pointer to SkyFrame structure */ + AstSystemType system; /* Code identifying type of sky coordinates */ + const char *extra; /* Pointer to extra information */ + const char *p; /* Character pointer */ + const char *projection; /* Pointer to sky projection description */ + const char *result; /* Pointer to result string */ + const char *word; /* Pointer to critical word */ + double epoch; /* Value of Epoch attribute */ + double equinox; /* Value of Equinox attribute */ + int lextra; /* Length of extra information */ + int offset; /* Using offset coordinate system? */ + int pos; /* Buffer position to enter text */ + +/* Check the global error status. */ + if ( !astOK ) return NULL; + +/* Get a pointer to the structure holding thread-specific global data. */ + astGET_GLOBALS(this_frame); + +/* Initialise. */ + result = NULL; + pos = 0; + +/* Obtain a pointer to the SkyFrame structure. */ + this = (AstSkyFrame *) this_frame; + +/* See if a Title string has been set. If so, use the parent astGetTitle + method to obtain a pointer to it. */ + if ( astTestTitle( this ) ) { + result = (*parent_gettitle)( this_frame, status ); + +/* Otherwise, we will generate a default Title string. Obtain the values of the + SkyFrame's attributes that determine what this string will be. */ + } else { + epoch = astGetEpoch( this ); + equinox = astGetEquinox( this ); + projection = astGetProjection( this ); + system = astGetSystem( this ); + +/* See if an offset coordinate system is being used.*/ + offset = ( astTestSkyRef( this, 0 ) || astTestSkyRef( this, 1 ) ) + && ( astGetSkyRefIs( this ) != AST__IGNORED_REF ); + +/* Use this to determine if the word "coordinates" or "offsets" should be + used.*/ + word = offset ? "offsets" : "coordinates"; + +/* Classify the coordinate system type and create an appropriate Title + string. (Note that when invoking the astFmtDecimalYr function we must + use a separate sprintf on each occasion so as not to over-write its + internal buffer before the result string has been used.) */ + if ( astOK ) { + result = gettitle_buff; + switch ( system ) { + +/* FK4 equatorial coordinates. */ +/* --------------------------- */ +/* Display the Equinox and Epoch values. */ + case AST__FK4: + pos = sprintf( gettitle_buff, "FK4 equatorial %s", word ); + if( astTestEquinox( this ) || astGetUseDefs( this ) ) { + pos += sprintf( gettitle_buff + pos, "; mean equinox B%s", + astFmtDecimalYr( palEpb( equinox ), 9 ) ); + } + if( astTestEpoch( this ) || astGetUseDefs( this ) ) { + pos += sprintf( gettitle_buff + pos, + "; epoch B%s", astFmtDecimalYr( palEpb( epoch ), 9 ) ); + } + break; + +/* FK4 coordinates with no E-terms of aberration. */ +/* ---------------------------------------------- */ +/* Display the Equinox and Epoch values. */ + case AST__FK4_NO_E: + pos = sprintf( gettitle_buff, "FK4 equatorial %s; no E-terms", word ); + if( astTestEquinox( this ) || astGetUseDefs( this ) ) { + pos += sprintf( gettitle_buff + pos, "; mean equinox B%s", + astFmtDecimalYr( palEpb( equinox ), 9 ) ); + } + if( astTestEpoch( this ) || astGetUseDefs( this ) ) { + pos += sprintf( gettitle_buff + pos, + "; epoch B%s", astFmtDecimalYr( palEpb( epoch ), 9 ) ); + } + break; + +/* FK5 equatorial coordinates. */ +/* --------------------------- */ +/* Display only the Equinox value. */ + case AST__FK5: + pos = sprintf( gettitle_buff, "FK5 equatorial %s", word ); + if( astTestEquinox( this ) || astGetUseDefs( this ) ) { + pos += sprintf( gettitle_buff + pos, "; mean equinox J%s", + astFmtDecimalYr( palEpj( equinox ), 9 ) ); + } + break; + +/* J2000 equatorial coordinates. */ +/* ----------------------------- */ +/* Based on the dynamically determined mean equator and equinox of J2000, + rather than on a model such as FK4 or FK5 */ + case AST__J2000: + pos = sprintf( gettitle_buff, "J2000 equatorial %s", word ); + break; + +/* ICRS coordinates. */ +/* ----------------- */ +/* ICRS is only like RA/Dec by co-incidence, it is not really an + equatorial system by definition. */ + case AST__ICRS: + pos = sprintf( gettitle_buff, "ICRS %s", word ); + break; + +/* AzEl coordinates. */ +/* ----------------- */ + case AST__AZEL: + pos = sprintf( gettitle_buff, "Horizon (Azimuth/Elevation) %s", word ); + break; + +/* Geocentric apparent equatorial coordinates. */ +/* ------------------------------------------ */ +/* Display only the Epoch value. */ + case AST__GAPPT: + pos = sprintf( gettitle_buff, + "Geocentric apparent equatorial %s; " + "; epoch J%s", word, astFmtDecimalYr( palEpj( epoch ), 9 ) ); + break; + +/* Ecliptic coordinates. */ +/* --------------------- */ +/* Display only the Equinox value. */ + case AST__ECLIPTIC: + pos = sprintf( gettitle_buff, "Ecliptic %s", word ); + if( astTestEquinox( this ) || astGetUseDefs( this ) ) { + pos += sprintf( gettitle_buff + pos, "; mean equinox J%s", + astFmtDecimalYr( palEpj( equinox ), 9 ) ); + } + break; + +/* Helio-ecliptic coordinates. */ +/* --------------------------- */ +/* Display only the Epoch value (equinox is fixed). */ + case AST__HELIOECLIPTIC: + pos = sprintf( gettitle_buff, "Helio-ecliptic %s; mean equinox J2000", word ); + if( astTestEpoch( this ) || astGetUseDefs( this ) ) { + pos += sprintf( gettitle_buff + pos, "; epoch J%s", + astFmtDecimalYr( palEpj( epoch ), 9 ) ); + } + break; + +/* Galactic coordinates. */ +/* --------------------- */ +/* Do not display an Equinox or Epoch value. */ + case AST__GALACTIC: + pos = sprintf( gettitle_buff, "IAU (1958) galactic %s", word ); + break; + +/* Supergalactic coordinates. */ +/* -------------------------- */ +/* Do not display an Equinox or Epoch value. */ + case AST__SUPERGALACTIC: + pos = sprintf( gettitle_buff, + "De Vaucouleurs supergalactic %s", word ); + break; + +/* Unknown coordinates. */ +/* -------------------------- */ + case AST__UNKNOWN: + pos = sprintf( gettitle_buff, + "Spherical %s", word ); + break; + +/* Report an error if the coordinate system was not recognised. */ + default: + astError( AST__SCSIN, "astGetTitle(%s): Corrupt %s contains " + "invalid sky coordinate system identification code " + "(%d).", status, astGetClass( this ), astGetClass( this ), + (int) system ); + break; + } + +/* If OK, we add either a description of the sky projection, or (if used) + a description of the origin or pole of the offset coordinate system. + We include only one of these two strings in order to keep the length + of the title down to a reasonable value.*/ + if ( astOK ) { + +/* If the SkyRef attribute has set values, create a description of the offset + coordinate system. */ + if( offset ){ + word = ( astGetSkyRefIs( this ) == AST__POLE_REF )?"pole":"origin"; + lextra = sprintf( gettitle_buff2, "%s at %s ", word, + astFormat( this, 0, astGetSkyRef( this, 0 ) ) ); + lextra += sprintf( gettitle_buff2 + lextra, "%s", + astFormat( this, 1, astGetSkyRef( this, 1 ) ) ); + extra = gettitle_buff2; + +/* Otherwise, get the sky projection description. */ + } else { + extra = projection; + +/* Determine the length of the extra information, after removing trailing + white space. */ + for ( lextra = (int) strlen( extra ); lextra > 0; lextra-- ) { + if ( !isspace( extra[ lextra - 1 ] ) ) break; + } + } + +/* If non-blank extra information is available, append it to the title string, + checking that the end of the buffer is not over-run. */ + if ( lextra ) { + p = "; "; + while ( ( pos < AST__SKYFRAME_GETTITLE_BUFF_LEN ) && *p ) gettitle_buff[ pos++ ] = *p++; + p = extra; + while ( ( pos < AST__SKYFRAME_GETTITLE_BUFF_LEN ) && + ( p < ( extra + lextra ) ) ) gettitle_buff[ pos++ ] = *p++; + if( extra == projection ) { + p = " projection"; + while ( ( pos < AST__SKYFRAME_GETTITLE_BUFF_LEN ) && *p ) gettitle_buff[ pos++ ] = *p++; + } + gettitle_buff[ pos ] = '\0'; + } + } + } + } + +/* If an error occurred, clear the returned pointer value. */ + if ( !astOK ) result = NULL; + +/* Return the result. */ + return result; +} + +static const char *GetUnit( AstFrame *this_frame, int axis, int *status ) { +/* +* Name: +* GetUnit + +* Purpose: +* Obtain a pointer to the Unit string for a SkyFrame's axis. + +* Type: +* Private function. + +* Synopsis: +* #include "skyframe.h" +* const char *GetUnit( AstFrame *this_frame, int axis ) + +* Class Membership: +* SkyFrame member function (over-rides the astGetUnit method inherited +* from the Frame class). + +* Description: +* This function returns a pointer to the Unit string for a specified axis +* of a SkyFrame. If the Unit attribute has not been set for the axis, a +* pointer to a suitable default string is returned instead. This string may +* depend on the value of the Format attribute for the axis and, in turn, on +* the type of sky coordinate system that the SkyFrame describes. + +* Parameters: +* this +* Pointer to the SkyFrame. +* axis +* The number of the axis (zero-based) for which information is required. + +* Returned Value: +* A pointer to a null-terminated string containing the Unit value. + +* Notes: +* - A NULL pointer will be returned if this function is invoked with the +* global error status set, or if it should fail for any reason. +*/ + +/* Local Variables: */ + AstSkyFrame *this; /* Pointer to the SkyFrame structure */ + const char *result; /* Pointer value to return */ + int format_set; /* Format attribute set? */ + +/* Check the global error status. */ + if ( !astOK ) return NULL; + +/* Obtain a pointer to the SkyFrame structure. */ + this = (AstSkyFrame *) this_frame; + +/* Validate the axis index. */ + (void) astValidateAxis( this, axis, 1, "astGetUnit" ); + +/* The Unit value may depend on the value of the Format attribute, so + determine if a Format value has been set for the axis and set a + temporary value if it has not. Use the GetFormat member function + for this class together with member functions inherited from the + parent class (rather than using the object's methods directly) + because if any of these methods have been over-ridden by a derived + class the Format string syntax may no longer be compatible with + this class. */ + format_set = (*parent_testformat)( this_frame, axis, status ); + if ( !format_set ) { + (*parent_setformat)( this_frame, axis, GetFormat( this_frame, axis, status ), status ); + } + +/* Use the parent GetUnit method to return a pointer to the required Unit + string. */ + result = (*parent_getunit)( this_frame, axis, status ); + +/* If necessary, clear any temporary Format value that was set above. */ + if ( !format_set ) (*parent_clearformat)( this_frame, axis, status ); + +/* If an error occurred, clear the returned value. */ + if ( !astOK ) result = NULL; + +/* Return the result. */ + return result; +} + +void astInitSkyFrameVtab_( AstSkyFrameVtab *vtab, const char *name, int *status ) { +/* +*+ +* Name: +* astInitSkyFrameVtab + +* Purpose: +* Initialise a virtual function table for a SkyFrame. + +* Type: +* Protected function. + +* Synopsis: +* #include "skyframe.h" +* void astInitSkyFrameVtab( AstSkyFrameVtab *vtab, const char *name ) + +* Class Membership: +* SkyFrame vtab initialiser. + +* Description: +* This function initialises the component of a virtual function +* table which is used by the SkyFrame class. + +* Parameters: +* vtab +* Pointer to the virtual function table. The components used by +* all ancestral classes will be initialised if they have not already +* been initialised. +* name +* Pointer to a constant null-terminated character string which contains +* the name of the class to which the virtual function table belongs (it +* is this pointer value that will subsequently be returned by the Object +* astClass function). +*- +*/ + +/* Local Variables: */ + astDECLARE_GLOBALS /* Pointer to thread-specific global data */ + AstFrameVtab *frame; /* Pointer to Frame component of Vtab */ + AstObjectVtab *object; /* Pointer to Object component of Vtab */ + int stat; /* SLALIB status */ + +/* Check the local error status. */ + if ( !astOK ) return; + +/* Get a pointer to the thread specific global data structure. */ + astGET_GLOBALS(NULL); + +/* Initialize the component of the virtual function table used by the + parent class. */ + astInitFrameVtab( (AstFrameVtab *) vtab, name ); + +/* Store a unique "magic" value in the virtual function table. This + will be used (by astIsASkyFrame) to determine if an object belongs + to this class. We can conveniently use the address of the (static) + class_check variable to generate this unique value. */ + vtab->id.check = &class_check; + vtab->id.parent = &(((AstFrameVtab *) vtab)->id); + +/* Initialise member function pointers. */ +/* ------------------------------------ */ +/* Store pointers to the member functions (implemented here) that + provide virtual methods for this class. */ + vtab->ClearAsTime = ClearAsTime; + vtab->ClearEquinox = ClearEquinox; + vtab->ClearNegLon = ClearNegLon; + vtab->ClearProjection = ClearProjection; + vtab->GetAsTime = GetAsTime; + vtab->GetEquinox = GetEquinox; + vtab->GetNegLon = GetNegLon; + vtab->GetIsLatAxis = GetIsLatAxis; + vtab->GetIsLonAxis = GetIsLonAxis; + vtab->GetLatAxis = GetLatAxis; + vtab->GetLonAxis = GetLonAxis; + vtab->GetProjection = GetProjection; + vtab->SetAsTime = SetAsTime; + vtab->SetEquinox = SetEquinox; + vtab->SetNegLon = SetNegLon; + vtab->SetProjection = SetProjection; + vtab->SkyOffsetMap = SkyOffsetMap; + vtab->TestAsTime = TestAsTime; + vtab->TestEquinox = TestEquinox; + vtab->TestNegLon = TestNegLon; + vtab->TestProjection = TestProjection; + + vtab->TestSkyTol = TestSkyTol; + vtab->SetSkyTol = SetSkyTol; + vtab->GetSkyTol = GetSkyTol; + vtab->ClearSkyTol = ClearSkyTol; + + vtab->TestSkyRef = TestSkyRef; + vtab->SetSkyRef = SetSkyRef; + vtab->GetSkyRef = GetSkyRef; + vtab->ClearSkyRef = ClearSkyRef; + + vtab->TestSkyRefP = TestSkyRefP; + vtab->SetSkyRefP = SetSkyRefP; + vtab->GetSkyRefP = GetSkyRefP; + vtab->ClearSkyRefP = ClearSkyRefP; + + vtab->TestSkyRefIs = TestSkyRefIs; + vtab->SetSkyRefIs = SetSkyRefIs; + vtab->GetSkyRefIs = GetSkyRefIs; + vtab->ClearSkyRefIs = ClearSkyRefIs; + + vtab->TestAlignOffset = TestAlignOffset; + vtab->SetAlignOffset = SetAlignOffset; + vtab->GetAlignOffset = GetAlignOffset; + vtab->ClearAlignOffset = ClearAlignOffset; + +/* Save the inherited pointers to methods that will be extended, and + replace them with pointers to the new member functions. */ + object = (AstObjectVtab *) vtab; + frame = (AstFrameVtab *) vtab; + parent_getobjsize = object->GetObjSize; + object->GetObjSize = GetObjSize; + + parent_clearattrib = object->ClearAttrib; + object->ClearAttrib = ClearAttrib; + parent_getattrib = object->GetAttrib; + object->GetAttrib = GetAttrib; + parent_setattrib = object->SetAttrib; + object->SetAttrib = SetAttrib; + parent_testattrib = object->TestAttrib; + object->TestAttrib = TestAttrib; + + parent_gettop = frame->GetTop; + frame->GetTop = GetTop; + + parent_setobsalt = frame->SetObsAlt; + frame->SetObsAlt = SetObsAlt; + + parent_setobslat = frame->SetObsLat; + frame->SetObsLat = SetObsLat; + + parent_setobslon = frame->SetObsLon; + frame->SetObsLon = SetObsLon; + + parent_clearobslon = frame->ClearObsLon; + frame->ClearObsLon = ClearObsLon; + + parent_clearobsalt = frame->ClearObsAlt; + frame->ClearObsAlt = ClearObsAlt; + + parent_clearobslat = frame->ClearObsLat; + frame->ClearObsLat = ClearObsLat; + + parent_getbottom = frame->GetBottom; + frame->GetBottom = GetBottom; + + parent_getepoch = frame->GetEpoch; + frame->GetEpoch = GetEpoch; + + parent_format = frame->Format; + frame->Format = Format; + parent_gap = frame->Gap; + frame->Gap = Gap; + parent_getdirection = frame->GetDirection; + frame->GetDirection = GetDirection; + parent_getdomain = frame->GetDomain; + frame->GetDomain = GetDomain; + parent_getsystem = frame->GetSystem; + frame->GetSystem = GetSystem; + parent_setsystem = frame->SetSystem; + frame->SetSystem = SetSystem; + parent_clearsystem = frame->ClearSystem; + frame->ClearSystem = ClearSystem; + parent_getalignsystem = frame->GetAlignSystem; + frame->GetAlignSystem = GetAlignSystem; + parent_getformat = frame->GetFormat; + frame->GetFormat = GetFormat; + parent_getlabel = frame->GetLabel; + frame->GetLabel = GetLabel; + parent_getsymbol = frame->GetSymbol; + frame->GetSymbol = GetSymbol; + parent_gettitle = frame->GetTitle; + frame->GetTitle = GetTitle; + parent_getunit = frame->GetUnit; + frame->GetUnit = GetUnit; + parent_match = frame->Match; + frame->Match = Match; + parent_overlay = frame->Overlay; + frame->Overlay = Overlay; + parent_subframe = frame->SubFrame; + frame->SubFrame = SubFrame; + parent_unformat = frame->Unformat; + frame->Unformat = Unformat; + + parent_setdtai = frame->SetDtai; + frame->SetDtai = SetDtai; + parent_setdut1 = frame->SetDut1; + frame->SetDut1 = SetDut1; + + parent_cleardtai = frame->ClearDtai; + frame->ClearDtai = ClearDtai; + parent_cleardut1 = frame->ClearDut1; + frame->ClearDut1 = ClearDut1; + +/* Store replacement pointers for methods which will be over-ridden by new + member functions implemented here. */ + frame->Angle = Angle; + frame->Distance = Distance; + frame->FrameGrid = FrameGrid; + frame->Intersect = Intersect; + frame->Norm = Norm; + frame->NormBox = NormBox; + frame->Resolve = Resolve; + frame->ResolvePoints = ResolvePoints; + frame->Offset = Offset; + frame->Offset2 = Offset2; + frame->ValidateSystem = ValidateSystem; + frame->SystemString = SystemString; + frame->SystemCode = SystemCode; + frame->LineDef = LineDef; + frame->LineContains = LineContains; + frame->LineCrossing = LineCrossing; + frame->LineOffset = LineOffset; + frame->GetActiveUnit = GetActiveUnit; + frame->TestActiveUnit = TestActiveUnit; + frame->MatchAxesX = MatchAxesX; + +/* Store pointers to inherited methods that will be invoked explicitly + by this class. */ + parent_clearformat = frame->ClearFormat; + parent_setformat = frame->SetFormat; + parent_testformat = frame->TestFormat; + +/* Declare the copy constructor, destructor and class dump + function. */ + astSetCopy( vtab, Copy ); + astSetDelete( vtab, Delete ); + astSetDump( vtab, Dump, "SkyFrame", + "Description of celestial coordinate system" ); + +/* Initialize constants for converting between hours, degrees and + radians, etc.. */ + LOCK_MUTEX2 + palDtf2r( 1, 0, 0.0, &hr2rad, &stat ); + palDaf2r( 1, 0, 0.0, °2rad, &stat ); + palDaf2r( 180, 0, 0.0, &pi, &stat ); + piby2 = 0.5*pi; + UNLOCK_MUTEX2 + +/* If we have just initialised the vtab for the current class, indicate + that the vtab is now initialised, and store a pointer to the class + identifier in the base "object" level of the vtab. */ + if( vtab == &class_vtab ) { + class_init = 1; + astSetVtabClassIdentifier( vtab, &(vtab->id) ); + } +} + +static void 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 ) { +/* +* Name: +* Intersect + +* Purpose: +* Find the point of intersection between two geodesic curves. + +* Type: +* Private function. + +* Synopsis: +* #include "skyframe.h" +* void 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 ) + +* Class Membership: +* SkyFrame member function (over-rides the astIntersect method +* inherited from the Frame class). + +* Description: +* This function finds the coordinate values at the point of +* intersection between two geodesic curves. Each curve is specified +* by two points on the curve. + +* Parameters: +* this +* Pointer to the SkyFrame. +* a1 +* An array of double, with one element for each Frame axis. +* This should contain the coordinates of a point on the first +* geodesic curve. +* a2 +* An array of double, with one element for each Frame axis. +* This should contain the coordinates of a second point on the +* first geodesic curve. +* b1 +* An array of double, with one element for each Frame axis. +* This should contain the coordinates of a point on the second +* geodesic curve. +* b2 +* An array of double, with one element for each Frame axis. +* This should contain the coordinates of a second point on +* the second geodesic curve. +* cross +* An array of double, with one element for each Frame axis +* in which the coordinates of the required intersection +* point will be returned. These will be AST__BAD if the curves do +* not intersect. +* status +* Pointer to the inherited status variable. + +* Notes: +* - The geodesic curve used by this function is the path of +* shortest distance between two points, as defined by the +* astDistance function. +* - This function will return "bad" coordinate values (AST__BAD) +* if any of the input coordinates has this value. +* - For SkyFrames each curve will be a great circle, and in general +* each pair of curves will intersect at two diametrically opposite +* points on the sky. The returned position is the one which is +* closest to point "a1". +*/ + +/* Local Variables: */ + AstSkyFrame *this; /* Pointer to the SkyFrame structure */ + const int *perm; /* Pointer to axis permutation array */ + double aa1[ 2 ]; /* Permuted coordinates for a1 */ + double aa2[ 2 ]; /* Permuted coordinates for a2 */ + double bb1[ 2 ]; /* Permuted coordinates for b1 */ + double bb2[ 2 ]; /* Permuted coordinates for b2 */ + double cc[ 2 ]; /* Permuted coords at intersection */ + double d1; /* Cos(distance from a1 to vp) */ + double d2; /* Cos(distance from a1 to -vp) */ + double na[ 3 ]; /* Normal to the a1/a2 great circle */ + double nb[ 3 ]; /* Normal to the b1/b2 great circle */ + double va1[ 3 ]; /* Vector pointing at a1 */ + double va2[ 3 ]; /* Vector pointing at a2 */ + double vb1[ 3 ]; /* Vector pointing at b1 */ + double vb2[ 3 ]; /* Vector pointing at b2 */ + double vmod; /* Length of "vp" */ + double vp[ 3 ]; /* Vector pointing at the intersection */ + double vpn[ 3 ]; /* Normalised vp */ + int iaxis; /* Axis index */ + +/* Initialise. */ + cross[ 0 ] = AST__BAD; + cross[ 1 ] = AST__BAD; + +/* Check the global error status. */ + if ( !astOK ) return; + +/* Obtain a pointer to the SkyFrame structure. */ + this = (AstSkyFrame *) this_frame; + +/* Check that all supplied values are OK. */ + if ( ( a1[ 0 ] != AST__BAD ) && ( a1[ 1 ] != AST__BAD ) && + ( a2[ 0 ] != AST__BAD ) && ( a2[ 1 ] != AST__BAD ) && + ( b1[ 0 ] != AST__BAD ) && ( b1[ 1 ] != AST__BAD ) && + ( b2[ 0 ] != AST__BAD ) && ( b2[ 1 ] != AST__BAD ) ) { + +/* Obtain a pointer to the SkyFrame's axis permutation array. */ + perm = astGetPerm( this ); + if ( astOK ) { + +/* Apply the axis permutation array to obtain the coordinates of + the points in the required (longitude,latitude) order. */ + for( iaxis = 0; iaxis < 2; iaxis++ ) { + aa1[ perm[ iaxis ] ] = a1[ iaxis ]; + aa2[ perm[ iaxis ] ] = a2[ iaxis ]; + bb1[ perm[ iaxis ] ] = b1[ iaxis ]; + bb2[ perm[ iaxis ] ] = b2[ iaxis ]; + } + +/* Convert each (lon,lat) pair into a unit length 3-vector. */ + palDcs2c( aa1[ 0 ], aa1[ 1 ], va1 ); + palDcs2c( aa2[ 0 ], aa2[ 1 ], va2 ); + palDcs2c( bb1[ 0 ], bb1[ 1 ], vb1 ); + palDcs2c( bb2[ 0 ], bb2[ 1 ], vb2 ); + +/* Find the normal vectors to the two great cicles. */ + palDvxv( va1, va2, na ); + palDvxv( vb1, vb2, nb ); + +/* The cross product of the two normal vectors points to one of the + two diametrically opposite intersections. */ + palDvxv( na, nb, vp ); + +/* Normalise the "vp" vector, also obtaining its original modulus. */ + palDvn( vp, vpn, &vmod ); + if( vmod != 0.0 ) { + +/* We want the intersection which is closest to "a1". The dot product + gives the cos(distance) between two positions. So find the dot + product between "a1" and "vpn", and then between "a1" and the point + diametrically opposite "vpn". */ + d1 = palDvdv( vpn, va1 ); + vpn[ 0 ] = -vpn[ 0 ]; + vpn[ 1 ] = -vpn[ 1 ]; + vpn[ 2 ] = -vpn[ 2 ]; + d2 = palDvdv( vpn, va1 ); + +/* Revert to "vpn" if it is closer to "a1". */ + if( d1 > d2 ) { + vpn[ 0 ] = -vpn[ 0 ]; + vpn[ 1 ] = -vpn[ 1 ]; + vpn[ 2 ] = -vpn[ 2 ]; + } + +/* Convert the vector back into a (lon,lat) pair, and put the longitude + into the range 0 to 2.pi. */ + palDcc2s( vpn, cc, cc + 1 ); + *cc = palDranrm( *cc ); + +/* Permute the result coordinates to undo the effect of the SkyFrame + axis permutation array. */ + cross[ 0 ] = cc[ perm[ 0 ] ]; + cross[ 1 ] = cc[ perm[ 1 ] ]; + } + } + } +} + +static int IsEquatorial( AstSystemType system, int *status ) { +/* +* Name: +* IsEquatorial + +* Purpose: +* Test for an equatorial sky coordinate system. + +* Type: +* Private function. + +* Synopsis: +* #include "skyframe.h" +* int IsEquatorial( AstSystemType system, int *status ) + +* Class Membership: +* SkyFrame member function. + +* Description: +* This function returns a boolean value to indicate if a sky coordinate +* system is equatorial. + +* Parameters: +* system +* Code to identify the sky coordinate system. +* status +* Pointer to the inherited status variable. + +* Returned Value: +* Non-zero if the sky coordinate system is equatorial, otherwise zero. + +* Notes: +* - A value of zero is returned if this function is invoked with the +* global error status set or if it should fail for any reason. +*/ + +/* Local Variables: */ + int result; /* Result value to return */ + +/* Check the global error status. */ + if ( !astOK ) return 0; + +/* Determine if the sky coordinate system is an equatorial one. Note, + ICRS is not equatorial by definition, but is included here because it + is normally treated as an equatorial system in terms of the axis + labels, formats, etc. */ + result = ( ( system == AST__FK4 ) || + ( system == AST__FK4_NO_E ) || + ( system == AST__ICRS ) || + ( system == AST__FK5 ) || + ( system == AST__J2000 ) || + ( system == AST__GAPPT ) ); + +/* Return the result. */ + return result; +} + +static int LineContains( AstFrame *this, AstLineDef *l, int def, double *point, int *status ) { +/* +* Name: +* LineContains + +* Purpose: +* Determine if a line contains a point. + +* Type: +* Private function. + +* Synopsis: +* #include "skyframe.h" +* int LineContains( AstFrame *this, AstLineDef *l, int def, double *point, int *status ) + +* Class Membership: +* SkyFrame member function (over-rides the protected astLineContains +* method inherited from the Frame class). + +* Description: +* This function determines if the supplied point is on the supplied +* line within the supplied Frame. The start point of the line is +* considered to be within the line, but the end point is not. The tests +* are that the point of closest approach of the line to the point should +* be between the start and end, and that the distance from the point to +* the point of closest aproach should be less than 1.0E-7 of the length +* of the line. + +* Parameters: +* this +* Pointer to the Frame. +* l +* Pointer to the structure defining the line. +* def +* Should be set non-zero if the "point" array was created by a +* call to astLineCrossing (in which case it may contain extra +* information following the axis values),and zero otherwise. +* point +* Point to an array containing the axis values of the point to be +* tested, possibly followed by extra cached information (see "def"). +* status +* Pointer to the inherited status variable. + +* Returned Value: +* A non-zero value is returned if the line contains the point. + +* Notes: +* - The pointer supplied for "l" should have been created using the +* astLineDef method. These structures contained cached information about +* the lines which improve the efficiency of this method when many +* repeated calls are made. An error will be reported if the structure +* does not refer to the Frame specified by "this". +* - Zero will be returned if this function is invoked with the global +* error status set, or if it should fail for any reason. +*- +*/ + +/* Local Variables: */ + SkyLineDef *sl; /* SkyLine information */ + const int *perm; /* Pointer to axis permutation array */ + double *b; /* Pointer to Cartesian coords array */ + double bb[3]; /* Buffer for Cartesian coords */ + double p1[2]; /* Buffer for Spherical coords */ + double t1, t2; + int result; /* Returned value */ + +/* Initialise */ + result =0; + +/* Check the global error status. */ + if ( !astOK ) return result; + +/* Check that the line refers to the supplied Frame. */ + if( l->frame != this ) { + astError( AST__INTER, "astLineContains(%s): The supplied line does " + "not relate to the supplied %s (AST internal programming " + "error).", status, astGetClass( this ), astGetClass( this ) ); + +/* Check the axis values are good */ + } else if( point[ 0 ] != AST__BAD && point[ 1 ] != AST__BAD ){ + +/* Get a pointer to an array holding the corresponding Cartesian coords. */ + if( def ) { + b = point + 2; + + } else { + perm = astGetPerm( this ); + if ( perm ) { + p1[ perm[ 0 ] ] = point[ 0 ]; + p1[ perm[ 1 ] ] = point[ 1 ]; + palDcs2c( p1[ 0 ], p1[ 1 ], bb ); + b = bb; + } else { + b = NULL; + } + } + +/* Recast the supplied AstLineDef into a SkyLineDef to get the different + structure (we know from the above check on the Frame that it is safe to + do this). */ + sl = (SkyLineDef *) l; + +/* Check that the point of closest approach of the line to the point is + within the limits of the line. */ + if( LineIncludes( sl, b, status ) ){ + +/* Check that the point is 90 degrees away from the pole of the great + circle containing the line. */ + t1 = palDvdv( sl->q, b ); + t2 = 1.0E-7*sl->length; + if( t2 < 1.0E-10 ) t2 = 1.0E-10; + if( fabs( t1 ) <= t2 ) result = 1; + } + } + +/* Return the result. */ + return result; +} + +static int LineCrossing( AstFrame *this, AstLineDef *l1, AstLineDef *l2, + double **cross, int *status ) { +/* +* Name: +* LineCrossing + +* Purpose: +* Determine if two lines cross. + +* Type: +* Private function. + +* Synopsis: +* #include "skyframe.h" +* int LineCrossing( AstFrame *this, AstLineDef *l1, AstLineDef *l2, +* double **cross, int *status ) + +* Class Membership: +* SkyFrame member function (over-rides the protected astLineCrossing +* method inherited from the Frame class). + +* Description: +* This function determines if the two suplied line segments cross, +* and if so returns the axis values at the point where they cross. +* A flag is also returned indicating if the crossing point occurs +* within the length of both line segments, or outside one or both of +* the line segments. + +* Parameters: +* this +* Pointer to the Frame. +* l1 +* Pointer to the structure defining the first line. +* l2 +* Pointer to the structure defining the second line. +* cross +* Pointer to a location at which to put a pointer to a dynamically +* alocated array containing the axis values at the crossing. If +* NULL is supplied no such array is returned. Otherwise, the returned +* array should be freed using astFree when no longer needed. If the +* lines are parallel (i.e. do not cross) then AST__BAD is returned for +* all axis values. Note usable axis values are returned even if the +* lines cross outside the segment defined by the start and end points +* of the lines. The order of axes in the returned array will take +* account of the current axis permutation array if appropriate. Note, +* sub-classes such as SkyFrame may append extra values to the end +* of the basic frame axis values. A NULL pointer is returned if an +* error occurs. +* status +* Pointer to the inherited status variable. + +* Returned Value: +* A non-zero value is returned if the lines cross at a point which is +* within the [start,end) segment of both lines. If the crossing point +* is outside this segment on either line, or if the lines are parallel, +* zero is returned. Note, the start point is considered to be inside +* the length of the segment, but the end point is outside. + +* Notes: +* - The pointers supplied for "l1" and "l2" should have been created +* using the astLineDef method. These structures contained cached +* information about the lines which improve the efficiency of this method +* when many repeated calls are made. An error will be reported if +* either structure does not refer to the Frame specified by "this". +* - Zero will be returned if this function is invoked with the global +* error status set, or if it should fail for any reason. +*/ + +/* Local Variables: */ + SkyLineDef *sl1; /* SkyLine information for line 1 */ + SkyLineDef *sl2; /* SkyLine information for line 2 */ + const int *perm; /* Pointer to axis permutation array */ + double *crossing; /* Pointer to returned array */ + double *b; /* Pointer to Cartesian coords */ + double len; /* Vector length */ + double p[ 2 ]; /* Temporary (lon,lat) pair */ + double temp[ 3 ]; /* Temporary vector */ + int result; /* Returned value */ + +/* Initialise */ + result = 0; + if( cross ) *cross = NULL; + +/* Check the global error status. */ + if ( !astOK ) return result; + +/* Allocate returned array (2 elements for the lon and lat values, plus 3 + for the corresponding (x,y,z) coords). */ + crossing = astMalloc( sizeof(double)*5 ); + +/* Check that both lines refer to the supplied Frame. */ + if( l1->frame != this ) { + astError( AST__INTER, "astLineCrossing(%s): First supplied line does " + "not relate to the supplied %s (AST internal programming " + "error).", status, astGetClass( this ), astGetClass( this ) ); + + } else if( l2->frame != this ) { + astError( AST__INTER, "astLineCrossing(%s): Second supplied line does " + "not relate to the supplied %s (AST internal programming " + "error).", status, astGetClass( this ), astGetClass( this ) ); + +/* Recast the supplied AstLineDefs into a SkyLineDefs to get the different + structure (we know from the above check on the Frame that it is safe to + do this). */ + } else if( crossing ){ + sl1 = (SkyLineDef *) l1; + sl2 = (SkyLineDef *) l2; + +/* Point of intersection of the two great circles is perpendicular to the + pole vectors of both great circles. Put the Cartesian coords in elements + 2 to 4 of the returned array. */ + palDvxv( sl1->q, sl2->q, temp ); + b = crossing + 2; + palDvn( temp, b, &len ); + +/* See if this point is within the length of both arcs. If so return it. */ + if( LineIncludes( sl2, b, status ) && LineIncludes( sl1, b, status ) ) { + result = 1; + +/* If not, see if the negated b vector is within the length of both arcs. + If so return it. Otherwise, we return zero. */ + } else { + b[ 0 ] *= -1.0; + b[ 1 ] *= -1.0; + b[ 2 ] *= -1.0; + if( LineIncludes( sl2, b, status ) && LineIncludes( sl1, b, status ) ) result = 1; + } + +/* Store the spherical coords in elements 0 and 1 of the returned array. */ + palDcc2s( b, p, p + 1 ); + +/* Permute the spherical axis value into the order used by the SkyFrame. */ + perm = astGetPerm( this ); + if( perm ){ + crossing[ 0 ] = p[ perm[ 0 ] ]; + crossing[ 1 ] = p[ perm[ 1 ] ]; + } + } + +/* If an error occurred, return 0. */ + if( !astOK ) { + result = 0; + crossing = astFree( crossing ); + } + +/* Return the array */ + if( cross ) { + *cross = crossing; + } else { + crossing = astFree( crossing ); + } + +/* Return the result. */ + return result; +} + +static AstLineDef *LineDef( AstFrame *this, const double start[2], + const double end[2], int *status ) { +/* +* Name: +* LineDef + +* Purpose: +* Creates a structure describing a line segment in a 2D Frame. + +* Type: +* Private function. + +* Synopsis: +* #include "skyframe.h" +* AstLineDef *LineDef( AstFrame *this, const double start[2], +* const double end[2], int *status ) + +* Class Membership: +* SkyFrame member function (over-rides the protected astLineDef +* method inherited from the Frame class). + +* Description: +* This function creates a structure containing information describing a +* given line segment within the supplied 2D Frame. This may include +* information which allows other methods such as astLineCrossing to +* function more efficiently. Thus the returned structure acts as a +* cache to store intermediate values used by these other methods. + +* Parameters: +* this +* Pointer to the Frame. Must have 2 axes. +* start +* An array of 2 doubles marking the start of the line segment. +* end +* An array of 2 doubles marking the end of the line segment. +* status +* Pointer to the inherited status variable. + +* Returned Value: +* Pointer to the memory structure containing the description of the +* line. This structure should be freed using astFree when no longer +* needed. A NULL pointer is returned (without error) if any of the +* supplied axis values are AST__BAD. + +* Notes: +* - A null pointer will be returned if this function is invoked +* with the global error status set, or if it should fail for any +* reason. +*/ + +/* Local Variables: */ + SkyLineDef *result; /* Returned value */ + const int *perm; /* Axis permutation array */ + double le; /* Length of end vector */ + double len; /* Permuted point1 coordinates */ + double ls; /* Length of start vector */ + double p1[ 2 ]; /* Permuted point1 coordinates */ + double p2[ 2 ]; /* Permuted point2 coordinates */ + double temp[3]; /* Cartesian coords at offset position */ + +/* Initialise */ + result = NULL; + +/* Check the global error status. */ + if ( !astOK ) return NULL; + +/* Check the axis values are good */ + if( start[ 0 ] != AST__BAD && start[ 1 ] != AST__BAD && + end[ 0 ] != AST__BAD && end[ 1 ] != AST__BAD ) { + +/* Allocate memory for the returned structure. */ + result = astMalloc( sizeof( SkyLineDef ) ); + +/* Obtain a pointer to the SkyFrame's axis permutation array. */ + perm = astGetPerm( this ); + if ( perm ) { + +/* Apply the axis permutation array to obtain the coordinates of the two + input points in the required (longitude,latitude) order. */ + p1[ perm[ 0 ] ] = start[ 0 ]; + p1[ perm[ 1 ] ] = start[ 1 ]; + p2[ perm[ 0 ] ] = end[ 0 ]; + p2[ perm[ 1 ] ] = end[ 1 ]; + +/* Convert each point into a 3-vector of unit length and store in the + returned structure. */ + palDcs2c( p1[ 0 ], p1[ 1 ], result->start ); + palDcs2c( p2[ 0 ], p2[ 1 ], result->end ); + +/* Calculate the great circle distance between the points in radians and + store in the result structure. Correct for rounding errors in palDcs2c + that can result in the vectors not having exactly unit length. */ + result->length = palDvdv( result->start, result->end ); + ls = result->start[0]*result->start[0] + + result->start[1]*result->start[1] + + result->start[2]*result->start[2]; + le = result->end[0]*result->end[0] + + result->end[1]*result->end[1] + + result->end[2]*result->end[2]; + result->length = acos( result->length/sqrt( ls*le ) ); + +/* Find a unit vector representing the pole of the system in which the + equator is given by the great circle. This is such that going the + short way from the start to the end, the pole is to the left of the + line as seen by the observer (i.e. from the centre of the sphere). + If the line has zero length, or 180 degrees length, the pole is + undefined, so we use an arbitrary value. */ + if( result->length == 0.0 || result->length > pi - 5.0E-11 ) { + palDcs2c( p1[ 0 ] + 0.01, p1[ 1 ] + 0.01, temp ); + palDvxv( temp, result->start, result->dir ); + } else { + palDvxv( result->end, result->start, result->dir ); + } + palDvn( result->dir, result->q, &len ); + +/* Also store a point which is 90 degrees along the great circle from the + start. */ + palDvxv( result->start, result->q, result->dir ); + +/* Store a pointer to the defining SkyFrame. */ + result->frame = this; + +/* Indicate that the line is considered to be terminated at the start and + end points. */ + result->infinite = 0; + +/* Normalise the spherical start and end positions stored in the returned + structure. */ + result->start_2d[ 0 ] = start[ 0 ]; + result->start_2d[ 1 ] = start[ 1 ]; + result->end_2d[ 0 ] = end[ 0 ]; + result->end_2d[ 1 ] = end[ 1 ]; + + astNorm( this, result->start_2d ); + astNorm( this, result->end_2d ); + } + } + +/* Free the returned pointer if an error occurred. */ + if( !astOK ) result = astFree( result ); + +/* Return a pointer to the output structure. */ + return (AstLineDef *) result; +} + +static int LineIncludes( SkyLineDef *l, double point[3], int *status ) { +/* +* Name: +* LineIncludes + +* Purpose: +* Determine if a line includes a point which is known to be in the +* great circle. + +* Type: +* Private function. + +* Synopsis: +* #include "skyframe.h" +* int LineIncludes( SkyLineDef *l, double point[3], int *status ) + +* Class Membership: +* SkyFrame member function (over-rides the protected astLineIncludes +* method inherited from the Frame class). + +* Description: +* The supplied point is assumed to be a point on the great circle of +* which the supplied line is a segment. This function returns true if +* "point" is within the bounds of the segment (the end point of the +* line is assumed * not to be part of the segment). + +* Parameters: +* l +* Pointer to the structure defining the line. +* point +* An array holding the Cartesian coords of the point to be tested. +* status +* Pointer to the inherited status variable. + +* Returned Value: +* A non-zero value is returned if the line includes the point. + +* Notes: +* - Zero will be returned if this function is invoked with the global +* error status set, or if it should fail for any reason. +*/ + +/* Local Variables: */ + double t1, t2, t3; + +/* Check the global error status. */ + if ( !astOK ) return 0; + +/* If the line is of infite length, it is assumed to include the supplied + point. */ + if( l->infinite ) return 1; + +/* Otherwise, get the unsigned distance of the point from the start of the + line in the range 0 - 180 degs. Check it is less than the line length. + Then check that the point is not more than 90 degs away from the quarter + point. */ + t1 = palDvdv( l->start, point ); + t2 = acos( t1 ); + t3 = palDvdv( l->dir, point ); + return ( ((l->length > 0) ? t2 < l->length : t2 == 0.0 ) && t3 >= -1.0E-8 ); +} + +static void LineOffset( AstFrame *this, AstLineDef *line, double par, + double prp, double point[2], int *status ){ +/* +* Name: +* LineOffset + +* Purpose: +* Find a position close to a line. + +* Type: +* Private function. + +* Synopsis: +* #include "skyframe.h" +* void LineOffset( AstFrame *this, AstLineDef *line, double par, +* double prp, double point[2], int *status ) + +* Class Membership: +* SkyFrame member function (over-rides the protected astLineOffset +* method inherited from the Frame class). + +* Description: +* This function returns a position formed by moving a given distance along +* the supplied line, and then a given distance away from the supplied line. + +* Parameters: +* this +* Pointer to the Frame. +* line +* Pointer to the structure defining the line. +* par +* The distance to move along the line from the start towards the end. +* prp +* The distance to move at right angles to the line. Positive +* values result in movement to the left of the line, as seen from +* the observer, when moving from start towards the end. +* status +* Pointer to the inherited status variable. + +* Notes: +* - The pointer supplied for "line" should have been created using the +* astLineDef method. This structure contains cached information about the +* line which improves the efficiency of this method when many repeated +* calls are made. An error will be reported if the structure does not +* refer to the Frame specified by "this". +*- +*/ + +/* Local Variables; */ + SkyLineDef *sl; + const int *perm; + double c; + double nx; + double ny; + double nz; + double p[2]; + double s; + double v[3]; + +/* Check the global error status. */ + if ( !astOK ) return; + +/* Check that the line refers to the supplied Frame. */ + if( line->frame != this ) { + astError( AST__INTER, "astLineOffset(%s): The supplied line does " + "not relate to the supplied %s (AST internal programming " + "error).", status, astGetClass( this ), astGetClass( this ) ); + +/* This implementation uses spherical geometry. */ + } else { + +/* Get a pointer to the SkyLineDef structure. */ + sl = (SkyLineDef *) line; + +/* Move a distance par from start to end. */ + c = cos( par ); + s = sin( par ); + nx = c * sl->start[ 0 ] + s * sl->dir[ 0 ]; + ny = c * sl->start[ 1 ] + s * sl->dir[ 1 ]; + nz = c * sl->start[ 2 ] + s * sl->dir[ 2 ]; + +/* Move a distance prp from this point towards the pole point. */ + if( prp != 0.0 ) { + c = cos( prp ); + s = sin( prp ); + v[ 0 ] = c * nx + s * sl->q[ 0 ]; + v[ 1 ] = c * ny + s * sl->q[ 1 ]; + v[ 2 ] = c * nz + s * sl->q[ 2 ]; + } else { + v[ 0 ] = nx; + v[ 1 ] = ny; + v[ 2 ] = nz; + } + +/* Convert to lon/lat */ + palDcc2s( v, p, p + 1 ); + +/* Permute the spherical axis value into the order used by the SkyFrame. */ + perm = astGetPerm( this ); + if( perm ){ + point[ 0 ] = p[ perm[ 0 ] ]; + point[ 1 ] = p[ perm[ 1 ] ]; + } + } +} + +static int MakeSkyMapping( AstSkyFrame *target, AstSkyFrame *result, + AstSystemType align_sys, AstMapping **map, int *status ) { +/* +* Name: +* MakeSkyMapping + +* Purpose: +* Generate a Mapping between two SkyFrames. + +* Type: +* Private function. + +* Synopsis: +* #include "skyframe.h" +* int MakeSkyMapping( AstSkyFrame *target, AstSkyFrame *result, +* AstSystemType align_sys, AstMapping **map, int *status ) + +* Class Membership: +* SkyFrame member function. + +* Description: +* This function takes two SkyFrames and generates a Mapping that +* converts between them, taking account of differences in their +* coordinate systems, equinox value, epoch, etc. (but not allowing +* for any axis permutations). + +* Parameters: +* target +* Pointer to the first SkyFrame. +* result +* Pointer to the second SkyFrame. +* align_sys +* The system in which to align the two SkyFrames. +* map +* Pointer to a location which is to receive a pointer to the +* returned Mapping. The forward transformation of this Mapping +* will convert from "target" coordinates to "result" +* coordinates, and the inverse transformation will convert in +* the opposite direction (all coordinate values in radians). +* status +* Pointer to the inherited status variable. + +* Returned Value: +* Non-zero if the Mapping could be generated, or zero if the two +* SkyFrames are sufficiently un-related that no meaningful Mapping +* can be produced. + +* Notes: +* A value of zero is returned if this function is invoked with the +* global error status set or if it should fail for any reason. +*/ + +/* Local Constants: */ +#define MAX_ARGS 4 /* Max arguments for an SlaMap conversion */ + +/* Local Variables: */ + AstMapping *omap; /* Mapping from coorinates to offsets */ + AstMapping *tmap2; /* Temporary Mapping */ + AstMapping *tmap; /* Temporary Mapping */ + AstSlaMap *slamap; /* Pointer to SlaMap */ + AstSystemType result_system; /* Code to identify result coordinate system */ + AstSystemType system; /* Code to identify coordinate system */ + AstSystemType target_system; /* Code to identify target coordinate system */ + double args[ MAX_ARGS ]; /* Conversion argument array */ + double epoch; /* Epoch as Modified Julian Date */ + double epoch_B; /* Besselian epoch as decimal years */ + double epoch_J; /* Julian epoch as decimal years */ + double equinox; /* Equinox as Modified Julian Date */ + double equinox_B; /* Besselian equinox as decimal years */ + double equinox_J; /* Julian equinox as decimal years */ + double diurab; /* Magnitude of diurnal aberration vector */ + double last; /* Local Apparent Sidereal Time */ + double lat; /* Observers latitude */ + double result_epoch; /* Result frame Epoch */ + double result_equinox; /* Result frame Epoch */ + double target_epoch; /* Target frame Epoch */ + double target_equinox; /* Target frame Epoch */ + int isunit; /* Is the SlaMap effectively a unit mapping? */ + int match; /* Mapping can be generated? */ + int step1; /* Convert target to FK5 J2000? */ + int step2; /* Convert FK5 J2000 to align sys? */ + int step3; /* Convert align sys to FK5 J2000? */ + int step4; /* Convert FK5 J2000 to result? */ + +/* Check the global error status. */ + if ( !astOK ) return 0; + +/* Initialise the returned values. */ + match = 1; + *map = NULL; + +/* Initialise variables to avoid "used of uninitialised variable" + messages from dumb compilers. */ + epoch_B = 0.0; + epoch_J = 0.0; + equinox_B = 0.0; + equinox_J = 0.0; + +/* Get the two epoch values. */ + result_epoch = astGetEpoch( result ); + target_epoch = astGetEpoch( target ); + +/* Get the two equinox values. */ + result_equinox = astGetEquinox( result ); + target_equinox = astGetEquinox( target ); + +/* Get the two system values. */ + result_system = astGetSystem( result ); + target_system = astGetSystem( target ); + +/* If either system is not references to the equinox given by the Equinox + attribute, then use the equinox of the other system rather than + adopting the arbitrary default of J2000. */ + if( !EQREF(result_system) ) result_equinox = target_equinox; + if( !EQREF(target_system) ) target_equinox = result_equinox; + +/* If both systems are unknown, assume they are the same. Return a UnitMap. + We need to do this, otherwise a simple change of Title (for instance) + will result in a FrameSet whose current Frame has System=AST__UNKNOWN + loosing its integrity. */ + if( target_system == AST__UNKNOWN && result_system == AST__UNKNOWN ) { + *map = (AstMapping *) astUnitMap( 2, "", status ); + return 1; + } + +/* The total Mapping is divided into two parts in series; the first part + converts from the target SkyFrame to the alignment system, using the + Epoch and Equinox of the target Frame, the second part converts from + the alignment system to the result SkyFrame, using the Epoch and Equinox + of the result Frame. Each of these parts has an arbitrary input and an + output system, and therefore could be implemented using a collection + of NxN conversions. To reduce the complexity, each part is implement + by converting from the input system to FK5 J2000, and then from FK5 + J2000 to the output system. This scheme required only N conversions + rather than NxN. Thus overall the total Mapping is made up of 4 steps + in series. Some of these steps may be ommitted if they are effectively + a UnitMap. Determine which steps need to be included. Assume all need + to be done to begin with. */ + step1 = 1; + step2 = 1; + step3 = 1; + step4 = 1; + +/* If the target system is the same as the alignment system, neither of the + first 2 steps need be done. */ + if( target_system == align_sys ) { + step1 = 0; + step2 = 0; + } + +/* If the result system is the same as the alignment system, neither of the + last 2 steps need be done. */ + if( result_system == align_sys ) { + step3 = 0; + step4 = 0; + } + +/* If the two epochs are the same, or if the alignment system is FK5 J2000, + steps 2 and 3 are not needed. */ + if( step2 && step3 ) { + if( align_sys == AST__FK5 || result_epoch == target_epoch ) { + step2 = 0; + step3 = 0; + } + } + +/* None are needed if the target and result SkyFrames have the same + System, Epoch and Equinox. */ + if( result_system == target_system && + result_epoch == target_epoch && + result_equinox == target_equinox ) { + step1 = 0; + step2 = 0; + step3 = 0; + step4 = 0; + } + +/* Create an initial (null) SlaMap. */ + slamap = astSlaMap( 0, "", status ); + +/* Define local macros as shorthand for adding sky coordinate + conversions to this SlaMap. Each macro simply stores details of + the additional arguments in the "args" array and then calls + astSlaAdd. The macros differ in the number of additional argument + values. */ + #define TRANSFORM_0(cvt) \ + astSlaAdd( slamap, cvt, 0, NULL ); + + #define TRANSFORM_1(cvt,arg0) \ + args[ 0 ] = arg0; \ + astSlaAdd( slamap, cvt, 1, args ); + + #define TRANSFORM_2(cvt,arg0,arg1) \ + args[ 0 ] = arg0; \ + args[ 1 ] = arg1; \ + astSlaAdd( slamap, cvt, 2, args ); + + #define TRANSFORM_3(cvt,arg0,arg1,arg2) \ + args[ 0 ] = arg0; \ + args[ 1 ] = arg1; \ + args[ 2 ] = arg2; \ + astSlaAdd( slamap, cvt, 3, args ); + + #define TRANSFORM_4(cvt,arg0,arg1,arg2,arg3) \ + args[ 0 ] = arg0; \ + args[ 1 ] = arg1; \ + args[ 2 ] = arg2; \ + args[ 3 ] = arg3; \ + astSlaAdd( slamap, cvt, 4, args ); + +/* Convert _to_ FK5 J2000.0 coordinates. */ +/* ===================================== */ +/* The overall conversion is formulated in four phases. In this first + phase, we convert from the target coordinate system to intermediate sky + coordinates expressed using the FK5 system, mean equinox J2000.0. */ + +/* Obtain the sky coordinate system, equinox, epoch, etc, of the target + SkyFrame. */ + system = target_system; + equinox = target_equinox; + epoch = target_epoch; + last = GetLAST( target, status ); + diurab = GetDiurab( target, status ); + lat = astGetObsLat( target ); + if( astOK && step1 ) { + +/* Convert the equinox and epoch values (stored as Modified Julian + Dates) into the equivalent Besselian and Julian epochs (as decimal + years). */ + equinox_B = palEpb( equinox ); + equinox_J = palEpj( equinox ); + epoch_B = palEpb( epoch ); + epoch_J = palEpj( epoch ); + +/* Formulate the conversion... */ + +/* From FK4. */ +/* --------- */ +/* If necessary, apply the old-style FK4 precession model to bring the + equinox to B1950.0, with rigorous handling of the E-terms of + aberration. Then convert directly to FK5 J2000.0 coordinates. */ + if ( system == AST__FK4 ) { + VerifyMSMAttrs( target, result, 1, "Equinox Epoch", "astMatch", status ); + if ( equinox_B != 1950.0 ) { + TRANSFORM_1( "SUBET", equinox_B ) + TRANSFORM_2( "PREBN", equinox_B, 1950.0 ) + TRANSFORM_1( "ADDET", 1950.0 ) + } + TRANSFORM_1( "FK45Z", epoch_B ) + +/* From FK4 with no E-terms. */ +/* ------------------------- */ +/* This is the same as above, except that we do not need to subtract + the E-terms initially as they are already absent. */ + } else if ( system == AST__FK4_NO_E ) { + VerifyMSMAttrs( target, result, 1, "Equinox Epoch", "astMatch", status ); + if ( equinox_B != 1950.0 ) { + TRANSFORM_2( "PREBN", equinox_B, 1950.0 ) + } + TRANSFORM_1( "ADDET", 1950.0 ) + TRANSFORM_1( "FK45Z", epoch_B ) + +/* From FK5. */ +/* --------- */ +/* We simply need to apply a precession correction for the change of + equinox. Omit even this if the equinox is already J2000.0. */ + } else if ( system == AST__FK5 ) { + VerifyMSMAttrs( target, result, 1, "Equinox", "astMatch", status ); + if ( equinox_J != 2000.0 ) { + TRANSFORM_2( "PREC", equinox_J, 2000.0 ); + } + +/* From J2000. */ +/* ----------- */ +/* Convert from J2000 to ICRS, then from ICRS to FK5. */ + } else if ( system == AST__J2000 ) { + VerifyMSMAttrs( target, result, 1, "Epoch", "astMatch", status ); + TRANSFORM_0( "J2000H" ) + TRANSFORM_1( "HFK5Z", epoch_J ); + +/* From geocentric apparent. */ +/* ------------------------- */ +/* This conversion is supported directly by SLALIB. */ + } else if ( system == AST__GAPPT ) { + VerifyMSMAttrs( target, result, 1, "Epoch", "astMatch", status ); + TRANSFORM_2( "AMP", epoch, 2000.0 ) + +/* From ecliptic coordinates. */ +/* -------------------------- */ +/* This conversion is supported directly by SLALIB. */ + } else if ( system == AST__ECLIPTIC ) { + VerifyMSMAttrs( target, result, 1, "Equinox", "astMatch", status ); + TRANSFORM_1( "ECLEQ", equinox ) + +/* From helio-ecliptic coordinates. */ +/* -------------------------------- */ + } else if ( system == AST__HELIOECLIPTIC ) { + VerifyMSMAttrs( target, result, 1, "Epoch", "astMatch", status ); + TRANSFORM_1( "HEEQ", epoch ) + +/* From galactic coordinates. */ +/* -------------------------- */ +/* This conversion is supported directly by SLALIB. */ + } else if ( system == AST__GALACTIC ) { + TRANSFORM_0( "GALEQ" ) + +/* From ICRS. */ +/* ---------- */ +/* This conversion is supported directly by SLALIB. */ + } else if ( system == AST__ICRS ) { + VerifyMSMAttrs( target, result, 1, "Epoch", "astMatch", status ); + TRANSFORM_1( "HFK5Z", epoch_J ); + +/* From supergalactic coordinates. */ +/* ------------------------------- */ +/* Convert to galactic coordinates and then to FK5 J2000.0 + equatorial. */ + } else if ( system == AST__SUPERGALACTIC ) { + TRANSFORM_0( "SUPGAL" ) + TRANSFORM_0( "GALEQ" ) + +/* From AzEl. */ +/* ---------- */ +/* Rotate from horizon to equator (H2E), shift hour angle into RA (H2R), + go from geocentric apparent to FK5 J2000. */ + } else if ( system == AST__AZEL ) { + VerifyMSMAttrs( target, result, 1, "ObsLon ObsLat Epoch", "astMatch", status ); + TRANSFORM_2( "H2E", lat, diurab ) + TRANSFORM_1( "H2R", last ) + TRANSFORM_2( "AMP", epoch, 2000.0 ) + +/* From unknown coordinates. */ +/* ------------------------- */ +/* No conversion is possible. */ + } else if ( system == AST__UNKNOWN ) { + match = 0; + } + } + +/* Convert _from_ FK5 J2000.0 coordinates _to_ the alignment system. */ +/* ============================================================ */ +/* In this second phase, we convert to the system given by the align_sys + argument (if required), still using the properties of the target Frame. */ + if ( astOK && match && step2 ) { + +/* Align in FK4. */ +/* --------------- */ +/* Convert directly from FK5 J2000.0 to FK4 B1950.0 coordinates at the + appropriate epoch. Then, if necessary, apply the old-style FK4 + precession model to bring the equinox to that required, with + rigorous handling of the E-terms of aberration. */ + if ( align_sys == AST__FK4 ) { + VerifyMSMAttrs( target, result, 1, "Equinox Epoch", "astMatch", status ); + TRANSFORM_1( "FK54Z", epoch_B ) + if ( equinox_B != 1950.0 ) { + TRANSFORM_1( "SUBET", 1950.0 ) + TRANSFORM_2( "PREBN", 1950.0, equinox_B ) + TRANSFORM_1( "ADDET", equinox_B ) + } + +/* Align in FK4 with no E-terms. */ +/* ------------------------------- */ +/* This is the same as above, except that we do not need to add the + E-terms at the end. */ + } else if ( align_sys == AST__FK4_NO_E ) { + VerifyMSMAttrs( target, result, 1, "Equinox Epoch", "astMatch", status ); + TRANSFORM_1( "FK54Z", epoch_B ) + TRANSFORM_1( "SUBET", 1950.0 ) + if ( equinox_B != 1950.0 ) { + TRANSFORM_2( "PREBN", 1950.0, equinox_B ) + } + +/* Align in FK5. */ +/* ------------- */ +/* We simply need to apply a precession correction for the change of + equinox. Omit even this if the required equinox is J2000.0. */ + } else if ( align_sys == AST__FK5 ) { + VerifyMSMAttrs( target, result, 1, "Equinox", "astMatch", status ); + if ( equinox_J != 2000.0 ) { + TRANSFORM_2( "PREC", 2000.0, equinox_J ) + } + +/* Align in J2000. */ +/* --------------- */ +/* Mov from FK5 to ICRS, and from ICRS to J2000. */ + } else if ( align_sys == AST__J2000 ) { + VerifyMSMAttrs( target, result, 1, "Epoch", "astMatch", status ); + TRANSFORM_1( "FK5HZ", epoch_J ) + TRANSFORM_0( "HJ2000" ) + +/* Align in geocentric apparent. */ +/* ------------------------------- */ +/* This conversion is supported directly by SLALIB. */ + } else if ( align_sys == AST__GAPPT ) { + VerifyMSMAttrs( target, result, 1, "Epoch", "astMatch", status ); + TRANSFORM_2( "MAP", 2000.0, epoch ) + +/* Align in ecliptic coordinates. */ +/* -------------------------------- */ +/* This conversion is supported directly by SLALIB. */ + } else if ( align_sys == AST__ECLIPTIC ) { + VerifyMSMAttrs( target, result, 1, "Equinox", "astMatch", status ); + TRANSFORM_1( "EQECL", equinox ) + +/* Align in helio-ecliptic coordinates. */ +/* ------------------------------------ */ + } else if ( align_sys == AST__HELIOECLIPTIC ) { + VerifyMSMAttrs( target, result, 1, "Epoch", "astMatch", status ); + TRANSFORM_1( "EQHE", epoch ) + +/* Align in galactic coordinates. */ +/* -------------------------------- */ +/* This conversion is supported directly by SLALIB. */ + } else if ( align_sys == AST__GALACTIC ) { + TRANSFORM_0( "EQGAL" ) + +/* Align in ICRS. */ +/* -------------- */ +/* This conversion is supported directly by SLALIB. */ + } else if ( align_sys == AST__ICRS ) { + VerifyMSMAttrs( target, result, 1, "Epoch", "astMatch", status ); + TRANSFORM_1( "FK5HZ", epoch_J ) + +/* Align in supergalactic coordinates. */ +/* ------------------------------------- */ +/* Convert to galactic coordinates and then to supergalactic. */ + } else if ( align_sys == AST__SUPERGALACTIC ) { + TRANSFORM_0( "EQGAL" ) + TRANSFORM_0( "GALSUP" ) + +/* Align in AzEl coordinates. */ +/* -------------------------- */ +/* Go from FK5 J2000 to geocentric apparent (MAP), shift RA into hour angle + (R2H), rotate from equator to horizon (E2H). */ + } else if ( align_sys == AST__AZEL ) { + VerifyMSMAttrs( target, result, 1, "ObsLon ObsLat Epoch", "astMatch", status ); + TRANSFORM_2( "MAP", 2000.0, epoch ) + TRANSFORM_1( "R2H", last ) + TRANSFORM_2( "E2H", lat, diurab ) + +/* Align in unknown coordinates. */ +/* ------------------------------- */ +/* No conversion is possible. */ + } else if ( align_sys == AST__UNKNOWN ) { + match = 0; + } + } + +/* Convert _from_ the alignment system _to_ FK5 J2000.0 coordinates */ +/* =========================================================== */ +/* In this third phase, we convert from the alignment system (if required) + to the intermediate FK5 J2000 system, using the properties of the + result SkyFrame. */ + +/* Obtain the sky coordinate system, equinox, epoch, etc, of the result + SkyFrame. */ + system = result_system; + equinox = result_equinox; + epoch = result_epoch; + diurab = GetDiurab( result, status ); + last = GetLAST( result, status ); + lat = astGetObsLat( result ); + +/* Convert the equinox and epoch values (stored as Modified Julian + Dates) into the equivalent Besselian and Julian epochs (as decimal + years). */ + if( astOK ) { + equinox_B = palEpb( equinox ); + equinox_J = palEpj( equinox ); + epoch_B = palEpb( epoch ); + epoch_J = palEpj( epoch ); + } + +/* Check we need to do the conversion. */ + if ( astOK && match && step3 ) { + +/* Formulate the conversion... */ + +/* From FK4. */ +/* --------- */ +/* If necessary, apply the old-style FK4 precession model to bring the + equinox to B1950.0, with rigorous handling of the E-terms of + aberration. Then convert directly to FK5 J2000.0 coordinates. */ + if ( align_sys == AST__FK4 ) { + VerifyMSMAttrs( target, result, 3, "Equinox Epoch", "astMatch", status ); + if ( equinox_B != 1950.0 ) { + TRANSFORM_1( "SUBET", equinox_B ) + TRANSFORM_2( "PREBN", equinox_B, 1950.0 ) + TRANSFORM_1( "ADDET", 1950.0 ) + } + TRANSFORM_1( "FK45Z", epoch_B ) + +/* From FK4 with no E-terms. */ +/* ------------------------- */ +/* This is the same as above, except that we do not need to subtract + the E-terms initially as they are already absent. */ + } else if ( align_sys == AST__FK4_NO_E ) { + VerifyMSMAttrs( target, result, 3, "Equinox Epoch", "astMatch", status ); + if ( equinox_B != 1950.0 ) { + TRANSFORM_2( "PREBN", equinox_B, 1950.0 ) + } + TRANSFORM_1( "ADDET", 1950.0 ) + TRANSFORM_1( "FK45Z", epoch_B ) + +/* From FK5. */ +/* --------- */ +/* We simply need to apply a precession correction for the change of + equinox. Omit even this if the equinox is already J2000.0. */ + } else if ( align_sys == AST__FK5 ) { + VerifyMSMAttrs( target, result, 3, "Equinox", "astMatch", status ); + if ( equinox_J != 2000.0 ) { + TRANSFORM_2( "PREC", equinox_J, 2000.0 ); + } + +/* From geocentric apparent. */ +/* ------------------------- */ +/* This conversion is supported directly by SLALIB. */ + } else if ( align_sys == AST__GAPPT ) { + VerifyMSMAttrs( target, result, 3, "Epoch", "astMatch", status ); + TRANSFORM_2( "AMP", epoch, 2000.0 ) + +/* From ecliptic coordinates. */ +/* -------------------------- */ +/* This conversion is supported directly by SLALIB. */ + } else if ( align_sys == AST__ECLIPTIC ) { + VerifyMSMAttrs( target, result, 3, "Equinox", "astMatch", status ); + TRANSFORM_1( "ECLEQ", equinox ) + +/* From helio-ecliptic coordinates. */ +/* -------------------------------- */ + } else if ( align_sys == AST__HELIOECLIPTIC ) { + VerifyMSMAttrs( target, result, 3, "Epoch", "astMatch", status ); + TRANSFORM_1( "HEEQ", epoch ) + +/* From galactic coordinates. */ +/* -------------------------- */ +/* This conversion is supported directly by SLALIB. */ + } else if ( align_sys == AST__GALACTIC ) { + TRANSFORM_0( "GALEQ" ) + +/* From ICRS. */ +/* ---------- */ +/* This conversion is supported directly by SLALIB. */ + } else if ( align_sys == AST__ICRS ) { + VerifyMSMAttrs( target, result, 3, "Epoch", "astMatch", status ); + TRANSFORM_1( "HFK5Z", epoch_J ) + +/* From J2000. */ +/* ----------- */ +/* From J2000 to ICRS, and from ICRS to FK5. */ + } else if ( align_sys == AST__J2000 ) { + VerifyMSMAttrs( target, result, 3, "Epoch", "astMatch", status ); + TRANSFORM_0( "J2000H" ) + TRANSFORM_1( "HFK5Z", epoch_J ) + +/* From supergalactic coordinates. */ +/* ------------------------------- */ +/* Convert to galactic coordinates and then to FK5 J2000.0 + equatorial. */ + } else if ( align_sys == AST__SUPERGALACTIC ) { + TRANSFORM_0( "SUPGAL" ) + TRANSFORM_0( "GALEQ" ) + +/* From AzEl. */ +/* ---------- */ +/* Rotate from horizon to equator (H2E), shift hour angle into RA (H2R), + go from geocentric apparent to FK5 J2000. */ + } else if ( align_sys == AST__AZEL ) { + VerifyMSMAttrs( target, result, 3, "ObsLon ObsLat Epoch", "astMatch", status ); + TRANSFORM_2( "H2E", lat, diurab ) + TRANSFORM_1( "H2R", last ) + TRANSFORM_2( "AMP", epoch, 2000.0 ) + +/* From unknown coordinates. */ +/* ------------------------------- */ +/* No conversion is possible. */ + } else if ( align_sys == AST__UNKNOWN ) { + match = 0; + } + } + +/* Convert _from_ FK5 J2000.0 coordinates. */ +/* ======================================= */ +/* In this fourth and final phase, we convert to the result coordinate + system from the intermediate FK5 J2000 sky coordinates generated above. */ + if ( astOK && match && step4 ) { + +/* To FK4. */ +/* ------- */ +/* Convert directly from FK5 J2000.0 to FK4 B1950.0 coordinates at the + appropriate epoch. Then, if necessary, apply the old-style FK4 + precession model to bring the equinox to that required, with + rigorous handling of the E-terms of aberration. */ + if ( system == AST__FK4 ) { + VerifyMSMAttrs( target, result, 3, "Equinox Epoch", "astMatch", status ); + TRANSFORM_1( "FK54Z", epoch_B ) + if ( equinox_B != 1950.0 ) { + TRANSFORM_1( "SUBET", 1950.0 ) + TRANSFORM_2( "PREBN", 1950.0, equinox_B ) + TRANSFORM_1( "ADDET", equinox_B ) + } + +/* To FK4 with no E-terms. */ +/* ----------------------- */ +/* This is the same as above, except that we do not need to add the + E-terms at the end. */ + } else if ( system == AST__FK4_NO_E ) { + VerifyMSMAttrs( target, result, 3, "Equinox Epoch", "astMatch", status ); + TRANSFORM_1( "FK54Z", epoch_B ) + TRANSFORM_1( "SUBET", 1950.0 ) + if ( equinox_B != 1950.0 ) { + TRANSFORM_2( "PREBN", 1950.0, equinox_B ) + } + +/* To FK5. */ +/* ------- */ +/* We simply need to apply a precession correction for the change of + equinox. Omit even this if the required equinox is J2000.0. */ + } else if ( system == AST__FK5 ) { + VerifyMSMAttrs( target, result, 3, "Equinox", "astMatch", status ); + if ( equinox_J != 2000.0 ) { + TRANSFORM_2( "PREC", 2000.0, equinox_J ) + } + +/* To geocentric apparent. */ +/* ----------------------- */ +/* This conversion is supported directly by SLALIB. */ + } else if ( system == AST__GAPPT ) { + VerifyMSMAttrs( target, result, 3, "Epoch", "astMatch", status ); + TRANSFORM_2( "MAP", 2000.0, epoch ) + +/* To ecliptic coordinates. */ +/* ------------------------ */ +/* This conversion is supported directly by SLALIB. */ + } else if ( system == AST__ECLIPTIC ) { + VerifyMSMAttrs( target, result, 3, "Equinox", "astMatch", status ); + TRANSFORM_1( "EQECL", equinox ) + +/* To helio-ecliptic coordinates. */ +/* ------------------------------ */ + } else if ( system == AST__HELIOECLIPTIC ) { + VerifyMSMAttrs( target, result, 3, "Epoch", "astMatch", status ); + TRANSFORM_1( "EQHE", epoch ) + +/* To galactic coordinates. */ +/* ------------------------ */ +/* This conversion is supported directly by SLALIB. */ + } else if ( system == AST__GALACTIC ) { + TRANSFORM_0( "EQGAL" ) + +/* To ICRS. */ +/* -------- */ +/* This conversion is supported directly by SLALIB. */ + } else if ( system == AST__ICRS ) { + VerifyMSMAttrs( target, result, 3, "Epoch", "astMatch", status ); + TRANSFORM_1( "FK5HZ", epoch_J ) + +/* To J2000. */ +/* --------- */ +/* From FK5 to ICRS, then from ICRS to J2000. */ + } else if ( system == AST__J2000 ) { + VerifyMSMAttrs( target, result, 3, "Epoch", "astMatch", status ); + TRANSFORM_1( "FK5HZ", epoch_J ) + TRANSFORM_0( "HJ2000" ) + +/* To supergalactic coordinates. */ +/* ----------------------------- */ +/* Convert to galactic coordinates and then to supergalactic. */ + } else if ( system == AST__SUPERGALACTIC ) { + TRANSFORM_0( "EQGAL" ) + TRANSFORM_0( "GALSUP" ) + +/* To AzEl */ +/* ------- */ +/* Go from FK5 J2000 to geocentric apparent (MAP), shift RA into hour angle + (R2H), rotate from equator to horizon (E2H). */ + } else if ( system == AST__AZEL ) { + VerifyMSMAttrs( target, result, 3, "ObsLon ObsLat Epoch", "astMatch", status ); + TRANSFORM_2( "MAP", 2000.0, epoch ) + TRANSFORM_1( "R2H", last ) + TRANSFORM_2( "E2H", lat, diurab ) + +/* To unknown coordinates. */ +/* ----------------------------- */ +/* No conversion is possible. */ + } else if ( system == AST__UNKNOWN ) { + match = 0; + } + } + +/* See of the slamap created above is effectively a unit mapping to + within the tolerance of the more accurate SkyFrame (target or result). */ + isunit = TestSlaUnit( target, result, slamap, status ); + +/* Now need to take account of the possibility that the input or output + SkyFrame may represent an offset system rather than a coordinate system. + Form the Mapping from the target coordinate system to the associated + offset system. A UnitMap is returned if the target does not use an + offset system. */ + omap = SkyOffsetMap( target, status ); + +/* Invert it to get the Mapping from the actual used system (whther + offsets or coordinates) to the coordinate system. */ + astInvert( omap ); + +/* Combine it with the slamap created earlier, so that its coordinate + outputs feed the inputs of the slamap. We only do this if the slamap + is not effectively a unit mapping. Annul redundant pointers afterwards. */ + if( ! isunit ) { + tmap = (AstMapping *) astCmpMap( omap, slamap, 1, "", status ); + } else { + tmap = astClone( omap ); + } + omap = astAnnul( omap ); + slamap =astAnnul( slamap ); + +/* Now form the Mapping from the result coordinate system to the associated + offset system. A UnitMap is returned if the result does not use an + offset system. */ + omap = SkyOffsetMap( result, status ); + +/* Combine it with the above CmpMap, so that the CmpMap outputs feed the + new Mapping inputs. Annul redundant pointers afterwards. */ + tmap2 = (AstMapping *) astCmpMap( tmap, omap, 1, "", status ); + omap =astAnnul( omap ); + tmap =astAnnul( tmap ); + +/* Simplify the Mapping produced above (this eliminates any redundant + conversions) and annul the original pointer. */ + *map = astSimplify( tmap2 ); + tmap2 = astAnnul( tmap2 ); + +/* If an error occurred, annul the returned Mapping and clear the + returned values. */ + if ( !astOK ) { + *map = astAnnul( *map ); + match = -1; + } + +/* Return the result. */ + return match; + +/* Undefine macros local to this function. */ +#undef MAX_ARGS +#undef TRANSFORM_0 +#undef TRANSFORM_1 +#undef TRANSFORM_2 +#undef TRANSFORM_3 +} + +static int Match( AstFrame *template_frame, AstFrame *target, int matchsub, + int **template_axes, int **target_axes, AstMapping **map, + AstFrame **result, int *status ) { +/* +* Name: +* Match + +* Purpose: +* Determine if conversion is possible between two coordinate systems. + +* Type: +* Private function. + +* Synopsis: +* #include "skyframe.h" +* int Match( AstFrame *template, AstFrame *target, int matchsub, +* int **template_axes, int **target_axes, +* AstMapping **map, AstFrame **result, int *status ) + +* Class Membership: +* SkyFrame member function (over-rides the protected astMatch method +* inherited from the Frame class). + +* Description: +* This function matches a "template" SkyFrame to a "target" Frame and +* determines whether it is possible to convert coordinates between them. +* If it is, a mapping that performs the transformation is returned along +* with a new Frame that describes the coordinate system that results when +* this mapping is applied to the "target" coordinate system. In addition, +* information is returned to allow the axes in this "result" Frame to be +* associated with the corresponding axes in the "target" and "template" +* Frames from which they are derived. + +* Parameters: +* template +* Pointer to the template SkyFrame. This describes the coordinate system +* (or set of possible coordinate systems) into which we wish to convert +* our coordinates. +* target +* Pointer to the target Frame. This describes the coordinate system in +* which we already have coordinates. +* matchsub +* If zero then a match only occurs if the template is of the same +* class as the target, or of a more specialised class. If non-zero +* then a match can occur even if this is not the case. +* template_axes +* Address of a location where a pointer to int will be returned if the +* requested coordinate conversion is possible. This pointer will point +* at a dynamically allocated array of integers with one element for each +* axis of the "result" Frame (see below). It must be freed by the caller +* (using astFree) when no longer required. +* +* For each axis in the result Frame, the corresponding element of this +* array will return the index of the template SkyFrame axis from which +* it is derived. If it is not derived from any template SkyFrame axis, +* a value of -1 will be returned instead. +* target_axes +* Address of a location where a pointer to int will be returned if the +* requested coordinate conversion is possible. This pointer will point +* at a dynamically allocated array of integers with one element for each +* axis of the "result" Frame (see below). It must be freed by the caller +* (using astFree) when no longer required. +* +* For each axis in the result Frame, the corresponding element of this +* array will return the index of the target Frame axis from which it +* is derived. If it is not derived from any target Frame axis, a value +* of -1 will be returned instead. +* map +* Address of a location where a pointer to a new Mapping will be +* returned if the requested coordinate conversion is possible. If +* returned, the forward transformation of this Mapping may be used to +* convert coordinates between the "target" Frame and the "result" +* Frame (see below) and the inverse transformation will convert in the +* opposite direction. +* result +* Address of a location where a pointer to a new Frame will be returned +* if the requested coordinate conversion is possible. If returned, this +* Frame describes the coordinate system that results from applying the +* returned Mapping (above) to the "target" coordinate system. In +* general, this Frame will combine attributes from (and will therefore +* be more specific than) both the target and the template Frames. In +* particular, when the template allows the possibility of transformaing +* to any one of a set of alternative coordinate systems, the "result" +* Frame will indicate which of the alternatives was used. +* status +* Pointer to the inherited status variable. + +* Returned Value: +* A non-zero value is returned if the requested coordinate conversion is +* possible. Otherwise zero is returned (this will not in itself result in +* an error condition). + +* Notes: +* - A value of zero will be returned if this function is invoked with the +* global error status set, or if it should fail for any reason. + +* Implementation Notes: +* This implementation addresses the matching of a SkyFrame class object to +* any other class of Frame. A SkyFrame will match any class of SkyFrame +* (i.e. possibly from a derived class) but will not match a less +* specialised class of Frame. +*/ + +/* Local Variables: */ + AstFrame *frame0; /* Pointer to Frame underlying axis 0 */ + AstFrame *frame1; /* Pointer to Frame underlying axis 1 */ + AstSkyFrame *template; /* Pointer to template SkyFrame structure */ + int iaxis; /* Axis index */ + int iaxis0; /* Axis index underlying axis 0 */ + int iaxis1; /* Axis index underlying axis 1 */ + int match; /* Coordinate conversion possible? */ + int swap1; /* Template axes swapped? */ + int swap2; /* Target axes swapped? */ + int swap; /* Additional axis swap needed? */ + int target_axis0; /* Index of 1st SkyFrame axis in the target */ + int target_axis1; /* Index of 2nd SkyFrame axis in the target */ + int target_naxes; /* Number of target axes */ + +/* Initialise the returned values. */ + *template_axes = NULL; + *target_axes = NULL; + *map = NULL; + *result = NULL; + match = 0; + +/* Check the global error status. */ + if ( !astOK ) return match; + +/* Initialise variables to avoid "used of uninitialised variable" + messages from dumb compilers. */ + swap = 0; + target_axis0 = -1; + target_axis1 = -1; + +/* Obtain a pointer to the template SkyFrame structure. */ + template = (AstSkyFrame *) template_frame; + +/* Obtain the number of axes in the target Frame. */ + target_naxes = astGetNaxes( target ); + +/* The first criterion for a match is that the template matches as a + Frame class object. This ensures that the number of axes (2) and + domain, etc. of the target Frame are suitable. Invoke the parent + "astMatch" method to verify this. */ + match = (*parent_match)( template_frame, target, matchsub, + template_axes, target_axes, map, result, status ); + +/* If a match was found, annul the returned objects, which are not + needed, but keep the memory allocated for the axis association + arrays, which we will re-use. */ + if ( astOK && match ) { + *map = astAnnul( *map ); + *result = astAnnul( *result ); + } + +/* If OK so far, obtain pointers to the primary Frames which underlie + all target axes. Stop when a SkyFrame axis is found. */ + if ( match && astOK ) { + + match = 0; + for( iaxis = 0; iaxis < target_naxes; iaxis++ ) { + astPrimaryFrame( target, iaxis, &frame0, &iaxis0 ); + if( astIsASkyFrame( frame0 ) ) { + target_axis0 = iaxis; + match = 1; + break; + } else { + frame0 = astAnnul( frame0 ); + } + } + +/* Check at least one SkyFrame axis was found it the target. */ + if( match ) { + +/* If so, search the remaining target axes for another axis that is + derived from the same SkyFrame. */ + match = 0; + for( iaxis++ ; iaxis < target_naxes; iaxis++ ) { + astPrimaryFrame( target, iaxis, &frame1, &iaxis1 ); + if( frame1 == frame0 ) { + target_axis1 = iaxis; + frame1 = astAnnul( frame1 ); + match = 1; + break; + } else { + frame1 = astAnnul( frame1 ); + } + } + +/* Annul the remaining Frame pointer used in the above tests. */ + frame0 = astAnnul( frame0 ); + } + +/* If this test is passed, we can now test that the underlying axis indices + are 0 and 1, in either order. This then ensures that we have a + single SkyFrame (not a compound Frame) with both axes present. */ + if ( match && astOK ) { + match = ( ( ( iaxis0 == 0 ) && ( iaxis1 == 1 ) ) || + ( ( iaxis1 == 0 ) && ( iaxis0 == 1 ) ) ); + } + + } + +/* If a possible match has been detected, we must now decide how the + order of the axes in the result Frame relates to the order of axes + in the target Frame. There are two factors involved. The first + depends on whether the axis permutation array for the template + SkyFrame (whose method we are executing) causes an axis + reversal. Determine this by permuting axis index zero. */ + if ( astOK && match ) { + swap1 = ( astValidateAxis( template, 0, 1, "astMatch" ) != 0 ); + +/* The second factor depends on whether the axes of the underlying + primary SkyFrame are reversed when seen in the target Frame. */ + swap2 = ( iaxis0 != 0 ); + +/* Combine these to determine if an additional axis swap will be + needed. */ + swap = ( swap1 != swap2 ); + +/* Now check to see if this additional swap is permitted by the + template's Permute attribute. */ + match = ( !swap || astGetPermute( template ) ); + } + +/* If the Frames still match, we next set up the axis association + arrays. */ + if ( astOK && match ) { + +/* If the target axis order is to be preserved, then the target axis + association involves no permutation but the template axis + association may involve an axis swap. */ + if ( astGetPreserveAxes( template ) ) { + (*template_axes)[ 0 ] = swap; + (*template_axes)[ 1 ] = !swap; + (*target_axes)[ 0 ] = target_axis0; + (*target_axes)[ 1 ] = target_axis1; + +/* Otherwise, any swap applies to the target axis association + instead. */ + } else { + (*template_axes)[ 0 ] = 0; + (*template_axes)[ 1 ] = 1; + (*target_axes)[ 0 ] = swap ? target_axis1 : target_axis0; + (*target_axes)[ 1 ] = swap ? target_axis0 : target_axis1; + } + +/* Use the target's "astSubFrame" method to create a new Frame (the + result Frame) with copies of the target axes in the required + order. This process also overlays the template attributes on to the + target Frame and returns a Mapping between the target and result + Frames which effects the required coordinate conversion. */ + match = astSubFrame( target, template, 2, *target_axes, *template_axes, + map, result ); + } + +/* If an error occurred, or conversion to the result Frame's + coordinate system was not possible, then free all memory, annul the + returned objects, and reset the returned value. */ + if ( !astOK || !match ) { + *template_axes = astFree( *template_axes ); + *target_axes = astFree( *target_axes ); + if( *map ) *map = astAnnul( *map ); + if( *result ) *result = astAnnul( *result ); + match = 0; + } + +/* Return the result. */ + return match; +} + +static void MatchAxesX( AstFrame *frm2_frame, AstFrame *frm1, int *axes, + int *status ) { +/* +* Name: +* MatchAxesX + +* Purpose: +* Find any corresponding axes in two Frames. + +* Type: +* Private function. + +* Synopsis: +* #include "skyframe.h" +* void MatchAxesX( AstFrame *frm2, AstFrame *frm1, int *axes ) +* int *status ) + +* Class Membership: +* SkyFrame member function (over-rides the protected astMatchAxesX +* method inherited from the Frame class). + +* This function looks for corresponding axes within two supplied +* Frames. An array of integers is returned that contains an element +* for each axis in the second supplied Frame. An element in this array +* will be set to zero if the associated axis within the second Frame +* has no corresponding axis within the first Frame. Otherwise, it +* will be set to the index (a non-zero positive integer) of the +* corresponding axis within the first supplied Frame. + +* Parameters: +* frm2 +* Pointer to the second Frame. +* frm1 +* Pointer to the first Frame. +* axes +* Pointer to an integer array in which to return the indices of +* the axes (within the first Frame) that correspond to each axis +* within the second Frame. Axis indices start at 1. A value of zero +* will be stored in the returned array for each axis in the second +* Frame that has no corresponding axis in the first Frame. +* +* The number of elements in this array must be greater than or +* equal to the number of axes in the second Frame. +* status +* Pointer to inherited status value. + +* Notes: +* - Corresponding axes are identified by the fact that a Mapping +* can be found between them using astFindFrame or astConvert. Thus, +* "corresponding axes" are not necessarily identical. For instance, +* SkyFrame axes in two Frames will match even if they describe +* different celestial coordinate systems +*/ + +/* Local Variables: */ + AstFrame *resfrm; + AstMapping *resmap; + AstSkyFrame *frm2; + int *frm2_axes; + int *frm1_axes; + int max_axes; + int min_axes; + int preserve_axes; + +/* Check the global error status. */ + if ( !astOK ) return; + +/* Get a pointer to the SkyFrame. */ + frm2 = (AstSkyFrame *) frm2_frame; + +/* Temporarily ensure that the PreserveAxes attribute is non-zero in + the first supplied Frame. This means thte result Frame returned by + astMatch below will have the axis count and order of the target Frame + (i.e. "pfrm"). */ + if( astTestPreserveAxes( frm1 ) ) { + preserve_axes = astGetPreserveAxes( frm1 ) ? 1 : 0; + } else { + preserve_axes = -1; + } + astSetPreserveAxes( frm1, 1 ); + +/* Temporarily ensure that the MaxAxes and MinAxes attributes in the + first supplied Frame are set so the Frame can be used as a template + in astMatch for matching any number of axes. */ + if( astTestMaxAxes( frm1 ) ) { + max_axes = astGetMaxAxes( frm1 ); + } else { + max_axes = -1; + } + astSetMaxAxes( frm1, 10000 ); + + if( astTestMinAxes( frm1 ) ) { + min_axes = astGetMinAxes( frm1 ); + } else { + min_axes = -1; + } + astSetMinAxes( frm1, 1 ); + +/* Attempt to find a sub-frame within the first supplied Frame that + corresponds to the supplied SkyFrame. */ + if( astMatch( frm1, frm2, 1, &frm1_axes, &frm2_axes, &resmap, &resfrm ) ) { + +/* If successfull, Store the one-based index within "frm1" of the + corresponding axes. */ + axes[ 0 ] = frm1_axes[ 0 ] + 1; + axes[ 1 ] = frm1_axes[ 1 ] + 1; + +/* Free resources */ + frm1_axes = astFree( frm1_axes ); + frm2_axes = astFree( frm2_axes ); + resmap = astAnnul( resmap ); + resfrm = astAnnul( resfrm ); + +/* If no corresponding SkyFrame was found store zeros in the returned array. */ + } else { + axes[ 0 ] = 0; + axes[ 1 ] = 0; + } + +/* Re-instate the original attribute values in the first supplied Frame. */ + if( preserve_axes == -1 ) { + astClearPreserveAxes( frm1 ); + } else { + astSetPreserveAxes( frm1, preserve_axes ); + } + + if( max_axes == -1 ) { + astClearMaxAxes( frm1 ); + } else { + astSetMaxAxes( frm1, max_axes ); + } + + if( min_axes == -1 ) { + astClearMinAxes( frm1 ); + } else { + astSetMinAxes( frm1, min_axes ); + } +} + +static void Norm( AstFrame *this_frame, double value[], int *status ) { +/* +* Name: +* Norm + +* Purpose: +* Normalise a set of SkyFrame coordinates. + +* Type: +* Private function. + +* Synopsis: +* #include "skyframe.h" +* void Norm( AstAxis *this, double value[], int *status ) + +* Class Membership: +* SkyFrame member function (over-rides the astNorm method inherited +* from the Frame class). + +* Description: +* This function converts a set of SkyFrame coordinate values, +* which might potentially be unsuitable for display to a user (for +* instance, may lie outside the expected range of values) into a +* set of acceptable alternative values suitable for display. +* +* This is done by wrapping coordinates so that the latitude lies +* in the range (-pi/2.0) <= latitude <= (pi/2.0). If the NegLon +* attribute is zero (the default), then the wrapped longitude value +* lies in the range 0.0 <= longitude < (2.0*pi). Otherwise, it lies +* in the range -pi <= longitude < pi. + +* Parameters: +* this +* Pointer to the SkyFrame. +* value +* An array of double, with one element for each SkyFrame axis. +* This should contain the initial set of coordinate values, +* which will be modified in place. +* status +* Pointer to the inherited status variable. +*/ + +/* Local Variables: */ + AstSkyFrame *this; /* Pointer to the SkyFrame structure */ + const int *perm; /* Axis permutation array */ + double sky_lat; /* Sky latitude value */ + double sky_long; /* Sky longitude value */ + double v[ 2 ]; /* Permuted value coordinates */ + +/* Check the global error status. */ + if ( !astOK ) return; + +/* Obtain a pointer to the SkyFrame structure. */ + this = (AstSkyFrame *) this_frame; + +/* Obtain a pointer to the SkyFrame's axis permutation array. */ + perm = astGetPerm( this ); + if ( astOK ) { + +/* Obtain the sky longitude and latitude values, allowing for any axis + permutation. */ + v[ perm[ 0 ] ] = value[ 0 ]; + v[ perm[ 1 ] ] = value[ 1 ]; + sky_long = v[ 0 ]; + sky_lat = v[ 1 ]; + +/* Test if both values are OK (i.e. not "bad"). */ + if ( ( sky_long != AST__BAD ) && ( sky_lat != AST__BAD ) ) { + +/* Fold the longitude value into the range 0 to 2*pi and the latitude into + the range -pi to +pi. */ + sky_long = palDranrm( sky_long ); + sky_lat = palDrange( sky_lat ); + +/* If the latitude now exceeds pi/2, shift the longitude by pi in whichever + direction will keep it in the range 0 to 2*pi. */ + if ( sky_lat > ( pi / 2.0 ) ) { + sky_long += ( sky_long < pi ) ? pi : -pi; + +/* Reflect the latitude value through the pole, so it lies in the range 0 to + pi/2. */ + sky_lat = pi - sky_lat; + +/* If the latitude is less than -pi/2, shift the longitude in the same way + as above. */ + } else if ( sky_lat < -( pi / 2.0 ) ) { + sky_long += ( sky_long < pi ) ? pi : -pi; + +/* But reflect the latitude through the other pole, so it lies in the range + -pi/2 to 0. */ + sky_lat = -pi - sky_lat; + } + +/* If only the longitude value is valid, wrap it into the range 0 to 2*pi. */ + } else if ( sky_long != AST__BAD ) { + sky_long = palDranrm( sky_long ); + +/* If only the latitude value is valid, wrap it into the range -pi to +pi. */ + } else if ( sky_lat != AST__BAD ) { + sky_lat = palDrange( sky_lat ); + +/* Then refect through one of the poles (as above), if necessary, to move it + into the range -pi/2 to +pi/2. */ + if ( sky_lat > ( pi / 2.0 ) ) { + sky_lat = pi - sky_lat; + } else if ( sky_lat < -( pi / 2.0 ) ) { + sky_lat = -pi - sky_lat; + } + } + +/* Convert 2*pi longitude into zero. Allow for a small error. */ + if ( fabs( sky_long - ( 2.0 * pi ) ) <= + ( 2.0 * pi ) * ( DBL_EPSILON * (double) FLT_RADIX ) ) sky_long = 0.0; + +/* If the NegLon attribute is set, and the longitude value is good, + convert it into the range -pi to +pi. */ + if( sky_long != AST__BAD && astGetNegLon( this ) ) { + sky_long = palDrange( sky_long ); + } + +/* Return the new values, allowing for any axis permutation. */ + v[ 0 ] = sky_long; + v[ 1 ] = sky_lat; + value[ 0 ] = v[ perm[ 0 ] ]; + value[ 1 ] = v[ perm[ 1 ] ]; + } +} + +static void NormBox( AstFrame *this_frame, double lbnd[], double ubnd[], + AstMapping *reg, int *status ) { +/* +* Name: +* NormBox + +* Purpose: +* Extend a box to include effect of any singularities in the Frame. + +* Type: +* Private function. + +* Synopsis: +* #include "skyframe.h" +* void astNormBox( AstFrame *this, double lbnd[], double ubnd[], +* AstMapping *reg, int *status ) + +* Class Membership: +* SkyFrame member function (over-rides the astNormBox method inherited +* from the Frame class). + +* Description: +* This function modifies a supplied box to include the effect of any +* singularities in the co-ordinate system represented by the Frame. +* For a normal Cartesian coordinate system, the box will be returned +* unchanged. Other classes of Frame may do other things. For instance, +* a SkyFrame will check to see if the box contains either the north +* or south pole and extend the box appropriately. + +* Parameters: +* this +* Pointer to the Frame. +* lbnd +* An array of double, with one element for each Frame axis +* (Naxes attribute). Initially, this should contain a set of +* lower axis bounds for the box. They will be modified on exit +* to include the effect of any singularities within the box. +* ubnd +* An array of double, with one element for each Frame axis +* (Naxes attribute). Initially, this should contain a set of +* upper axis bounds for the box. They will be modified on exit +* to include the effect of any singularities within the box. +* reg +* A Mapping which should be used to test if any singular points are +* inside or outside the box. The Mapping should leave an input +* position unchanged if the point is inside the box, and should +* set all bad if the point is outside the box. +* status +* Pointer to the inherited status variable. +*/ + +/* Local Variables: */ + AstSkyFrame *this; /* Pointer to the SkyFrame structure */ + const int *perm; /* Axis permutation array */ + double lb[ 2 ]; /* Permuted lower bounds */ + double t; /* Temporary storage */ + double t2; /* Temporary storage */ + double ub[ 2 ]; /* Permuted upper bounds */ + double x[2]; /* 1st axis values at poles */ + double xo[2]; /* Tested 1st axis values at poles */ + double y[2]; /* 2nd axis values at poles */ + double yo[2]; /* Tested 2nd axis values at poles */ + +/* Check the global error status. */ + if ( !astOK ) return; + +/* Obtain a pointer to the SkyFrame structure. */ + this = (AstSkyFrame *) this_frame; + +/* Obtain a pointer to the SkyFrame's axis permutation array. */ + perm = astGetPerm( this ); + if( perm ) { + +/* Obtain the sky longitude and latitude limits, allowing for any axis + permutation. */ + lb[ perm[ 0 ] ] = lbnd[ 0 ]; + lb[ perm[ 1 ] ] = lbnd[ 1 ]; + ub[ perm[ 0 ] ] = ubnd[ 0 ]; + ub[ perm[ 1 ] ] = ubnd[ 1 ]; + +/* Use the supplied Mapping to test if box includes either pole. */ + if( perm[ 0 ] == 0 ) { + x[ 0 ] = 0.0; + y[ 0 ] = AST__DPIBY2; + x[ 1 ] = 0.0; + y[ 1 ] = -AST__DPIBY2; + } else { + x[ 0 ] = AST__DPIBY2; + y[ 0 ] = 0.0; + x[ 1 ] = -AST__DPIBY2; + y[ 1 ] = 0.0; + } + astTran2( reg, 2, x, y, 1, xo, yo ); + +/* If the box includes the north pole... */ + if( xo[ 0 ] != AST__BAD ) { + +/* Find the lowest latitude after normalisation. */ + if( ub[ 1 ] != AST__BAD && lb[ 1 ] != AST__BAD ){ + t = palDrange( ub[ 1 ] ); + t2 = palDrange( lb[ 1 ] ); + if( t2 < t ) t = t2; + } else { + t = AST__BAD; + } + +/* Set the lower returned limit to this value and the upper returned limit + to +90 degs */ + lb[ 1 ] = t; + ub[ 1 ] = AST__DPIBY2; + +/* Set the longitude range to 0 to 2PI */ + lb[ 0 ] = 0; + ub[ 0 ] = 2*AST__DPI; + + } + +/* If the box includes the south pole... */ + if( xo[ 1 ] != AST__BAD ) { + +/* Find the highest latitude after normalisation. */ + if( ub[ 1 ] != AST__BAD && lb[ 1 ] != AST__BAD ){ + t = palDrange( ub[ 1 ] ); + t2 = palDrange( lb[ 1 ] ); + if( t2 > t ) t = t2; + } else { + t = AST__BAD; + } + +/* Set the upper returned limit to this value and the lower returned limit + to -90 degs */ + lb[ 1 ] = -AST__DPIBY2; + ub[ 1 ] = t; + +/* Set the longitude range to 0 to 2PI */ + lb[ 0 ] = 0; + ub[ 0 ] = 2*AST__DPI; + } + +/* Return the modified limits. */ + lbnd[ 0 ] = lb[ perm[ 0 ] ]; + lbnd[ 1 ] = lb[ perm[ 1 ] ]; + ubnd[ 0 ] = ub[ perm[ 0 ] ]; + ubnd[ 1 ] = ub[ perm[ 1 ] ]; + } +} + +static void Offset( AstFrame *this_frame, const double point1[], + const double point2[], double offset, double point3[], int *status ) { +/* +* Name: +* Offset + +* Purpose: +* Calculate an offset along a geodesic curve. + +* Type: +* Private function. + +* Synopsis: +* #include "skyframe.h" +* void Offset( AstFrame *this, +* const double point1[], const double point2[], +* double offset, double point3[], int *status ) + +* Class Membership: +* SkyFrame member function (over-rides the astOffset method +* inherited from the Frame class). + +* Description: +* This function finds the SkyFrame coordinate values of a point +* which is offset a specified distance along the geodesic curve +* (i.e. great circle) between two other points. + +* Parameters: +* this +* Pointer to the SkyFrame. +* point1 +* An array of double, with one element for each SkyFrame axis. +* This should contain the coordinates of the point marking the +* start of the geodesic curve. +* point2 +* An array of double, with one element for each SkyFrame axis. +* This should contain the coordinates of the point marking the +* end of the geodesic curve. +* offset +* The required offset from the first point along the geodesic +* curve, in radians. If this is positive, it will be towards +* the second point. If it is negative, it will be in the +* opposite direction. This offset need not imply a position +* lying between the two points given, as the curve will be +* extrapolated if necessary. +* point3 +* An array of double, with one element for each SkyFrame axis +* in which the coordinates of the required point will be +* returned. +* status +* Pointer to the inherited status variable. + +* Notes: +* - The geodesic curve used by this function is the path of +* shortest distance between two points, as defined by the +* astDistance function. +* - This function will return "bad" coordinate values (AST__BAD) +* if any of the input coordinates has this value. +* - "Bad" coordinate values will also be returned if the two +* points supplied are coincident (or otherwise fail to uniquely +* specify a geodesic curve) but the requested offset is non-zero. +*/ + +/* Local Variables: */ + AstSkyFrame *this; /* Pointer to the SkyFrame structure */ + const int *perm; /* Pointer to axis permutation array */ + double mrot[ 3 ][ 3 ]; /* Rotation matrix */ + double p1[ 2 ]; /* Permuted coordinates for point1 */ + double p2[ 2 ]; /* Permuted coordinates for point2 */ + double p3[ 2 ]; /* Permuted coordinates for point3 */ + double scale; /* Scale factor */ + double v1[ 3 ]; /* 3-vector for p1 */ + double v2[ 3 ]; /* 3-vector for p2 */ + double v3[ 3 ]; /* 3-vector for p3 */ + double vmod; /* Modulus of vector */ + double vrot[ 3 ]; /* Vector along rotation axis */ + +/* Check the global error status. */ + if ( !astOK ) return; + +/* Obtain a pointer to the SkyFrame structure. */ + this = (AstSkyFrame *) this_frame; + +/* Obtain a pointer to the SkyFrame's axis permutation array. */ + perm = astGetPerm( this ); + if ( astOK ) { + +/* Check that all supplied coordinates are OK. If not, generate "bad" + output coordinates. */ + if ( ( point1[ 0 ] == AST__BAD ) || ( point1[ 1 ] == AST__BAD ) || + ( point2[ 0 ] == AST__BAD ) || ( point2[ 1 ] == AST__BAD ) ) { + point3[ 0 ] = AST__BAD; + point3[ 1 ] = AST__BAD; + +/* Otherwise, apply the axis permutation array to obtain the + coordinates of the two input points in the required + (longitude,latitude) order. */ + } else { + p1[ perm[ 0 ] ] = point1[ 0 ]; + p1[ perm[ 1 ] ] = point1[ 1 ]; + p2[ perm[ 0 ] ] = point2[ 0 ]; + p2[ perm[ 1 ] ] = point2[ 1 ]; + +/* Convert each point into a 3-vector of unit length. */ + palDcs2c( p1[ 0 ], p1[ 1 ], v1 ); + palDcs2c( p2[ 0 ], p2[ 1 ], v2 ); + +/* Find the cross product between these two vectors (the vector order + is reversed here to compensate for the sense of rotation introduced + by palDav2m and palDmxv below). */ + palDvxv( v2, v1, v3 ); + +/* Normalise the cross product vector, also obtaining its original + modulus. */ + palDvn( v3, vrot, &vmod ); + +/* If the original modulus was zero, the input points are either + coincident or diametrically opposite, so do not uniquely define a + great circle. In either case, we can only generate output + coordinates if the offset required is an exact multiple of pi. If + it is, generate the 3-vector that results from rotating the first + input point through this angle. */ + if ( vmod == 0.0 ) { + if ( sin( offset ) == 0.0 ) { + scale = cos( offset ); + v3[ 0 ] = v1[ 0 ] * scale; + v3[ 1 ] = v1[ 1 ] * scale; + v3[ 2 ] = v1[ 2 ] * scale; + +/* Convert the 3-vector back into spherical cooordinates and then + constrain the longitude result to lie in the range 0 to 2*pi + (palDcc2s doesn't do this itself). */ + palDcc2s( v3, &p3[ 0 ], &p3[ 1 ] ); + p3[ 0 ] = palDranrm( p3[ 0 ] ); + +/* If the offset was not a multiple of pi, generate "bad" output + coordinates. */ + } else { + p3[ 0 ] = AST__BAD; + p3[ 1 ] = AST__BAD; + } + +/* If the two input points define a great circle, scale the normalised + cross product vector to make its length equal to the required + offset (angle) between the first input point and the result. */ + } else { + vrot[ 0 ] *= offset; + vrot[ 1 ] *= offset; + vrot[ 2 ] *= offset; + +/* Generate the rotation matrix that implements this rotation and use + it to rotate the first input point (3-vector) to give the required + result (3-vector). */ + palDav2m( vrot, mrot ); + palDmxv( mrot, v1, v3 ); + +/* Convert the 3-vector back into spherical cooordinates and then + constrain the longitude result to lie in the range 0 to 2*pi. */ + palDcc2s( v3, &p3[ 0 ], &p3[ 1 ] ); + p3[ 0 ] = palDranrm( p3[ 0 ] ); + } + +/* Permute the result coordinates to undo the effect of the SkyFrame + axis permutation array. */ + point3[ 0 ] = p3[ perm[ 0 ] ]; + point3[ 1 ] = p3[ perm[ 1 ] ]; + } + } +} + +static AstMapping *SkyOffsetMap( AstSkyFrame *this, int *status ){ +/* +*++ +* Name: +c astSkyOffsetMap +f AST_SKYOFFSETMAP + +* Purpose: +* Returns a Mapping which goes from absolute coordinates to offset +* coordinates. + +* Type: +* Public virtual function. + +* Synopsis: +c #include "skyframe.h" +c AstMapping *astSkyOffsetMap( AstSkyFrame *this ) +f RESULT = AST_SKYOFFSETMAP( THIS, STATUS ) + +* Class Membership: +* SkyFrame method. + +* Description: +* This function returns a Mapping in which the forward transformation +* transforms a position in the coordinate system given by the System +* attribute of the supplied SkyFrame, into the offset coordinate system +* specified by the SkyRef, SkyRefP and SkyRefIs attributes of the +* supplied SkyFrame. +* +* A UnitMap is returned if the SkyFrame does not define an offset +* coordinate system. + +* Parameters: +c this +f THIS = INTEGER (Given) +* Pointer to the SkyFrame. +f STATUS = INTEGER (Given and Returned) +f The global status. + +* Returned Value: +c astSkyOffsetMap() +f AST_SKYOFFSETMAP = INTEGER +* Pointer to the returned Mapping. + +* Notes: +* - A null Object pointer (AST__NULL) will be returned if this +c function is invoked with the AST error status set, or if it +f function is invoked with STATUS set to an error value, or if it +* should fail for any reason. +*-- +*/ + +/* Local Variables: */ + AstCmpMap *map3; /* Partial Mapping. */ + AstMapping *result; /* The returned Mapping. */ + AstMatrixMap *map1; /* Spherical rotation in 3D cartesian space */ + AstSphMap *map2; /* 3D Cartesian to 2D spherical Mapping */ + double *vx; /* Pointer to x unit vector. */ + double *vy; /* Pointer to y unit vector. */ + double *vz; /* Pointer to z unit vector. */ + double mat[ 9 ]; /* Spherical rotation matrix */ + double vmod; /* Length of vector (+ve) */ + double vp[ 3 ]; /* Unit vector representin SkyRefP position. */ + int lataxis; /* Index of the latitude axis */ + int lonaxis; /* Index of the longitude axis */ + +/* Initialise. */ + result = NULL; + +/* Check the global error status. */ + if ( !astOK ) return result; + +/* Return a UnitMap if the offset coordinate system is not defined. */ + if( astGetSkyRefIs( this ) == AST__IGNORED_REF || + ( !astTestSkyRef( this, 0 ) && !astTestSkyRef( this, 1 ) ) ) { + result = (AstMapping *) astUnitMap( 2, "", status ); + +/* Otherwise... */ + } else { + +/* Get the longitude and latitude at the reference point and at a point + on the primary meridian. */ + lataxis = astGetLatAxis( this ); + lonaxis = 1 - lataxis; + +/* Initialise pointers to the rows of the 3x3 matrix. Each row will be + used to store a unit vector. */ + vx = mat; + vy = mat + 3; + vz = mat + 6; + +/* The following trig converts between (longitude,latitude) and (x,y,z) + on a unit sphere, in which (0,0) is at (1,0,0), (0,pi/2) is (0,0,1) + and (pi/2,0) is at (0,1,0). */ + +/* First deal with cases where the SkyRef attribute holds the standard + coords at the origin of the offset coordinate system. */ + if( astGetSkyRefIs( this ) == AST__ORIGIN_REF ) { + +/* Convert each point into a 3-vector of unit length. The SkyRef position + defines the X axis in the offset coord system. */ + palDcs2c( astGetSkyRef( this, lonaxis ), astGetSkyRef( this, lataxis ), vx ); + palDcs2c( astGetSkyRefP( this, lonaxis ), astGetSkyRefP( this, lataxis ), vp ); + +/* The Y axis is perpendicular to both the X axis and the skyrefp + position. That is, it is parallel to the cross product of the 2 above + vectors.*/ + palDvxv( vp, vx, vy ); + +/* Normalize the y vector. */ + palDvn( vy, vy, &vmod ); + +/* Report an error if the modulus of the vector is zero.*/ + if( vmod == 0.0 ) { + astError( AST__BADOC, "astConvert(%s): The position specified by the SkyRefP " + "attribute is either coincident, with or opposite to, the " + "position specified by the SkyRef attribute.", status, astGetClass( this ) ); + +/* If OK, form the Z axis as the cross product of the x and y axes. */ + } else { + palDvxv( vx, vy, vz ); + + } + +/* Now deal with cases where the SkyRef attribute holds the standard + coords at the north pole of the offset coordinate system. */ + } else { + +/* Convert each point into a 3-vector of unit length. The SkyRef position + defines the Z axis in the offset coord system. */ + palDcs2c( astGetSkyRef( this, lonaxis ), astGetSkyRef( this, lataxis ), vz ); + palDcs2c( astGetSkyRefP( this, lonaxis ), astGetSkyRefP( this, lataxis ), vp ); + +/* The Y axis is perpendicular to both the Z axis and the skyrefp + position. That is, it is parallel to the cross product of the 2 above + vectors.*/ + palDvxv( vz, vp, vy ); + +/* Normalize the y vector. */ + palDvn( vy, vy, &vmod ); + +/* Report an error if the modulus of the vector is zero.*/ + if( vmod == 0.0 ) { + astError( AST__BADOC, "astConvert(%s): The position specified by the SkyRefP " + "attribute is either coincident, with or opposite to, the " + "position specified by the SkyRef attribute.", status, astGetClass( this ) ); + +/* If OK, form the X axis as the cross product of the y and z axes. */ + } else { + palDvxv( vy, vz, vx ); + } + } + +/* Create a MatrixMap which implements the above spherical rotation. Each + row in this matrix represents one of the unit axis vectors found above. */ + map1 = astMatrixMap( 3, 3, 0, mat, "", status ); + +/* Create a 3D cartesian to 2D spherical Mapping. */ + map2 = astSphMap( "UnitRadius=1", status ); + +/* Form a series CmpMap which converts from 2D (long,lat) in the base + System to 2D (long,lat) in the offset coordinate system. */ + map3 = astCmpMap( map1, map2, 1, "", status ); + astInvert( map2 ); + result = (AstMapping *) astCmpMap( map2, map3, 1, "", status ); + +/* Free resources. */ + map1 = astAnnul( map1 ); + map2 = astAnnul( map2 ); + map3 = astAnnul( map3 ); + } + +/* Annul the returned Mapping if anything has gone wrong. */ + if( !astOK ) result = astAnnul( result ); + +/* Return the result. */ + return result; + +} + +static double Offset2( AstFrame *this_frame, const double point1[2], + double angle, double offset, double point2[2], int *status ) { +/* +* Name: +* Offset2 + +* Purpose: +* Calculate an offset along a geodesic curve at a given bearing. + +* Type: +* Private function. + +* Synopsis: +* #include "skyframe.h" +* double Offset2( AstFrame *this_frame, const double point1[2], +* double angle, double offset, double point2[2], int *status ) + +* Class Membership: +* SkyFrame member function (over-rides the astOffset2 method +* inherited from the Frame class). + +* Description: +* This function finds the SkyFrame coordinate values of a point +* which is offset a specified distance along the geodesic curve +* (i.e. great circle) at a given angle from a given starting point. + +* Parameters: +* this +* Pointer to the SkyFrame. +* point1 +* An array of double, with one element for each SkyFrame axis. +* This should contain the coordinates of the point marking the +* start of the geodesic curve. +* angle +* The angle (in radians) from the positive direction of the second +* axis, to the direction of the required position, as seen from +* the starting position. Positive rotation is in the sense of +* rotation from the positive direction of axis 2 to the positive +* direction of axis 1. +* offset +* The required offset from the first point along the geodesic +* curve, in radians. If this is positive, it will be towards +* the given angle. If it is negative, it will be in the +* opposite direction. +* point2 +* An array of double, with one element for each SkyFrame axis +* in which the coordinates of the required point will be +* returned. +* status +* Pointer to the inherited status variable. + +* Returned Value: +* The direction of the geodesic curve at the end point. That is, the +* angle (in radians) between the positive direction of the second +* axis and the continuation of the geodesic curve at the requested +* end point. Positive rotation is in the sense of rotation from +* the positive direction of axis 2 to the positive direction of axis +* 1. + +* Notes: +* - The geodesic curve used by this function is the path of +* shortest distance between two points, as defined by the +* astDistance function. +* - This function will return "bad" coordinate values (AST__BAD) +* if any of the input coordinates has this value. +*/ + +/* Local Variables: */ + AstSkyFrame *this; /* Pointer to the SkyFrame structure */ + const int *perm; /* Pointer to axis permutation array */ + double p1[ 2 ]; /* Permuted coordinates for point1 */ + double p2[ 2 ]; /* Permuted coordinates for point2 */ + double result; /* The returned answer */ + double cosoff; /* Cosine of offset */ + double cosa1; /* Cosine of longitude at start */ + double cosb1; /* Cosine of latitude at start */ + double pa; /* A position angle measured from north */ + double q1[ 3 ]; /* Vector PI/2 away from R4 in meridian of R4 */ + double q2[ 3 ]; /* Vector PI/2 away from R4 on equator */ + double q3[ 3 ]; /* Vector PI/2 away from R4 on great circle */ + double r0[ 3 ]; /* Reference position vector */ + double r3[ 3 ]; /* Vector PI/2 away from R0 on great circle */ + double sinoff; /* Sine of offset */ + double sina1; /* Sine of longitude at start */ + double sinb1; /* Sine of latitude at start */ + +/* Initialise. */ + result = AST__BAD; + +/* Check the global error status. */ + if ( !astOK ) return result; + +/* Obtain a pointer to the SkyFrame structure. */ + this = (AstSkyFrame *) this_frame; + +/* Obtain a pointer to the SkyFrame's axis permutation array. */ + perm = astGetPerm( this ); + if ( astOK ) { + +/* Check that all supplied values are OK. If not, generate "bad" + output coordinates. */ + if ( ( point1[ 0 ] == AST__BAD ) || ( point1[ 1 ] == AST__BAD ) || + ( angle == AST__BAD ) || ( offset == AST__BAD ) ) { + point2[ 0 ] = AST__BAD; + point2[ 1 ] = AST__BAD; + +/* Otherwise, apply the axis permutation array to obtain the + coordinates of the starting point in the required (longitude,latitude) + order. */ + } else { + p1[ perm[ 0 ] ] = point1[ 0 ]; + p1[ perm[ 1 ] ] = point1[ 1 ]; + +/* If the axes are permuted, convert the supplied angle into a position + angle. */ + pa = ( perm[ 0 ] == 0 )? angle: piby2 - angle; + +/* Use Shcal to calculate the required vectors R0 (representing + the reference point) and R3 (representing the point which is 90 + degrees away from the reference point, along the required great + circle). The XY plane defines zero latitude, Z is in the direction + of increasing latitude, X is towards zero longitude, and Y is + towards longitude 90 degrees. */ + Shcal( p1[ 0 ], p1[ 1 ], pa, r0, r3, status ); + +/* Use Shapp to use R0 and R3 to calculate the new position. */ + Shapp( offset, r0, r3, p1[ 0 ], p2, status ); + +/* Normalize the result. */ + astNorm( this, p2 ); + +/* Create the vector Q1 representing the point in the meridian of the + required point which has latitude 90 degrees greater than the + required point. */ + sina1 = sin( p2[ 0 ] ); + cosa1 = cos( p2[ 0 ] ); + sinb1 = sin( p2[ 1 ] ); + cosb1 = cos( p2[ 1 ] ); + + q1[ 0 ] = -sinb1*cosa1; + q1[ 1 ] = -sinb1*sina1; + q1[ 2 ] = cosb1; + +/* Create the vector Q2 representing the point on the equator (i.e. a + latitude of zero), which has a longitude 90 degrees to the west of + the required point. */ + q2[ 0 ] = -sina1; + q2[ 1 ] = cosa1; + q2[ 2 ] = 0.0; + +/* Create the vector Q3 representing the point which is 90 degrees away + from the required point, along the required great circle. */ + cosoff = cos( offset ); + sinoff = sin( offset ); + + q3[ 0 ] = -sinoff*r0[ 0 ] + cosoff*r3[ 0 ]; + q3[ 1 ] = -sinoff*r0[ 1 ] + cosoff*r3[ 1 ]; + q3[ 2 ] = -sinoff*r0[ 2 ] + cosoff*r3[ 2 ]; + +/* Calculate the position angle of the great circle at the required + point. */ + pa = atan2( palDvdv( q3, q2 ), palDvdv( q3, q1 ) ); + +/* Convert this from a pa into the required angle. */ + result = ( perm[ 0 ] == 0 )? pa: piby2 - pa; + +/* Ensure that the end angle is in the range 0 to 2*pi. */ + result = palDranrm( result ); + +/* Permute the result coordinates to undo the effect of the SkyFrame + axis permutation array. */ + point2[ 0 ] = p2[ perm[ 0 ] ]; + point2[ 1 ] = p2[ perm[ 1 ] ]; + } + } + +/* Return the result. */ + return result; + +} + +static void Overlay( AstFrame *template, const int *template_axes, + AstFrame *result, int *status ) { +/* +* Name: +* Overlay + +* Purpose: +* Overlay the attributes of a template SkyFrame on to another Frame. + +* Type: +* Private function. + +* Synopsis: +* #include "skyframe.h" +* void Overlay( AstFrame *template, const int *template_axes, +* AstFrame *result, int *status ) + +* Class Membership: +* SkyFrame member function (over-rides the protected astOverlay method +* inherited from the Frame class). + +* Description: +* This function overlays attributes of a SkyFrame (the "template") on to +* another Frame, so as to over-ride selected attributes of that second +* Frame. Normally only those attributes which have been specifically set +* in the template will be transferred. This implements a form of +* defaulting, in which a Frame acquires attributes from the template, but +* retains its original attributes (as the default) if new values have not +* previously been explicitly set in the template. +* +* Note that if the result Frame is a SkyFrame and a change of sky +* coordinate system occurs as a result of overlaying its System +* attribute, then some of its original attribute values may no +* longer be appropriate (e.g. the Title, or attributes describing +* its axes). In this case, these will be cleared before overlaying +* any new values. + +* Parameters: +* template +* Pointer to the template SkyFrame, for which values should have been +* explicitly set for any attribute which is to be transferred. +* template_axes +* Pointer to an array of int, with one element for each axis of the +* "result" Frame (see below). For each axis in the result frame, the +* corresponding element of this array should contain the (zero-based) +* index of the template axis to which it corresponds. This array is used +* to establish from which template axis any axis-dependent attributes +* should be obtained. +* +* If any axis in the result Frame is not associated with a template +* axis, the corresponding element of this array should be set to -1. +* +* If a NULL pointer is supplied, the template and result axis +* indices are assumed to be identical. +* result +* Pointer to the Frame which is to receive the new attribute values. +* status +* Pointer to the inherited status variable. + +* Returned Value: +* void + +* Notes: +* - In general, if the result Frame is not from the same class as the +* template SkyFrame, or from a class derived from it, then attributes may +* exist in the template SkyFrame which do not exist in the result Frame. In +* this case, these attributes will not be transferred. +*/ + + +/* Local Variables: */ + AstSystemType new_alignsystem;/* Code identifying new alignment coords */ + AstSystemType new_system; /* Code identifying new sky cordinates */ + AstSystemType old_system; /* Code identifying old sky coordinates */ + int axis; /* Loop counter for result SkyFrame axes */ + int skyref_changed; /* Has the SkyRef attribute changed? */ + int reset_system; /* Was the template System value cleared? */ + int skyframe; /* Result Frame is a SkyFrame? */ + int tax0; /* Template axis for result axis 0 */ + int tax1; /* Template axis for result axis 1 */ + +/* Check the global error status. */ + if ( !astOK ) return; + +/* Indicate that we do not need to reset the System attribute of the + template. */ + reset_system = 0; + new_system = AST__UNKNOWN; + +/* If the result Frame is a SkyFrame, we must test to see if overlaying its + System attribute will change the type of sky coordinate system it + describes. Determine the value of this attribute for the result and template + SkyFrames. We also need to do this if either SkyRef attribute would + change. */ + skyframe = astIsASkyFrame( result ); + if ( skyframe ) { + old_system = astGetSystem( result ); + new_system = astGetSystem( template ); + skyref_changed = ( astGetSkyRef( result, 0 ) != + astGetSkyRef( template, 0 ) ) || + ( astGetSkyRef( result, 1 ) != + astGetSkyRef( template, 1 ) ); + +/* If the coordinate system will change, any value already set for the result + SkyFrame's Title will no longer be appropriate, so clear it. */ + if ( new_system != old_system || skyref_changed ) { + astClearTitle( result ); + +/* Test if the old and new sky coordinate systems are similar enough to make + use of the same axis attribute values (e.g. if they are both equatorial + systems, then they can both use the same axis labels, etc.,so long as + the SKyRefIs value has not changed). */ + if ( IsEquatorial( new_system, status ) != IsEquatorial( old_system, status ) || + skyref_changed ) { + +/* If necessary, clear inappropriate values for all those axis attributes + whose access functions are over-ridden by this class (these access functions + will then provide suitable defaults appropriate to the new coordinate system + instead). */ + for ( axis = 0; axis < 2; axis++ ) { + astClearAsTime( result, axis ); + astClearDirection( result, axis ); + astClearFormat( result, axis ); + astClearLabel( result, axis ); + astClearSymbol( result, axis ); + astClearUnit( result, axis ); + } + } + } + +/* If the result Frame is not a SkyFrame, we must temporarily clear the + System and AlignSystem values since the values used by this class are only + appropriate to this class. */ + } else { + if( astTestSystem( template ) ) { + new_system = astGetSystem( template ); + astClearSystem( template ); + new_alignsystem = astGetAlignSystem( template ); + astClearAlignSystem( template ); + reset_system = 1; + } + } + +/* Invoke the parent class astOverlay method to transfer attributes inherited + from the parent class. */ + (*parent_overlay)( template, template_axes, result, status ); + +/* Reset the System and AlignSystem values if necessary */ + if( reset_system ) { + astSetSystem( template, new_system ); + astSetAlignSystem( template, new_alignsystem ); + } + +/* Check if the result Frame is a SkyFrame or from a class derived from + SkyFrame. If not, we cannot transfer SkyFrame attributes to it as it is + insufficiently specialised. In this case simply omit these attributes. */ + if ( skyframe && astOK ) { + +/* Define a macro that tests whether an attribute is set in the template and, + if so, transfers its value to the result. */ +#define OVERLAY(attr) \ + if ( astTest##attr( template ) ) { \ + astSet##attr( result, astGet##attr( template ) ); \ + } + +/* Store template axis indices */ + if( template_axes ) { + tax0 = template_axes[ 0 ]; + tax1 = template_axes[ 1 ]; + } else { + tax0 = 0; + tax1 = 1; + } + +/* Define a similar macro that does the same for SkyFrame specific axis + attributes. */ +#define OVERLAY2(attr) \ + if( astTest##attr( template, tax0 ) ) { \ + astSet##attr( result, 0, astGet##attr( template, tax0 ) ); \ + } \ + if( astTest##attr( template, tax1 ) ) { \ + astSet##attr( result, 1, astGet##attr( template, tax1 ) ); \ + } + +/* Use the macro to transfer each SkyFrame attribute in turn. */ + OVERLAY(Equinox); + OVERLAY(Projection); + OVERLAY(NegLon); + OVERLAY(SkyTol); + OVERLAY(AlignOffset); + OVERLAY(SkyRefIs); + OVERLAY2(SkyRef); + OVERLAY2(SkyRefP); + } + +/* Undefine macros local to this function. */ +#undef OVERLAY +#undef OVERLAY2 +} + +static void Resolve( AstFrame *this_frame, const double point1[], + const double point2[], const double point3[], + double point4[], double *d1, double *d2, int *status ){ +/* +* Name: +* Resolve + +* Purpose: +* Resolve a vector into two orthogonal components + +* Type: +* Private function. + +* Synopsis: +* #include "skyframe.h" +* void Resolve( AstFrame *this, const double point1[], +* const double point2[], const double point3[], +* double point4[], double *d1, double *d2, int *status ) + +* Class Membership: +* SkyFrame member function (over-rides the astResolve method +* inherited from the Frame class). + +* Description: +* This function resolves a vector into two perpendicular components. +* The vector from point 1 to point 2 is used as the basis vector. +* The vector from point 1 to point 3 is resolved into components +* parallel and perpendicular to this basis vector. The lengths of the +* two components are returned, together with the position of closest +* aproach of the basis vector to point 3. +* +* Each vector is a geodesic curve. For a SkyFrame, these are great +* circles on the celestial sphere. + +* Parameters: +* this +* Pointer to the Frame. +* point1 +* An array of double, with one element for each Frame axis +* (Naxes attribute). This marks the start of the basis vector, +* and of the vector to be resolved. +* point2 +* An array of double, with one element for each Frame axis +* (Naxes attribute). This marks the end of the basis vector. +* point3 +* An array of double, with one element for each Frame axis +* (Naxes attribute). This marks the end of the vector to be +* resolved. +* point4 +* An array of double, with one element for each Frame axis +* in which the coordinates of the point of closest approach of the +* basis vector to point 3 will be returned. +* d1 +* The address of a location at which to return the distance from +* point 1 to point 4 (that is, the length of the component parallel +* to the basis vector). Positive values are in the same sense as +* movement from point 1 to point 2. +* d2 +* The address of a location at which to return the distance from +* point 4 to point 3 (that is, the length of the component +* perpendicular to the basis vector). The returned value is always +* positive. +* status +* Pointer to the inherited status variable. + +* Notes: +* - This function will return "bad" coordinate values (AST__BAD) +* if any of the input coordinates has this value, or if the required +* output values are undefined. +*/ + +/* Local Variables: */ + AstSkyFrame *this; /* Pointer to the SkyFrame structure */ + const int *perm; /* Pointer to axis permutation array */ + double n1[ 3 ]; /* Unit normal to grt crcl thru p1 and p2 */ + double n2[ 3 ]; /* Unit normal to grt crcl thru p3 and p4 */ + double p1[ 2 ]; /* Permuted coordinates for point1 */ + double p2[ 2 ]; /* Permuted coordinates for point2 */ + double p3[ 2 ]; /* Permuted coordinates for point3 */ + double p4[ 2 ]; /* Permuted coordinates for point4 */ + double v1[ 3 ]; /* 3-vector for p1 */ + double v2[ 3 ]; /* 3-vector for p2 */ + double v3[ 3 ]; /* 3-vector for p3 */ + double v4[ 3 ]; /* 3-vector for p4 */ + double v5[ 3 ]; /* 3-vector 90 degs away from p1 */ + double vmod; /* Modulus of vector */ + double vtemp[ 3 ]; /* Temporary vector workspace */ + +/* Check the global error status. */ + if ( !astOK ) return; + +/* Obtain a pointer to the SkyFrame structure. */ + this = (AstSkyFrame *) this_frame; + +/* Store initial bad output values. */ + point4[ 0 ] = AST__BAD; + point4[ 1 ] = AST__BAD; + *d1 = AST__BAD; + *d2 = AST__BAD; + +/* Check that all supplied values are OK. */ + if ( ( point1[ 0 ] != AST__BAD ) && ( point1[ 1 ] != AST__BAD ) && + ( point2[ 0 ] != AST__BAD ) && ( point2[ 1 ] != AST__BAD ) && + ( point3[ 0 ] != AST__BAD ) && ( point3[ 1 ] != AST__BAD ) ) { + +/* If so, obtain a pointer to the SkyFrame's axis permutation array. */ + perm = astGetPerm( this ); + if ( astOK ) { + +/* Apply the axis permutation array to obtain the coordinates of the + three supplied point in the required (longitude,latitude) order. */ + p1[ perm[ 0 ] ] = point1[ 0 ]; + p1[ perm[ 1 ] ] = point1[ 1 ]; + p2[ perm[ 0 ] ] = point2[ 0 ]; + p2[ perm[ 1 ] ] = point2[ 1 ]; + p3[ perm[ 0 ] ] = point3[ 0 ]; + p3[ perm[ 1 ] ] = point3[ 1 ]; + +/* Convert each point into a 3-vector of unit length. */ + palDcs2c( p1[ 0 ], p1[ 1 ], v1 ); + palDcs2c( p2[ 0 ], p2[ 1 ], v2 ); + palDcs2c( p3[ 0 ], p3[ 1 ], v3 ); + +/* Find the cross product between the first two vectors, and normalize is. + This is the unit normal to the great circle plane defining parallel + distance. */ + palDvxv( v2, v1, vtemp ); + palDvn( vtemp, n1, &vmod ); + +/* Return with bad values if the normal is undefined (i.e. if the first two + vectors are identical or diametrically opposite). */ + if( vmod > 0.0 ) { + +/* Now take the cross product of the normal vector and v1. This gives a + point, v5, on the great circle which is 90 degrees away from v1, in the + direction of v2. */ + palDvxv( v1, n1, v5 ); + +/* Find the cross product of the outlying point (point 3), and the vector + n1 found above, and normalize it. This is the unit normal to the great + circle plane defining perpendicular distance. */ + palDvxv( v3, n1, vtemp ); + palDvn( vtemp, n2, &vmod ); + +/* Return with bad values if the normal is undefined (i.e. if the + outlying point is normal to the great circle defining the basis + vector). */ + if( vmod > 0.0 ) { + +/* The point of closest approach, point 4, is the point which is normal + to both normal vectors (i.e. the intersection of the two great circles). + This is the cross product of n1 and n2. No need to normalize this time + since both n1 and n2 are unit vectors, and so v4 will already be a + unit vector. */ + palDvxv( n1, n2, v4 ); + +/* The dot product of v4 and v1 is the cos of the parallel distance, + d1, whilst the dot product of v4 and v5 is the sin of the parallel + distance. Use these to get the parallel distance with the correct + sign, in the range -PI to +PI. */ + *d1 = atan2( palDvdv( v4, v5 ), palDvdv( v4, v1 ) ); + +/* The dot product of v4 and v3 is the cos of the perpendicular distance, + d2, whilst the dot product of n1 and v3 is the sin of the perpendicular + distance. Use these to get the perpendicular distance. */ + *d2 = fabs( atan2( palDvdv( v3, n1 ), palDvdv( v3, v4 ) ) ); + +/* Convert the 3-vector representing the intersection of the two planes + back into spherical cooordinates and then constrain the longitude result + to lie in the range 0 to 2*pi. */ + palDcc2s( v4, &p4[ 0 ], &p4[ 1 ] ); + p4[ 0 ] = palDranrm( p4[ 0 ] ); + +/* Permute the result coordinates to undo the effect of the SkyFrame + axis permutation array. */ + point4[ 0 ] = p4[ perm[ 0 ] ]; + point4[ 1 ] = p4[ perm[ 1 ] ]; + } + } + } + } + + return; + +} + +static AstPointSet *ResolvePoints( AstFrame *this_frame, const double point1[], + const double point2[], AstPointSet *in, + AstPointSet *out, int *status ) { +/* +* Name: +* ResolvePoints + +* Purpose: +* Resolve a set of vectors into orthogonal components + +* Type: +* Private function. + +* Synopsis: +* #include "frame.h" +* AstPointSet *astResolvePoints( AstFrame *this, const double point1[], +* const double point2[], AstPointSet *in, +* AstPointSet *out ) + +* Class Membership: +* SkyFrame member function (over-rides the astResolvePoints method +* inherited from the Frame class). + +* Description: +* This function takes a Frame and a set of vectors encapsulated +* in a PointSet, and resolves each one into two orthogonal components, +* returning these two components in another PointSet. +* +* This is exactly the same as the public astResolve method, except +* that this method allows many vectors to be processed in a single call, +* thus reducing the computational cost of overheads of many +* individual calls to astResolve. + +* Parameters: +* this +* Pointer to the Frame. +* point1 +* An array of double, with one element for each Frame axis +* (Naxes attribute). This marks the start of the basis vector, +* and of the vectors to be resolved. +* point2 +* An array of double, with one element for each Frame axis +* (Naxes attribute). This marks the end of the basis vector. +* in +* Pointer to the PointSet holding the ends of the vectors to be +* resolved. +* out +* Pointer to a PointSet which will hold the length of the two +* resolved components. A NULL value may also be given, in which +* case a new PointSet will be created by this function. + +* Returned Value: +* Pointer to the output (possibly new) PointSet. The first axis will +* hold the lengths of the vector components parallel to the basis vector. +* These values will be signed (positive values are in the same sense as +* movement from point 1 to point 2. The second axis will hold the lengths +* of the vector components perpendicular to the basis vector. These +* values will be signed only if the Frame is 2-dimensional, in which +* case a positive value indicates that rotation from the basis vector +* to the tested vector is in the same sense as rotation from the first +* to the second axis of the Frame. + +* Notes: +* - The number of coordinate values per point in the input +* PointSet must match the number of axes in the supplied Frame. +* - If an output PointSet is supplied, it must have space for +* sufficient number of points and 2 coordinate values per point. +* - A null pointer will be returned if this function is invoked +* with the global error status set, or if it should fail for any +* reason. +* - We assume spherical geometry throughout this function. +*/ + +/* Local Variables: */ + AstPointSet *result; /* Pointer to output PointSet */ + AstSkyFrame *this; /* Pointer to SkyFrame structure */ + const int *perm; /* Pointer to axis permutation array */ + double **ptr_in; /* Pointers to input axis values */ + double **ptr_out; /* Pointers to returned axis values */ + double *d1; /* Pointer to next parallel component value */ + double *d2; /* Pointer to next perpendicular component value */ + double *point3x; /* Pointer to next first axis value */ + double *point3y; /* Pointer to next second axis value */ + double n1[ 3 ]; /* Unit normal to grt crcl thru p1 and p2 */ + double n2[ 3 ]; /* Unit normal to grt crcl thru p3 and p4 */ + double p1[ 2 ]; /* Permuted coordinates for point1 */ + double p2[ 2 ]; /* Permuted coordinates for point2 */ + double p3[ 2 ]; /* Permuted coordinates for point3 */ + double sign; /* Sign for perpendicular distances */ + double v1[ 3 ]; /* 3-vector for p1 */ + double v2[ 3 ]; /* 3-vector for p2 */ + double v3[ 3 ]; /* 3-vector for p3 */ + double v4[ 3 ]; /* 3-vector for p4 */ + double v5[ 3 ]; /* 3-vector 90 degs away from p1 */ + double vmod; /* Modulus of vector */ + double vtemp[ 3 ]; /* Temporary vector workspace */ + int ipoint; /* Index of next point */ + int ncoord_in; /* Number of input PointSet coordinates */ + int ncoord_out; /* Number of coordinates in output PointSet */ + int npoint; /* Number of points to transform */ + int npoint_out; /* Number of points in output PointSet */ + int ok; /* OK to proceed? */ + +/* Initialise. */ + result = NULL; + +/* Check the global error status. */ + if ( !astOK ) return result; + +/* Get a pointer to the SkyFrame structure. */ + this = (AstSkyFrame *) this_frame; + +/* Obtain the number of input vectors to resolve and the number of coordinate + values per vector. */ + npoint = astGetNpoint( in ); + ncoord_in = astGetNcoord( in ); + +/* If OK, check that the number of input coordinates matches the number + required by the Frame. Report an error if these numbers do not match. */ + if ( astOK && ( ncoord_in != 2 ) ) { + astError( AST__NCPIN, "astResolvePoints(%s): Bad number of coordinate " + "values (%d) in input %s.", status, astGetClass( this ), ncoord_in, + astGetClass( in ) ); + astError( AST__NCPIN, "The %s given requires 2 coordinate values for " + "each input point.", status, astGetClass( this ) ); + } + +/* If still OK, and a non-NULL pointer has been given for the output PointSet, + then obtain the number of points and number of coordinates per point for + this PointSet. */ + if ( astOK && out ) { + npoint_out = astGetNpoint( out ); + ncoord_out = astGetNcoord( out ); + +/* Check that the dimensions of this PointSet are adequate to accommodate the + output coordinate values and report an error if they are not. */ + if ( astOK ) { + if ( npoint_out < npoint ) { + astError( AST__NOPTS, "astResolvePoints(%s): Too few points (%d) in " + "output %s.", status, astGetClass( this ), npoint_out, + astGetClass( out ) ); + astError( AST__NOPTS, "The %s needs space to hold %d transformed " + "point(s).", status, astGetClass( this ), npoint ); + } else if ( ncoord_out < 2 ) { + astError( AST__NOCTS, "astResolvePoints(%s): Too few coordinate " + "values per point (%d) in output %s.", status, + astGetClass( this ), ncoord_out, astGetClass( out ) ); + astError( AST__NOCTS, "The %s supplied needs space to store 2 " + "coordinate value(s) per transformed point.", status, + astGetClass( this ) ); + } + } + } + +/* If all the validation stages are passed successfully, and a NULL output + pointer was given, then create a new PointSet to encapsulate the output + coordinate data. */ + if ( astOK ) { + if ( !out ) { + result = astPointSet( npoint, 2, "", status ); + +/* Otherwise, use the PointSet supplied. */ + } else { + result = out; + } + } + +/* Get pointers to the input and output axis values */ + ptr_in = astGetPoints( in ); + ptr_out = astGetPoints( result ); + +/* Obtain a pointer to the SkyFrame's axis permutation array. */ + perm = astGetPerm( this ); + +/* If the axes have been swapped we need to swap the sign of the returned + perpendicular distances. */ + sign = ( perm[ 0 ] == 0 ) ? -1.0 : 1.0; + +/* Check pointers can be used safely */ + if( astOK ) { + +/* Apply the axis permutation array to obtain the coordinates of the + two supplied points in the required (longitude,latitude) order. */ + p1[ perm[ 0 ] ] = point1[ 0 ]; + p1[ perm[ 1 ] ] = point1[ 1 ]; + p2[ perm[ 0 ] ] = point2[ 0 ]; + p2[ perm[ 1 ] ] = point2[ 1 ]; + +/* Convert these points into 3-vectors of unit length. */ + palDcs2c( p1[ 0 ], p1[ 1 ], v1 ); + palDcs2c( p2[ 0 ], p2[ 1 ], v2 ); + +/* Find the cross product between the vectors, and normalize it. This is the + unit normal to the great circle plane defining parallel distance. */ + palDvxv( v2, v1, vtemp ); + palDvn( vtemp, n1, &vmod ); + +/* Return with bad values if the normal is undefined (i.e. if the first two + vectors are identical or diametrically opposite). */ + ok = 0; + if( vmod > 0.0 ) { + ok = 1; + +/* Now take the cross product of the normal vector and v1. This gives a + point, v5, on the great circle which is 90 degrees away from v1, in the + direction of v2. */ + palDvxv( v1, n1, v5 ); + } + +/* Store pointers to the first two axis arrays in the returned PointSet. */ + d1 = ptr_out[ 0 ]; + d2 = ptr_out[ 1 ]; + +/* Store pointers to the axis values in the supplied PointSet. */ + point3x = ptr_in[ 0 ]; + point3y = ptr_in[ 1 ]; + +/* Check supplied values can be used */ + if( ok ) { + +/* Loop round each supplied vector. */ + for( ipoint = 0; ipoint < npoint; ipoint++, d1++, d2++, + point3x++, point3y++ ) { + +/* Store bad output values if either input axis value is bad. */ + if( *point3x == AST__BAD || *point3y == AST__BAD ){ + *d1 = AST__BAD; + *d2 = AST__BAD; + +/* If both are good... */ + } else { + +/* Apply the axis permutation array to obtain the coordinates in the + required (longitude,latitude) order. */ + p3[ perm[ 0 ] ] = *point3x; + p3[ perm[ 1 ] ] = *point3y; + +/* Convert into a 3-vector of unit length. */ + palDcs2c( p3[ 0 ], p3[ 1 ], v3 ); + +/* Find the cross product of the outlying point (point 3), and the vector + n1 found above, and normalize it. This is the unit normal to the great + circle plane defining perpendicular distance. */ + palDvxv( v3, n1, vtemp ); + palDvn( vtemp, n2, &vmod ); + +/* Return with bad values if the normal is undefined (i.e. if the + outlying point is normal to the great circle defining the basis + vector). */ + if( vmod <= 0.0 ) { + *d1 = AST__BAD; + *d2 = AST__BAD; + } else { + +/* The point of closest approach, point 4, is the point which is normal + to both normal vectors (i.e. the intersection of the two great circles). + This is the cross product of n1 and n2. No need to normalize this time + since both n1 and n2 are unit vectors, and so v4 will already be a + unit vector. */ + palDvxv( n1, n2, v4 ); + +/* The dot product of v4 and v1 is the cos of the parallel distance, + d1, whilst the dot product of v4 and v5 is the sin of the parallel + distance. Use these to get the parallel distance with the correct + sign, in the range -PI to +PI. */ + *d1 = atan2( palDvdv( v4, v5 ), palDvdv( v4, v1 ) ); + +/* The dot product of v4 and v3 is the cos of the perpendicular distance, + d2, whilst the dot product of n1 and v3 is the sin of the perpendicular + distance. Use these to get the perpendicular distance. */ + *d2 = sign*atan2( palDvdv( v3, n1 ), palDvdv( v3, v4 ) ); + } + } + } + +/* If supplied values cannot be used, fill the returned PointSet with bad + values */ + } else { + for( ipoint = 0; ipoint < npoint; ipoint++, d1++, d2++ ) { + *d1 = AST__BAD; + *d2 = AST__BAD; + } + } + } + +/* Annul the returned PointSet if an error occurred. */ + if( !astOK ) result = astAnnul( result ); + +/* Return a pointer to the output PointSet. */ + return result; +} + +static void SetAsTime( AstSkyFrame *this, int axis, int value, int *status ) { +/* +* Name: +* SetAsTime + +* Purpose: +* Set a value for the AsTime attribute for a SkyFrame's axis. + +* Type: +* Private function. + +* Synopsis: +* #include "skyframe.h" +* void SetAsTime( AstSkyFrame *this, int axis, int value, int *status ) + +* Class Membership: +* SkyFrame member function. + +* Description: +* This function sets the boolean value of the AsTime attribute for a +* specified axis of a SkyFrame. This value indicates whether axis values +* should be formatted as times (as opposed to angles) by default. + +* Parameters: +* this +* Pointer to the SkyFrame. +* axis +* Index of the axis for which a value is to be set (zero based). +* value +* The boolean value to be set. +* status +* Pointer to the inherited status variable. + +* Returned Value: +* void. +*/ + +/* Local Variables: */ + AstAxis *ax; /* Pointer to Axis object */ + AstSkyAxis *new_ax; /* Pointer to new SkyAxis object */ + +/* Check the global error status. */ + if ( !astOK ) return; + +/* Validate the axis index. */ + (void) astValidateAxis( this, axis, 1, "astSetAsTime" ); + +/* Obtain a pointer to the Axis object. */ + ax = astGetAxis( this, axis ); + +/* Check if the Axis object is a SkyAxis. If not, we will replace it with + one. */ + if ( !astIsASkyAxis( ax ) ) { + +/* Create a new SkyAxis and overlay the attributes of the original Axis. */ + new_ax = astSkyAxis( "", status ); + astAxisOverlay( ax, new_ax ); + +/* Modify the SkyFrame to use the new Skyaxis and annul the original Axis + pointer. Retain a pointer to the new SkyAxis. */ + astSetAxis( this, axis, new_ax ); + ax = astAnnul( ax ); + ax = (AstAxis *) new_ax; + } + +/* Set a value for the Axis AsTime attribute. */ + astSetAxisAsTime( ax, value ); + +/* Annul the Axis pointer. */ + ax = astAnnul( ax ); +} + +static void SetAttrib( AstObject *this_object, const char *setting, int *status ) { +/* +* Name: +* SetAttrib + +* Purpose: +* Set an attribute value for a SkyFrame. + +* Type: +* Private function. + +* Synopsis: +* #include "skyframe.h" +* void SetAttrib( AstObject *this, const char *setting, int *status ) + +* Class Membership: +* SkyFrame member function (extends the astSetAttrib method inherited from +* the Mapping class). + +* Description: +* This function assigns an attribute value for a SkyFrame, the attribute +* and its value being specified by means of a string of the form: +* +* "attribute= value " +* +* Here, "attribute" specifies the attribute name and should be in lower +* case with no white space present. The value to the right of the "=" +* should be a suitable textual representation of the value to be assigned +* and this will be interpreted according to the attribute's data type. +* White space surrounding the value is only significant for string +* attributes. + +* Parameters: +* this +* Pointer to the SkyFrame. +* setting +* Pointer to a null terminated string specifying the new attribute +* value. +* status +* Pointer to the inherited status variable. + +* Returned Value: +* void + +* Attributes: +* As well as those attributes inherited from the parent class, this +* function also accepts values for the following additional attributes: +* +* Equinox (double, read as a string) + +* Notes: +* This protected method is intended to be invoked by the Object astSet +* method and makes additional attributes accessible to it. +*/ + +/* Local Vaiables: */ + AstSkyFrame *this; /* Pointer to the SkyFrame structure */ + double dval; /* Floating point attribute value */ + double dval1; /* Floating point attribute value */ + double dval2; /* Floating point attribute value */ + double mjd; /* Modified Julian Date */ + int astime; /* Value of AsTime attribute */ + int axis; /* Axis index */ + int equinox; /* Offset of Equinox attribute value */ + int ival; /* Integer attribute value */ + int len; /* Length of setting string */ + int nc; /* Number of characters read by astSscanf */ + int neglon; /* Display -ve longitudes? */ + int ok; /* Can string be used? */ + int offset; /* Offset of start of attribute value */ + int projection; /* Offset of projection attribute value */ + +/* Check the global error status. */ + if ( !astOK ) return; + +/* Obtain a pointer to the SkyFrame structure. */ + this = (AstSkyFrame *) this_object; + +/* Obtain the length of the setting string. */ + len = strlen( setting ); + +/* Test for each recognised attribute in turn, using "astSscanf" to parse the + setting string and extract the attribute value (or an offset to it in the + case of string values). In each case, use the value set in "nc" to check + that the entire string was matched. Once a value has been obtained, use the + appropriate method to set it. */ + +/* AsTime(axis). */ +/* ------------- */ + if ( nc = 0, + ( 2 == astSscanf( setting, "astime(%d)= %d %n", &axis, &astime, &nc ) ) + && ( nc >= len ) ) { + astSetAsTime( this, axis - 1, astime ); + +/* Equinox. */ +/* -------- */ + } else if ( nc = 0, + ( 0 == astSscanf( setting, "equinox=%n%*[^\n]%n", + &equinox, &nc ) ) && ( nc >= len ) ) { + +/* Convert the Equinox value to a Modified Julian Date before use. */ + mjd = astReadDateTime( setting + equinox ); + if ( astOK ) { + astSetEquinox( this, mjd ); + +/* Report contextual information if the conversion failed. */ + } else { + astError( AST__ATTIN, "astSetAttrib(%s): Invalid equinox value " + "\"%s\" given for sky coordinate system.", status, + astGetClass( this ), setting + equinox ); + } + +/* NegLon. */ +/* ------- */ + } else if ( nc = 0, + ( 1 == astSscanf( setting, "neglon= %d %n", &neglon, &nc ) ) + && ( nc >= len ) ) { + astSetNegLon( this, neglon ); + +/* SkyTol. */ +/* ------- */ + } else if ( nc = 0, + ( 1 == astSscanf( setting, "skytol= %lg %n", &dval, &nc ) ) + && ( nc >= len ) ) { + astSetSkyTol( this, dval ); + +/* Projection. */ +/* ----------- */ + } else if ( nc = 0, + ( 0 == astSscanf( setting, "projection=%n%*[^\n]%n", + &projection, &nc ) ) + && ( nc >= len ) ) { + astSetProjection( this, setting + projection ); + +/* SkyRef. */ +/* ------- */ + } else if ( nc = 0, + ( 0 == astSscanf( setting, "skyref=%n%*[^\n]%n", + &offset, &nc ) ) + && ( nc >= len ) ) { + ok = 0; + nc = astUnformat( this, 0, setting + offset, &dval1 ); + if( setting[ offset + nc ] == ',' ) { + nc++; + nc += astUnformat( this, 1, setting + offset + nc, &dval2 ); + if( nc == strlen( setting + offset ) ) { + astSetSkyRef( this, 0, dval1 ); + astSetSkyRef( this, 1, dval2 ); + ok = 1; + } + } + + if( !ok && astOK ) { + astError( AST__BADOC, "astSetAttrib(%s): Invalid axis values string " + "\"%.*s\" given for SkyRef attribute.", status, astGetClass( this ), + (int) astChrLen( setting + offset ), setting + offset ); + } + +/* SkyRef(axis). */ +/* ------------- */ + } else if ( nc = 0, + ( 2 == astSscanf( setting, "skyref(%d)= %lg %n", + &axis, &dval, &nc ) ) + && ( nc >= len ) ) { + astSetSkyRef( this, axis - 1, dval ); + +/* SkyRefIs. */ +/* --------- */ + } else if ( nc = 0, + ( 0 == astSscanf( setting, "skyrefis=%n%*[^\n]%n", + &offset, &nc ) ) + && ( nc >= len ) ) { + + if( astChrMatch( setting + offset, POLE_STRING ) ) { + astSetSkyRefIs( this, AST__POLE_REF ); + + } else if( astChrMatch( setting + offset, ORIGIN_STRING ) ) { + astSetSkyRefIs( this, AST__ORIGIN_REF ); + + } else if( astChrMatch( setting + offset, IGNORED_STRING ) ) { + astSetSkyRefIs( this, AST__IGNORED_REF ); + + } else if( astOK ) { + astError( AST__OPT, "astSet(%s): option '%s' is unknown in '%s'.", status, + astGetClass( this ), setting+offset, setting ); + } + +/* SkyRefP. */ +/* -------- */ + } else if ( nc = 0, + ( 0 == astSscanf( setting, "skyrefp=%n%*[^\n]%n", + &offset, &nc ) ) + && ( nc >= len ) ) { + + ok = 0; + nc = astUnformat( this, 0, setting + offset, &dval1 ); + if( setting[ offset + nc ] == ',' ) { + nc++; + nc += astUnformat( this, 1, setting + offset + nc, &dval2 ); + if( nc == strlen( setting + offset ) ) { + astSetSkyRefP( this, 0, dval1 ); + astSetSkyRefP( this, 1, dval2 ); + ok = 1; + } + } + + if( !ok && astOK ) { + astError( AST__BADOC, "astSetAttrib(%s): Invalid axis values string " + "\"%.*s\" given for SkyRefP attribute.", status, astGetClass( this ), + (int) astChrLen( setting + offset ), setting + offset ); + } + + +/* SkyRefP(axis). */ +/* -------------- */ + } else if ( nc = 0, + ( 2 == astSscanf( setting, "skyrefp(%d)= %lg %n", + &axis, &dval, &nc ) ) + && ( nc >= len ) ) { + astSetSkyRefP( this, axis - 1, dval ); + +/* AlignOffset. */ +/* ------------ */ + } else if ( nc = 0, + ( 1 == astSscanf( setting, "alignoffset= %d %n", &ival, &nc ) ) + && ( nc >= len ) ) { + astSetAlignOffset( this, ival ); + +/* Define a macro to see if the setting string matches any of the + read-only attributes of this class. */ +#define MATCH(attrib) \ + ( nc = 0, ( 0 == astSscanf( setting, attrib "=%*[^\n]%n", &nc ) ) && \ + ( nc >= len ) ) + +/* If the attribute was not recognised, use this macro to report an error + if a read-only attribute has been specified. */ + } else if ( !strncmp( setting, "islataxis", 9 ) || + !strncmp( setting, "islonaxis", 9 ) || + MATCH( "lataxis" ) || + MATCH( "lonaxis" ) ) { + astError( AST__NOWRT, "astSet: The setting \"%s\" is invalid for a %s.", status, + setting, astGetClass( this ) ); + astError( AST__NOWRT, "This is a read-only attribute." , status); + +/* Pass any unrecognised setting to the parent method for further + interpretation. */ + } else { + (*parent_setattrib)( this_object, setting, status ); + } +} + +static void SetCachedLAST( AstSkyFrame *this, double last, double epoch, + double obslon, double obslat, double obsalt, + double dut1, double dtai, int *status ) { +/* +* Name: +* SetCachedLAST + +* Purpose: +* Store a LAST value in the cache in the SkyFrame vtab. + +* Type: +* Private function. + +* Synopsis: +* #include "skyframe.h" +* void SetCachedLAST( AstSkyFrame *this, double last, double epoch, +* double obslon, double obslat, double obsalt, +* double dut1, double dtai, int *status ) + +* Class Membership: +* SkyFrame member function. + +* Description: +* This function stores the supplied LAST value in a cache in the +* SkyFrame virtual function table for later use by GetCachedLAST. + +* Parameters: +* this +* Pointer to the SkyFrame. +* last +* The Local Apparent Sidereal Time (radians). +* epoch +* The epoch (MJD). +* obslon +* Observatory geodetic longitude (radians) +* obslat +* Observatory geodetic latitude (radians) +* obsalt +* Observatory geodetic altitude (metres) +* dut1 +* The UT1-UTC correction, in seconds. +* dtai +* The TAI-UTC correction, in seconds. +* status +* Pointer to the inherited status variable. + +*/ + +/* Local Variables: */ + astDECLARE_GLOBALS + AstSkyLastTable *table; + double *ep; + double *lp; + double lp_ref; + int i; + int itable; + +/* Get a pointer to the structure holding thread-specific global data. */ + astGET_GLOBALS(this); + +/* Initialise */ + table = NULL; + +/* Check the global error status. */ + if ( !astOK ) return; + +/* Ensure no threads are allowed to read the table whilst we are writing + to it. */ + LOCK_WLOCK1 + +/* Loop round every LAST table held in the vtab. Each table refers to a + different observatory position and/or DUT1 and/or DTAI value. */ + for( itable = 0; itable < nlast_tables; itable++ ) { + table = last_tables[ itable ]; + +/* See if the table refers to the given position, dut1 and dtai value, allowing + some small tolerance. If it does, leave the loop. */ + if( fabs( table->obslat - obslat ) < 2.0E-7 && + fabs( table->obslon - obslon ) < 2.0E-7 && + fabs( table->obsalt - obsalt ) < 1.0 && + fabs( table->dut1 - dut1 ) < 1.0E-5 && + EQUAL( table->dtai, dtai, 1.0E-5 ) ) break; + +/* Ensure "table" ends up NULL if no suitable table is found. */ + table = NULL; + } + +/* If no table was found, create one now, and add it into the vtab cache. */ + if( !table ) { + + astBeginPM; + table = astMalloc( sizeof( AstSkyLastTable ) ); + itable = nlast_tables++; + last_tables = astGrow( last_tables, nlast_tables, + sizeof( AstSkyLastTable * ) ); + astEndPM; + + if( astOK ) { + last_tables[ itable ] = table; + table->obslat = obslat; + table->obslon = obslon; + table->obsalt = obsalt; + table->dut1 = dut1; + table->dtai = dtai; + table->nentry = 1; + + astBeginPM; + table->epoch = astMalloc( sizeof( double ) ); + table->last = astMalloc( sizeof( double ) ); + astEndPM; + + if( astOK ) { + table->epoch[ 0 ] = epoch; + table->last[ 0 ] = last; + } + } + + +/* If we have a table, add the new point into it. */ + } else { + +/* Extend the epoch and last arrays. */ + astBeginPM; + table->epoch = astGrow( table->epoch, ++(table->nentry), sizeof( double ) ); + table->last = astGrow( table->last, table->nentry, sizeof( double ) ); + astEndPM; + +/* Check memory allocation was successful. */ + if( astOK ) { + +/* Get pointers to the last original elements in the arrays of epoch and + corresponding LAST values in the table. */ + ep = table->epoch + table->nentry - 2; + lp = table->last + table->nentry - 2; + +/* Starting from the end of the arrays, shuffle all entries up one + element until an element is found which is less than the supplied epoch + value. This maintains the epoch array in monotonic increasing order. */ + for( i = table->nentry - 2; i >= 0; i--,ep--,lp-- ) { + if( *ep <= epoch ) break; + ep[ 1 ] = *ep; + lp[ 1 ] = *lp; + } + +/* Store the new epoch and LAST value. Add or subtract 2.PI as needed + from the new LAST value to ensure it is continuous with an adjacent + LAST value. This is needed for interpolation between the two values + to be meaningful. */ + ep[ 1 ] = epoch; + +/* For most cases, compare with the previous LAST value. If the new epoch + value is smaller than any epoch already in the table, there will be no + previous LAST value. So compare with the next value instead. */ + if( i >= 0 ) { + lp_ref = lp[ 0 ]; + } else { + lp_ref = lp[ 2 ]; + } + + if( last > lp_ref + AST__DPI ) { + lp[ 1 ] = last - 2*AST__DPI; + + } else if( last < lp_ref - AST__DPI ) { + lp[ 1 ] = last + 2*AST__DPI; + + } else { + lp[ 1 ] = last; + } + } + } + +/* Indicate other threads are now allowed to read the table. */ + UNLOCK_RWLOCK1 + +} + +static void SetDtai( AstFrame *this_frame, double val, int *status ) { +/* +* Name: +* SetDtai + +* Purpose: +* Set the value of the Dtai attribute for a SkyFrame. + +* Type: +* Private function. + +* Synopsis: +* #include "skyframe.h" +* void SetDtai( AstFrame *this, double val, int *status ) + +* Class Membership: +* SkyFrame member function (over-rides the astSetDtai method +* inherited from the Frame class). + +* Description: +* This function clears the Dtai value and updates the LAST value +* stored in the SkyFrame. + +* Parameters: +* this +* Pointer to the SkyFrame. +* val +* New Dtai value. +* status +* Pointer to the inherited status variable. + +*/ + +/* Local Variables: */ + AstSkyFrame *this; + double orig; + +/* Check the global error status. */ + if ( !astOK ) return; + +/* Obtain a pointer to the SkyFrame structure. */ + this = (AstSkyFrame *) this_frame; + +/* Note the original Dtai value. */ + orig = astGetDtai( this ); + +/* Invoke the parent method to set the Frame Dtai value. */ + (*parent_setdtai)( this_frame, val, status ); + +/* If the DTAI value has changed significantly, indicate that the LAST value + will need to be re-calculated when it is next needed. */ + if( ! EQUAL( orig, val, 1.0E-6 ) ) { + this->last = AST__BAD; + this->eplast = AST__BAD; + this->klast = AST__BAD; + } +} + +static void SetDut1( AstFrame *this_frame, double val, int *status ) { +/* +* Name: +* SetDut1 + +* Purpose: +* Set the value of the Dut1 attribute for a SkyFrame. + +* Type: +* Private function. + +* Synopsis: +* #include "skyframe.h" +* void SetDut1( AstFrame *this, double val, int *status ) + +* Class Membership: +* SkyFrame member function (over-rides the astSetDut1 method +* inherited from the Frame class). + +* Description: +* This function clears the Dut1 value and updates the LAST value +* stored in the SkyFrame. + +* Parameters: +* this +* Pointer to the SkyFrame. +* val +* New Dut1 value. +* status +* Pointer to the inherited status variable. + +*/ + +/* Local Variables: */ + AstSkyFrame *this; + double orig; + +/* Check the global error status. */ + if ( !astOK ) return; + +/* Obtain a pointer to the SkyFrame structure. */ + this = (AstSkyFrame *) this_frame; + +/* Note the original Dut1 value. */ + orig = astGetDut1( this ); + +/* Invoke the parent method to set the Frame Dut1 value. */ + (*parent_setdut1)( this_frame, val, status ); + +/* If the DUT1 value has changed significantly, indicate that the LAST value + will need to be re-calculated when it is next needed. */ + if( fabs( orig - val ) > 1.0E-6 ) { + this->last = AST__BAD; + this->eplast = AST__BAD; + this->klast = AST__BAD; + } +} + +static void SetLast( AstSkyFrame *this, int *status ) { +/* +* Name: +* SetLast + +* Purpose: +* Set the Local Appearent Sidereal Time for a SkyFrame. + +* Type: +* Private function. + +* Synopsis: +* #include "skyframe.h" +* void SetLast( AstSkyFrame *this, int *status ) + +* Class Membership: +* SkyFrame member function. + +* Description: +* This function sets the Local Apparent Sidereal Time at the epoch +* and geographical longitude given by the current values of the Epoch +* and ObsLon attributes associated with the supplied SkyFrame. + +* Parameters: +* this +* Pointer to the SkyFrame. +* status +* Pointer to the inherited status variable. + +* Notes: +* - A value of AST__BAD will be returned if this function is invoked +* with the global error status set, or if it should fail for any reason. +*/ + +/* Local Variables: */ + double epoch; /* Epoch as a TDB MJD */ + +/* Check the global error status. */ + if ( !astOK ) return; + +/* Get the SkyFrame Epoch as a TDB MJD. */ + epoch = astGetEpoch( this ); + +/* Calculate the LAST value (in rads) and store in the SkyFrame structure. */ + this->last = CalcLAST( this, epoch, astGetObsLon( this ), + astGetObsLat( this ), astGetObsAlt( this ), + astGetDut1( this ), astGetDtai( this ), status ); + +/* Save the TDB MJD to which this LAST corresponds. */ + this->eplast = epoch; + +/* The ratio between solar and sidereal time is a slowly varying function + of epoch. The GetLAST function returns a fast approximation to LAST + by using the ratio between solar and sidereal time. Indicate that + GetLAST should re-calculate the ratio by setting the ratio value bad. */ + this->klast = AST__BAD; +} + +static void SetObsAlt( AstFrame *this, double val, int *status ) { +/* +* Name: +* SetObsAlt + +* Purpose: +* Set the value of the ObsAlt attribute for a SkyFrame. + +* Type: +* Private function. + +* Synopsis: +* #include "skyframe.h" +* void SetObsAlt( AstFrame *this, double val, int *status ) + +* Class Membership: +* SkyFrame member function (over-rides the astSetObsAlt method +* inherited from the Frame class). + +* Description: +* This function sets the ObsAlt value. + +* Parameters: +* this +* Pointer to the SkyFrame. +* val +* New ObsAlt value. +* status +* Pointer to the inherited status variable. + +*/ + +/* Local Variables: */ + double orig; + +/* Check the global error status. */ + if ( !astOK ) return; + +/* Note the original ObsAlt value. */ + orig = astGetObsAlt( this ); + +/* Invoke the parent method to set the Frame ObsAlt. */ + (*parent_setobsalt)( this, val, status ); + +/* If the altitude has changed significantly, indicate that the LAST value + and magnitude of the diurnal aberration vector will need to be + re-calculated when next needed. */ + if( fabs( orig - val ) > 0.001 ) { + ( (AstSkyFrame *) this )->last = AST__BAD; + ( (AstSkyFrame *) this )->eplast = AST__BAD; + ( (AstSkyFrame *) this )->klast = AST__BAD; + ( (AstSkyFrame *) this )->diurab = AST__BAD; + } +} + +static void SetObsLat( AstFrame *this, double val, int *status ) { +/* +* Name: +* SetObsLat + +* Purpose: +* Set the value of the ObsLat attribute for a SkyFrame. + +* Type: +* Private function. + +* Synopsis: +* #include "skyframe.h" +* void SetObsLat( AstFrame *this, double val, int *status ) + +* Class Membership: +* SkyFrame member function (over-rides the astSetObsLat method +* inherited from the Frame class). + +* Description: +* This function sets the ObsLat value. + +* Parameters: +* this +* Pointer to the SkyFrame. +* val +* New ObsLat value. +* status +* Pointer to the inherited status variable. + +*/ + +/* Local Variables: */ + double orig; + +/* Check the global error status. */ + if ( !astOK ) return; + +/* Note the original ObsLat value. */ + orig = astGetObsLat( this ); + +/* Invoke the parent method to set the Frame ObsLat. */ + (*parent_setobslat)( this, val, status ); + +/* If the altitude has changed significantly, indicate that the LAST value + and magnitude of the diurnal aberration vector will need to be + re-calculated when next needed. */ + if( fabs( orig - val ) > 1.0E-8 ) { + ( (AstSkyFrame *) this )->last = AST__BAD; + ( (AstSkyFrame *) this )->eplast = AST__BAD; + ( (AstSkyFrame *) this )->klast = AST__BAD; + ( (AstSkyFrame *) this )->diurab = AST__BAD; + } +} + +static void SetObsLon( AstFrame *this, double val, int *status ) { +/* +* Name: +* SetObsLon + +* Purpose: +* Set the value of the ObsLon attribute for a SkyFrame. + +* Type: +* Private function. + +* Synopsis: +* #include "skyframe.h" +* void SetObsLon( AstFrame *this, double val, int *status ) + +* Class Membership: +* SkyFrame member function (over-rides the astSetObsLon method +* inherited from the Frame class). + +* Description: +* This function sets the ObsLon value. + +* Parameters: +* this +* Pointer to the SkyFrame. +* val +* New ObsLon value. +* status +* Pointer to the inherited status variable. + +*/ + +/* Local Variables: */ + double orig; + +/* Check the global error status. */ + if ( !astOK ) return; + +/* Note the original ObsLon value. */ + orig = astGetObsLon( this ); + +/* Invoke the parent method to set the Frame ObsLon. */ + (*parent_setobslon)( this, val, status ); + +/* If the longitude has changed significantly, indicate that the LAST value + will need to be re-calculated when it is next needed. */ + if( fabs( orig - val ) > 1.0E-8 ) { + ( (AstSkyFrame *) this )->last = AST__BAD; + ( (AstSkyFrame *) this )->eplast = AST__BAD; + ( (AstSkyFrame *) this )->klast = AST__BAD; + } +} + +static void SetSystem( AstFrame *this_frame, AstSystemType system, int *status ) { +/* +* Name: +* SetSystem + +* Purpose: +* Set the System attribute for a SkyFrame. + +* Type: +* Private function. + +* Synopsis: +* #include "skyframe.h" +* void SetSystem( AstFrame *this_frame, AstSystemType system, int *status ) + +* Class Membership: +* SkyFrame member function (over-rides the astSetSystem protected +* method inherited from the Frame class). + +* Description: +* This function assigns a new value to the System attribute for a SkyFrame. + +* Parameters: +* this +* Pointer to the SkyFrame. +* system +* The new System value. +* status +* Pointer to the inherited status variable. + +*/ + +/* Local Variables: */ + AstFrameSet *fs; /* FrameSet to be used as the Mapping */ + AstSkyFrame *sfrm; /* Copy of original SkyFrame */ + AstSkyFrame *this; /* Pointer to SkyFrame structure */ + double xin[ 2 ]; /* Axis 0 values */ + double xout[ 2 ]; /* Axis 0 values */ + double yin[ 2 ]; /* Axis 1 values */ + double yout[ 2 ]; /* Axis 1 values */ + int aloff; /* The AlignOffset attribute value */ + int aloff_set; /* Is the AlignOffset attribute set? */ + int skyref_set; /* Is either SkyRef attribute set? */ + int skyrefis; /* The SkyRefIs attribute value */ + int skyrefis_set; /* Is the SkyRefIs attribute set? */ + int skyrefp_set; /* Is either SkyRefP attribute set? */ + +/* Check the global error status. */ + if ( !astOK ) return; + +/* Obtain a pointer to the SkyFrame structure. */ + this = (AstSkyFrame *) this_frame; + +/* See if either the SkyRef or SkyRefP attribute is set. */ + skyref_set = astTestSkyRef( this, 0 ) || astTestSkyRef( this, 1 ); + skyrefp_set = astTestSkyRefP( this, 0 ) || astTestSkyRefP( this, 1 ); + +/* If so, we will need to transform their values into the new coordinate + system. Save a copy of the SkyFrame with its original System value. */ + sfrm = ( skyref_set || skyrefp_set )?astCopy( this ):NULL; + +/* Use the parent method to set the new System value. */ + (*parent_setsystem)( this_frame, system, status ); + +/* Now modify the SkyRef and SkyRefP attributes if necessary. */ + if( sfrm ) { + +/* Save the AlignOffset, SkyRefIs, SkyRef and SkyRefP values. */ + aloff_set = astTestAlignOffset( sfrm ); + aloff = astGetAlignOffset( sfrm ); + skyrefis_set = astTestSkyRefIs( sfrm ); + skyrefis = astGetSkyRefIs( sfrm ); + + xin[ 0 ] = astGetSkyRef( sfrm, 0 ); + xin[ 1 ] = astGetSkyRefP( sfrm, 0 ); + yin[ 0 ] = astGetSkyRef( sfrm, 1 ); + yin[ 1 ] = astGetSkyRefP( sfrm, 1 ); + +/* Clear the SkyRef and SkyRefP values to avoid infinite recursion in the + following call to astConvert. */ + if( skyref_set ) { + astClearSkyRef( sfrm, 0 ); + astClearSkyRef( sfrm, 1 ); + astClearSkyRef( this, 0 ); + astClearSkyRef( this, 1 ); + } + + if( skyrefp_set ) { + astClearSkyRefP( sfrm, 0 ); + astClearSkyRefP( sfrm, 1 ); + astClearSkyRefP( this, 0 ); + astClearSkyRefP( this, 1 ); + } + +/* Also set AlignOffset and SkyRefIs so that the following call to + astConvert does not align in offset coords. */ + astSetAlignOffset( sfrm, 0 ); + astSetSkyRefIs( sfrm, AST__IGNORED_REF ); + +/* Get the Mapping from the original System to the new System. Invoking + astConvert will recursively invoke SetSystem again. This is why we need + to be careful to ensure that SkyRef and SKyRefP are cleared above - doing + so ensure we do not end up with infinite recursion. */ + fs = astConvert( sfrm, this, "" ); + +/* If the conversion is not possible, clear the SkyRef and SkyRefP + values. */ + if( !fs ) { + if( skyref_set ) { + astClearSkyRef( this, 0 ); + astClearSkyRef( this, 1 ); + } + if( skyrefp_set ) { + astClearSkyRefP( this, 0 ); + astClearSkyRefP( this, 1 ); + } + +/* Use the Mapping to find the SkyRef and SkyRefP positions in the new + coordinate system. */ + } else { + astTran2( fs, 2, xin, yin, 1, xout, yout ); + +/* Store the values as required. */ + if( skyref_set ) { + astSetSkyRef( this, 0, xout[ 0 ] ); + astSetSkyRef( this, 1, yout[ 0 ] ); + } + + if( skyrefp_set ) { + astSetSkyRefP( this, 0, xout[ 1 ] ); + astSetSkyRefP( this, 1, yout[ 1 ] ); + } + +/* Restore the original SkyRefIs and AlignOffset values. */ + if( aloff_set ) { + astSetAlignOffset( this, aloff ); + } else { + astClearAlignOffset( this ); + } + + if( skyrefis_set ) { + astSetSkyRefIs( this, skyrefis ); + } else { + astClearSkyRefIs( this ); + } + +/* Free resources. */ + fs = astAnnul( fs ); + } + sfrm = astAnnul( sfrm ); + } +} + +static void Shapp( double dist, double *r0, double *r3, double a0, + double *p4, int *status ){ +/* +* Name: +* Shapp + +* Purpose: +* Use the vectors calculated by Shcal to find a sky position +* which is offset along a given position angle. + +* Type: +* Private function. + +* Synopsis: +* #include "skyframe.h" +* void Shapp( double dist, double *r0, double *r3, double a0, +* double *p4, int *status ) + +* Class Membership: +* SkyFrame member function. + +* Description: +* This function uses the vectors R0 and R3 calculated previously by +* Shcal to find the sky position which is offset away from the +* "reference" position (see function Offset2) by a given arc +* distance, along a given great circle. +* +* No checks are made for AST__BAD values. + +* Parameters: +* dist +* The arc distance to move away from the reference position +* in the given direction, in radians. +* r0 +* Pointer to an array holding the 3-vector representing the reference +* position. +* r3 +* Pointer to an array holding the 3-vector representing the +* point which is 90 degrees away from the reference point, along +* the required great circle. +* a0 +* The sky longitude of the reference position, in radians. +* p4 +* Pointer to an array of 2 doubles in which to put the sky longitude +* and latitude of the required point, in radians. +* status +* Pointer to the inherited status variable. + +*/ + +/* Local Variables: */ + double cosdst; /* Cosine of DIST */ + double r4[ 3 ]; /* Required position vector */ + double sindst; /* Sine of DIST */ + +/* Check the global error status. */ + if ( !astOK ) return; + +/* Store commonly used values. */ + sindst = sin( dist ); + cosdst = cos( dist ); + +/* The vector R4 representing the required point is produced as a + linear sum of R0 and R3. */ + r4[ 0 ] = cosdst*r0[ 0 ] + sindst*r3[ 0 ]; + r4[ 1 ] = cosdst*r0[ 1 ] + sindst*r3[ 1 ]; + r4[ 2 ] = cosdst*r0[ 2 ] + sindst*r3[ 2 ]; + +/* Create the longitude of the required point. If this point is at + a pole it is assigned the same longitude as the reference point. */ + if( r4[ 0 ] != 0.0 || r4[ 1 ] != 0.0 ) { + p4[ 0 ] = atan2( r4[ 1 ], r4[ 0 ] ); + } else { + p4[ 0 ] = a0; + } + +/* Create the latitude of the required point. */ + if( r4[ 2 ] > 1.0 ) { + r4[ 2 ] = 1.0; + } else if( r4[ 2 ] < -1.0 ) { + r4[ 2 ] = -1.0; + } + p4[ 1 ] = asin( r4[ 2 ] ); + +} + +static void Shcal( double a0, double b0, double angle, double *r0, + double *r3, int *status ) { +/* +* Name: +* Shcal + +* Purpose: +* Calculate vectors required by Offset2. + +* Type: +* Private function. + +* Synopsis: +* #include "skyframe.h" +* void Shcal( double a0, double b0, double angle, double *r0, +* double *r3, int *status ) + +* Class Membership: +* SkyFrame member function. + +* Description: +* This function calculates the 3-vector R0, representing the given +* sky position (A0,B0), and the 3-vector R3, representing the sky +* position which is 90 degrees away from R0, along a great circle +* passing through R0 at a position angle given by ANGLE. Each +* 3-vector holds Cartesian (X,Y,Z) values with origin at the centre +* of the celestial sphere. The XY plane is the "equator", the Z +* axis is in the direction of the "north pole", X is towards zero +* longitude (A=0), and Y is towards longitude 90 degrees. +* +* No checks are made for AST__BAD input values. + +* Parameters: +* a0 +* The sky longitude of the given position, in radians. +* b0 +* The sky latitude of the given position, in radians. +* angle +* The position angle of a great circle passing through the given +* position. That is, the angle from north to the required +* direction, in radians. Positive angles are in the sense of +* rotation from north to east. +* r0 +* A pointer to an array to receive 3-vector R0. See above. +* r3 +* A pointer to an array to receive 3-vector R3. See above. +* status +* Pointer to the inherited status variable. + +*/ + +/* Local Variables: */ + double cosa0; /* Cosine of A0 */ + double cosb0; /* Cosine of B0 */ + double cospa; /* Cosine of ANGLE */ + double r1[ 3 ]; /* Vector PI/2 away from R0 in meridian of R0 */ + double r2[ 3 ]; /* Vector PI/2 away from R0 on equator */ + double sinpa; /* Sine of ANGLE */ + double sina0; /* Sine of A0 */ + double sinb0; /* Sine of B0 */ + +/* Check the global error status. */ + if ( !astOK ) return; + +/* Store commonly used values. */ + sina0 = sin( a0 ); + cosa0 = cos( a0 ); + sinb0 = sin( b0 ); + cosb0 = cos( b0 ); + sinpa = sin( angle ); + cospa = cos( angle ); + +/* Create the vector R0 representing the given point. The XY plane + defines zero latitude, Z is in the direction of increasing latitude, + X is towards zero longitude, and Y is towards longitude 90 degrees. */ + r0[ 0 ] = cosb0*cosa0; + r0[ 1 ] = cosb0*sina0; + r0[ 2 ] = sinb0; + +/* Create the vector R1 representing the point in the meridian of the + given point which has latitude 90 degrees greater than the + given point. */ + r1[ 0 ] = -sinb0*cosa0; + r1[ 1 ] = -sinb0*sina0; + r1[ 2 ] = cosb0; + +/* Create the vector R2 representing the point on the equator (i.e. a + latitude of zero), which has a longitude 90 degrees to the west of + the given point. */ + r2[ 0 ] = -sina0; + r2[ 1 ] = cosa0; + r2[ 2 ] = 0.0; + +/* Create the vector R3 representing the point which is 90 degrees away + from the given point, along the required great circle. */ + r3[ 0 ] = cospa*r1[ 0 ] + sinpa*r2[ 0 ]; + r3[ 1 ] = cospa*r1[ 1 ] + sinpa*r2[ 1 ]; + r3[ 2 ] = cospa*r1[ 2 ] + sinpa*r2[ 2 ]; + +/* Return */ + return; +} + +static int SubFrame( AstFrame *target_frame, AstFrame *template, + int result_naxes, const int *target_axes, + const int *template_axes, AstMapping **map, + AstFrame **result, int *status ) { +/* +* Name: +* SubFrame + +* Purpose: +* Select axes from a SkyFrame and convert to the new coordinate system. + +* Type: +* Private function. + +* Synopsis: +* #include "skyframe.h" +* int SubFrame( AstFrame *target, AstFrame *template, +* int result_naxes, const int *target_axes, +* const int *template_axes, AstMapping **map, +* AstFrame **result, int *status ) + +* Class Membership: +* SkyFrame member function (over-rides the protected astSubFrame method +* inherited from the Frame class). + +* Description: +* This function selects a requested sub-set (or super-set) of the axes from +* a "target" SkyFrame and creates a new Frame with copies of the selected +* axes assembled in the requested order. It then optionally overlays the +* attributes of a "template" Frame on to the result. It returns both the +* resulting Frame and a Mapping that describes how to convert between the +* coordinate systems described by the target and result Frames. If +* necessary, this Mapping takes account of any differences in the Frames' +* attributes due to the influence of the template. + +* Parameters: +* target +* Pointer to the target SkyFrame, from which axes are to be selected. +* template +* Pointer to the template Frame, from which new attributes for the +* result Frame are to be obtained. Optionally, this may be NULL, in +* which case no overlaying of template attributes will be performed. +* result_naxes +* Number of axes to be selected from the target Frame. This number may +* be greater than or less than the number of axes in this Frame (or +* equal). +* target_axes +* Pointer to an array of int with result_naxes elements, giving a list +* of the (zero-based) axis indices of the axes to be selected from the +* target SkyFrame. The order in which these are given determines the +* order in which the axes appear in the result Frame. If any of the +* values in this array is set to -1, the corresponding result axis will +* not be derived from the target Frame, but will be assigned default +* attributes instead. +* template_axes +* Pointer to an array of int with result_naxes elements. This should +* contain a list of the template axes (given as zero-based axis indices) +* with which the axes of the result Frame are to be associated. This +* array determines which axes are used when overlaying axis-dependent +* attributes of the template on to the result. If any element of this +* array is set to -1, the corresponding result axis will not receive any +* template attributes. +* +* If the template argument is given as NULL, this array is not used and +* a NULL pointer may also be supplied here. +* map +* Address of a location to receive a pointer to the returned Mapping. +* The forward transformation of this Mapping will describe how to +* convert coordinates from the coordinate system described by the target +* SkyFrame to that described by the result Frame. The inverse +* transformation will convert in the opposite direction. +* result +* Address of a location to receive a pointer to the result Frame. +* status +* Pointer to the inherited status variable. + +* Returned Value: +* A non-zero value is returned if coordinate conversion is possible +* between the target and the result Frame. Otherwise zero is returned and +* *map and *result are returned as NULL (but this will not in itself +* result in an error condition). In general, coordinate conversion should +* always be possible if no template Frame is supplied but may not always +* be possible otherwise. + +* Notes: +* - A value of zero will be returned if this function is invoked with the +* global error status set, or if it should fail for any reason. + +* Implementation Notes: +* - This implementation addresses the selection of axes from a SkyFrame +* object. This results in another object of the same class only if both +* axes of the SkyFrame are selected, once each. Otherwise, the result is a +* Frame class object which inherits the SkyFrame's axis information (if +* appropriate) but none of the other properties of a SkyFrame. +* - In the event that a SkyFrame results, the returned Mapping will take +* proper account of the relationship between the target and result sky +* coordinate systems. +* - In the event that a Frame class object results, the returned Mapping +* will only represent a selection/permutation of axes. + +* Implementation Deficiencies: +* - Any axis selection is currently permitted. Probably this should be +* restricted so that each axis can only be selected once. The +* astValidateAxisSelection method will do this but currently there are bugs +* in the CmpFrame class that cause axis selections which will not pass this +* test. Install the validation when these are fixed. +*/ + +/* Local Variables: */ + AstAxis *ax; /* Pointer to result Frame Axis object */ + AstMapping *tmpmap; /* Temporary Mapping pointer */ + AstPermMap *permmap; /* Pointer to PermMap */ + AstSkyFrame *target; /* Pointer to the SkyFrame structure */ + AstSkyFrame *temp; /* Pointer to copy of target SkyFrame */ + AstSystemType align_sys; /* System in which to align the SkyFrames */ + int match; /* Coordinate conversion is possible? */ + int perm[ 2 ]; /* Permutation array for axis swap */ + int result_swap; /* Swap result SkyFrame coordinates? */ + int set_usedefs; /* Set the returned UseDefs attribute zero?*/ + int target_axis; /* Target SkyFrame axis index */ + int target_swap; /* Swap target SkyFrame coordinates? */ + +/* Initialise the returned values. */ + *map = NULL; + *result = NULL; + match = 0; + +/* Check the global error status. */ + if ( !astOK ) return match; + +/* Obtain a pointer to the target SkyFrame structure. */ + target = (AstSkyFrame *) target_frame; + +/* Result is a SkyFrame. */ +/* --------------------- */ +/* Check if the result Frame is to have two axes obtained by selecting + both of the target SkyFrame axes, in either order. If so, the + result will also be a SkyFrame. */ + if ( ( result_naxes == 2 ) && + ( ( ( target_axes[ 0 ] == 0 ) && ( target_axes[ 1 ] == 1 ) ) || + ( ( target_axes[ 0 ] == 1 ) && ( target_axes[ 1 ] == 0 ) ) ) ) { + +/* If a template has not been supplied, or is the same object as the + target, we are simply extracting axes from the supplied SkyFrame. In + this case we temporarily force the UseDefs attribute to 1 so that (for + instance) the astPickAxes method can function correctly. E.g. if you + have a SkyFrame with no set Epoch and UseDefs set zero, and you try to + swap the axes, the attempt would fail because MakeSkyMapping would be + unable to determine the Mapping from original to swapped SkyFrame, + because of the lack of an Epoch value. */ + set_usedefs = 0; + if( !template || template == target_frame ) { + if( !astGetUseDefs( target ) ) { + astClearUseDefs( target ); + set_usedefs = 1; + } + } + +/* Form the result from a copy of the target and then permute its axes + into the order required. */ + *result = astCopy( target ); + astPermAxes( *result, target_axes ); + +/* If required, overlay the template attributes on to the result SkyFrame. + Also get the system in which to align the two SkyFrames. This is the + value of the AlignSystem attribute from the template (if there is a + template). */ + if ( template ) { + astOverlay( template, template_axes, *result ); + align_sys = astGetAlignSystem( template ); + + } else { + align_sys = astGetAlignSystem( target ); + } + +/* See whether alignment occurs in offset coordinates or absolute + coordinates. If the current call to this function is part of the + process of restoring a FrameSet's integrity following changes to + the FrameSet's current Frame, then we ignore the setting of the + AlignOffset attributes and use 0. This ensures that when the System + attribute (for instance) is changed via a FrameSet pointer, the + Mappings within the FrameSet are modified to produce offsets in the + new System. If we are not currently restoring a FrameSet's integrity, + then we align in offsets if the template is a SkyFrame and both template + and target want alignment to occur in the offset coordinate system. In + this case we use a UnitMap to connect them. */ + if( ( astGetFrameFlags( target_frame ) & AST__INTFLAG ) == 0 ) { + if( astGetAlignOffset( target ) && + astGetSkyRefIs( target ) != AST__IGNORED_REF && + template && astIsASkyFrame( template ) ){ + if( astGetAlignOffset( (AstSkyFrame *) template ) && + astGetSkyRefIs( (AstSkyFrame *) template ) != AST__IGNORED_REF ) { + match = 1; + *map = (AstMapping *) astUnitMap( 2, "", status ); + } + } + } + +/* Otherwise, generate a Mapping that takes account of changes in the sky + coordinate system (equinox, epoch, etc.) between the target SkyFrame and + the result SkyFrame. If this Mapping can be generated, set "match" to + indicate that coordinate conversion is possible. */ + if( ! *map ) { + match = ( MakeSkyMapping( target, (AstSkyFrame *) *result, + align_sys, map, status ) != 0 ); + } + +/* If required, re-instate the original zero value of UseDefs. */ + if( set_usedefs ) { + astSetUseDefs( target, 0 ); + astSetUseDefs( *result, 0 ); + } + +/* If a Mapping has been obtained, it will expect coordinate values to be + supplied in (longitude,latitude) pairs. Test whether we need to swap the + order of the target SkyFrame coordinates to conform with this. */ + if ( astOK && match ) { + target_swap = ( astValidateAxis( target, 0, 1, "astSubFrame" ) != 0 ); + +/* Coordinates will also be delivered in (longitude,latitude) pairs, so check + to see whether the result SkyFrame coordinate order should be swapped. */ + result_swap = ( target_swap != ( target_axes[ 0 ] != 0 ) ); + +/* If either set of coordinates needs swapping, create a PermMap that + will swap a pair of coordinates. */ + permmap = NULL; + if ( target_swap || result_swap ) { + perm[ 0 ] = 1; + perm[ 1 ] = 0; + permmap = astPermMap( 2, perm, 2, perm, NULL, "", status ); + } + +/* If necessary, prefix this PermMap to the main Mapping. */ + if ( target_swap ) { + tmpmap = (AstMapping *) astCmpMap( permmap, *map, 1, "", status ); + *map = astAnnul( *map ); + *map = tmpmap; + } + +/* Also, if necessary, append it to the main Mapping. */ + if ( result_swap ) { + tmpmap = (AstMapping *) astCmpMap( *map, permmap, 1, "", status ); + *map = astAnnul( *map ); + *map = tmpmap; + } + +/* Annul the pointer to the PermMap (if created). */ + if ( permmap ) permmap = astAnnul( permmap ); + } + +/* Result is not a SkyFrame. */ +/* ------------------------- */ +/* In this case, we select axes as if the target were from the Frame + class. However, since the resulting data will then be separated + from their enclosing SkyFrame, default attribute values may differ + if the methods for obtaining them were over-ridden by the SkyFrame + class. To overcome this, we ensure that these values are explicitly + set for the result Frame (rather than relying on their + defaults). */ + } else { + +/* Make a temporary copy of the target SkyFrame. We will explicitly + set the attribute values in this copy so as not to modify the + original. */ + temp = astCopy( target ); + +/* Define a macro to test if an attribute is set. If not, set it + explicitly to its default value. */ +#define SET(attribute) \ + if ( !astTest##attribute( temp ) ) { \ + astSet##attribute( temp, astGet##attribute( temp ) ); \ + } + +/* Set attribute values which apply to the Frame as a whole and which + we want to retain, but whose defaults are over-ridden by the + SkyFrame class. */ + SET(Domain) + SET(Title) + +/* Now loop to set explicit attribute values for each axis. */ + for ( target_axis = 0; target_axis < 2; target_axis++ ) { + +/* Define a macro to test if an axis attribute is set. If not, set it + explicitly to its default value. */ +#define SET_AXIS(attribute) \ + if ( !astTest##attribute( temp, target_axis ) ) { \ + astSet##attribute( temp, target_axis, \ + astGet##attribute( temp, target_axis ) ); \ + } + +/* Use this macro to set explicit values for all the axis attributes + for which the SkyFrame class over-rides the default value. */ + SET_AXIS(AsTime) + SET_AXIS(Format) + SET_AXIS(Label) + SET_AXIS(Symbol) + SET_AXIS(Unit) + +/* Now handle axis attributes for which there are no SkyFrame access + methods. For these we require a pointer to the temporary + SkyFrame's Axis object. */ + ax = astGetAxis( temp, target_axis ); + +/* Set an explicit value for the IsLatitude and CentreZero attributes. */ + if( astValidateAxis( temp, target_axis, 1, "astSubFrame" ) == 1 ) { + astSetAxisIsLatitude( ax, 1 ); + astSetAxisCentreZero( ax, 1 ); + + } else { + astSetAxisIsLatitude( ax, 0 ); + astSetAxisCentreZero( ax, astGetNegLon( temp ) ); + } + +/* Annul the Axis object pointer. */ + ax = astAnnul( ax ); + } + +/* Clear attributes which have an extended range of values allowed by + this class. */ + astClearSystem( temp ); + astClearAlignSystem( temp ); + +/* Invoke the astSubFrame method inherited from the Frame class to + produce the result Frame by selecting the required set of axes and + overlaying the template Frame's attributes. */ + match = (*parent_subframe)( (AstFrame *) temp, template, + result_naxes, target_axes, template_axes, + map, result, status ); + +/* Delete the temporary copy of the target SkyFrame. */ + temp = astDelete( temp ); + } + +/* Ensure the returned Frame does not have active units. */ + astSetActiveUnit( *result, 0 ); + +/* If an error occurred or no match was found, annul the returned + objects and reset the returned result. */ + if ( !astOK || !match ) { + if( *map ) *map = astAnnul( *map ); + if( *result ) *result = astAnnul( *result ); + match = 0; + } + +/* Return the result. */ + return match; + +/* Undefine macros local to this function. */ +#undef SET +#undef SET_AXIS +} + +static AstSystemType SystemCode( AstFrame *this, const char *system, int *status ) { +/* +* Name: +* SystemCode + +* Purpose: +* Convert a string into a coordinate system type code. + +* Type: +* Private function. + +* Synopsis: +* #include "skyframe.h" +* AstSystemType SystemCode( AstFrame *this, const char *system, int *status ) + +* Class Membership: +* SkyFrame member function (over-rides the astSystemCode method +* inherited from the Frame class). + +* Description: +* This function converts a string used for the external +* description of a sky coordinate system into a SkyFrame +* coordinate system type code (System attribute value). It is the +* inverse of the astSystemString function. + +* Parameters: +* this +* The Frame. +* system +* Pointer to a constant null-terminated string containing the +* external description of the sky coordinate system. +* status +* Pointer to the inherited status variable. + +* Returned Value: +* The System type code. + +* Notes: +* - A value of AST__BADSYSTEM is returned if the sky coordinate +* system description was not recognised. This does not produce an +* error. +* - A value of AST__BADSYSTEM is also returned if this function +* is invoked with the global error status set or if it should fail +* for any reason. +*/ + +/* Local Variables: */ + AstSystemType result; /* Result value to return */ + +/* Initialise. */ + result = AST__BADSYSTEM; + +/* Check the global error status. */ + if ( !astOK ) return result; + +/* Match the "system" string against each possibility and assign the + result. */ + if ( astChrMatch( "FK4", system ) ) { + result = AST__FK4; + + } else if ( astChrMatch( "FK4_NO_E", system ) || + astChrMatch( "FK4-NO-E", system ) ) { + result = AST__FK4_NO_E; + + } else if ( astChrMatch( "FK5", system ) || + astChrMatch( "Equatorial", system ) ) { + result = AST__FK5; + + } else if ( astChrMatch( "J2000", system ) ) { + result = AST__J2000; + + } else if ( astChrMatch( "ICRS", system ) ) { + result = AST__ICRS; + + } else if ( astChrMatch( "AZEL", system ) ) { + result = AST__AZEL; + + } else if ( astChrMatch( "GAPPT", system ) || + astChrMatch( "GEOCENTRIC", system ) || + astChrMatch( "APPARENT", system ) ) { + result = AST__GAPPT; + + } else if ( astChrMatch( "ECLIPTIC", system ) ) { + result = AST__ECLIPTIC; + + } else if ( astChrMatch( "HELIOECLIPTIC", system ) ) { + result = AST__HELIOECLIPTIC; + + } else if ( astChrMatch( "GALACTIC", system ) ) { + result = AST__GALACTIC; + + } else if ( astChrMatch( "SUPERGALACTIC", system ) ) { + result = AST__SUPERGALACTIC; + + } else if ( astChrMatch( "UNKNOWN", system ) ) { + result = AST__UNKNOWN; + } + +/* Return the result. */ + return result; +} + +static const char *SystemString( AstFrame *this, AstSystemType system, int *status ) { +/* +* Name: +* SystemString + +* Purpose: +* Convert a coordinate system type code into a string. + +* Type: +* Private function. + +* Synopsis: +* #include "skyframe.h" +* const char *SystemString( AstFrame *this, AstSystemType system, int *status ) + +* Class Membership: +* SkyFrame member function (over-rides the astSystemString method +* inherited from the Frame class). + +* Description: +* This function converts a SkyFrame coordinate system type code +* (System attribute value) into a string suitable for use as an +* external representation of the coordinate system type. + +* Parameters: +* this +* The Frame. +* system +* The coordinate system type code. +* status +* Pointer to the inherited status variable. + +* Returned Value: +* Pointer to a constant null-terminated string containing the +* textual equivalent of the type code supplied. + +* Notes: +* - A NULL pointer value is returned if the sky coordinate system +* code was not recognised. This does not produce an error. +* - A NULL pointer value is also returned if this function is +* invoked with the global error status set or if it should fail +* for any reason. +*/ + +/* Local Variables: */ + const char *result; /* Pointer value to return */ + +/* Initialise. */ + result = NULL; + +/* Check the global error status. */ + if ( !astOK ) return result; + +/* Match the "system" value against each possibility and convert to a + string pointer. (Where possible, return the same string as would be + used in the FITS WCS representation of the coordinate system). */ + switch ( system ) { + case AST__FK4: + result = "FK4"; + break; + + case AST__FK4_NO_E: + result = "FK4-NO-E"; + break; + + case AST__FK5: + result = "FK5"; + break; + + case AST__J2000: + result = "J2000"; + break; + + case AST__ICRS: + result = "ICRS"; + break; + + case AST__GAPPT: + result = "GAPPT"; + break; + + case AST__AZEL: + result = "AZEL"; + break; + + case AST__ECLIPTIC: + result = "ECLIPTIC"; + break; + + case AST__HELIOECLIPTIC: + result = "HELIOECLIPTIC"; + break; + + case AST__GALACTIC: + result = "GALACTIC"; + break; + + case AST__SUPERGALACTIC: + result = "SUPERGALACTIC"; + break; + + case AST__UNKNOWN: + result = "Unknown"; + break; + } + +/* Return the result pointer. */ + return result; +} + +static int TestActiveUnit( AstFrame *this_frame, int *status ) { +/* +* Name: +* TestActiveUnit + +* Purpose: +* Test the ActiveUnit flag for a SkyFrame. + +* Type: +* Private function. + +* Synopsis: +* #include "skyframe.h" +* int TestActiveUnit( AstFrame *this_frame, int *status ) + +* Class Membership: +* SkyFrame member function (over-rides the astTestActiveUnit protected +* method inherited from the Frame class). + +* Description: +* This function test the value of the ActiveUnit flag for a SkyFrame, +* which is always "unset". + +* Parameters: +* this +* Pointer to the SkyFrame. +* status +* Pointer to the inherited status variable. + +* Returned Value: +* The result of the test (0). + +*/ + return 0; +} + +static int TestAsTime( AstSkyFrame *this, int axis, int *status ) { +/* +* Name: +* TestAsTime + +* Purpose: +* Determine if a value has been set for a SkyFrame's AsTime attribute. + +* Type: +* Private function. + +* Synopsis: +* #include "skyframe.h" +* int TestAsTime( AstSkyFrame *this, int axis, int *status ) + +* Class Membership: +* SkyFrame member function. + +* Description: +* This function returns a boolean value to indicate if a value has +* previously been set for the AsTime attribute for a specified axis of a +* SkyFrame. This attribute indicates whether axis values should be +* formatted as times (as opposed to angles) by default. + +* Parameters: +* this +* Pointer to the SkyFrame. +* axis +* Index of the axis for which information is required (zero based). +* status +* Pointer to the inherited status variable. + +* Returned Value: +* Zero or one, according to whether the AsTime attribute has been set. + +* Notes: +* - A value of zero will be returned if this function is invoked with the +* global error status set, or if it should fail for any reason. +*/ + +/* Local Variables. */ + AstAxis *ax; /* Pointer to Axis object */ + int result; /* Result to be returned */ + +/* Check the global error status. */ + if ( !astOK ) return 0; + +/* Validate the axis index. */ + (void) astValidateAxis( this, axis, 1, "astTestAsTime" ); + +/* Obtain a pointer to the Axis object. */ + ax = astGetAxis( this, axis ); + +/* Determine if the AsTime attribute has been set for it (it cannot have been + set unless the object is a SkyAxis). */ + result = ( astIsASkyAxis( ax ) && astTestAxisAsTime( ax ) ); + +/* Annul the Axis pointer. */ + ax = astAnnul( ax ); + +/* Return the result. */ + return result; +} + +static int TestAttrib( AstObject *this_object, const char *attrib, int *status ) { +/* +* Name: +* TestAttrib + +* Purpose: +* Test if a specified attribute value is set for a SkyFrame. + +* Type: +* Private function. + +* Synopsis: +* #include "skyframe.h" +* int TestAttrib( AstObject *this, const char *attrib, int *status ) + +* Class Membership: +* SkyFrame member function (over-rides the astTestAttrib protected +* method inherited from the Frame class). + +* Description: +* This function returns a boolean result (0 or 1) to indicate whether +* a value has been set for one of a SkyFrame's attributes. + +* Parameters: +* this +* Pointer to the SkyFrame. +* attrib +* Pointer to a null terminated string specifying the attribute +* name. This should be in lower case with no surrounding white +* space. +* status +* Pointer to the inherited status variable. + +* Returned Value: +* One if a value has been set, otherwise zero. + +* Notes: +* - This function uses one-based axis numbering so that it is +* suitable for external (public) use. +* - A value of zero will be returned if this function is invoked +* with the global status set, or if it should fail for any reason. +*/ + +/* Local Variables: */ + AstSkyFrame *this; /* Pointer to the SkyFrame structure */ + int axis; /* SkyFrame axis number */ + int len; /* Length of attrib string */ + int nc; /* No. characters read by astSscanf */ + int result; /* Result value to return */ + +/* Initialise. */ + result = 0; + +/* Check the global error status. */ + if ( !astOK ) return result; + +/* Obtain a pointer to the SkyFrame structure. */ + this = (AstSkyFrame *) this_object; + +/* Obtain the length of the attrib string. */ + len = strlen( attrib ); + +/* Check the attribute name and test the appropriate attribute. */ + +/* AsTime(axis). */ +/* ------------- */ + if ( nc = 0, + ( 1 == astSscanf( attrib, "astime(%d)%n", &axis, &nc ) ) + && ( nc >= len ) ) { + result = astTestAsTime( this, axis - 1 ); + +/* Equinox. */ +/* -------- */ + } else if ( !strcmp( attrib, "equinox" ) ) { + result = astTestEquinox( this ); + +/* NegLon. */ +/* ------- */ + } else if ( !strcmp( attrib, "neglon" ) ) { + result = astTestNegLon( this ); + +/* SkyTol. */ +/* ------- */ + } else if ( !strcmp( attrib, "skytol" ) ) { + result = astTestSkyTol( this ); + +/* Projection. */ +/* ----------- */ + } else if ( !strcmp( attrib, "projection" ) ) { + result = astTestProjection( this ); + +/* SkyRefIs. */ +/* --------- */ + } else if ( !strcmp( attrib, "skyrefis" ) ) { + result = astTestSkyRefIs( this ); + +/* SkyRef. */ +/* ------- */ + } else if ( !strcmp( attrib, "skyref" ) ) { + result = astTestSkyRef( this, 0 ) || astTestSkyRef( this, 1 ); + +/* SkyRef(axis). */ +/* ------------- */ + } else if ( nc = 0, + ( 1 == astSscanf( attrib, "skyref(%d)%n", &axis, &nc ) ) + && ( nc >= len ) ) { + result = astTestSkyRef( this, axis - 1 ); + +/* SkyRefP. */ +/* -------- */ + } else if ( !strcmp( attrib, "skyrefp" ) ) { + result = astTestSkyRefP( this, 0 ) || astTestSkyRefP( this, 1 ); + +/* SkyRefP(axis). */ +/* ------------- */ + } else if ( nc = 0, + ( 1 == astSscanf( attrib, "skyrefp(%d)%n", &axis, &nc ) ) + && ( nc >= len ) ) { + result = astTestSkyRefP( this, axis - 1 ); + +/* AlignOffset */ +/* ----------- */ + } else if ( !strcmp( attrib, "alignoffset" ) ) { + result = astTestAlignOffset( this ); + +/* If the name is not recognised, test if it matches any of the + read-only attributes of this class. If it does, then return + zero. */ + } else if ( !strncmp( attrib, "islataxis", 9 ) || + !strncmp( attrib, "islonaxis", 9 ) || + !strcmp( attrib, "lataxis" ) || + !strcmp( attrib, "lonaxis" ) ) { + result = 0; + +/* If the attribute is not recognised, pass it on to the parent method + for further interpretation. */ + } else { + result = (*parent_testattrib)( this_object, attrib, status ); + } + +/* Return the result, */ + return result; +} + +static int TestSlaUnit( AstSkyFrame *sf1, AstSkyFrame *sf2, AstSlaMap *slamap, + int *status ){ +/* +* Name: +* Unformat + +* Purpose: +* See if a slamap is effectively a unit mapping. + +* Type: +* Private function. + +* Synopsis: +* #include "skyframe.h" +* int TestSlaUnit( AstSkyFrame *sf1, AstSkyFrame *sf2, AstSlaMap *slamap, +* int *status ) + +* Class Membership: +* SkyFrame member function. + +* Description: +* This function tests a SlaMap to see if it is effectively a unit +* transformatuon to within a tolerance given by the smaller tolerance +* of the two supplied SkyFrames. + +* Parameters: +* sf1 +* Pointer to the first SkyFrame. +* sf2 +* Pointer to the second SkyFrame (may be NULL) +* slamap +* Pointer to the SlaMap to test. +* status +* Pointer to the inherited status variable. + +* Returned Value: +* Non-zero if the SlaMap is effectively a unit mapping, and zero +* otherwise. + +*/ + +/* Number of test points. */ +#define NTEST 14 + +/* Local Variables: */ + double maxshift; /* Max. shift produced by slamap (rads) */ + double olat[NTEST]; /* Transformed latitudes */ + double olon[NTEST]; /* Transformed longitudes */ + double shift; /* Shift produced by slamap (rads) */ + double tol2; /* Second tolerance (in radians) */ + double tol; /* Used tolerance (in radians) */ + int i; /* Loop count */ + int result; /* Returned flag */ + +/* A grid of lon/lat points covering the sphere. */ + double lat[ NTEST ] = { 0.0, 0.0, 0.0, 0.0, + 0.8, 0.8, 0.8, 0.8, + -0.8, -0.8, -0.8, -0.8, + 1.570796, -1.570796 }; + double lon[ NTEST ] = { 0.0, 1.57, 3.14, 4.71, + 0.8, 2.37, 3.94, 5.51, + 0.8, 2.37, 3.94, 5.51, + 0.0, 0.0 }; + +/* Initialise. */ + result = 0; + +/* Check the global error status. */ + if ( !astOK ) return result; + +/* If the SlaMap is empty (i.e. has no conversions in it), then is it a + UnitMap. So save time by not transforming the test values. */ + if( astSlaIsEmpty( slamap ) ) { + result = 1; + +/* Otherwise, get the smaller of the tolerances associated with the + supplied SkyFrames, in radians. */ + } else { + tol = astGetSkyTol( sf1 ); + if( sf2 ) { + tol2 = astGetSkyTol( sf2 ); + if( tol2 < tol ) tol = tol2; + } + +/* If the tolerance is zero, there is no need to do the test. */ + if( tol > 0.0 ) { + +/* Transform the test point using the SlaMap. */ + astTran2( slamap, NTEST, lon, lat, 1, olon, olat ); + +/* Find the maximum shift produced by the SlaMap at any of the test + positions. Again, to avoid the slow-down produced by checking for + axis permutation, use palDsep rather than astDistance. */ + maxshift = 0.0; + for( i = 0; i < NTEST; i++ ) { + shift = palDsep( lon[ i ], lat[ i ], olon[ i ], olat[ i ] ); + if( shift > maxshift ) maxshift = shift; + } + +/* Convert the max shift to arc-seconds and do the check. */ + result = ( maxshift*AST__DR2D*3600 < tol ); + } + } + + return result; +} + +static int Unformat( AstFrame *this_frame, int axis, const char *string, + double *value, int *status ) { +/* +* Name: +* Unformat + +* Purpose: +* Read a formatted coordinate value for a SkyFrame axis. + +* Type: +* Private function. + +* Synopsis: +* #include "skyframe.h" +* int Unformat( AstFrame *this, int axis, const char *string, +* double *value, int *status ) + +* Class Membership: +* SkyFrame member function (over-rides the public astUnformat +* method inherited from the Frame class). + +* Description: +* This function reads a formatted coordinate value for a SkyFrame +* axis (supplied as a string) and returns the equivalent numerical +* value as a double. It also returns the number of characters read +* from the string. + +* Parameters: +* this +* Pointer to the SkyFrame. +* axis +* The number of the SkyFrame axis for which the coordinate +* value is to be read (axis numbering starts at zero for the +* first axis). +* string +* Pointer to a constant null-terminated string containing the +* formatted coordinate value. +* value +* Pointer to a double in which the coordinate value read will +* be returned (in radians). +* status +* Pointer to the inherited status variable. + +* Returned Value: +* The number of characters read from the string to obtain the +* coordinate value. + +* Notes: +* - Any white space at the beginning of the string will be +* skipped, as also will any trailing white space following the +* coordinate value read. The function's return value will reflect +* this. +* - A function value of zero (and no coordinate value) will be +* returned, without error, if the string supplied does not contain +* a suitably formatted value. +* - The string "<bad>" is recognised as a special case and will +* generate the value AST__BAD, without error. The test for this +* string is case-insensitive and permits embedded white space. +* - A function result of zero will be returned and no coordinate +* value will be returned via the "value" pointer if this function +* is invoked with the global error status set, or if it should +* fail for any reason. +*/ + +/* Local Variables: */ + AstSkyFrame *this; /* Pointer to the SkyFrame structure */ + double coord; /* Coordinate value read */ + int format_set; /* Format attribute set? */ + int nc; /* Number of characters read */ + +/* Initialise. */ + nc = 0; + +/* Check the global error status. */ + if ( !astOK ) return nc; + +/* Obtain a pointer to the SkyFrame structure. */ + this = (AstSkyFrame *) this_frame; + +/* Validate the axis index. */ + (void) astValidateAxis( this, axis, 1, "astUnformat" ); + +/* Determine if a Format value has been set for the axis and set a + temporary value if it has not. Use the GetFormat member function + for this class together with member functions inherited from the + parent class (rather than using the object's methods directly) + because if any of these methods have been over-ridden by a derived + class the Format string syntax may no longer be compatible with + this class. */ + format_set = (*parent_testformat)( this_frame, axis, status ); + if ( !format_set ) { + (*parent_setformat)( this_frame, axis, GetFormat( this_frame, axis, status ), status ); + } + +/* Use the Unformat member function inherited from the parent class to + read the coordinate value. */ + nc = (*parent_unformat)( this_frame, axis, string, &coord, status ); + +/* If necessary, clear any temporary Format value that was set above. */ + if ( !format_set ) (*parent_clearformat)( this_frame, axis, status ); + +/* If an error occurred, clear the number of characters read. */ + if ( !astOK ) { + nc = 0; + +/* Otherwise, if characters were read, return the coordinate value. */ + } else if ( nc ) { + *value = coord; + } + +/* Return the number of characters read. */ + return nc; +} + +static int ValidateSystem( AstFrame *this, AstSystemType system, const char *method, int *status ) { +/* +* +* Name: +* ValidateSystem + +* Purpose: +* Validate a value for a Frame's System attribute. + +* Type: +* Protected virtual function. + +* Synopsis: +* #include "frame.h" +* int ValidateSystem( AstFrame *this, AstSystemType system, +* const char *method, int *status ) + +* Class Membership: +* SkyFrame member function (over-rides the astValidateSystem method +* inherited from the Frame class). + +* Description: +* This function checks the validity of the supplied system value. +* If the value is valid, it is returned unchanged. Otherwise, an +* error is reported and a value of AST__BADSYSTEM is returned. + +* Parameters: +* this +* Pointer to the Frame. +* system +* The system value to be checked. +* method +* Pointer to a constant null-terminated character string +* containing the name of the method that invoked this function +* to validate an axis index. This method name is used solely +* for constructing error messages. +* status +* Pointer to the inherited status variable. + +* Returned Value: +* The validated system value. + +* Notes: +* - A value of AST__BADSYSTEM will be returned if this function is invoked +* with the global error status set, or if it should fail for any +* reason. +*/ + +/* Local Variables: */ + AstSystemType result; /* Validated system value */ + +/* Initialise. */ + result = AST__BADSYSTEM; + +/* Check the global error status. */ + if ( !astOK ) return result; + +/* If the value is out of bounds, report an error. */ + if ( system < FIRST_SYSTEM || system > LAST_SYSTEM ) { + astError( AST__AXIIN, "%s(%s): Bad value (%d) given for the System " + "or AlignSystem attribute of a %s.", status, method, + astGetClass( this ), (int) system, astGetClass( this ) ); + +/* Otherwise, return the supplied value. */ + } else { + result = system; + } + +/* Return the result. */ + return result; +} + +static void VerifyMSMAttrs( AstSkyFrame *target, AstSkyFrame *result, + int which, const char *attrs, const char *method, int *status ) { +/* +* Name: +* VerifyMSMAttrs + +* Purpose: +* Verify that usable attribute values are available. + +* Type: +* Private function. + +* Synopsis: +* #include "skyframe.h" +* void VerifyMSMAttrs( AstSkyFrame *target, AstSkyFrame *result, +* int which, const char *attrs, const char *method, int *status ) + +* Class Membership: +* SkyFrame member function + +* Description: +* This function tests each attribute listed in "attrs". It returns +* without action if 1) an explicit value has been set for each attribute +* in the SkyFrame indicated by "which" or 2) the UseDefs attribute of the +* "which" SkyFrame is non-zero. +* +* If UseDefs is zero (indicating that default values should not be +* used for attributes), and any of the named attributes does not have +* an explicitly set value, then an error is reported. +* +* The displayed error message assumes that tjis function was called +* as part of the process of producing a Mapping from "target" to "result". + +* Parameters: +* target +* Pointer to the target SkyFrame. +* result +* Pointer to the result SkyFrame. +* which +* If 2, both the target and result SkyFrames are checked for the +* supplied attributes. If less than 2, only the target SkyFrame is +* checked. If greater than 2, only the result SkyFrame is checked. +* attrs +* A string holding a space separated list of attribute names. +* method +* A string holding the name of the calling method for use in error +* messages. +* status +* Pointer to the inherited status variable. + +*/ + +/* Local Variables: */ + const char *a; + const char *p; + const char *desc; + int len; + int set1; + int set2; + int state; + int usedef1; + int usedef2; + +/* Check inherited status */ + if( !astOK ) return; + +/* Get the UseDefs attributes of the two SkyFrames. */ + usedef1 = astGetUseDefs( target ); + usedef2 = astGetUseDefs( result ); + +/* If both SkyFrames have a non-zero value for its UseDefs attribute, then + all attributes are assumed to have usable values, since the defaults + will be used if no explicit value has been set. So we only need to do + any checks if UseDefs is zero for either SkyFrame. */ + if( !usedef1 || !usedef2 ) { + +/* Stop compiler warnings about uninitialised variables */ + a = NULL; + desc = NULL; + len = 0; + set1 = 0; + set2 = 0; + +/* Loop round the "attrs" string identifying the start and length of each + non-blank word in the string. */ + state = 0; + p = attrs; + while( 1 ) { + if( state == 0 ) { + if( !isspace( *p ) ) { + a = p; + len = 1; + state = 1; + } + } else { + if( isspace( *p ) || !*p ) { + +/* The end of a word has just been reached. Compare it to each known + attribute value. Get a flag indicating if the attribute has a set + value, and a string describing the attribute.*/ + if( len > 0 ) { + + if( !strncmp( "Equinox", a, len ) ) { + set1 = astTestEquinox( target ); + set2 = astTestEquinox( result ); + desc = "reference equinox"; + + } else if( !strncmp( "Dtai", a, len ) ) { + set1 = astTestDtai( target ); + set2 = astTestDtai( result ); + desc = "TAI-UTC correction"; + + } else if( !strncmp( "Dut1", a, len ) ) { + set1 = astTestDut1( target ); + set2 = astTestDut1( result ); + desc = "UT1-UTC correction"; + + } else if( !strncmp( "Epoch", a, len ) ) { + set1 = astTestEpoch( target ); + set2 = astTestEpoch( result ); + desc = "epoch of observation"; + + } else if( !strncmp( "ObsLon", a, len ) ) { + set1 = astTestObsLon( target ); + set2 = astTestObsLon( result ); + desc = "longitude of observer"; + + } else if( !strncmp( "ObsLat", a, len ) ) { + set1 = astTestObsLat( target ); + set2 = astTestObsLat( result ); + desc = "latitude of observer"; + + } else if( !strncmp( "ObsAlt", a, len ) ) { + set1 = astTestObsAlt( target ); + set2 = astTestObsAlt( result ); + desc = "altitude of observer"; + + } else { + astError( AST__INTER, "VerifyMSMAttrs(SkyFrame): " + "Unknown attribute name \"%.*s\" supplied (AST " + "internal programming error).", status, len, a ); + } + +/* If the attribute is not set in the target but should be, report an + error. */ + if( !usedef1 && !set1 && which < 3 ) { + astClearTitle( target ); + astClearTitle( result ); + astError( AST__NOVAL, "%s(%s): Cannot convert " + "celestial coordinates from %s to %s.", status, + method, astGetClass( target ), + astGetC( target, "Title" ), + astGetC( result, "Title" ) ); + astError( AST__NOVAL, "No value has been set for " + "the \"%.*s\" attribute (%s) in the input %s.", status, + len, a, desc, astGetClass( target ) ); + break; + } + +/* If the attribute is not set in the result but should be, report an + error. */ + if( !usedef2 && !set2 && which > 1 ) { + astClearTitle( target ); + astClearTitle( result ); + astError( AST__NOVAL, "%s(%s): Cannot convert " + "celestial coordinates from %s to %s.", status, + method, astGetClass( result ), + astGetC( target, "Title" ), + astGetC( result, "Title" ) ); + astError( AST__NOVAL, "No value has been set for " + "the \"%.*s\" attribute (%s) in the output %s.", status, + len, a, desc, astGetClass( result ) ); + break; + } + +/* Continue the word search algorithm. */ + } + len = 0; + state = 0; + } else { + len++; + } + } + if( !*(p++) ) break; + } + } +} + +/* Functions which access class attributes. */ +/* ---------------------------------------- */ +/* +*att++ +* Name: +* AlignOffset + +* Purpose: +* Align SkyFrames using the offset coordinate system? + +* Type: +* Public attribute. + +* Synopsis: +* Integer (boolean). + +* Description: +* This attribute is a boolean value which controls how a SkyFrame +* behaves when it is used (by +c astFindFrame or astConvert) as a template to match another (target) +f AST_FINDFRAME or AST_CONVERT) as a template to match another (target) +* SkyFrame. It determines the coordinate system in which the two +* SkyFrames are aligned if a match occurs. +* +* If the template and target SkyFrames both have defined offset coordinate +* systems (i.e. the SkyRefIs attribute is set to either "Origin" or " +* Pole"), and they both have a non-zero value for AlignOffset, then +* alignment occurs within the offset coordinate systems (that is, a +* UnitMap will always be used to align the two SkyFrames). If either +* the template or target SkyFrame has zero (the default value) for +* AlignOffset, or if either SkyFrame has SkyRefIs set to "Ignored", then +* alignment occurring within the coordinate system specified by the +* AlignSystem attribute. + +* Applicability: +* SkyFrame +* All SkyFrames have this attribute. +*att-- +*/ +astMAKE_CLEAR(SkyFrame,AlignOffset,alignoffset,-INT_MAX) +astMAKE_GET(SkyFrame,AlignOffset,int,0,( ( this->alignoffset != -INT_MAX ) ? + this->alignoffset : 0 )) +astMAKE_SET(SkyFrame,AlignOffset,int,alignoffset,( value != 0 )) +astMAKE_TEST(SkyFrame,AlignOffset,( this->alignoffset != -INT_MAX )) + +/* +*att++ +* Name: +* AsTime(axis) + +* Purpose: +* Format celestal coordinates as times? + +* Type: +* Public attribute. + +* Synopsis: +* Integer (boolean). + +* Description: +* This attribute specifies the default style of formatting to be +c used (e.g. by astFormat) for the celestial coordinate values +f used (e.g. by AST_FORMAT) for the celestial coordinate values +* described by a SkyFrame. It takes a separate boolean value for +* each SkyFrame axis so that, for instance, the setting +* "AsTime(2)=0" specifies the default formatting style for +* celestial latitude values. +* +* If the AsTime attribute for a SkyFrame axis is zero, then +* coordinates on that axis will be formatted as angles by default +* (using degrees, minutes and seconds), otherwise they will be +* formatted as times (using hours, minutes and seconds). +* +* The default value of AsTime is chosen according to the sky +* coordinate system being represented, as determined by the +* SkyFrame's System attribute. This ensures, for example, that +* right ascension values will be formatted as times by default, +* following normal conventions. + +* Applicability: +* SkyFrame +* All SkyFrames have this attribute. + +* Notes: +* - The AsTime attribute operates by changing the default value of +* the corresponding Format(axis) attribute. This, in turn, may +* also affect the value of the Unit(axis) attribute. +* - Only the default style of formatting is affected by the AsTime +* value. If an explicit Format(axis) value is set, it will +* over-ride any effect from the AsTime attribute. +*att-- +*/ + +/* +*att++ +* Name: +* Equinox + +* Purpose: +* Epoch of the mean equinox. + +* Type: +* Public attribute. + +* Synopsis: +* Floating point. + +* Description: +* This attribute is used to qualify those celestial coordinate +* systems described by a SkyFrame which are notionally based on +* the ecliptic (the plane of the Earth's orbit around the Sun) +* and/or the Earth's equator. +* +* Both of these planes are in motion and their positions are +* difficult to specify precisely. In practice, therefore, a model +* ecliptic and/or equator are used instead. These, together with +* the point on the sky that defines the coordinate origin (the +* intersection of the two planes termed the "mean equinox") move +* with time according to some model which removes the more rapid +* fluctuations. The SkyFrame class supports both the FK4 and +* FK5 models. +* +* The position of a fixed source expressed in any of these +* coordinate systems will appear to change with time due to +* movement of the coordinate system itself (rather than motion of +* the source). Such coordinate systems must therefore be +* qualified by a moment in time (the "epoch of the mean equinox" +* or "equinox" for short) which allows the position of the model +* coordinate system on the sky to be determined. This is the role +* of the Equinox attribute. +* +* The Equinox attribute is stored as a Modified Julian Date, but +* when setting or getting its value you may use the same formats +* as for the Epoch attribute (q.v.). +* +* The default Equinox value is B1950.0 (Besselian) for the old +* FK4-based coordinate systems (see the System attribute) and +* J2000.0 (Julian) for all others. + +* Applicability: +* SkyFrame +* All SkyFrames have this attribute. + +* Notes: +* - Care must be taken to distinguish the Equinox value, which +* relates to the definition of a time-dependent coordinate system +* (based on solar system reference planes which are in motion), +* from the superficially similar Epoch value. The latter is used +* to qualify coordinate systems where the positions of sources +* change with time (or appear to do so) for a variety of other +* reasons, such as aberration of light caused by the observer's +* motion, etc. +* - See the description of the System attribute for details of +* which qualifying attributes apply to each celestial coordinate +* system. +*att-- +*/ +/* Clear the Equinox value by setting it to AST__BAD. */ +astMAKE_CLEAR(SkyFrame,Equinox,equinox,AST__BAD) + +/* Provide a default value of B1950.0 or J2000.0 depending on the System + setting. */ +astMAKE_GET(SkyFrame,Equinox,double,AST__BAD,( + ( this->equinox != AST__BAD ) ? this->equinox : + ( ( ( astGetSystem( this ) == AST__FK4 ) || + ( astGetSystem( this ) == AST__FK4_NO_E ) ) ? + palEpb2d( 1950.0 ) : palEpj2d( 2000.0 ) ) )) + +/* Allow any Equinox value to be set, unless the System is Helio-ecliptic + (in which case clear the value so that J2000 is used). */ +astMAKE_SET(SkyFrame,Equinox,double,equinox,((astGetSystem(this)!=AST__HELIOECLIPTIC)?value:AST__BAD)) + +/* An Equinox value is set if it is not equal to AST__BAD. */ +astMAKE_TEST(SkyFrame,Equinox,( this->equinox != AST__BAD )) + + +/* +*att++ +* Name: +* IsLatAxis(axis) + +* Purpose: +* Is the specified celestial axis a latitude axis? + +* Type: +* Public attribute. + +* Synopsis: +* Integer (boolean), read-only. + +* Description: +* This is a read-only boolean attribute that indicates the nature of +* the specified axis. The attribute has a non-zero value if the +* specified axis is a celestial latitude axis (Declination, Galactic +* latitude, etc), and is zero otherwise. + +* Applicability: +* SkyFrame +* All SkyFrames have this attribute. + +* Notes: +* - When specifying this attribute by name, it should be +* subscripted with the number of the SkyFrame axis to be tested. +*att-- +*/ + +/* +*att++ +* Name: +* IsLonAxis(axis) + +* Purpose: +* Is the specified celestial axis a longitude axis? + +* Type: +* Public attribute. + +* Synopsis: +* Integer (boolean), read-only. + +* Description: +* This is a read-only boolean attribute that indicates the nature of +* the specified axis. The attribute has a non-zero value if the +* specified axis is a celestial longitude axis (Right Ascension, Galactic +* longitude, etc), and is zero otherwise. + +* Applicability: +* SkyFrame +* All SkyFrames have this attribute. + +* Notes: +* - When specifying this attribute by name, it should be +* subscripted with the number of the SkyFrame axis to be tested. +*att-- +*/ + +/* +*att++ +* Name: +* LatAxis + +* Purpose: +* Index of the latitude axis. + +* Type: +* Public attribute. + +* Synopsis: +* Integer. + +* Description: +* This read-only attribute gives the index (1 or 2) of the latitude +* axis within the SkyFrame (taking into account any current axis +* permutations). + +* Applicability: +* SkyFrame +* All SkyFrames have this attribute. + +*att-- +*/ + +/* +*att++ +* Name: +* LonAxis + +* Purpose: +* Index of the longitude axis. + +* Type: +* Public attribute. + +* Synopsis: +* Integer. + +* Description: +* This read-only attribute gives the index (1 or 2) of the longitude +* axis within the SkyFrame (taking into account any current axis +* permutations). + +* Applicability: +* SkyFrame +* All SkyFrames have this attribute. + +*att-- +*/ + +/* +*att++ +* Name: +* NegLon + +* Purpose: +* Display negative longitude values? + +* Type: +* Public attribute. + +* Synopsis: +* Integer (boolean). + +* Description: +* This attribute is a boolean value which controls how longitude values +c are normalized for display by astNorm. +f are normalized for display by AST_NORM. +* +* If the NegLon attribute is zero, then normalized +* longitude values will be in the range zero to 2.pi. If NegLon is +* non-zero, then normalized longitude values will be in the range -pi +* to pi. +* +* The default value depends on the current value of the SkyRefIs +* attribute, If SkyRefIs has a value of "Origin", then the default for +* NegLon is one, otherwise the default is zero. + +* Applicability: +* SkyFrame +* All SkyFrames have this attribute. +*att-- +*/ +/* Clear the NegLon value by setting it to -INT_MAX. */ +astMAKE_CLEAR(SkyFrame,NegLon,neglon,-INT_MAX) + +/* Supply a default of 0 for absolute coords and 1 for offset coords if + no NegLon value has been set. */ +astMAKE_GET(SkyFrame,NegLon,int,0,( ( this->neglon != -INT_MAX ) ? +this->neglon : (( astGetSkyRefIs( this ) == AST__ORIGIN_REF )? 1 : 0))) + +/* Set a NegLon value of 1 if any non-zero value is supplied. */ +astMAKE_SET(SkyFrame,NegLon,int,neglon,( value != 0 )) + +/* The NegLon value is set if it is not -INT_MAX. */ +astMAKE_TEST(SkyFrame,NegLon,( this->neglon != -INT_MAX )) + +/* +*att++ +* Name: +* SkyTol + +* Purpose: +* The smallest significant shift in sky coordinates. + +* Type: +* Public attribute. + +* Synopsis: +* Floating point. + +* Description: +* This attribute indicates the accuracy of the axis values that will +* be represented by the SkyFrame. If the arc-distance between two +* positions within the SkyFrame is smaller than the value of SkyTol, +* then the two positions will (for the puposes indicated below) be +* considered to be co-incident. +* +* This value is used only when constructing the Mapping between +* two different SkyFrames (for instance, when calling +c astConvert or astFindFrame). +f AST_CONVERT or AST_FINDFRAME). +* If the transformation between the two SkyFrames causes positions to +* shift by less than SkyTol arc-seconds, then the transformation is +* replaced by a UnitMap. This could in certain circumatances allow +* major simplifications to be made to the transformation between +* any pixel grids associated with the two SkyFrames (for instance, if +* each SkyFrame is part of the WCS FrameSet associated with an image). +* +* A common case is when two SkyFrames use the FK5 system, but have +* slightly different Epoch values. If the AlignSystem attribute has +* its default value of "ICRS", then the transformation between the +* two SkyFrames will include a very small rotation (FK5 rotates with +* respect to ICRS as a rate of about 0.0005 arc-seconds per year). In +* most circumstances such a small rotation is insignificant. Setting +* SkyTol to some suitably small non-zero value will cause this +* rotation to be ignored, allowing much simpler transformations to +* be used. +* +* The test to determine the shift introduced by transforming between +* the two SkyFrames is performed by transforming a set of 14 position +* spread evenly over the whole sky. The largest shift produced at any +* of these 14 positions is compared to the value of SkyTol. +* +* The SkyTol value is in units of arc-seconds, and the default value +* is 0.001. + +* Applicability: +* SkyFrame +* All SkyFrames have this attribute. +*att-- +*/ +astMAKE_CLEAR(SkyFrame,SkyTol,skytol,AST__BAD) +astMAKE_GET(SkyFrame,SkyTol,double,0.001,((this->skytol!=AST__BAD)?this->skytol:0.001)) +astMAKE_SET(SkyFrame,SkyTol,double,skytol,fabs(value)) +astMAKE_TEST(SkyFrame,SkyTol,(this->skytol!=AST__BAD)) + +/* +*att++ +* Name: +* Projection + +* Purpose: +* Sky projection description. + +* Type: +* Public attribute. + +* Synopsis: +* String. + +* Description: +* This attribute provides a place to store a description of the +* type of sky projection used when a SkyFrame is attached to a +* 2-dimensional object, such as an image or plotting surface. For +* example, typical values might be "orthographic", "Hammer-Aitoff" +* or "cylindrical equal area". +* +* The Projection value is purely descriptive and does not affect +* the celestial coordinate system represented by the SkyFrame in +* any way. If it is set to a non-blank string, the description +* provided may be used when forming the default value for the +* SkyFrame's Title attribute (so that typically it will appear in +* graphical output, for instance). The default value is an empty +* string. + +* Applicability: +* SkyFrame +* All SkyFrames have this attribute. +*att-- +*/ +/* Clear the Projection value by freeing the allocated memory and + assigning a NULL pointer. */ +astMAKE_CLEAR(SkyFrame,Projection,projection,astFree( this->projection )) + +/* If the Projection value is not set, return a pointer to an empty + string. */ +astMAKE_GET(SkyFrame,Projection,const char *,NULL,( this->projection ? + this->projection : "" )) + +/* Set a Projection value by freeing any previously allocated memory, + allocating new memory, storing the string and saving the pointer to + the copy. */ +astMAKE_SET(SkyFrame,Projection,const char *,projection,astStore( + this->projection, value, strlen( value ) + (size_t) 1 )) + +/* The Projection value is set if the pointer to it is not NULL. */ +astMAKE_TEST(SkyFrame,Projection,( this->projection != NULL )) + +/* +*att++ +* Name: +* SkyRefIs + +* Purpose: +* Selects the nature of the offset coordinate system. + +* Type: +* Public attribute. + +* Synopsis: +* String. + +* Description: +* This attribute controls how the values supplied for the SkyRef and +* SkyRefP attributes are used. These three attributes together allow +* a SkyFrame to represent offsets relative to some specified origin +* or pole within the coordinate system specified by the System attribute, +* rather than absolute axis values. SkyRefIs can take one of the +* case-insensitive values "Origin", "Pole" or "Ignored". +* +* If SkyRefIs is set to "Origin", then the coordinate system +* represented by the SkyFrame is modified to put the origin of longitude +* and latitude at the position specified by the SkyRef attribute. +* +* If SkyRefIs is set to "Pole", then the coordinate system represented +* by the SkyFrame is modified to put the north pole at the position +* specified by the SkyRef attribute. +* +* If SkyRefIs is set to "Ignored" (the default), then any value set for the +* SkyRef attribute is ignored, and the SkyFrame represents the coordinate +* system specified by the System attribute directly without any rotation. + +* Applicability: +* SkyFrame +* All SkyFrames have this attribute. + +*att-- +*/ +astMAKE_CLEAR(SkyFrame,SkyRefIs,skyrefis,AST__BAD_REF) +astMAKE_SET(SkyFrame,SkyRefIs,int,skyrefis,value) +astMAKE_TEST(SkyFrame,SkyRefIs,( this->skyrefis != AST__BAD_REF )) +astMAKE_GET(SkyFrame,SkyRefIs,int,AST__IGNORED_REF,(this->skyrefis == AST__BAD_REF ? AST__IGNORED_REF : this->skyrefis)) + +/* +*att++ +* Name: +* SkyRef(axis) + +* Purpose: +* Position defining the offset coordinate system. + +* Type: +* Public attribute. + +* Synopsis: +* Floating point. + +* Description: +* This attribute allows a SkyFrame to represent offsets, rather than +* absolute axis values, within the coordinate system specified by the +* System attribute. If supplied, SkyRef should be set to hold the +* longitude and latitude of a point within the coordinate system +* specified by the System attribute. The coordinate system represented +* by the SkyFrame will then be rotated in order to put the specified +* position at either the pole or the origin of the new coordinate system +* (as indicated by the SkyRefIs attribute). The orientation of the +* modified coordinate system is then controlled using the SkyRefP +* attribute. +* +* If an integer axis index is included in the attribute name (e.g. +* "SkyRef(1)") then the attribute value should be supplied as a single +* floating point axis value, in radians, when setting a value for the +* attribute, and will be returned in the same form when getting the value +* of the attribute. In this case the integer axis index should be "1" +* or "2" (the values to use for longitude and latitude axes are +* given by the LonAxis and LatAxis attributes). +* +* If no axis index is included in the attribute name (e.g. "SkyRef") then +* the attribute value should be supplied as a character string +* containing two formatted axis values (an axis 1 value followed by a +* comma, followed by an axis 2 value). The same form +* will be used when getting the value of the attribute. +* +* The default values for SkyRef are zero longitude and zero latitude. + +* Aligning SkyFrames with Offset Coordinate Systems: +* The offset coordinate system within a SkyFrame should normally be +* considered as a superficial "re-badging" of the axes of the coordinate +* system specified by the System attribute - it merely provides an +* alternative numerical "label" for each position in the System coordinate +* system. The SkyFrame retains full knowledge of the celestial coordinate +* system on which the offset coordinate system is based (given by the +* System attribute). For instance, the SkyFrame retains knowledge of the +* way that one celestial coordinate system may "drift" with respect to +* another over time. Normally, if you attempt to align two SkyFrames (e.g. +f using the AST_CONVERT or AST_FINDFRAME routine), +c using the astConvert or astFindFrame routine), +* the effect of any offset coordinate system defined in either SkyFrame +* will be removed, resulting in alignment being performed in the +* celestial coordinate system given by the AlignSystem attribute. +* However, by setting the AlignOffset attribute to a non-zero value, it +* is possible to change this behaviour so that the effect of the offset +* coordinate system is not removed when aligning two SkyFrames. + +* Applicability: +* SkyFrame +* All SkyFrames have this attribute. + +* Notes: +* - If the System attribute of the SkyFrame is changed, any position +* given for SkyRef is transformed into the new System. +* - If a value has been assigned to SkyRef attribute, then +* the default values for certain attributes are changed as follows: +* the default axis Labels for the SkyFrame are modified by appending +* " offset" to the end, the default axis Symbols for the SkyFrame are +* modified by prepending the character "D" to the start, and the +* default title is modified by replacing the projection information by the +* origin information. + +*att-- +*/ +MAKE_CLEAR(SkyRef,skyref,AST__BAD,2) +MAKE_SET(SkyRef,double,skyref,value,2) +MAKE_TEST(SkyRef,( this->skyref[axis_p] != AST__BAD ),2) +MAKE_GET(SkyRef,double,0.0,((this->skyref[axis_p]!=AST__BAD)?this->skyref[axis_p]:0.0),2) + +/* +*att++ +* Name: +* SkyRefP(axis) + +* Purpose: +* Position on primary meridian of offset coordinate system. + +* Type: +* Public attribute. + +* Synopsis: +* Floating point. + +* Description: +* This attribute is used to control the orientation of the offset +* coordinate system defined by attributes SkyRef and SkyRefIs. If used, +* it should be set to hold the longitude and latitude of a point within +* the coordinate system specified by the System attribute. The offset +* coordinate system represented by the SkyFrame will then be rotated in +* order to put the position supplied for SkyRefP on the zero longitude +* meridian. This rotation is about an axis from the centre of the +* celestial sphere to the point specified by the SkyRef attribute. +* The default value for SkyRefP is usually the north pole (that is, a +* latitude of +90 degrees in the coordinate system specified by the System +* attribute). The exception to this is if the SkyRef attribute is +* itself set to either the north or south pole. In these cases the +* default for SkyRefP is the origin (that is, a (0,0) in the coordinate +* system specified by the System attribute). +* +* If an integer axis index is included in the attribute name (e.g. +* "SkyRefP(1)") then the attribute value should be supplied as a single +* floating point axis value, in radians, when setting a value for the +* attribute, and will be returned in the same form when getting the value +* of the attribute. In this case the integer axis index should be "1" +* or "2" (the values to use for longitude and latitude axes are +* given by the LonAxis and LatAxis attributes). +* +* If no axis index is included in the attribute name (e.g. "SkyRefP") then +* the attribute value should be supplied as a character string +* containing two formatted axis values (an axis 1 value followed by a +* comma, followed by an axis 2 value). The same form +* will be used when getting the value of the attribute. + +* Applicability: +* SkyFrame +* All SkyFrames have this attribute. + +* Notes: +* - If the position given by the SkyRef attribute defines the origin +* of the offset coordinate system (that is, if the SkyRefIs attribute +* is set to "origin"), then there will in general be two orientations +* which will put the supplied SkyRefP position on the zero longitude +* meridian. The orientation which is actually used is the one which +* gives the SkyRefP position a positive latitude in the offset coordinate +* system (the other possible orientation would give the SkyRefP position +* a negative latitude). +* - An error will be reported if an attempt is made to use a +* SkyRefP value which is co-incident with SkyRef or with the point +* diametrically opposite to SkyRef on the celestial sphere. The +* reporting of this error is deferred until the SkyRef and SkyRefP +* attribute values are used within a calculation. +* - If the System attribute of the SkyFrame is changed, any position +* given for SkyRefP is transformed into the new System. + +*att-- +*/ +MAKE_CLEAR(SkyRefP,skyrefp,AST__BAD,2) +MAKE_SET(SkyRefP,double,skyrefp,value,2) +MAKE_TEST(SkyRefP,( this->skyrefp[axis_p] != AST__BAD ),2) + +/* Copy constructor. */ +/* ----------------- */ +static void Copy( const AstObject *objin, AstObject *objout, int *status ) { +/* +* Name: +* Copy + +* Purpose: +* Copy constructor for SkyFrame objects. + +* Type: +* Private function. + +* Synopsis: +* void Copy( const AstObject *objin, AstObject *objout, int *status ) + +* Description: +* This function implements the copy constructor for SkyFrame objects. + +* Parameters: +* objin +* Pointer to the object to be copied. +* objout +* Pointer to the object being constructed. +* status +* Pointer to the inherited status variable. + +* Notes: +* - This constructor makes a deep copy. +*/ + +/* Local Variables: */ + AstSkyFrame *in; /* Pointer to input SkyFrame */ + AstSkyFrame *out; /* Pointer to output SkyFrame */ + +/* Check the global error status. */ + if ( !astOK ) return; + +/* Obtain pointers to the input and output SkyFrames. */ + in = (AstSkyFrame *) objin; + out = (AstSkyFrame *) objout; + +/* For safety, first clear any references to the input memory from + the output SkyFrame. */ + out->projection = NULL; + +/* If necessary, allocate memory in the output SkyFrame and store a + copy of the input Projection string. */ + if ( in->projection ) out->projection = astStore( NULL, in->projection, + strlen( in->projection ) + (size_t) 1 ); + +/* If an error occurred, free any allocated memory. */ + if ( !astOK ) { + out->projection = astFree( out->projection ); + } +} + +/* Destructor. */ +/* ----------- */ +static void Delete( AstObject *obj, int *status ) { +/* +* Name: +* Delete + +* Purpose: +* Destructor for SkyFrame objects. + +* Type: +* Private function. + +* Synopsis: +* void Delete( AstObject *obj, int *status ) + +* Description: +* This function implements the destructor for SkyFrame objects. + +* Parameters: +* obj +* Pointer to the object to be deleted. +* status +* Pointer to the inherited status variable. + +* Notes: +* This function attempts to execute even if the global error status is +* set. +*/ + +/* Local Variables: */ + AstSkyFrame *this; /* Pointer to SkyFrame */ + +/* Obtain a pointer to the SkyFrame structure. */ + this = (AstSkyFrame *) obj; + +/* Free the memory used for the Projection string if necessary. */ + this->projection = astFree( this->projection ); +} + +/* Dump function. */ +/* -------------- */ +static void Dump( AstObject *this_object, AstChannel *channel, int *status ) { +/* +* Name: +* Dump + +* Purpose: +* Dump function for SkyFrame objects. + +* Type: +* Private function. + +* Synopsis: +* void Dump( AstObject *this, AstChannel *channel, int *status ) + +* Description: +* This function implements the Dump function which writes out data +* for the SkyFrame class to an output Channel. + +* Parameters: +* this +* Pointer to the SkyFrame whose data are being written. +* channel +* Pointer to the Channel to which the data are being written. +* status +* Pointer to the inherited status variable. +*/ + +/* Local Variables: */ + AstSkyFrame *this; /* Pointer to the SkyFrame structure */ + AstSystemType system; /* System attribute value */ + char buf[ 100 ]; /* Comment buffer */ + char key[ 10 ]; /* Buffer for keywords */ + const char *sval; /* Pointer to string value */ + const int *perm; /* Pointer to axis permutation array */ + double dval; /* Double value */ + int bessyr; /* Use a Besselian year value ?*/ + int helpful; /* Helpful to display un-set value? */ + int invperm[ 2 ]; /* Inverse permutation array */ + int ival; /* Integer value */ + int set; /* Attribute value set? */ + int axis; /* External (i.e. permuted) zero-based axis index */ + int axis_p; /* Internal zero-based axis index */ + +/* Check the global error status. */ + if ( !astOK ) return; + +/* Obtain a pointer to the SkyFrame structure. */ + this = (AstSkyFrame *) this_object; + +/* Get a pointer to the SkyFrame's axis permutation array (using a method, + to allow for any over-ride by a derived class). */ + perm = astGetPerm( this ); + +/* Generate an inverse axis permutation array from the forward permutation + values. This will be used to determine which axis should be enquired + about (using possibly over-ridden methods) to obtain data to + correspond with a particular internal value (i.e. instance variable) + relating to an axis. This step is needed so that the effect of any + axis permutation can be un-done before values are written out, as + output values are written by this function in un-permuted order. */ + for ( axis = 0; axis < 2; axis++ ) invperm[ perm[ axis ] ] = axis; + +/* Write out values representing the instance variables for the + SkyFrame class. Accompany these with appropriate comment strings, + possibly depending on the values being written.*/ + +/* In the case of attributes, we first use the appropriate (private) + Test... member function to see if they are set. If so, we then use + the (private) Get... function to obtain the value to be written + out. + + For attributes which are not set, we use the astGet... method to + obtain the value instead. This will supply a default value + (possibly provided by a derived class which over-rides this method) + which is more useful to a human reader as it corresponds to the + actual default attribute value. Since "set" will be zero, these + values are for information only and will not be read back. */ + +/* Projection. */ +/* ----------- */ + set = TestProjection( this, status ); + sval = set ? GetProjection( this, status ) : astGetProjection( this ); + astWriteString( channel, "Proj", set, 0, sval, + "Description of sky projection" ); + +/* NegLon. */ +/* ------- */ + set = TestNegLon( this, status ); + ival = set ? GetNegLon( this, status ) : astGetNegLon( this ); + astWriteInt( channel, "NegLon", set, 0, ival, + ival ? "Display negative longitude values" : + "Display positive longitude values" ); + +/* SkyTol. */ +/* ------- */ + set = TestSkyTol( this, status ); + dval = set ? GetSkyTol( this, status ) : astGetSkyTol( this ); + astWriteDouble( channel, "SkyTol", set, 1, dval, + "Smallest significant separation [arc-sec]"); + +/* Equinox. */ +/* -------- */ + set = TestEquinox( this, status ); + dval = set ? GetEquinox( this, status ) : astGetEquinox( this ); + +/* Decide whether the Equinox value is relevant to the current + coordinate system. */ + system = astGetSystem( this ); + helpful = ( ( system == AST__FK4 ) || + ( system == AST__FK4_NO_E ) || + ( system == AST__FK5 ) || + ( system == AST__ECLIPTIC ) ); + +/* Convert MJD to Besselian or Julian years, depending on the value. */ + bessyr = ( dval < palEpj2d( 1984.0 ) ); + dval = bessyr ? palEpb( dval ) : palEpj( dval ); + astWriteDouble( channel, "Eqnox", set, helpful, dval, + bessyr ? "Besselian epoch of mean equinox" : + "Julian epoch of mean equinox" ); + +/* SkyRefIs. */ +/* --------- */ + set = TestSkyRefIs( this, status ); + ival = set ? GetSkyRefIs( this, status ) : astGetSkyRefIs( this ); + if( ival == AST__POLE_REF ) { + astWriteString( channel, "SRefIs", set, 0, POLE_STRING, + "Rotated to put pole at ref. pos." ); + + } else if( ival == AST__IGNORED_REF ) { + astWriteString( channel, "SRefIs", set, 0, IGNORED_STRING, + "Not rotated (ref. pos. is ignored)" ); + + } else { + astWriteString( channel, "SRefIs", set, 0, ORIGIN_STRING, + "Rotated to put origin at ref. pos." ); + } + +/* SkyRef. */ +/* ------- */ +/* The inverse axis permutation array is used to obtain the axis index + to use when accessing the SkyRef attribute. This reverses the effect + of the SkyFrame's axis permutation array and yields a value appropriate + to the axis with internal index "axis". */ + for ( axis_p = 0; axis_p < 2; axis_p++ ) { + axis = invperm[ axis_p ]; + + set = TestSkyRef( this, axis, status ); + dval = set ? GetSkyRef( this, axis, status ) : astGetSkyRef( this, axis ); + sprintf( buf, "Ref. pos. %s %s", astGetSymbol( this, axis ), + astFormat( this, axis, dval ) ); + sprintf( key, "SRef%d", axis_p + 1 ); + astWriteDouble( channel, key, set, 0, dval, buf ); + } + +/* SkyRefP. */ +/* -------- */ + for ( axis_p = 0; axis_p < 2; axis_p++ ) { + axis = invperm[ axis_p ]; + + set = TestSkyRefP( this, axis, status ); + dval = set ? GetSkyRefP( this, axis, status ) : astGetSkyRefP( this, axis ); + sprintf( buf, "Ref. north %s %s", astGetSymbol( this, axis ), + astFormat( this, axis, dval ) ); + sprintf( key, "SRefP%d", axis_p + 1 ); + astWriteDouble( channel, key, set, 0, dval, buf ); + } + +/* AlignOffset. */ +/* ------------ */ + set = TestAlignOffset( this, status ); + ival = set ? GetAlignOffset( this, status ) : astGetAlignOffset( this ); + astWriteInt( channel, "AlOff", set, 0, ival, + ival ? "Align in offset coords" : + "Align in system coords" ); +} + +/* Standard class functions. */ +/* ========================= */ +/* Implement the astIsASkyFrame and astCheckSkyFrame functions using the macros + defined for this purpose in the "object.h" header file. */ +astMAKE_ISA(SkyFrame,Frame) +astMAKE_CHECK(SkyFrame) + +AstSkyFrame *astSkyFrame_( const char *options, int *status, ...) { +/* +*+ +* Name: +* astSkyFrame + +* Purpose: +* Create a SkyFrame. + +* Type: +* Protected function. + +* Synopsis: +* #include "skyframe.h" +* AstSkyFrame *astSkyFrame( const char *options, int *status, ... ) + +* Class Membership: +* SkyFrame constructor. + +* Description: +* This function creates a new SkyFrame and optionally initialises its +* attributes. + +* Parameters: +* options +* Pointer to a null terminated string containing an optional +* comma-separated list of attribute assignments to be used for +* initialising the new SkyFrame. The syntax used is the same as for the +* astSet method and may include "printf" format specifiers identified +* by "%" symbols in the normal way. +* status +* Pointer to the inherited status variable. +* ... +* If the "options" string contains "%" format specifiers, then an +* optional list of arguments may follow it in order to supply values to +* be substituted for these specifiers. The rules for supplying these +* are identical to those for the astSet method (and for the C "printf" +* function). + +* Returned Value: +* A pointer to the new SkyFrame. + +* Notes: +* - A NULL pointer will be returned if this function is invoked with the +* global error status set, or if it should fail for any reason. +*- + +* Implementation Notes: +* - This function implements the basic SkyFrame constructor which +* is available via the protected interface to the SkyFrame class. +* A public interface is provided by the astSkyFrameId_ function. +*/ + +/* Local Variables: */ + astDECLARE_GLOBALS /* Pointer to thread-specific global data */ + AstSkyFrame *new; /* Pointer to new SkyFrame */ + va_list args; /* Variable argument list */ + +/* Get a pointer to the thread specific global data structure. */ + astGET_GLOBALS(NULL); + +/* Check the global status. */ + if ( !astOK ) return NULL; + +/* Initialise the SkyFrame, allocating memory and initialising the virtual + function table as well if necessary. */ + new = astInitSkyFrame( NULL, sizeof( AstSkyFrame ), !class_init, &class_vtab, + "SkyFrame" ); + +/* If successful, note that the virtual function table has been initialised. */ + if ( astOK ) { + class_init = 1; + +/* Obtain the variable argument list and pass it along with the options string + to the astVSet method to initialise the new SkyFrame's attributes. */ + va_start( args, status ); + astVSet( new, options, NULL, args ); + va_end( args ); + +/* If an error occurred, clean up by deleting the new object. */ + if ( !astOK ) new = astDelete( new ); + } + +/* Return a pointer to the new SkyFrame. */ + return new; +} + +AstSkyFrame *astInitSkyFrame_( void *mem, size_t size, int init, + AstSkyFrameVtab *vtab, const char *name, int *status ) { +/* +*+ +* Name: +* astInitSkyFrame + +* Purpose: +* Initialise a SkyFrame. + +* Type: +* Protected function. + +* Synopsis: +* #include "skyframe.h" +* AstSkyFrame *astInitSkyFrame( void *mem, size_t size, int init, +* AstFrameVtab *vtab, const char *name ) + +* Class Membership: +* SkyFrame initialiser. + +* Description: +* This function is provided for use by class implementations to initialise +* a new SkyFrame object. It allocates memory (if necessary) to accommodate +* the SkyFrame plus any additional data associated with the derived class. +* It then initialises a SkyFrame structure at the start of this memory. If +* the "init" flag is set, it also initialises the contents of a virtual +* function table for a SkyFrame at the start of the memory passed via the +* "vtab" parameter. + +* Parameters: +* mem +* A pointer to the memory in which the SkyFrame is to be created. This +* must be of sufficient size to accommodate the SkyFrame data +* (sizeof(SkyFrame)) plus any data used by the derived class. If a value +* of NULL is given, this function will allocate the memory itself using +* the "size" parameter to determine its size. +* size +* The amount of memory used by the SkyFrame (plus derived class data). +* This will be used to allocate memory if a value of NULL is given for +* the "mem" parameter. This value is also stored in the SkyFrame +* structure, so a valid value must be supplied even if not required for +* allocating memory. +* init +* A logical flag indicating if the SkyFrame's virtual function table is +* to be initialised. If this value is non-zero, the virtual function +* table will be initialised by this function. +* vtab +* Pointer to the start of the virtual function table to be associated +* with the new SkyFrame. +* name +* Pointer to a constant null-terminated character string which contains +* the name of the class to which the new object belongs (it is this +* pointer value that will subsequently be returned by the astGetClass +* method). + +* Returned Value: +* A pointer to the new SkyFrame. + +* Notes: +* - A null pointer will be returned if this function is invoked with the +* global error status set, or if it should fail for any reason. +*- +*/ + +/* Local Variables: */ + AstSkyAxis *ax; /* Pointer to SkyAxis object */ + AstSkyFrame *new; /* Pointer to the new SkyFrame */ + int axis; /* Loop counter for axes */ + +/* Check the global status. */ + if ( !astOK ) return NULL; + +/* If necessary, initialise the virtual function table. */ + if ( init ) astInitSkyFrameVtab( vtab, name ); + +/* Initialise a Frame structure (the parent class) as the first component + within the SkyFrame structure, allocating memory if necessary. */ + new = (AstSkyFrame *) astInitFrame( mem, size, 0, + (AstFrameVtab *) vtab, name, 2 ); + + if ( astOK ) { + +/* Initialise the SkyFrame data. */ +/* ----------------------------- */ +/* Initialise all attributes to their "undefined" values. */ + new->equinox = AST__BAD; + new->projection = NULL; + new->neglon = -INT_MAX; + new->skytol = AST__BAD; + new->alignoffset = -INT_MAX; + new->skyrefis = AST__BAD_REF; + new->skyref[ 0 ] = AST__BAD; + new->skyref[ 1 ] = AST__BAD; + new->skyrefp[ 0 ] = AST__BAD; + new->skyrefp[ 1 ] = AST__BAD; + new->last = AST__BAD; + new->eplast = AST__BAD; + new->klast = AST__BAD; + new->diurab = AST__BAD; + +/* Loop to replace the Axis object associated with each SkyFrame axis with + a SkyAxis object instead. */ + for ( axis = 0; axis < 2; axis++ ) { + +/* Create the new SkyAxis, assign it to the required SkyFrame axis and then + annul the SkyAxis pointer. */ + ax = astSkyAxis( "", status ); + astSetAxis( new, axis, ax ); + ax = astAnnul( ax ); + } + +/* If an error occurred, clean up by deleting the new object. */ + if ( !astOK ) new = astDelete( new ); + } + +/* Return a pointer to the new object. */ + return new; +} + +AstSkyFrame *astLoadSkyFrame_( void *mem, size_t size, + AstSkyFrameVtab *vtab, const char *name, + AstChannel *channel, int *status ) { +/* +*+ +* Name: +* astLoadSkyFrame + +* Purpose: +* Load a SkyFrame. + +* Type: +* Protected function. + +* Synopsis: +* #include "skyframe.h" +* AstSkyFrame *astLoadSkyFrame( void *mem, size_t size, +* AstSkyFrameVtab *vtab, const char *name, +* AstChannel *channel ) + +* Class Membership: +* SkyFrame loader. + +* Description: +* This function is provided to load a new SkyFrame using data read +* from a Channel. It first loads the data used by the parent class +* (which allocates memory if necessary) and then initialises a +* SkyFrame structure in this memory, using data read from the +* input Channel. + +* Parameters: +* mem +* A pointer to the memory into which the SkyFrame is to be +* loaded. This must be of sufficient size to accommodate the +* SkyFrame data (sizeof(SkyFrame)) plus any data used by +* derived classes. If a value of NULL is given, this function +* will allocate the memory itself using the "size" parameter to +* determine its size. +* size +* The amount of memory used by the SkyFrame (plus derived class +* data). This will be used to allocate memory if a value of +* NULL is given for the "mem" parameter. This value is also +* stored in the SkyFrame structure, so a valid value must be +* supplied even if not required for allocating memory. +* +* If the "vtab" parameter is NULL, the "size" value is ignored +* and sizeof(AstSkyFrame) is used instead. +* vtab +* Pointer to the start of the virtual function table to be +* associated with the new SkyFrame. If this is NULL, a pointer +* to the (static) virtual function table for the SkyFrame class +* is used instead. +* name +* Pointer to a constant null-terminated character string which +* contains the name of the class to which the new object +* belongs (it is this pointer value that will subsequently be +* returned by the astGetClass method). +* +* If the "vtab" parameter is NULL, the "name" value is ignored +* and a pointer to the string "SkyFrame" is used instead. + +* Returned Value: +* A pointer to the new SkyFrame. + +* Notes: +* - A null pointer will be returned if this function is invoked +* with the global error status set, or if it should fail for any +* reason. +*- +*/ + +/* Local Variables: */ + AstSkyFrame *new; /* Pointer to the new SkyFrame */ + astDECLARE_GLOBALS /* Pointer to thread-specific global data */ + char *sval; /* Pointer to string value */ + const int *perm; /* Pointer to axis permutation array */ + double dval; /* Floating point attribute value */ + int axis; /* External axis index */ + int invperm[ 2 ]; /* Inverse permutation array */ + +/* Initialise. */ + new = NULL; + +/* Get a pointer to the thread specific global data structure. */ + astGET_GLOBALS(channel); + +/* Check the global error status. */ + if ( !astOK ) return new; + +/* If a NULL virtual function table has been supplied, then this is + the first loader to be invoked for this SkyFrame. In this case the + SkyFrame belongs to this class, so supply appropriate values to be + passed to the parent class loader (and its parent, etc.). */ + if ( !vtab ) { + size = sizeof( AstSkyFrame ); + vtab = &class_vtab; + name = "SkyFrame"; + +/* If required, initialise the virtual function table for this class. */ + if ( !class_init ) { + astInitSkyFrameVtab( vtab, name ); + class_init = 1; + } + } + +/* Invoke the parent class loader to load data for all the ancestral + classes of the current one, returning a pointer to the resulting + partly-built SkyFrame. */ + new = astLoadFrame( mem, size, (AstFrameVtab *) vtab, name, + channel ); + + if ( astOK ) { + +/* Get a pointer to the SkyFrame's axis permutation array (using a method, + to allow for any over-ride by a derived class). */ + perm = astGetPerm( new ); + +/* Generate an inverse axis permutation array from the forward permutation + values. This will be used to determine which axis should be enquired + about (using possibly over-ridden methods) to obtain data to + correspond with a particular internal value (i.e. instance variable) + relating to an axis. This step is needed so that the effect of any + axis permutation can be un-done before values are written out, as + output values are written by this function in un-permuted order. */ + for( axis = 0; axis < 2; axis++ ) invperm[ perm[ axis ] ] = axis; + +/* Read input data. */ +/* ================ */ +/* Request the input Channel to read all the input data appropriate to + this class into the internal "values list". */ + astReadClassData( channel, "SkyFrame" ); + +/* Now read each individual data item from this list and use it to + initialise the appropriate instance variable(s) for this class. */ + +/* In the case of attributes, we first read the "raw" input value, + supplying the "unset" value as the default. If a "set" value is + obtained, we then use the appropriate (private) Set... member + function to validate and set the value properly. */ + +/* The attributes defining the offset coordinate system must be loaded + before the System attrivbute, since SetSystem uses them. */ + +/* AlignOffset */ +/* ----------- */ + new->alignoffset = astReadInt( channel, "aloff", -INT_MAX ); + if ( TestAlignOffset( new, status ) ) SetAlignOffset( new, new->alignoffset, status ); + +/* SkyRefIs. */ +/* --------- */ + sval = astReadString( channel, "srefis", " " ); + if( sval ){ + new->skyrefis = AST__BAD_REF; + if( astChrMatch( sval, POLE_STRING ) ) { + new->skyrefis = AST__POLE_REF; + } else if( astChrMatch( sval, ORIGIN_STRING ) ) { + new->skyrefis = AST__ORIGIN_REF; + } else if( astChrMatch( sval, IGNORED_STRING ) ) { + new->skyrefis = AST__IGNORED_REF; + } else if( !astChrMatch( sval, " " ) && astOK ){ + astError( AST__INTER, "astRead(SkyFrame): Corrupt SkyFrame contains " + "invalid SkyRefIs attribute value (%s).", status, sval ); + } + if( TestSkyRefIs( new, status ) ) SetSkyRefIs( new, new->skyrefis, status ); + sval = astFree( sval ); + } + +/* SkyRef. */ +/* ------- */ + new->skyref[ 0 ] = astReadDouble( channel, "sref1", AST__BAD ); + axis = invperm[ 0 ]; + if ( TestSkyRef( new, axis, status ) ) SetSkyRef( new, axis, new->skyref[ 0 ], status ); + + new->skyref[ 1 ] = astReadDouble( channel, "sref2", AST__BAD ); + axis = invperm[ 1 ]; + if ( TestSkyRef( new, axis, status ) ) SetSkyRef( new, axis, new->skyref[ 1 ], status ); + +/* SkyRefP. */ +/* -------- */ + new->skyrefp[ 0 ] = astReadDouble( channel, "srefp1", AST__BAD ); + axis = invperm[ 0 ]; + if ( TestSkyRefP( new, axis, status ) ) SetSkyRefP( new, axis, new->skyrefp[ 0 ], status ); + + new->skyrefp[ 1 ] = astReadDouble( channel, "srefp2", AST__BAD ); + axis = invperm[ 1 ]; + if ( TestSkyRefP( new, axis, status ) ) SetSkyRefP( new, axis, new->skyrefp[ 1 ], status ); + +/* System. */ +/* ------- */ +/* The System attribute is now part of the Frame class, but this code is + retained to allow this version of AST to read SkyFrames dumped by + previous versions. */ + +/* Check a value has not already been assigned to the Frames System + attribute. */ + if( !astTestSystem( new ) ){ + +/* Read the external representation as a string. */ + sval = astReadString( channel, "system", NULL ); + +/* If a value was read, use the SetAttrib method to validate and store the + new value in the correct place, then free the string. */ + if ( sval ) { + astSet( new, "System=%s", status, sval); + sval = astFree( sval ); + } + } + +/* Epoch. */ +/* ------ */ +/* The Epoch attribute is now part of the Frame class, but this code is + retained to allow this version of AST to read SkyFrames dumped by + previous versions. */ + +/* Check a value has not already been assigned to the Frames Epoch + attribute. */ + if( !astTestEpoch( new ) ){ + +/* Get the value. */ + dval = astReadDouble( channel, "epoch", AST__BAD ); + +/* If a value was read, use the SetAttrib method to validate and store the + new value in the correct place. */ + if( dval != AST__BAD ) { + if( dval < 1984.0 ) { + astSet( new, "Epoch=B%.*g", status, AST__DBL_DIG, dval); + } else { + astSet( new, "Epoch=J%.*g", status, AST__DBL_DIG, dval); + } + } + } + +/* Projection. */ +/* ----------- */ + new->projection = astReadString( channel, "proj", NULL ); + +/* Equinox. */ +/* -------- */ +/* Interpret this as Besselian or Julian depending on its value. */ + new->equinox = astReadDouble( channel, "eqnox", AST__BAD ); + if ( TestEquinox( new, status ) ) { + SetEquinox( new, ( new->equinox < 1984.0 ) ? palEpb2d( new->equinox ) : + palEpj2d( new->equinox ), status ); + } + +/* NegLon. */ +/* ------- */ + new->neglon = astReadInt( channel, "neglon", -INT_MAX ); + if ( TestNegLon( new, status ) ) SetNegLon( new, new->neglon, status ); + +/* SkyTol. */ +/* ------- */ + new->skytol = astReadDouble( channel, "skytol", AST__BAD ); + if ( TestSkyTol( new, status ) ) SetSkyTol( new, new->skytol, status ); + +/* Other values */ +/* ------------ */ + new->last = AST__BAD; + new->eplast = AST__BAD; + new->klast = AST__BAD; + new->diurab = AST__BAD; + +/* If an error occurred, clean up by deleting the new SkyFrame. */ + if ( !astOK ) new = astDelete( new ); + } + +/* Return the new SkyFrame pointer. */ + return new; +} + +/* Virtual function interfaces. */ +/* ============================ */ +/* These provide the external interface to the virtual functions defined by + this class. Each simply checks the global error status and then locates and + executes the appropriate member function, using the function pointer stored + in the object's virtual function table (this pointer is located using the + astMEMBER macro defined in "object.h"). + + Note that the member function may not be the one defined here, as it may + have been over-ridden by a derived class. However, it should still have the + same interface. */ +void astClearAsTime_( AstSkyFrame *this, int axis, int *status ) { + if ( !astOK ) return; + (**astMEMBER(this,SkyFrame,ClearAsTime))( this, axis, status ); +} +int astGetAsTime_( AstSkyFrame *this, int axis, int *status ) { + if ( !astOK ) return 0; + return (**astMEMBER(this,SkyFrame,GetAsTime))( this, axis, status ); +} +void astSetAsTime_( AstSkyFrame *this, int axis, int value, int *status ) { + if ( !astOK ) return; + (**astMEMBER(this,SkyFrame,SetAsTime))( this, axis, value, status ); +} +int astTestAsTime_( AstSkyFrame *this, int axis, int *status ) { + if ( !astOK ) return 0; + return (**astMEMBER(this,SkyFrame,TestAsTime))( this, axis, status ); +} +int astGetIsLatAxis_( AstSkyFrame *this, int axis, int *status ) { + if ( !astOK ) return 0; + return (**astMEMBER(this,SkyFrame,GetIsLatAxis))( this, axis, status ); +} +int astGetIsLonAxis_( AstSkyFrame *this, int axis, int *status ) { + if ( !astOK ) return 0; + return (**astMEMBER(this,SkyFrame,GetIsLonAxis))( this, axis, status ); +} +int astGetLatAxis_( AstSkyFrame *this, int *status ) { + if ( !astOK ) return 1; + return (**astMEMBER(this,SkyFrame,GetLatAxis))( this, status ); +} +int astGetLonAxis_( AstSkyFrame *this, int *status ) { + if ( !astOK ) return 0; + return (**astMEMBER(this,SkyFrame,GetLonAxis))( this, status ); +} +double astGetSkyRefP_( AstSkyFrame *this, int axis, int *status ) { + if ( !astOK ) return 0.0; + return (**astMEMBER(this,SkyFrame,GetSkyRefP))( this, axis, status ); +} +AstMapping *astSkyOffsetMap_( AstSkyFrame *this, int *status ) { + if ( !astOK ) return NULL; + return (**astMEMBER(this,SkyFrame,SkyOffsetMap))( this, status ); +} + +/* Special public interface functions. */ +/* =================================== */ +/* These provide the public interface to certain special functions + whose public interface cannot be handled using macros (such as + astINVOKE) alone. In general, they are named after the + corresponding protected version of the function, but with "Id" + appended to the name. */ + +/* Public Interface Function Prototypes. */ +/* ------------------------------------- */ +/* The following functions have public prototypes only (i.e. no + protected prototypes), so we must provide local prototypes for use + within this module. */ +AstSkyFrame *astSkyFrameId_( const char *, ... ); + +/* Special interface function implementations. */ +/* ------------------------------------------- */ +AstSkyFrame *astSkyFrameId_( const char *options, ... ) { +/* +*++ +* Name: +c astSkyFrame +f AST_SKYFRAME + +* Purpose: +* Create a SkyFrame. + +* Type: +* Public function. + +* Synopsis: +c #include "skyframe.h" +c AstSkyFrame *astSkyFrame( const char *options, ... ) +f RESULT = AST_SKYFRAME( OPTIONS, STATUS ) + +* Class Membership: +* SkyFrame constructor. + +* Description: +* This function creates a new SkyFrame and optionally initialises +* its attributes. +* +* A SkyFrame is a specialised form of Frame which describes +* celestial longitude/latitude coordinate systems. The particular +* celestial coordinate system to be represented is specified by +* setting the SkyFrame's System attribute (currently, the default +* is ICRS) qualified, as necessary, by a mean Equinox value and/or +* an Epoch. +* +* For each of the supported celestial coordinate systems, a SkyFrame +* can apply an optional shift of origin to create a coordinate system +* representing offsets within the celestial coordinate system from some +* specified point. This offset coordinate system can also be rotated to +* define new longitude and latitude axes. See attributes SkyRef, SkyRefIs +* and SkyRefP +* +* All the coordinate values used by a SkyFrame are in +* radians. These may be formatted in more conventional ways for +c display by using astFormat. +f display by using AST_FORMAT. + +* Parameters: +c options +f OPTIONS = CHARACTER * ( * ) (Given) +c Pointer to a null-terminated string containing an optional +c comma-separated list of attribute assignments to be used for +c initialising the new SkyFrame. The syntax used is identical to +c that for the astSet function and may include "printf" format +c specifiers identified by "%" symbols in the normal way. +c If no initialisation is required, a zero-length string may be +c supplied. +f A character string containing an optional comma-separated +f list of attribute assignments to be used for initialising the +f new SkyFrame. The syntax used is identical to that for the +f AST_SET routine. If no initialisation is required, a blank +f value may be supplied. +c ... +c If the "options" string contains "%" format specifiers, then +c an optional list of additional arguments may follow it in +c order to supply values to be substituted for these +c specifiers. The rules for supplying these are identical to +c those for the astSet function (and for the C "printf" +c function). +f STATUS = INTEGER (Given and Returned) +f The global status. + +* Returned Value: +c astSkyFrame() +f AST_SKYFRAME = INTEGER +* A pointer to the new SkyFrame. + +* Examples: +c frame = astSkyFrame( "" ); +c Creates a SkyFrame to describe the default ICRS celestial +c coordinate system. +c frame = astSkyFrame( "System = FK5, Equinox = J2005, Digits = 10" ); +c Creates a SkyFrame to describe the FK5 celestial +c coordinate system, with a mean Equinox of J2005.0. +c Because especially accurate coordinates will be used, +c additional precision (10 digits) has been requested. This will +c be used when coordinate values are formatted for display. +c frame = astSkyFrame( "System = FK4, Equinox = 1955-sep-2" ); +c Creates a SkyFrame to describe the old FK4 celestial +c coordinate system. A default Epoch value (B1950.0) is used, +c but the mean Equinox value is given explicitly as "1955-sep-2". +c frame = astSkyFrame( "System = GAPPT, Epoch = %s", date ); +c Creates a SkyFrame to describe the Geocentric Apparent +c celestial coordinate system. The Epoch value, which specifies +c the date of observation, is obtained from a date/time string +c supplied via the string pointer "date". +f FRAME = AST_SKYFRAME( ' ', STATUS ) +f Creates a SkyFrame to describe the default ICRS celestial +f coordinate system. +f FRAME = AST_SKYFRAME( 'System = FK5, Equinox = J2005, Digits = 10', STATUS ) +f Creates a SkyFrame to describe the FK5 celestial +f coordinate system, with a mean Equinox of J2005.0. +f Because especially accurate coordinates will be used, +f additional precision (10 digits) has been requested. This will +f be used when coordinate values are formatted for display. +f FRAME = AST_SKYFRAME( 'System = FK4, Equinox = 1955-SEP-2', STATUS ) +f Creates a SkyFrame to describe the old FK4 celestial +f coordinate system. A default Epoch value (B1950.0) is used, +f but the mean Equinox value is given explicitly as "1955-SEP-2". +f FRAME = AST_SKYFRAME( 'System = GAPPT, Epoch = ' // DATE, STATUS ) +f Creates a SkyFrame to describe the Geocentric Apparent +f celestial coordinate system. The Epoch value, which specifies +f the date of observation, is obtained from a date/time string +f contained in the character variable DATE. + +* Notes: +* - Currently, the default celestial coordinate system is +* ICRS. However, this default may change in future as new +* astrometric standards evolve. The intention is to track the most +* modern appropriate standard. For this reason, you should use the +* default only if this is what you intend (and can tolerate any +* associated slight change in behaviour with future versions of +* this function). If you intend to use the ICRS system +* indefinitely, then you should specify it explicitly using an +c "options" value of "System=ICRS". +f OPTIONS value of "System=ICRS". +* - Whichever celestial coordinate system is represented, it will +* have two axes. The first of these will be the longitude axis +* and the second will be the latitude axis. This order can be +c changed using astPermAxes if required. +f changed using AST_PERMAXES if required. +* - When conversion between two SkyFrames is requested (as when +c supplying SkyFrames to astConvert), +f supplying SkyFrames AST_CONVERT), +* account will be taken of the nature of the celestial coordinate +* systems they represent, together with any qualifying mean Equinox or +* Epoch values, etc. The AlignSystem attribute will also be taken into +* account. The results will therefore fully reflect the +* relationship between positions on the sky measured in the two +* systems. +* - A null Object pointer (AST__NULL) will be returned if this +c function is invoked with the AST error status set, or if it +f function is invoked with STATUS set to an error value, or if it +* should fail for any reason. +*-- + +* Implementation Notes: +* - This function implements the external (public) interface to +* the astSkyFrame constructor function. It returns an ID value +* (instead of a true C pointer) to external users, and must be +* provided because astSkyFrame_ has a variable argument list which +* cannot be encapsulated in a macro (where this conversion would +* otherwise occur). +* - The variable argument list also prevents this function from +* invoking astSkyFrame_ directly, so it must be a +* re-implementation of it in all respects, except for the final +* conversion of the result to an ID value. +*/ + +/* Local Variables: */ + astDECLARE_GLOBALS /* Pointer to thread-specific global data */ + AstSkyFrame *new; /* Pointer to new SkyFrame */ + va_list args; /* Variable argument list */ + + int *status; /* Pointer to inherited status value */ + +/* Get a pointer to the inherited status value. */ + status = astGetStatusPtr; + +/* Get a pointer to the thread specific global data structure. */ + astGET_GLOBALS(NULL); + +/* Check the global status. */ + if ( !astOK ) return NULL; + +/* Initialise the SkyFrame, allocating memory and initialising the virtual + function table as well if necessary. */ + new = astInitSkyFrame( NULL, sizeof( AstSkyFrame ), !class_init, &class_vtab, + "SkyFrame" ); + +/* If successful, note that the virtual function table has been initialised. */ + if ( astOK ) { + class_init = 1; + +/* Obtain the variable argument list and pass it along with the options string + to the astVSet method to initialise the new SkyFrame's attributes. */ + va_start( args, options ); + astVSet( new, options, NULL, args ); + va_end( args ); + +/* If an error occurred, clean up by deleting the new object. */ + if ( !astOK ) new = astDelete( new ); + } + +/* Return an ID value for the new SkyFrame. */ + return astMakeId( new ); +} + + + + + + + |