1
2# -*- coding: utf-8 -*-
3
4u'''I{Veness}' Terrestrial Reference Frames (TRF).
5
6Classes L{RefFrame}, registry L{RefFrames} and L{TRFError}.
7
8Transcoded from I{Chris Veness'} (C) 2006-2019 JavaScript originals
9U{latlon-ellipsoidal-referenceframe.js<https://GitHub.com/ChrisVeness/geodesy/blob/master/
10latlon-ellipsoidal-referenceframe.js>} and U{latlon-ellipsoidal-referenceframe-txparams.js
11<https://GitHub.com/ChrisVeness/geodesy/blob/master/latlon-ellipsoidal-referenceframe-txparams.js>}.
12
13Following is a copy of the comments in I{Veness}' U{latlon-ellipsoidal-referenceframe.js
14<https://GitHub.com/ChrisVeness/geodesy/blob/master/latlon-ellipsoidal-referenceframe.js>}.
15
16Modern geodetic reference frames: a latitude/longitude point defines a geographic location on,
17above or below the earth’s surface, measured in degrees from the equator and the U{International
18Reference Meridian<https://WikiPedia.org/wiki/IERS_Reference_Meridian>} (IRM) and metres above
19the ellipsoid within a given I{Terrestrial Reference Frame} at a given I{epoch}.
20
21This is scratching the surface of complexities involved in high precision geodesy, but may
22be of interest and/or value to those with less demanding requirements.  More information U{here
23<https://www.Movable-Type.co.UK/scripts/geodesy-library.html>} and U{here
24<https://www.Movable-Type.co.UK/scripts/geodesy-library.html#latlon-ellipsoidal-referenceframe>}.
25
26Note that I{ITRF solutions} do not directly use an ellipsoid, but are specified by Cartesian
27coordinates.  The GRS80 ellipsoid is recommended for transformations to geographical coordinates.
28
29Note WGS84(G730/G873/G1150) are coincident with ITRF at 10-centimetre level, see also U{here
30<ftp://ITRF.ENSG.IGN.FR/pub/itrf/WGS84.TXT>}.  WGS84(G1674) and ITRF20014 / ITRF2008 I{"are likely
31to agree at the centimeter level"}, see also U{QPS/Qinsy<https://Confluence.QPS.NL/qinsy/
32en/how-to-deal-with-etrs89-datum-and-time-dependent-transformation-parameters-45353274.html>}.
33
34@var RefFrames.ETRF2000: RefFrame(name='ETRF2000', epoch=2005, ellipsoid=Ellipsoid(name='GRS80')
35@var RefFrames.GDA2020: RefFrame(name='GDA2020', epoch=2020, ellipsoid=Ellipsoid(name='GRS80')
36@var RefFrames.GDA94: RefFrame(name='GDA94', epoch=1994, ellipsoid=Ellipsoid(name='GRS80')
37@var RefFrames.ITRF2000: RefFrame(name='ITRF2000', epoch=1997, ellipsoid=Ellipsoid(name='GRS80')
38@var RefFrames.ITRF2005: RefFrame(name='ITRF2005', epoch=2000, ellipsoid=Ellipsoid(name='GRS80')
39@var RefFrames.ITRF2008: RefFrame(name='ITRF2008', epoch=2005, ellipsoid=Ellipsoid(name='GRS80')
40@var RefFrames.ITRF2014: RefFrame(name='ITRF2014', epoch=2010, ellipsoid=Ellipsoid(name='GRS80')
41@var RefFrames.ITRF91: RefFrame(name='ITRF91', epoch=1988, ellipsoid=Ellipsoid(name='GRS80')
42@var RefFrames.ITRF93: RefFrame(name='ITRF93', epoch=1988, ellipsoid=Ellipsoid(name='GRS80')
43@var RefFrames.NAD83: RefFrame(name='NAD83', epoch=1997, ellipsoid=Ellipsoid(name='GRS80')
44@var RefFrames.WGS84g1150: RefFrame(name='WGS84g1150', epoch=2001, ellipsoid=Ellipsoid(name='WGS84')
45@var RefFrames.WGS84g1674: RefFrame(name='WGS84g1674', epoch=2005, ellipsoid=Ellipsoid(name='WGS84')
46@var RefFrames.WGS84g1762: RefFrame(name='WGS84g1762', epoch=2005, ellipsoid=Ellipsoid(name='WGS84')
47'''
48
49from pygeodesy.basics import map1, _xinstanceof
50from pygeodesy.datums import _ellipsoid, Transform
51from pygeodesy.ellipsoids import Ellipsoids
52from pygeodesy.errors import TRFError
53from pygeodesy.interns import NN, _COMMASPACE_, _conversion_, _ellipsoid_, \
54                             _epoch_, _exists_, _float as _F, _GRS80_, _NAD83_, \
55                             _name_, _no_, _s_, _SPACE_, _sx_, _sy_, _sz_, \
56                             _to_, _tx_, _ty_, _tz_, _WGS84_, _0_0, _0_001, \
57                             _0_01, _0_1, _0_26, _0_5, _1_0
58from pygeodesy.lazily import _ALL_LAZY
59from pygeodesy.named import classname, _lazyNamedEnumItem as _lazy, \
60                           _NamedDict as _XD, _NamedEnum, _NamedEnumItem, \
61                           _NamedTuple
62from pygeodesy.props import Property_RO
63from pygeodesy.streprs import Fmt
64from pygeodesy.units import Epoch, Float
65
66from math import ceil
67
68__all__ = _ALL_LAZY.trf
69__version__ = '21.08.12'
70
71_0_02  = _F(  0.02)
72_0_06  = _F(  0.06)
73_0_09  = _F(  0.09)
74_366_0 = _F(366)
75_mDays = (0, 31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31, 0)
76
77_ETRF2000_   = 'ETRF2000'
78_GDA2020_    = 'GDA2020'
79_GDA94_      = 'GDA94'
80_ITRF_       = 'ITRF'
81_ITRF88_     = 'ITRF88'
82_ITRF89_     = 'ITRF89'
83_ITRF90_     = 'ITRF90'
84_ITRF91_     = 'ITRF91'
85_ITRF92_     = 'ITRF92'
86_ITRF93_     = 'ITRF93'
87_ITRF94_     = 'ITRF94'
88_ITRF96_     = 'ITRF96'
89_ITRF97_     = 'ITRF97'
90_ITRF2000_   = 'ITRF2000'
91_ITRF2005_   = 'ITRF2005'
92_ITRF2008_   = 'ITRF2008'
93_ITRF2014_   = 'ITRF2014'
94_WGS84g1150_ = 'WGS84g1150'
95_WGS84g1674_ = 'WGS84g1674'
96_WGS84g1762_ = 'WGS84g1762'
97
98
99class RefFrame(_NamedEnumItem):
100    '''Terrestrial Reference Frame (TRF) parameters.
101    '''
102    _ellipsoid =  None  # ellipsoid GRS80 or WGS84 (L{Ellipsoid} or L{Ellipsoid2})
103    _epoch     = _0_0   # epoch, calendar year (L{Epoch} or C{float})
104
105    def __init__(self, epoch, ellipsoid, name=NN):
106        '''New L{RefFrame}.
107
108           @arg epoch: Epoch, a fractional calendar year (C{scalar} or C{str}).
109           @arg ellipsoid: The ellipsoid (L{Ellipsoid}, L{Ellipsoid2},
110                           L{datum} or L{a_f2Tuple}).
111           @kwarg name: Optional, unique name (C{str}).
112
113           @raise NameError: A L{RefFrame} with that B{C{name}}
114                             already exists.
115
116           @raise TRFError: Invalid B{C{epoch}}.
117
118           @raise TypeError: Invalid B{C{ellipsoid}}.
119        '''
120        self._ellipsoid = _ellipsoid(ellipsoid, name=name)
121        self._epoch = Epoch(epoch)
122        self._register(RefFrames, name)
123
124    @Property_RO
125    def ellipsoid(self):
126        '''Get this reference frame's ellipsoid (L{Ellipsoid} or L{Ellipsoid2}).
127        '''
128        return self._ellipsoid
129
130    @Property_RO
131    def epoch(self):
132        '''Get this reference frame's epoch (C{Epoch}).
133        '''
134        return self._epoch
135
136    def toStr(self, **unused):  # PYCHOK expected
137        '''Return this reference frame as a text string.
138
139           @return: This L{RefFrame}'s attributes (C{str}).
140        '''
141        e = self.ellipsoid
142        t = (Fmt.EQUAL(_name_, repr(self.name)),
143             Fmt.EQUAL(_epoch_, self.epoch),
144             Fmt.PAREN(Fmt.EQUAL(_ellipsoid_, classname(e)),
145                       Fmt.EQUAL(_name_, repr(e.name))))
146        return _COMMASPACE_.join(t)
147
148
149class RefFrames(_NamedEnum):
150    '''(INTERNAL) L{RefFrame} registry, I{must} be a sub-class
151       to accommodate the L{_LazyNamedEnumItem} properties.
152    '''
153    def _Lazy(self, epoch, ellipsoid_name, name=NN):
154        '''(INTERNAL) Instantiate the L{RefFrame}.
155        '''
156        return RefFrame(epoch, Ellipsoids.get(ellipsoid_name), name=name)
157
158RefFrames = RefFrames(RefFrame)  # PYCHOK singleton
159'''Some pre-defined L{RefFrame}s, all I{lazily} instantiated.'''
160# <https://GitHub.com/ChrisVeness/geodesy/blob/master/latlon-ellipsoidal-referenceframe.js>
161RefFrames._assert(
162    ETRF2000   = _lazy(_ETRF2000_,   _F(2005), _GRS80_),  # ETRF2000(R08)
163    GDA2020    = _lazy(_GDA2020_,    _F(2020), _GRS80_),  # Australia
164    GDA94      = _lazy(_GDA94_,      _F(1994), _GRS80_),  # Australia
165    ITRF2000   = _lazy(_ITRF2000_,   _F(1997), _GRS80_),
166    ITRF2005   = _lazy(_ITRF2005_,   _F(2000), _GRS80_),
167    ITRF2008   = _lazy(_ITRF2008_,   _F(2005), _GRS80_),  # aks ITRF08
168    ITRF2014   = _lazy(_ITRF2014_,   _F(2010), _GRS80_),
169    ITRF91     = _lazy(_ITRF91_,     _F(1988), _GRS80_),
170    ITRF93     = _lazy(_ITRF93_,     _F(1988), _GRS80_),
171    NAD83      = _lazy(_NAD83_,      _F(1997), _GRS80_),  # CORS96
172    WGS84g1150 = _lazy(_WGS84g1150_, _F(2001), _WGS84_),
173    WGS84g1674 = _lazy(_WGS84g1674_, _F(2005), _WGS84_),
174    WGS84g1762 = _lazy(_WGS84g1762_, _F(2005), _WGS84_))  # same epoch
175
176
177def date2epoch(year, month, day):
178    '''Return the reference frame C{epoch} for a calendar day.
179
180       @arg year: Year of the date (C{scalar}).
181       @arg month: Month in the B{C{year}} (C{scalar}, 1..12).
182       @arg day: Day in the B{C{month}} (C{scalar}, 1..31).
183
184       @return: Epoch, the fractional year (C{float}).
185
186       @raise TRFError: Invalid B{C{year}}, B{C{month}} or B{C{day}}.
187
188       @note: Any B{C{year}} is considered a leap year, i.e. having
189              29 days in February.
190    '''
191    try:
192        y, m, d = map1(int, year, month, day)
193        if y > 0 and 1 <= m <= 12 and 1 <= d <= _mDays[m]:
194            return Epoch(y + float(sum(_mDays[:m]) + d) / _366_0, low=0)
195
196        t = NN  # _invalid_
197    except (TRFError, TypeError, ValueError) as x:
198        t = str(x)
199    raise TRFError(year=year, month=month, day=day, txt=t)
200
201
202def epoch2date(epoch):
203    '''Return the date for a reference frame C{epoch}.
204
205       @arg epoch: Fractional year (C{scalar}).
206
207       @return: 3-Tuple C{(year, month, day)}.
208
209       @raise TRFError: Invalid B{C{epoch}}.
210
211       @note: Any B{C{year}} is considered a leap year, i.e. having
212              29 days in February.
213    '''
214    e = Epoch(epoch, Error=TRFError, low=0)
215    y = int(e)
216    d = int(ceil(_366_0 * (e - y)))
217    for m, n in enumerate(_mDays[1:]):
218        if d > n:
219            d -= n
220        else:
221            break
222    return y, (m + 1), max(1, d)
223
224
225_mas = _mm =  _ppb = Float  # as == arcseconds
226_Forward   =  _0_001  # mm2m, ppb2ppM, mas2as
227_Inverse   = -_0_001  # same, inverse transforms
228
229
230def _intermediate(n1, n2):
231    '''(INTERNAL) Find a trf* "in between" C{n1} and C{n2}.
232    '''
233    f1 = set(m for n, m in _trfXs.keys() if n == n1)  # from trf1
234    t2 = set(n for n, m in _trfXs.keys() if m == n2)  # to trf2
235    n = f1.intersection(t2)
236    return n.pop() if n else NN
237
238
239def _reframeTransforms2(rf2, rf, epoch):
240    '''(INTERNAL) Get 0, 1 or 2 Helmert L{Transform}s to convert
241       reference frame B{C{rf}} observed at B{C{epoch}} into B{C{rf2}}.
242    '''
243    e = rf.epoch if epoch is None else Epoch(epoch)
244
245    n2 = rf2.name  # .upper()
246    n1 = rf.name   # .upper()
247    if n1 == n2 or (n1.startswith(_ITRF_) and n2.startswith(_WGS84_)) \
248                or (n2.startswith(_ITRF_) and n1.startswith(_WGS84_)):
249        return e, ()  # PYCHOK returns
250
251    if (n1, n2) in _trfXs:
252        return e, (_2Transform((n1, n2), e, _Forward),)  # PYCHOK returns
253
254    if (n2, n1) in _trfXs:
255        return e, (_2Transform((n2, n1), e, _Inverse),)  # PYCHOK returns
256
257    n = _intermediate(n1, n2)
258    if n:
259        return e, (_2Transform((n1, n), e, _Forward),  # PYCHOK returns
260                   _2Transform((n, n2), e, _Forward))
261
262    n = _intermediate(n2, n1)
263    if n:
264        return e, (_2Transform((n, n1), e, _Inverse),  # PYCHOK returns
265                   _2Transform((n2, n), e, _Inverse))
266
267    t = _SPACE_(RefFrame.__name__, repr(n1), _to_, repr(n2))
268    raise TRFError(_no_(_conversion_), txt=t)
269
270
271def _2Transform(n1_n2, epoch, _Forward_Inverse):
272    '''(INTERNAL) Combine the dual Helmert transforms from TRF
273       conversion C{_trfXs[n1_n2]} into a into a single Helmert
274       L{Transform} observed at B{C{epoch}}.
275
276       @note: Translations in C{millimeter} are converted to
277              C{meter} and rotations in C{milliarcseconds} to
278              C{arcseconds}.
279    '''
280    X = _trfXs[n1_n2]
281    e = epoch - X.epoch  # fractional delta years
282    d = dict((n, (x + r * e) * _Forward_Inverse) for
283              n,  x,  r in zip(Transform7Tuple._Names_, X.xform, X.rates))
284    return Transform(**d)
285
286
287class Transform7Tuple(_NamedTuple):
288    '''7-Tuple C{(tx, ty, tz, s, sx, sy, sz)} Helmert transformation
289       with translations C{tx}, C{ty} and C{tz} in C{millimeter},
290       scale C{s} in C{ppb} and rotations C{sx}, C{sy} and C{sz} in
291       C{milliarcseconds}.
292
293       @see: L{Transform}.
294    '''
295    _Names_ = (_tx_, _ty_, _tz_, _s_,  _sx_, _sy_, _sz_)
296    _Units_ = (_mm,  _mm,  _mm,  _ppb, _mas, _mas, _mas)
297
298    def __new__(cls, tx=_0_0, ty=_0_0, tz=_0_0, s=_0_0,
299                     sx=_0_0, sy=_0_0, sz=_0_0, name=NN):
300        '''New L{Transform7Tuple}.
301
302           @kwarg tx: Optional X translation (C{millimeter}).
303           @kwarg ty: Optional Y translation (C{millimeter}).
304           @kwarg tz: Optional Z translation (C{millimeter}).
305           @kwarg s: Optional scale (C{float}), ppb.
306           @kwarg sx: Optional X rotation (C{milliarcseconds}).
307           @kwarg sy: Optional Y rotation (C{milliarcseconds}).
308           @kwarg sz: Optional Z rotation (C{milliarcseconds}).
309           @kwarg name: Optional name (C{str}).
310        '''
311        t = map1(_F, tx, ty, tz, s, sx, sy, sz)
312        return _NamedTuple.__new__(cls, *t, name=name)
313
314
315def trfXform(reframe1, reframe2, epoch=None, xform=None, rates=None):
316    '''Define a new Terrestrial Reference Frame (TRF) conversion.
317
318       @arg reframe1: Source reframe (L{RefFrame}), converting I{from}.
319       @arg reframe2: Destination reframe (L{RefFrame}), converting I{to}.
320       @kwarg epoch: Epoch, a fractional calendar year (C{scalar} or C{str})
321                     or C{None} for C{B{reframe2}.epoch}.
322       @kwarg xform: Helmert transform (C{Tranform7Tuple}).
323       @kwarg rates: Helmert transform (C{Tranform7Tuple}).
324
325       @raise TRFError: Invalid B{C{epoch}} or TRF conversion already exists.
326    '''
327    _xinstanceof(RefFrame, reframe1=reframe1, reframe2=reframe2)
328    e = reframe2.epoch if epoch is None else Epoch(epoch=epoch, Error=TRFError)
329    _xinstanceof(Transform7Tuple, xform=xform, rates=rates)
330    _trfX(reframe1.name, reframe2.name, epoch=e, xform=xform, rates=rates)
331
332
333def _trfX(n1, n2, **epoch_xform_rates):
334    '''(INTERNAL) New C{_trfXs} entry.
335    '''
336    n1_n2 = n1, n2
337    if n1_n2 in _trfXs:
338        raise TRFError(trfX=n1_n2, txt=_exists_)  # _NameError
339    _trfXs[n1_n2] = _XD(X=n1_n2, **epoch_xform_rates)
340
341
342_T = Transform7Tuple
343# TRF conversions specified as an epoch and dual 7-parameter Helmert transforms.  Most
344# from U{Transformation Parameters<http://ITRF.IGN.FR/trans_para.php>}, more at U{QPS
345# <https://Confluence.QPS.NL/qinsy/files/en/29856813/45482834/2/1453459502000/ITRF_Transformation_Parameters.xlsx>}.
346_trfXs = dict()  # key is [(from_TRF, to_TRF)] 2-tuple
347# see U{Transformation Parameters ITRF2014<http://ITRF.IGN.FR/doc_ITRF/Transfo-ITRF2014_ITRFs.txt>}
348_trfX(_ITRF2014_, _ITRF2008_, epoch=_F(2010),  # <http://ITRF.ENSG.IGN.FR/ITRF_solutions/2014/tp_14-08.php>
349                              xform=_T(  1.6,     1.9,     2.4,  -0.02,  _0_0,    _0_0,    _0_0),
350                              rates=_T( _0_0,    _0_0,   -_0_1,   0.03,  _0_0,    _0_0,    _0_0))
351_trfX(_ITRF2014_, _ITRF2005_, epoch=_F(2010),
352                              xform=_T(  2.6,    _1_0,    -2.3,   0.92,  _0_0,    _0_0,    _0_0),
353                              rates=_T(  0.3,    _0_0,   -_0_1,   0.03,  _0_0,    _0_0,    _0_0))
354_trfX(_ITRF2014_, _ITRF2000_, epoch=_F(2010),
355                              xform=_T(  0.7,     1.2,   -26.1,   2.12,  _0_0,    _0_0,    _0_0),
356                              rates=_T( _0_1,    _0_1,    -1.9,   0.11,  _0_0,    _0_0,    _0_0))
357_trfX(_ITRF2014_, _ITRF97_,   epoch=_F(2010),
358                              xform=_T(  7.4,   -_0_5,   -62.8,   3.8,   _0_0,    _0_0,    _0_26),
359                              rates=_T( _0_1,   -_0_5,    -3.3,   0.12,  _0_0,    _0_0,    _0_02))
360_trfX(_ITRF2014_, _ITRF96_,   epoch=_F(2010),
361                              xform=_T(  7.4,   -_0_5,   -62.8,   3.8,   _0_0,    _0_0,    _0_26),
362                              rates=_T( _0_1,   -_0_5,    -3.3,   0.12,  _0_0,    _0_0,    _0_02))
363_trfX(_ITRF2014_, _ITRF94_,   epoch=_F(2010),
364                              xform=_T(  7.4,   -_0_5,   -62.8,   3.8,   _0_0,    _0_0,    _0_26),
365                              rates=_T( _0_1,   -_0_5,    -3.3,   0.12,  _0_0,    _0_0,    _0_02))
366_trfX(_ITRF2014_, _ITRF93_,   epoch=_F(2010),
367                              xform=_T(-50.4,     3.3,   -60.2,   4.29,  -2.81,   -3.38,    0.4),
368                              rates=_T( -2.8,   -_0_1,    -2.5,   0.12,  -0.11,   -0.19,    0.07))
369_trfX(_ITRF2014_, _ITRF92_,   epoch=_F(2010),
370                              xform=_T( 15.4,     1.5,   -70.8,   3.09,  _0_0,    _0_0,    _0_26),
371                              rates=_T( _0_1,   -_0_5,    -3.3,   0.12,  _0_0,    _0_0,    _0_02))
372_trfX(_ITRF2014_, _ITRF91_,   epoch=_F(2010),
373                              xform=_T( 27.4,    15.5,   -76.8,   4.49,  _0_0,    _0_0,    _0_26),
374                              rates=_T( _0_1,   -_0_5,    -3.3,   0.12,  _0_0,    _0_0,    _0_02))
375_trfX(_ITRF2014_, _ITRF90_,   epoch=_F(2010),
376                              xform=_T( 25.4,    11.5,   -92.8,   4.79,  _0_0,    _0_0,    _0_26),
377                              rates=_T( _0_1,   -_0_5,    -3.3,   0.12,  _0_0,    _0_0,    _0_02))
378_trfX(_ITRF2014_, _ITRF89_,   epoch=_F(2010),
379                              xform=_T( 30.4,    35.5,  -130.8,   8.19,  _0_0,    _0_0,    _0_26),
380                              rates=_T( _0_1,   -_0_5,    -3.3,   0.12,  _0_0,    _0_0,    _0_02))
381_trfX(_ITRF2014_, _ITRF88_,   epoch=_F(2010),
382                              xform=_T( 25.4,   -_0_5,  -154.8,  11.29,  _0_1,    _0_0,    _0_26),
383                              rates=_T( _0_1,   -_0_5,    -3.3,   0.12,  _0_0,    _0_0,    _0_02))
384
385# see U{Transformation Parameters ITRF2008<http://ITRF.IGN.FR/doc_ITRF/Transfo-ITRF2008_ITRFs.txt>}
386#   _trfX(_ITRF2008_, _ITRF2005_, epoch=_F(2005),  # <http://ITRF.ENSG.IGN.FR/ITRF_solutions/2008/tp_08-05.php>
387#                                xform=_T(-_0_5,    -0.9,    -4.7,   0.94,  _0_0,    _0_0,    _0_0),
388#                                rates=_T(  0.3,    _0_0,    _0_0,  _0_0,   _0_0,    _0_0,    _0_0))
389_trfX(_ITRF2008_, _ITRF2005_, epoch=_F(2000),
390                              xform=_T( -2.0,    -0.9,    -4.7,   0.94,  _0_0,    _0_0,    _0_0),
391                              rates=_T(  0.3,    _0_0,    _0_0,  _0_0,   _0_0,    _0_0,    _0_0))
392_trfX(_ITRF2008_, _ITRF2000_, epoch=_F(2000),
393                              xform=_T( -1.9,    -1.7,   -10.5,   1.34,  _0_0,    _0_0,    _0_0),
394                              rates=_T( _0_1,    _0_1,    -1.8,   0.08,  _0_0,    _0_0,    _0_0))
395_trfX(_ITRF2008_, _ITRF97_,   epoch=_F(2000),
396                              xform=_T(  4.8,     2.6,   -33.2,   2.92,  _0_0,    _0_0,    _0_06),
397                              rates=_T( _0_1,   -_0_5,    -3.2,  _0_09,  _0_0,    _0_0,    _0_02))
398_trfX(_ITRF2008_, _ITRF96_,   epoch=_F(2000),
399                              xform=_T(  4.8,     2.6,   -33.2,   2.92,  _0_0,    _0_0,    _0_06),
400                              rates=_T( _0_1,   -_0_5,    -3.2,  _0_09,  _0_0,    _0_0,    _0_02))
401_trfX(_ITRF2008_, _ITRF94_,   epoch=_F(2000),
402                              xform=_T(  4.8,     2.6,   -33.2,   2.92,  _0_0,    _0_0,    _0_06),
403                              rates=_T( _0_1,   -_0_5,    -3.2,  _0_09,  _0_0,    _0_0,    _0_02))
404_trfX(_ITRF2008_, _ITRF93_,   epoch=_F(2000),
405                              xform=_T(-24.0,     2.4,   -38.6,   3.41,  -1.71,   -1.48,   -0.3),
406                              rates=_T( -2.8,   -_0_1,    -2.4,  _0_09,  -0.11,   -0.19,    0.07))
407_trfX(_ITRF2008_, _ITRF92_,   epoch=_F(2000),
408                              xform=_T( 12.8,     4.6,   -41.2,   2.21,  _0_0,    _0_0,    _0_06),
409                              rates=_T( _0_1,   -_0_5,    -3.2,  _0_09,  _0_0,    _0_0,    _0_02))
410_trfX(_ITRF2008_, _ITRF91_,   epoch=_F(2000),
411                              xform=_T( 24.8,    18.6,   -47.2,   3.61,  _0_0,    _0_0,    _0_06),
412                              rates=_T( _0_1,   -_0_5,    -3.2,  _0_09,  _0_0,    _0_0,    _0_02))
413_trfX(_ITRF2008_, _ITRF90_,   epoch=_F(2000),
414                              xform=_T( 22.8,    14.6,   -63.2,   3.91,  _0_0,    _0_0,    _0_06),
415                              rates=_T( _0_1,   -_0_5,    -3.2,  _0_09,  _0_0,    _0_0,    _0_02))
416_trfX(_ITRF2008_, _ITRF89_,   epoch=_F(2000),
417                              xform=_T( 27.8,    38.6,  -101.2,   7.31,  _0_0,    _0_0,    _0_06),
418                              rates=_T( _0_1,   -_0_5,    -3.2,  _0_09,  _0_0,    _0_0,    _0_02))
419_trfX(_ITRF2008_, _ITRF88_,   epoch=_F(2000),
420                              xform=_T( 22.8,     2.6,  -125.2,  10.41,  _0_1,    _0_0,    _0_06),
421                              rates=_T( _0_1,   -_0_5,    -3.2,  _0_09,  _0_0,    _0_0,    _0_02))
422
423_trfX(_ITRF2005_, _ITRF2000_, epoch=_F(2000),  # <http://ITRF.ENSG.IGN.FR/ITRF_solutions/2005/tp_05-00.php>
424                              xform=_T( _0_1,    -0.8,    -5.8,   0.4,   _0_0,    _0_0,    _0_0),
425                              rates=_T( -0.2,    _0_1,    -1.8,   0.08,  _0_0,    _0_0,    _0_0))
426
427_trfX(_ITRF2000_, _ITRF97_,   epoch=_F(1997),
428                              xform=_T( 0.67,     0.61,   -1.85,  1.55,  _0_0,    _0_0,    _0_0),
429                              rates=_T(_0_0,     -0.06,   -0.14, _0_01,  _0_0,    _0_0,    _0_02))
430_trfX(_ITRF2000_, _ITRF96_,   epoch=_F(1997),
431                              xform=_T( 0.67,     0.61,   -1.85,  1.55,  _0_0,    _0_0,    _0_0),
432                              rates=_T(_0_0,     -0.06,   -0.14, _0_01,  _0_0,    _0_0,    _0_02))
433_trfX(_ITRF2000_, _ITRF94_,   epoch=_F(1997),
434                              xform=_T( 0.67,     0.61,   -1.85,  1.55,  _0_0,    _0_0,    _0_0),
435                              rates=_T(_0_0,     -0.06,   -0.14, _0_01,  _0_0,    _0_0,    _0_02))
436_trfX(_ITRF2000_, _ITRF93_,   epoch=_F(1988),
437                              xform=_T( 12.7,     6.5,   -20.9,   1.95,  -0.39,    0.8,    -1.14),
438                              rates=_T( -2.9,    -0.2,    -0.6,  _0_01,  -0.11,   -0.19,    0.07))
439_trfX(_ITRF2000_, _ITRF92_,   epoch=_F(1988),
440                              xform=_T( 1.47,     1.35,   -1.39,  0.75,  _0_0,    _0_0,    -0.18),
441                              rates=_T(_0_0,     -0.06,   -0.14, _0_01,  _0_0,    _0_0,    _0_02))
442_trfX(_ITRF2000_, _ITRF91_,   epoch=_F(1988),
443                              xform=_T( 26.7,    27.5,   -19.9,   2.15,  _0_0,    _0_0,    -0.18),
444                              rates=_T( _0_0,    -0.6,    -1.4,  _0_01,  _0_0,    _0_0,    _0_02))
445_trfX(_ITRF2000_, _ITRF90_,   epoch=_F(1988),
446                              xform=_T( 2.47,     2.35,   -3.59,  2.45,  _0_0,    _0_0,    -0.18),
447                              rates=_T(_0_0,     -0.06,   -0.14, _0_01,  _0_0,    _0_0,    _0_02))
448_trfX(_ITRF2000_, _ITRF89_,   epoch=_F(1988),
449                              xform=_T( 2.97,     4.75,   -7.39,  5.85,  _0_0,    _0_0,    -0.18),
450                              rates=_T(_0_0,     -0.06,   -0.14, _0_01,  _0_0,    _0_0,    _0_02))
451_trfX(_ITRF2000_, _ITRF88_,   epoch=_F(1988),
452                              xform=_T( 2.47,     1.15,   -9.79,  8.95,  _0_1,    _0_0,    -0.18),
453                              rates=_T(_0_0,     -0.06,   -0.14, _0_01,  _0_0,    _0_0,    _0_02))
454
455# see U{Boucher, C. & Altamimi, Z. "Memo: Specifications for reference frame fixing in the
456# analysis of a EUREF GPS campaign" (2011) <https://ETRS89.ENSG.IGN.FR/memo-V8.pdf>} and
457# Altamimi, Z. U{"Key results of ITRF2014 and implication to ETRS89 realization", EUREF2016
458# <https://www.EUREF.EU/symposia/2016SanSebastian/01-02-Altamimi.pdf>}.
459_trfX(_ITRF2014_, _ETRF2000_, epoch=_F(2000),
460                              xform=_T( 53.7,    51.2,   -55.1,   1.02,   0.891,   5.39,   -8.712),
461                              rates=_T( _0_1,    _0_1,    -1.9,   0.11,   0.081,   0.49,   -0.792))
462_trfX(_ITRF2008_, _ETRF2000_, epoch=_F(2000),
463                              xform=_T( 52.1,    49.3,   -58.5,   1.34,   0.891,   5.39,   -8.712),
464                              rates=_T( _0_1,    _0_1,    -1.8,   0.08,   0.081,   0.49,   -0.792))
465_trfX(_ITRF2005_, _ETRF2000_, epoch=_F(2000),
466                              xform=_T( 54.1,    50.2,   -53.8,   0.4,    0.891,   5.39,   -8.712),
467                              rates=_T( -0.2,    _0_1,    -1.8,   0.08,   0.081,   0.49,   -0.792))
468_trfX(_ITRF2000_, _ETRF2000_, epoch=_F(2000),
469                              xform=_T( 54.0,    51.0,   -48.0,  _0_0,    0.891,   5.39,   -8.712),
470                              rates=_T( _0_0,    _0_0,    _0_0,  _0_0,    0.081,   0.49,   -0.792))
471
472# see U{Solar, T. & Snay, R.A. "Transforming Positions and Velocities between the
473# International Terrestrial Reference Frame of 2000 and North American Datum of 1983"
474# (2004)<https://www.NGS.NOAA.gov/CORS/Articles/SolerSnayASCE.pdf>}
475_trfX(_ITRF2000_, _NAD83_,    epoch=_F(1997),  # note NAD83(CORS96)
476                              xform=_T(995.6, -1901.3,  -521.5,   0.62,  25.915,   9.426,  11.599),
477                              rates=_T(  0.7,    -0.7,    _0_5,  -0.18,   0.067,  -0.757,  -0.051))
478
479# GDA2020 "Geocentric Datum of Australia 2020 Technical Manual", v1.5, 2020-12-09, Table 3.3 and 3.4
480# <https://www.ICSM.gov.AU/sites/default/files/2020-12/GDA2020%20Technical%20Manual%20V1.5_4.pdf>
481# (the GDA2020 xforms are different but the rates are the same as GDA94, further below)
482_trfX(_ITRF2014_, _GDA2020_,  epoch=_F(2020),
483                              xform=_T( _0_0,    _0_0,   _0_0,  _0_0,    _0_0,    _0_0,    _0_0),
484                              rates=_T( _0_0,    _0_0,   _0_0,  _0_0,     1.50379, 1.18346, 1.20716))
485_trfX(_ITRF2008_, _GDA2020_,  epoch=_F(2020),
486                              xform=_T( 13.79,    4.55,   15.22,  2.5,    0.2808,  0.2677, -0.4638),
487                              rates=_T(  1.42,    1.34,    0.9,   0.109,  1.5461,  1.182,   1.1551))
488_trfX(_ITRF2005_, _GDA2020_,  epoch=_F(2020),
489                              xform=_T( 40.32,  -33.85,  -16.72,  4.286, -1.2893, -0.8492, -0.3342),
490                              rates=_T(  2.25,   -0.62,   -0.56,  0.294, -1.4707, -1.1443, -1.1701))
491_trfX(_ITRF2000_, _GDA2020_,  epoch=_F(2020),
492                              xform=_T(-105.52,  51.58,  231.68,  3.55,   4.2175,  6.3941,  0.8617),
493                              rates=_T(  -4.66,   3.55,   11.24,  0.249,  1.7454,  1.4868,  1.224))
494
495# see Table 2 in U{Dawson, J. & Woods, A. "ITRF to GDA94 coordinate transformations", Journal of Applied
496# Geodesy 4 (2010), 189-199<https://www.ResearchGate.net/publication/258401581_ITRF_to_GDA94_coordinate_transformations>}
497# (note, sign of rotations for GDA94 reversed as "Australia assumes rotation to be of coordinate axes",
498# rather than the more conventional "position around the coordinate axes")
499_trfX(_ITRF2008_, _GDA94_,    epoch=_F(1994),
500                              xform=_T(-84.68,  -19.42,   32.01,  9.71,  -0.4254,  2.2578,  2.4015),
501                              rates=_T(  1.42,    1.34,    0.9,   0.109,  1.5461,  1.182,   1.1551))
502_trfX(_ITRF2005_, _GDA94_,    epoch=_F(1994),
503                              xform=_T(-79.73,   -6.86,   38.03,  6.636,  0.0351, -2.1211, -2.1411),
504                              rates=_T(  2.25,   -0.62,   -0.56,  0.294, -1.4707, -1.1443, -1.1701))
505_trfX(_ITRF2000_, _GDA94_,    epoch=_F(1994),
506                              xform=_T(-45.91,  -29.85,  -20.37,  7.07,  -1.6705,  0.4594,  1.9356),
507                              rates=_T( -4.66,    3.55,   11.24,  0.249,  1.7454,  1.4868,  1.224))
508del _T
509
510if __name__ == '__main__':
511
512    from pygeodesy.interns import _COMMA_, _NL_, _NL_var_, _STAR_
513
514    D = date2epoch.__name__
515    E = epoch2date.__name__
516    y = 2021
517    for m in range(1, 13):
518        for d in (1, _mDays[m] - 1, _mDays[m]):
519            f = '%s(%d,%3d,%3d)' % (D, y, m, d)
520            e = date2epoch(y, m, d)
521            t = epoch2date(e)
522            x = NN if t == (y, m, d) else _STAR_
523            e = '%.3f' % (e,)
524            e = '%s, %s(%s)' % (e, E, e)
525            t = '%d,%3d,%3d' % t
526            print('# %s = %s = %s %s' % (f, e, t, x))
527
528    # __doc__ of this file, force all into registery
529    t = [NN] + RefFrames.toRepr(all=True).split(_NL_)
530    print(_NL_var_.join(i.strip(_COMMA_) for i in t))
531
532# **) MIT License
533#
534# Copyright (C) 2016-2021 -- mrJean1 at Gmail -- All Rights Reserved.
535#
536# Permission is hereby granted, free of charge, to any person obtaining a
537# copy of this software and associated documentation files (the "Software"),
538# to deal in the Software without restriction, including without limitation
539# the rights to use, copy, modify, merge, publish, distribute, sublicense,
540# and/or sell copies of the Software, and to permit persons to whom the
541# Software is furnished to do so, subject to the following conditions:
542#
543# The above copyright notice and this permission notice shall be included
544# in all copies or substantial portions of the Software.
545#
546# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
547# OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
548# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
549# THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR
550# OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
551# ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
552# OTHER DEALINGS IN THE SOFTWARE.
553
554# % python -m pygeodesy.trf
555#
556# date2epoch(2021,  1,  1) = 2021.003, epoch2date(2021.003) = 2021,  1,  1
557# date2epoch(2021,  1, 30) = 2021.082, epoch2date(2021.082) = 2021,  1, 30
558# date2epoch(2021,  1, 31) = 2021.085, epoch2date(2021.085) = 2021,  1, 31
559# date2epoch(2021,  2,  1) = 2021.087, epoch2date(2021.087) = 2021,  2,  2 *
560# date2epoch(2021,  2, 28) = 2021.161, epoch2date(2021.161) = 2021,  2, 28
561# date2epoch(2021,  2, 29) = 2021.164, epoch2date(2021.164) = 2021,  3,  1 *
562# date2epoch(2021,  3,  1) = 2021.167, epoch2date(2021.167) = 2021,  3,  2 *
563# date2epoch(2021,  3, 30) = 2021.246, epoch2date(2021.246) = 2021,  3, 31 *
564# date2epoch(2021,  3, 31) = 2021.249, epoch2date(2021.249) = 2021,  4,  1 *
565# date2epoch(2021,  4,  1) = 2021.251, epoch2date(2021.251) = 2021,  4,  1
566# date2epoch(2021,  4, 29) = 2021.328, epoch2date(2021.328) = 2021,  4, 29
567# date2epoch(2021,  4, 30) = 2021.331, epoch2date(2021.331) = 2021,  4, 30
568# date2epoch(2021,  5,  1) = 2021.333, epoch2date(2021.333) = 2021,  5,  1
569# date2epoch(2021,  5, 30) = 2021.413, epoch2date(2021.413) = 2021,  5, 30
570# date2epoch(2021,  5, 31) = 2021.415, epoch2date(2021.415) = 2021,  6,  1 *
571# date2epoch(2021,  6,  1) = 2021.418, epoch2date(2021.418) = 2021,  6,  2 *
572# date2epoch(2021,  6, 29) = 2021.495, epoch2date(2021.495) = 2021,  6, 30 *
573# date2epoch(2021,  6, 30) = 2021.497, epoch2date(2021.497) = 2021,  7,  1 *
574# date2epoch(2021,  7,  1) = 2021.500, epoch2date(2021.500) = 2021,  7,  1
575# date2epoch(2021,  7, 30) = 2021.579, epoch2date(2021.579) = 2021,  7, 30
576# date2epoch(2021,  7, 31) = 2021.582, epoch2date(2021.582) = 2021,  7, 31
577# date2epoch(2021,  8,  1) = 2021.585, epoch2date(2021.585) = 2021,  8,  1
578# date2epoch(2021,  8, 30) = 2021.664, epoch2date(2021.664) = 2021,  8, 31 *
579# date2epoch(2021,  8, 31) = 2021.667, epoch2date(2021.667) = 2021,  9,  1 *
580# date2epoch(2021,  9,  1) = 2021.669, epoch2date(2021.669) = 2021,  9,  2 *
581# date2epoch(2021,  9, 29) = 2021.746, epoch2date(2021.746) = 2021,  9, 30 *
582# date2epoch(2021,  9, 30) = 2021.749, epoch2date(2021.749) = 2021, 10,  1 *
583# date2epoch(2021, 10,  1) = 2021.751, epoch2date(2021.751) = 2021, 10,  1
584# date2epoch(2021, 10, 30) = 2021.831, epoch2date(2021.831) = 2021, 10, 30
585# date2epoch(2021, 10, 31) = 2021.833, epoch2date(2021.833) = 2021, 10, 31
586# date2epoch(2021, 11,  1) = 2021.836, epoch2date(2021.836) = 2021, 11,  1
587# date2epoch(2021, 11, 29) = 2021.913, epoch2date(2021.913) = 2021, 11, 29
588# date2epoch(2021, 11, 30) = 2021.915, epoch2date(2021.915) = 2021, 12,  1 *
589# date2epoch(2021, 12,  1) = 2021.918, epoch2date(2021.918) = 2021, 12,  2 *
590# date2epoch(2021, 12, 30) = 2021.997, epoch2date(2021.997) = 2021, 12, 31 *
591# date2epoch(2021, 12, 31) = 2022.000, epoch2date(2022.000) = 2022,  1,  1 *
592