### 12 An Introduction to Coordinate System Conversions

In this section, we start to look at techniques for converting between different coordinate systems. At this stage, the tools we have available are Frames (§7), SkyFrames (§8), SpecFrames (§9), TimeFrames (§10) and various Mappings (§5). These are sufficient to allow us to begin examining the problem, but more sophisticated approaches will also emerge later (§14.2).

#### 12.1 Converting between Celestial Coordinate Systems

We begin by examining how to convert between two celestial coordinate systems represented by SkyFrames, as this is both an illuminating and practical example. Consider the problem of converting celestial coordinates between:

(1)
The old FK4 system, with no E terms, a Besselian epoch of 1958.0 and a Besselian equinox of 1960.0.
(2)
An ecliptic coordinate system based on the mean equinox and ecliptic of Julian epoch 2010.5.

This example is arbitrary but not completely unrealistic. Unless you already have expertise with such conversions, you are unlikely to find it straightforward.

Using AST, we begin by creating two SkyFrames to represent these coordinate systems, as follows:

#include "star/ast.h"
AstSkyFrame *skyframe1, *skyframe2;

...

skyframe1 = astSkyFrame( "System=FK4-NO-E, Epoch=B1958, Equinox=B1960" );
skyframe2 = astSkyFrame( "System=Ecliptic, Equinox=J2010.5" );

Note how specifying the coordinate systems consists simply of initialising the attributes of each SkyFrame appropriately. The next step is to find a way of converting between these SkyFrames. This is done using astConvert, as follows:

AstFrameSet *cvt;

...

cvt = astConvert( skyframe1, skyframe2, "" );
if ( cvt == AST__NULL ) {
<conversion is not possible>
} else {
<conversion is possible>
}

The third argument of astConvert is not used here and should be an empty string.

astConvert will return a null result, AST__NULL (as defined in the “ast.h” header file), if conversion is not possible. In this example, conversion is possible, so it will return a pointer to a new Object that describes the conversion.

The Object returned is called a FrameSet. We have not discussed FrameSets yet (§13), but for the present purposes we can consider them simply as Objects that can behave both as Mappings and as Frames. It is the FrameSet’s behaviour as a Mapping in which we are mainly interested here, because the Mapping it implements is the one we require—i.e. it converts between the two celestial coordinate systems (§14.1).

For example, if “alpha1” and “delta1” are two arrays containing the longitude and latitude, in radians, of N points on the sky in the original coordinate system (corresponding to “skyframe1”), then they could be converted into the new coordinate system (represented by “skyframe2”) as follows:

#define N 10
double alpha1[ N ], delta1[ N ];
double alpha2[ N ], delta2[ N ];

...

astTran2( cvt, N, alpha1, delta1, 1, alpha2, delta2 );

The new coordinates are returned via the “alpha2” and “delta2” arrays. To transform coordinates in the opposite direction, we simply invert the 5th (boolean int) argument to astTran2, as follows:

astTran2( cvt, N, alpha2, delta2, 0, alpha1, delta1 );

The FrameSet returned by astConvert also contains information about the SkyFrames used in the conversion (§14.1). As we mentioned above, a FrameSet may be used as a Frame and in this case it behaves like the “destination” Frame used in the conversion (i.e. like “skyframe2”). We could therefore use the “cvt” FrameSet to calculate the distance between two points (with coordinates in radians) in the destination coordinate system, using astDistance:

double distance, point1[ 2 ], point2[ 2 ];

...

distance = astDistance( cvt, point1, point2 );

and the result would be the same as if the “skyframe2” SkyFrame had been used.

Another way to see how the FrameSet produced by astConvert retains information about the coordinate systems involved is to set its Report attribute (inherited from the Mapping class) so that it displays the coordinates before and after conversion (§4.8):

astSet( cvt, "Report=1" );
astTran2( cvt, N, alpha1, delta1, 1, alpha2, delta2 );

The output from this might look like the following:

(2:06:03.0, 34:22:39) --> (42.1087, 20.2717)
(2:08:20.6, 35:31:24) --> (43.0197, 21.1705)
(2:10:38.1, 36:40:09) --> (43.9295, 22.0716)
(2:12:55.6, 37:48:55) --> (44.8382, 22.9753)
(2:15:13.1, 38:57:40) --> (45.7459, 23.8814)
(2:17:30.6, 40:06:25) --> (46.6528, 24.7901)
(2:19:48.1, 41:15:11) --> (47.5589, 25.7013)
(2:22:05.6, 42:23:56) --> (48.4644, 26.6149)
(2:24:23.1, 43:32:41) --> (49.3695, 27.5311)
(2:26:40.6, 44:41:27) --> (50.2742, 28.4499)

Here, we see that the input FK4 equatorial coordinate values (given in radians) have been formatted automatically in sexagesimal notation using the conventional hours for right ascension and degrees for declination. Conversely, the output ecliptic coordinates are shown in decimal degrees, as is conventional for ecliptic coordinates. Both are displayed using the default precision of 7 digits.20

In fact, the “cvt” FrameSet has access to all the information in the original SkyFrames which were passed to astConvert. If you had set a new Digits attribute value for either of these, the formatting above would reflect the different precision you requested by displaying a greater or smaller number of digits.

#### 12.2 Converting between Spectral Coordinate Systems

The principles described in the previous section for converting between celestial coordinate systems also apply to the task of converting between spectral coordinate systems. As an example, let’s look at how we might convert between frequency measured in $GHz$ as measured in the rest frame of the telescope, and radio velocity measured in $km/s$ measured with respect the kinematic Local Standard of Rest.

First we create a default SpecFrame, and then set its attributes to describe the required radio velocity system (this is slightly more convenient, given the relatively large number of attributes, than specifying the attribute values in a single string such as would be passed to the SpecFrame constructor). We then take a copy of this SpecFrame, and change the attribute values so that the copy describes the original frequency system (modifying a copy, rather than creating a new SpecFrame from scratch, avoids the need to specify the epoch, reference position, etc a second time since they are all inherited by the copy):

#include "star/ast.h"
AstSpecFrame *specframe1, *specframe2;

...

specframe1 = astSpecFrame( "" );
astSet( specframe1, "Unit=km/s" );
astSet( specframe1, "Epoch=1996-Oct-2 12:13:56.985" );
astSet( specframe1, "ObsLon=W155:28:18" );
astSet( specframe1, "ObsLat=N19:49:34" );
astSet( specframe1, "RefRA=18:14:50.6" );
astSet( specframe1, "RefDec=-4:40:49" );
astSet( specframe1, "RestFreq=230.538 GHz" );
astSet( specframe1, "StdOfRest=LSRK" );

specframe2 = astCopy( specframe1 );
astSet( specframe1, "System=freq" );
astSet( specframe1, "Unit=GHz" );
astSet( specframe1, "StdOfRest=Topocentric" );

Note, the fact that a SpecFrame has only a single axis means that we were able to refer to the Unit attribute without an axis index. The other attributes are: the time of of observation (Epoch), the geographical position of the telescope (ObsLat & ObsLon), the position of the source on the sky (RefRA & RefDec), the rest frequency (RestFreq) and the standard of rest (StdOfRest).

The next step is to find a way of converting between these SpecFrames. We use exactly the same code that we did in the previous section where we were converting between celestial coordinate systems:

AstFrameSet *cvt;

...

cvt = astConvert( specframe1, specframe2, "" );
if ( cvt == AST__NULL ) {
<conversion is not possible>
} else {
<conversion is possible>
}

A before, this will give us a FrameSet (assuming conversion is possible, which should always be the case for our example), and we can use the FrameSet to convert between the two spectral coordinate systems. We use astTran1 in place of astTran2 since a SpecFrame has only one axis (unlike a SkyFrame which has two).

For example, if “frq” is an array containing the observed frequency, in GHz, of N spectral channels (describe by “specframe1”), then they could be converted into the new coordinate system (represented by “specframe2”) as follows:

#define N 10
double frq[ N ];
double vel[ N ];

...

astTran1( cvt, N, frq, 1, vel );

The radio velocity values are returned in the “vel” array.

#### 12.3 Converting between Time Coordinate Systems

All the principles outlined in the previous section about aligning spectral cocordinate systems (SpecFrames) can be applied directly to the problem of aligning time coordinate systems (TimeFrames).

#### 12.4 Handling SkyFrame Axis Permutations

We can illustrate an important point if we swap the axis order of either SkyFrame in the example above (§12.1) before identifying the conversion. Let’s assume we use astPermAxes7.9) to do this to the second SkyFrame, before applying astConvert, as follows:

int perm[ 2 ] = { 2, 1 };

...

astPermAxes( skyframe2, perm );
cvt = astConvert( skyframe1, skyframe2, "" );

Now, the destination SkyFrame system no longer represents the coordinate system:

(ecliptic longitude, ecliptic latitude)

but instead represents the transposed system:

(ecliptic latitude, ecliptic longitude)

As a consequence, when we use the FrameSet returned by astConvert to apply a coordinate transformation, we obtain something like the following:

(2:06:03.0, 34:22:39) --> (20.2717, 42.1087)
(2:08:20.6, 35:31:24) --> (21.1705, 43.0197)
(2:10:38.1, 36:40:09) --> (22.0716, 43.9295)
(2:12:55.6, 37:48:55) --> (22.9753, 44.8382)
(2:15:13.1, 38:57:40) --> (23.8814, 45.7459)
(2:17:30.6, 40:06:25) --> (24.7901, 46.6528)
(2:19:48.1, 41:15:11) --> (25.7013, 47.5589)
(2:22:05.6, 42:23:56) --> (26.6149, 48.4644)
(2:24:23.1, 43:32:41) --> (27.5311, 49.3695)
(2:26:40.6, 44:41:27) --> (28.4499, 50.2742)

When compared to the original (§12.1), the output coordinate order has been swapped to compensate for the different destination SkyFrame axis order.

In all, there are four possible axis combinations, corresponding to two possible axis orders for each of the source and destination SkyFrames, and astConvert will convert correctly between any of these. The point to note is that a SkyFrame contains knowledge about how to convert to and from other SkyFrames. Since its two axes (longitude and latitude) are distinguishable, the conversion is able to take account of the axis order.

If you need to identify the axes of a SkyFrame explicitly, taking into account any axis permutations, the LatAxis and LonAxis attributes can be used. These are read-only attributes which give the indices of the latitude and longitude axes respectively.

#### 12.5 Converting Between Frames

Having seen how clever SkyFrames are (§12.1 and §12.4), we will next examine how dumb a basic Frame can be in comparison. For example, if we create two 2-dimensional Frames and use astConvert to derive a conversion between them, as follows:

AstFrame *frame1, *frame2;

...

frame1 = astFrame( 2, "" );
frame2 = astFrame( 2, "" );
cvt = astConvert( frame1, frame2, "" );

then the coordinate transformation which the “cvt” FrameSet performs will be as follows:

(1, 2) --> (1, 2)
(2, 4) --> (2, 4)
(3, 6) --> (3, 6)
(4, 8) --> (4, 8)
(5, 10) --> (5, 10)

This is an identity transformation, exactly the same as a UnitMap5.10). Even if we permute the axis order of our Frames, as we did above (§12.4), we will fare no better. The conversion between our two basic Frames will always be an identity transformation.

The reason for this is that, unlike a SkyFrame, all basic Frames start life the same and have axes that are indistinguishable. Therefore, permuting their axes doesn’t make them look any different—they still represent the same coordinate system.

#### 12.6 The Choice of Alignment System

In practice, when AST is asked to find a conversion between two Frames describing two different coordinate systems on a given physical domain, it uses an intermediate “alignment” system. Thus, when finding a conversion from system A to system B, AST first finds the Mapping from system A to some alignment system, system C, and then finds the Mapping from this system C to the required system B. It finally concatenates these two Mappings to get the Mapping from system A to system B.

One advantage of this is that it cuts down the number of conversion algorithms required. If there are $N$ different Systems which may be used to describe positions within the Domain, then this approach requires about $2\ast N$ conversion algorithms to be written. The alternative approach of going directly from system A to system B would require about $N\ast N$ conversion algorithms.

In addition, the use of an intermediate alignment system highlights the nature of the conversion process. What do we mean by saying that a Mapping “converts a position in one coordinate system into the corresponding position in another”? In practice, it means that the input and output coordinates correspond to the same coordinates in some third coordinate system. The choice of this third coordinate system, the “alignment” system, can completely alter the nature of the Mapping. The Frame class has an attribute called AlignSystem which can be used to specify the alignment system.

As an example, consider the case of aligning two spectra calibrated in radio velocity, but each with a different rest frequency (each spectrum will be described by a SpecFrame). Since the rest frequencies differ, a given velocity will correspond to different frequencies in the two spectra. So when we come to “align” these two spectra (that is, find a Mapping which converts positions in one SpecFrame to the corresponding positions in the other), we have the choice of aligning the frequencies or aligning the velocities. Different Mappings will be required to describe these two forms of alignment. If we set AlignSystem to “Freq” then the returned Mapping will align the frequencies described by the two SpecFrames. On the other hand, if we set AlignSystem to “Vradio” then the returned Mapping will align the velocities.

Some choices of alignment system are redundant. For instance, in the above example, changing the alignment system from frequency to wavelength has no effect on the returned Mapping: if two spectra are aligned in frequency they will also be aligned in wavelength (assuming the speed of light doesn’t change).

The default value for AlignSystem depends on the class of Frame. For a SpecFrame, the default is wavelength (or equivalently, frequency) since this is the system in which observations are usually made. The SpecFrame class also has an attribute called AlignStdOfRest which allows the standard of rest of the alignment system to be specified. Similarly, the TimeFrame class has an attribute called AlignTimeScale which allows the time scale of the alignment system to be specified. Currently, the SkyFrame uses ICRS as the default for AlignSystem, since this is a close approximation to an inertial frame of rest.

20The leading digit is zero and is therefore not seen in this particular example.