1212from ... import units as u
1313from ..baseframe import frame_transform_graph
1414from ..transformations import FunctionTransform
15- from ..representation import UnitSphericalRepresentation , SphericalRepresentation
15+ from ..representation import (SphericalRepresentation , CartesianRepresentation ,
16+ UnitSphericalRepresentation )
1617from ... import _erfa as erfa
1718
1819from .icrs import ICRS
2425
2526@frame_transform_graph .transform (FunctionTransform , ICRS , CIRS )
2627def icrs_to_cirs (icrs_coo , cirs_frame ):
27- #parallax in arcsec
28- if isinstance (icrs_coo .data , UnitSphericalRepresentation ): # no distance
29- px = 0
30- else :
31- px = 1 / icrs_coo .distance .to (u .parsec ).value
32- srepr = icrs_coo .represent_as (UnitSphericalRepresentation )
33- i_ra = srepr .lon .to (u .radian ).value
34- i_dec = srepr .lat .to (u .radian ).value
35-
3628 #first set up the astrometry context for ICRS<->CIRS
3729 astrom , eo = erfa .apci13 (cirs_frame .obstime .jd1 , cirs_frame .obstime .jd2 )
3830
39- # TODO: possibly switch to something that is like atciq, but skips the first
40- # step, b/c that involves some wasteful computations b/c pm=0 here
41- cirs_ra , cirs_dec = erfa .atciq (i_ra , i_dec , 0 , 0 , px , 0 , astrom )
31+ if icrs_coo .data .get_name () == 'unitspherical' or icrs_coo .data .to_cartesian ().x .unit == u .one :
32+ # if no distance, just do the infinite-distance/no parallax calculation
33+ usrepr = icrs_coo .represent_as (UnitSphericalRepresentation )
34+ i_ra = usrepr .lon .to (u .radian ).value
35+ i_dec = usrepr .lat .to (u .radian ).value
36+ cirs_ra , cirs_dec = erfa .atciqz (i_ra , i_dec , astrom )
4237
43- dat = icrs_coo .data
44- if dat .get_name () == 'unitspherical' or dat .to_cartesian ().x .unit == u .one :
45- rep = UnitSphericalRepresentation (lat = u .Quantity (cirs_dec , u .radian , copy = False ),
46- lon = u .Quantity (cirs_ra , u .radian , copy = False ),
47- copy = False )
38+ newrep = UnitSphericalRepresentation (lat = u .Quantity (cirs_dec , u .radian , copy = False ),
39+ lon = u .Quantity (cirs_ra , u .radian , copy = False ),
40+ copy = False )
4841 else :
49- #compute the distance as just the cartesian sum from moving to the Earth
50- #we have to do this because the ERFA functions throw away distance info
51- distance = np .sum ((astrom ['eb' ]* u .au + icrs_coo .cartesian .xyz .T )** 2 , - 1 )** 0.5
52- rep = SphericalRepresentation (lat = u .Quantity (cirs_dec , u .radian , copy = False ),
53- lon = u .Quantity (cirs_ra , u .radian , copy = False ),
54- distance = distance , copy = False )
42+ # When there is a distance, we first offset for parallax to get the
43+ # astrometric coordinate direction and *then* run the ERFA transform for
44+ # no parallax/PM. This ensures reversiblity and is more sensible for
45+ # inside solar system objects
46+ newxyz = icrs_coo .cartesian .xyz .T - astrom ['eb' ]* u .au
47+ newcart = CartesianRepresentation (newxyz .T )
48+
49+ srepr = newcart .represent_as (SphericalRepresentation )
50+ i_ra = srepr .lon .to (u .radian ).value
51+ i_dec = srepr .lat .to (u .radian ).value
52+ cirs_ra , cirs_dec = erfa .atciqz (i_ra , i_dec , astrom )
5553
56- return cirs_frame .realize_frame (rep )
54+ newrep = SphericalRepresentation (lat = u .Quantity (cirs_dec , u .radian , copy = False ),
55+ lon = u .Quantity (cirs_ra , u .radian , copy = False ),
56+ distance = srepr .distance , copy = False )
57+
58+ return cirs_frame .realize_frame (newrep )
5759
5860@frame_transform_graph .transform (FunctionTransform , CIRS , ICRS )
5961def cirs_to_icrs (cirs_coo , icrs_frame ):
6062 srepr = cirs_coo .represent_as (UnitSphericalRepresentation )
6163 cirs_ra = srepr .lon .to (u .radian ).value
6264 cirs_dec = srepr .lat .to (u .radian ).value
6365
64- #first set up the astrometry context for ICRS<->CIRS
66+ # set up the astrometry context for ICRS<->cirs and then convert to
67+ # astrometric coordinate direction
6568 astrom , eo = erfa .apci13 (cirs_coo .obstime .jd1 , cirs_coo .obstime .jd2 )
69+ i_ra , i_dec = erfa .aticq (cirs_ra , cirs_dec , astrom )
70+
71+ if cirs_coo .data .get_name () == 'unitspherical' or cirs_coo .data .to_cartesian ().x .unit == u .one :
72+ # if no distance, just use the coordinate direction to yield the
73+ # infinite-distance/no parallax answer
74+ newrep = UnitSphericalRepresentation (lat = u .Quantity (i_dec , u .radian , copy = False ),
75+ lon = u .Quantity (i_ra , u .radian , copy = False ),
76+ copy = False )
77+ else :
78+ # When there is a distance, apply the parallax/offset to the SSB as the
79+ # last step - ensures round-tripping with the icrs_to_cirs transform
6680
67- icrs_ra , icrs_dec = erfa .aticq (cirs_ra , cirs_dec , astrom )
81+ # the distance in intermedrep is *not* a real distance as it does not
82+ # include the offset back to the SSB
83+ intermedrep = SphericalRepresentation (lat = u .Quantity (i_dec , u .radian , copy = False ),
84+ lon = u .Quantity (i_ra , u .radian , copy = False ),
85+ distance = cirs_coo .distance ,
86+ copy = False )
6887
69- dat = cirs_coo .data
70- if dat .get_name () == 'unitspherical' or dat .to_cartesian ().x .unit == u .one :
71- rep = UnitSphericalRepresentation (lat = u .Quantity (icrs_dec , u .radian , copy = False ),
72- lon = u .Quantity (icrs_ra , u .radian , copy = False ),
73- copy = False )
74- else :
75- #compute the distance as just the cartesian sum from moving to the SSB
76- #we have to do this because the ERFA functions throw away distance info
77- distance = np .sum ((- astrom ['eb' ]* u .au + cirs_coo .cartesian .xyz .T )** 2 , - 1 )** 0.5
78- rep = SphericalRepresentation (lat = u .Quantity (icrs_dec , u .radian , copy = False ),
79- lon = u .Quantity (icrs_ra , u .radian , copy = False ),
80- distance = distance , copy = False )
81- return icrs_frame .realize_frame (rep )
88+ newxyz = intermedrep .to_cartesian ().xyz .T + astrom ['eb' ]* u .au
89+ newrep = CartesianRepresentation (newxyz .T ).represent_as (SphericalRepresentation )
90+
91+ return icrs_frame .realize_frame (newrep )
8292
8393@frame_transform_graph .transform (FunctionTransform , CIRS , CIRS )
8494def cirs_to_cirs (from_coo , to_frame ):
@@ -98,65 +108,75 @@ def cirs_to_cirs(from_coo, to_frame):
98108
99109@frame_transform_graph .transform (FunctionTransform , ICRS , GCRS )
100110def icrs_to_gcrs (icrs_coo , gcrs_frame ):
101- #parallax in arcsec
102- if isinstance (icrs_coo .data , UnitSphericalRepresentation ): # no distance
103- px = 0
104- else :
105- px = 1 / icrs_coo .distance .to (u .parsec ).value
106- srepr = icrs_coo .represent_as (UnitSphericalRepresentation )
107- i_ra = srepr .lon .to (u .radian ).value
108- i_dec = srepr .lat .to (u .radian ).value
109-
110111 #first set up the astrometry context for ICRS<->GCRS
111112 pv = np .array ([gcrs_frame .obsgeoloc .value ,
112113 gcrs_frame .obsgeovel .value ])
113114 astrom = erfa .apcs13 (gcrs_frame .obstime .jd1 , gcrs_frame .obstime .jd2 , pv )
114115
115- # TODO: possibly switch to something that is like atciq, but skips the first
116- # step, b/c that involves some wasteful computations b/c pm=0 here
117- gcrs_ra , gcrs_dec = erfa .atciq (i_ra , i_dec , 0 , 0 , px , 0 , astrom )
116+ if icrs_coo .data .get_name () == 'unitspherical' or icrs_coo .data .to_cartesian ().x .unit == u .one :
117+ # if no distance, just do the infinite-distance/no parallax calculation
118+ usrepr = icrs_coo .represent_as (UnitSphericalRepresentation )
119+ i_ra = usrepr .lon .to (u .radian ).value
120+ i_dec = usrepr .lat .to (u .radian ).value
121+ gcrs_ra , gcrs_dec = erfa .atciqz (i_ra , i_dec , astrom )
118122
119- dat = icrs_coo .data
120- if dat .get_name () == 'unitspherical' or dat .to_cartesian ().x .unit == u .one :
121- rep = UnitSphericalRepresentation (lat = u .Quantity (gcrs_dec , u .radian , copy = False ),
122- lon = u .Quantity (gcrs_ra , u .radian , copy = False ),
123- copy = False )
123+ newrep = UnitSphericalRepresentation (lat = u .Quantity (gcrs_dec , u .radian , copy = False ),
124+ lon = u .Quantity (gcrs_ra , u .radian , copy = False ),
125+ copy = False )
124126 else :
125- #compute the distance as just the cartesian sum from moving to the Earth
126- #we have to do this because the ERFA functions throw away distance info
127- distance = np .sum ((astrom ['eb' ]* u .au + icrs_coo .cartesian .xyz .T )** 2 , - 1 )** 0.5
128- rep = SphericalRepresentation (lat = u .Quantity (gcrs_dec , u .radian , copy = False ),
129- lon = u .Quantity (gcrs_ra , u .radian , copy = False ),
130- distance = distance , copy = False )
131- return gcrs_frame .realize_frame (rep )
127+ # When there is a distance, we first offset for parallax to get the
128+ # BCRS coordinate direction and *then* run the ERFA transform for no
129+ # parallax/PM. This ensures reversiblity and is more sensible for
130+ # inside solar system objects
131+ newxyz = icrs_coo .cartesian .xyz .T - astrom ['eb' ]* u .au
132+ newcart = CartesianRepresentation (newxyz .T )
133+
134+ srepr = newcart .represent_as (SphericalRepresentation )
135+ i_ra = srepr .lon .to (u .radian ).value
136+ i_dec = srepr .lat .to (u .radian ).value
137+ gcrs_ra , gcrs_dec = erfa .atciqz (i_ra , i_dec , astrom )
138+
139+ newrep = SphericalRepresentation (lat = u .Quantity (gcrs_dec , u .radian , copy = False ),
140+ lon = u .Quantity (gcrs_ra , u .radian , copy = False ),
141+ distance = srepr .distance , copy = False )
142+
143+ return gcrs_frame .realize_frame (newrep )
132144
133145
134146@frame_transform_graph .transform (FunctionTransform , GCRS , ICRS )
135147def gcrs_to_icrs (gcrs_coo , icrs_frame ):
136148 srepr = gcrs_coo .represent_as (UnitSphericalRepresentation )
137- cirs_ra = srepr .lon .to (u .radian ).value
138- cirs_dec = srepr .lat .to (u .radian ).value
149+ gcrs_ra = srepr .lon .to (u .radian ).value
150+ gcrs_dec = srepr .lat .to (u .radian ).value
139151
140- #first set up the astrometry context for ICRS<->GCRS
152+ # set up the astrometry context for ICRS<->GCRS and then convert to BCRS
153+ # coordinate direction
141154 pv = np .array ([gcrs_coo .obsgeoloc .value ,
142155 gcrs_coo .obsgeovel .value ])
143156 astrom = erfa .apcs13 (gcrs_coo .obstime .jd1 , gcrs_coo .obstime .jd2 , pv )
157+ i_ra , i_dec = erfa .aticq (gcrs_ra , gcrs_dec , astrom )
158+
159+ if gcrs_coo .data .get_name () == 'unitspherical' or gcrs_coo .data .to_cartesian ().x .unit == u .one :
160+ # if no distance, just use the coordinate direction to yield the
161+ # infinite-distance/no parallax answer
162+ newrep = UnitSphericalRepresentation (lat = u .Quantity (i_dec , u .radian , copy = False ),
163+ lon = u .Quantity (i_ra , u .radian , copy = False ),
164+ copy = False )
165+ else :
166+ # When there is a distance, apply the parallax/offset to the SSB as the
167+ # last step - ensures round-tripping with the icrs_to_gcrs transform
144168
145- icrs_ra , icrs_dec = erfa .aticq (cirs_ra , cirs_dec , astrom )
169+ # the distance in intermedrep is *not* a real distance as it does not
170+ # include the offset back to the SSB
171+ intermedrep = SphericalRepresentation (lat = u .Quantity (i_dec , u .radian , copy = False ),
172+ lon = u .Quantity (i_ra , u .radian , copy = False ),
173+ distance = gcrs_coo .distance ,
174+ copy = False )
146175
147- dat = gcrs_coo .data
148- if dat .get_name () == 'unitspherical' or dat .to_cartesian ().x .unit == u .one :
149- rep = UnitSphericalRepresentation (lat = u .Quantity (icrs_dec , u .radian , copy = False ),
150- lon = u .Quantity (icrs_ra , u .radian , copy = False ),
151- copy = False )
152- else :
153- #compute the distance as just the cartesian sum from moving to the SSB
154- #we have to do this because the ERFA functions throw away distance info
155- distance = np .sum ((astrom ['eb' ]* u .au + gcrs_coo .cartesian .xyz .T )** 2 , axis = - 1 )** 0.5
156- rep = SphericalRepresentation (lat = u .Quantity (icrs_dec , u .radian , copy = False ),
157- lon = u .Quantity (icrs_ra , u .radian , copy = False ),
158- distance = distance , copy = False )
159- return icrs_frame .realize_frame (rep )
176+ newxyz = intermedrep .to_cartesian ().xyz .T + astrom ['eb' ]* u .au
177+ newrep = CartesianRepresentation (newxyz .T ).represent_as (SphericalRepresentation )
178+
179+ return icrs_frame .realize_frame (newrep )
160180
161181
162182@frame_transform_graph .transform (FunctionTransform , GCRS , GCRS )
0 commit comments