Coverage for pygeodesy/trf.py: 97%

177 statements  

« prev     ^ index     » next       coverage.py v7.2.2, created at 2023-09-01 13:41 -0400

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-2022 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.ITRF90: RefFrame(name='ITRF90', epoch=1988, ellipsoid=Ellipsoid(name='GRS80') 

42@var RefFrames.ITRF91: RefFrame(name='ITRF91', epoch=1988, ellipsoid=Ellipsoid(name='GRS80') 

43@var RefFrames.ITRF93: RefFrame(name='ITRF93', epoch=1988, ellipsoid=Ellipsoid(name='GRS80') 

44@var RefFrames.NAD83: RefFrame(name='NAD83', epoch=1997, ellipsoid=Ellipsoid(name='GRS80') 

45@var RefFrames.WGS84: RefFrame(name='WGS84', epoch=1984, ellipsoid=Ellipsoid(name='WGS84') 

46@var RefFrames.WGS84g1150: RefFrame(name='WGS84g1150', epoch=2001, ellipsoid=Ellipsoid(name='WGS84') 

47@var RefFrames.WGS84g1674: RefFrame(name='WGS84g1674', epoch=2005, ellipsoid=Ellipsoid(name='WGS84') 

48@var RefFrames.WGS84g1762: RefFrame(name='WGS84g1762', epoch=2005, ellipsoid=Ellipsoid(name='WGS84') 

49''' 

50 

51from pygeodesy.basics import map1, _xinstanceof, _zip 

52from pygeodesy.constants import _float as _F, _0_0s, _0_0, _0_001, \ 

53 _0_01, _0_1, _0_26, _0_5, _N_0_5, _1_0 

54from pygeodesy.datums import _a_ellipsoid, Transform 

55from pygeodesy.ellipsoids import Ellipsoids, Fmt, Property_RO 

56from pygeodesy.errors import _ALL_LAZY, _IsnotError, TRFError 

57from pygeodesy.interns import NN, _COMMASPACE_, _cartesian_, _conversion_, \ 

58 _ellipsoid_, _ellipsoidal_, _epoch_, _exists_, \ 

59 _GRS80_, _NAD83_, _name_, _no_, _s_, _SPACE_, \ 

60 _sx_, _sy_, _sz_, _to_, _tx_, _ty_, _tz_, _WGS84_ 

61# from pygeodesy.lazily import _ALL_LAZY # from .errors 

62from pygeodesy.named import classname, _lazyNamedEnumItem as _lazy, _NamedEnum, \ 

63 _NamedEnumItem, _NamedDict as _XD, _NamedTuple 

64# from pygeodesy.props import Property_RO # from .ellipsoids 

65# from pygeodesy.streprs import Fmt # from .ellipsoids 

66from pygeodesy.units import Epoch, Float 

67 

68from math import ceil 

69 

70__all__ = _ALL_LAZY.trf 

71__version__ = '23.05.26' 

72 

73_0_02 = 0.02 

74_0_06 = 0.06 

75_0_09 = 0.09 

76_0_12 = 0.12 

77_366_0 = 366.0 

78 

79_Forward = _0_001 # mm2m, ppb2ppM, mas2as 

80_Inverse = -_0_001 # same, inverse transforms 

81_mas = _mm = _ppb = Float # as == arcseconds 

82_mDays = (0, 31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31, 0) 

83_N_0_06 = -_0_06 

84_N_0_1 = -_0_1 

85 

86_ETRF2000_ = 'ETRF2000' 

87_GDA2020_ = 'GDA2020' 

88_GDA94_ = 'GDA94' 

89_ITRF_ = 'ITRF' 

90_ITRF88_ = 'ITRF88' 

91_ITRF89_ = 'ITRF89' 

92_ITRF90_ = 'ITRF90' 

93_ITRF91_ = 'ITRF91' 

94_ITRF92_ = 'ITRF92' 

95_ITRF93_ = 'ITRF93' 

96_ITRF94_ = 'ITRF94' 

97_ITRF96_ = 'ITRF96' 

98_ITRF97_ = 'ITRF97' 

99_ITRF2000_ = 'ITRF2000' 

100_ITRF2005_ = 'ITRF2005' 

101_ITRF2008_ = 'ITRF2008' 

102_ITRF2014_ = 'ITRF2014' 

103_WGS84g1150_ = 'WGS84g1150' 

104_WGS84g1674_ = 'WGS84g1674' 

105_WGS84g1762_ = 'WGS84g1762' 

106 

107 

108class Helmert7Tuple(_NamedTuple): 

109 '''7-Tuple C{(tx, ty, tz, s, sx, sy, sz)} of Helmert transform 

110 parameters with translations C{tx}, C{ty} and C{tz} in 

111 C{millimeter}, scale C{s} in C{ppb} and rotations C{sx}, 

112 C{sy} and C{sz} in C{milliarcseconds}. 

113 

114 @see: L{Datum} L{Transform}. 

115 ''' 

116 _Names_ = (_tx_, _ty_, _tz_, _s_, _sx_, _sy_, _sz_) 

117 _Units_ = (_mm, _mm, _mm, _ppb, _mas, _mas, _mas) 

118 

119 def __new__(cls, tx, ty, tz, s, sx, sy, sz, name=NN): 

120 '''New L{Helmert7Tuple}. 

121 ''' 

122 t = map1(_F, tx, ty, tz, s, sx, sy, sz) 

123 return _NamedTuple.__new__(cls, *t, name=name) 

124 

125 

126class RefFrame(_NamedEnumItem): 

127 '''Terrestrial Reference Frame (TRF) parameters. 

128 ''' 

129 _ellipsoid = None # ellipsoid GRS80 or WGS84 (L{Ellipsoid} or L{Ellipsoid2}) 

130 _epoch = _0_0 # epoch, calendar year (L{Epoch} or C{float}) 

131 

132 def __init__(self, epoch, ellipsoid, name=NN): 

133 '''New L{RefFrame}. 

134 

135 @arg epoch: Epoch, a fractional calendar year (C{scalar} or C{str}). 

136 @arg ellipsoid: The ellipsoid (L{Ellipsoid}, L{Ellipsoid2}, 

137 L{Datum} or L{a_f2Tuple}). 

138 @kwarg name: Optional, unique name (C{str}). 

139 

140 @raise NameError: A L{RefFrame} with that B{C{name}} 

141 already exists. 

142 

143 @raise TRFError: Invalid B{C{epoch}}. 

144 

145 @raise TypeError: Invalid B{C{ellipsoid}}. 

146 ''' 

147 self._ellipsoid = _a_ellipsoid(ellipsoid, name=name, raiser=_ellipsoid_) 

148 self._epoch = Epoch(epoch) 

149 self._register(RefFrames, name) 

150 

151 def __matmul__(self, other): # PYCHOK Python 3.5+ 

152 '''Convert cartesian or ellipsoidal B{C{other}} to this reframe. 

153 

154 @raise TypeError: Invalid B{C{other}}. 

155 ''' 

156 try: # only Cartesian- and LatLonEllipsoidalBase 

157 return other.toRefFrame(self) 

158 except AttributeError: 

159 pass 

160 raise _IsnotError(_cartesian_, _ellipsoidal_, other=other) 

161 

162 @Property_RO 

163 def ellipsoid(self): 

164 '''Get this reference frame's ellipsoid (L{Ellipsoid} or L{Ellipsoid2}). 

165 ''' 

166 return self._ellipsoid 

167 

168 @Property_RO 

169 def epoch(self): 

170 '''Get this reference frame's epoch (C{Epoch}). 

171 ''' 

172 return self._epoch 

173 

174 def toStr(self, name=NN, **unused): # PYCHOK expected 

175 '''Return this reference frame as a text string. 

176 

177 @kwarg name: Override name (C{str}) or C{None} to exclude the 

178 reframe's name. 

179 

180 @return: This L{RefFrame}'s attributes (C{str}). 

181 ''' 

182 e = self.ellipsoid 

183 t = (Fmt.EQUAL(_name_, repr(name or self.name)), 

184 Fmt.EQUAL(_epoch_, self.epoch), 

185 Fmt.PAREN(Fmt.EQUAL(_ellipsoid_, classname(e)), 

186 Fmt.EQUAL(_name_, repr(e.name)))) 

187 return _COMMASPACE_.join(t[1:] if name is None else t) 

188 

189 

190class RefFrames(_NamedEnum): 

191 '''(INTERNAL) L{RefFrame} registry, I{must} be a sub-class 

192 to accommodate the L{_LazyNamedEnumItem} properties. 

193 ''' 

194 def _Lazy(self, epoch, ellipsoid_name, name=NN): 

195 '''(INTERNAL) Instantiate the L{RefFrame}. 

196 ''' 

197 return RefFrame(epoch, Ellipsoids.get(ellipsoid_name), name=name) 

198 

199RefFrames = RefFrames(RefFrame) # PYCHOK singleton 

200'''Some pre-defined L{RefFrame}s, all I{lazily} instantiated.''' 

201# <https://GitHub.com/ChrisVeness/geodesy/blob/master/latlon-ellipsoidal-referenceframe.js> 

202RefFrames._assert( 

203 ETRF2000 = _lazy(_ETRF2000_, _F(2005), _GRS80_), # ETRF2000(R08) 

204 GDA2020 = _lazy(_GDA2020_, _F(2020), _GRS80_), # Australia 

205 GDA94 = _lazy(_GDA94_, _F(1994), _GRS80_), # Australia 

206 ITRF2000 = _lazy(_ITRF2000_, _F(1997), _GRS80_), 

207 ITRF2005 = _lazy(_ITRF2005_, _F(2000), _GRS80_), 

208 ITRF2008 = _lazy(_ITRF2008_, _F(2005), _GRS80_), # aka ITRF08 

209 ITRF2014 = _lazy(_ITRF2014_, _F(2010), _GRS80_), 

210 ITRF90 = _lazy(_ITRF90_, _F(1988), _GRS80_), 

211 ITRF91 = _lazy(_ITRF91_, _F(1988), _GRS80_), 

212 ITRF93 = _lazy(_ITRF93_, _F(1988), _GRS80_), 

213 NAD83 = _lazy(_NAD83_, _F(1997), _GRS80_), # CORS96 

214 WGS84 = _lazy(_WGS84_, _F(1984), _WGS84_), 

215 WGS84g1150 = _lazy(_WGS84g1150_, _F(2001), _WGS84_), 

216 WGS84g1674 = _lazy(_WGS84g1674_, _F(2005), _WGS84_), 

217 WGS84g1762 = _lazy(_WGS84g1762_, _F(2005), _WGS84_)) # same epoch 

218 

219 

220def date2epoch(year, month, day): 

221 '''Return the reference frame C{epoch} for a calendar day. 

222 

223 @arg year: Year of the date (C{scalar}). 

224 @arg month: Month in the B{C{year}} (C{scalar}, 1..12). 

225 @arg day: Day in the B{C{month}} (C{scalar}, 1..31). 

226 

227 @return: Epoch, the fractional year (C{float}). 

228 

229 @raise TRFError: Invalid B{C{year}}, B{C{month}} or B{C{day}}. 

230 

231 @note: Any B{C{year}} is considered a leap year, i.e. having 

232 29 days in February. 

233 ''' 

234 try: 

235 y, m, d = map1(int, year, month, day) 

236 if y > 0 and 1 <= m <= 12 and 1 <= d <= _mDays[m]: 

237 return Epoch(y + float(sum(_mDays[:m]) + d) / _366_0, low=0) 

238 

239 raise ValueError # _invalid_ 

240 except (TRFError, TypeError, ValueError) as x: 

241 raise TRFError(year=year, month=month, day=day, cause=x) 

242 

243 

244def epoch2date(epoch): 

245 '''Return the date for a reference frame C{epoch}. 

246 

247 @arg epoch: Fractional year (C{scalar}). 

248 

249 @return: 3-Tuple C{(year, month, day)}. 

250 

251 @raise TRFError: Invalid B{C{epoch}}. 

252 

253 @note: Any B{C{year}} is considered a leap year, i.e. having 

254 29 days in February. 

255 ''' 

256 e = Epoch(epoch, Error=TRFError, low=0) 

257 y = int(e) 

258 d = int(ceil(_366_0 * (e - y))) 

259 for m, n in enumerate(_mDays[1:]): 

260 if d > n: 

261 d -= n 

262 else: 

263 break 

264 return y, (m + 1), max(1, d) 

265 

266 

267def _intermediate(n1, n2): 

268 '''(INTERNAL) Find a trf* "in between" C{n1} and C{n2}. 

269 ''' 

270 f1 = set(m for n, m in _trfXs.keys() if n == n1) # from trf1 

271 t2 = set(n for n, m in _trfXs.keys() if m == n2) # to trf2 

272 n = f1.intersection(t2) 

273 return n.pop() if n else NN 

274 

275 

276def _reframeTransforms2(rf2, rf, epoch): 

277 '''(INTERNAL) Get 0, 1 or 2 Helmert L{Transform}s to convert 

278 reference frame B{C{rf}} observed at B{C{epoch}} into B{C{rf2}}. 

279 ''' 

280 e = rf.epoch if epoch is None else Epoch(epoch) 

281 

282 n2 = rf2.name # .upper() 

283 n1 = rf.name # .upper() 

284 if n1 == n2 or (n1.startswith(_ITRF_) and n2.startswith(_WGS84_)) \ 

285 or (n2.startswith(_ITRF_) and n1.startswith(_WGS84_)): 

286 return e, () # PYCHOK returns 

287 

288 if (n1, n2) in _trfXs: 

289 return e, (_2Transform((n1, n2), e, _Forward),) # PYCHOK returns 

290 

291 if (n2, n1) in _trfXs: 

292 return e, (_2Transform((n2, n1), e, _Inverse),) # PYCHOK returns 

293 

294 n = _intermediate(n1, n2) 

295 if n: 

296 return e, (_2Transform((n1, n), e, _Forward), # PYCHOK returns 

297 _2Transform((n, n2), e, _Forward)) 

298 

299 n = _intermediate(n2, n1) 

300 if n: 

301 return e, (_2Transform((n, n1), e, _Inverse), # PYCHOK returns 

302 _2Transform((n2, n), e, _Inverse)) 

303 

304 t = _SPACE_(RefFrame.__name__, repr(n1), _to_, repr(n2)) 

305 raise TRFError(_no_(_conversion_), txt=t) 

306 

307 

308def _2Transform(n1_n2, epoch, _Forward_Inverse): 

309 '''(INTERNAL) Combine two Helmert transforms from TRF 

310 conversion C{_trfXs[n1_n2]} observed at B{C{epoch}} 

311 into a single datum L{Transform}. 

312 

313 @note: Translations are converted from C{millimeter} 

314 to C{meter}, rotations from C{milliarcseconds} 

315 to C{arcseconds} and scale from C{ppb} to C{ppM}. 

316 ''' 

317 X = _trfXs[n1_n2] 

318 e = epoch - X.epoch # fractional delta years 

319 d = dict((n, (p + r * e) * _Forward_Inverse) for n, p, r in 

320 _zip(Helmert7Tuple._Names_, X.xform, X.rates)) # strict=True 

321 return Transform(**d) 

322 

323 

324def trfXform(reframe1, reframe2, epoch=None, xform=None, rates=None): 

325 '''Define a new Terrestrial Reference Frame (TRF) conversion. 

326 

327 @arg reframe1: Source reframe (L{RefFrame}), converting I{from}. 

328 @arg reframe2: Destination reframe (L{RefFrame}), converting I{to}. 

329 @kwarg epoch: Epoch, a fractional calendar year (C{scalar} or C{str}) 

330 or C{None} for C{B{reframe2}.epoch}. 

331 @kwarg xform: Helmert transform parameters (C{Helmert7Tuple}). 

332 @kwarg rates: Helmert rate parameters (C{Helmert7Tuple}). 

333 

334 @raise TRFError: Invalid B{C{epoch}} or TRF conversion already exists. 

335 ''' 

336 _xinstanceof(RefFrame, reframe1=reframe1, reframe2=reframe2) 

337 e = reframe2.epoch if epoch is None else Epoch(epoch=epoch, Error=TRFError) 

338 _xinstanceof(Helmert7Tuple, xform=xform, rates=rates) 

339 _trfX(reframe1.name, reframe2.name, epoch=e, xform=xform, rates=rates) 

340 

341 

342def _trfX(n1, n2, **epoch_xform_rates): 

343 '''(INTERNAL) New C{_trfXs} entry. 

344 ''' 

345 n1_n2 = n1, n2 

346 if n1_n2 in _trfXs: 

347 raise TRFError(trfX=n1_n2, txt=_exists_) # _NameError 

348 _trfXs[n1_n2] = _XD(X=n1_n2, **epoch_xform_rates) 

349 

350 

351_H = Helmert7Tuple 

352_H_0_0s = _H(*_0_0s(len(_H._Names_))) 

353# TRF conversions specified as an epoch and dual 7-parameter Helmert transforms. Most from U{Transformation 

354# Parameters<http://ITRF.IGN.Fr/trans_para.php>}, more at U{Quinsy QPS<https://confluence.QPS.NL/qinsy/ 

355# files/latest/en/182618383/182618384/1/1579182881000/ITRF_Transformation_Parameters.xlsx>}. See also 

356# U{Quinsy International Terrestrial Reference Frame 2014 (ITRF2014)<https://confluence.QPS.NL/qinsy/ 

357# latest/en/international-terrestrial-reference-frame-2014-itrf2014-182618383.html>}. 

358_trfXs = dict() # key is [(from_TRF, to_TRF)] 2-tuple 

359# see U{Transformation Parameters ITRF2014<http://ITRF.IGN.Fr/doc_ITRF/Transfo-ITRF2014_ITRFs.txt>} 

360_trfX(_ITRF2014_, _ITRF2008_, epoch=_F(2010), # <http://ITRF.ENSG.IGN.Fr/ITRF_solutions/2014/tp_14-08.php> 

361 xform=_H( 1.6, 1.9, 2.4, -0.02, _0_0, _0_0, _0_0), 

362 rates=_H( _0_0, _0_0, _N_0_1, 0.03, _0_0, _0_0, _0_0)) 

363_trfX(_ITRF2014_, _ITRF2005_, epoch=_F(2010), 

364 xform=_H( 2.6, _1_0, -2.3, 0.92, _0_0, _0_0, _0_0), 

365 rates=_H( 0.3, _0_0, _N_0_1, 0.03, _0_0, _0_0, _0_0)) 

366_trfX(_ITRF2014_, _ITRF2000_, epoch=_F(2010), 

367 xform=_H( 0.7, 1.2, -26.1, 2.12, _0_0, _0_0, _0_0), 

368 rates=_H( _0_1, _0_1, -1.9, 0.11, _0_0, _0_0, _0_0)) 

369_trfX(_ITRF2014_, _ITRF97_, epoch=_F(2010), 

370 xform=_H( 7.4, _N_0_5, -62.8, 3.8, _0_0, _0_0, _0_26), 

371 rates=_H( _0_1, _N_0_5, -3.3, _0_12, _0_0, _0_0, _0_02)) 

372_trfX(_ITRF2014_, _ITRF96_, epoch=_F(2010), 

373 xform=_H( 7.4, _N_0_5, -62.8, 3.8, _0_0, _0_0, _0_26), 

374 rates=_H( _0_1, _N_0_5, -3.3, _0_12, _0_0, _0_0, _0_02)) 

375_trfX(_ITRF2014_, _ITRF94_, epoch=_F(2010), 

376 xform=_H( 7.4, _N_0_5, -62.8, 3.8, _0_0, _0_0, _0_26), 

377 rates=_H( _0_1, _N_0_5, -3.3, _0_12, _0_0, _0_0, _0_02)) 

378_trfX(_ITRF2014_, _ITRF93_, epoch=_F(2010), 

379 xform=_H( -50.4, 3.3, -60.2, 4.29, -2.81, -3.38, 0.4), 

380 rates=_H( -2.8, _N_0_1, -2.5, _0_12, -0.11, -0.19, 0.07)) 

381_trfX(_ITRF2014_, _ITRF92_, epoch=_F(2010), 

382 xform=_H( 15.4, 1.5, -70.8, 3.09, _0_0, _0_0, _0_26), 

383 rates=_H( _0_1, _N_0_5, -3.3, _0_12, _0_0, _0_0, _0_02)) 

384_trfX(_ITRF2014_, _ITRF91_, epoch=_F(2010), 

385 xform=_H( 27.4, 15.5, -76.8, 4.49, _0_0, _0_0, _0_26), 

386 rates=_H( _0_1, _N_0_5, -3.3, _0_12, _0_0, _0_0, _0_02)) 

387_trfX(_ITRF2014_, _ITRF90_, epoch=_F(2010), 

388 xform=_H( 25.4, 11.5, -92.8, 4.79, _0_0, _0_0, _0_26), 

389 rates=_H( _0_1, _N_0_5, -3.3, _0_12, _0_0, _0_0, _0_02)) 

390_trfX(_ITRF2014_, _ITRF89_, epoch=_F(2010), 

391 xform=_H( 30.4, 35.5, -130.8, 8.19, _0_0, _0_0, _0_26), 

392 rates=_H( _0_1, _N_0_5, -3.3, _0_12, _0_0, _0_0, _0_02)) 

393_trfX(_ITRF2014_, _ITRF88_, epoch=_F(2010), 

394 xform=_H( 25.4, _N_0_5, -154.8, 11.29, _0_1, _0_0, _0_26), 

395 rates=_H( _0_1, _N_0_5, -3.3, _0_12, _0_0, _0_0, _0_02)) 

396 

397# see U{Transformation Parameters ITRF2008<http://ITRF.IGN.Fr/doc_ITRF/Transfo-ITRF2008_ITRFs.txt>} 

398# _trfX(_ITRF2008_, _ITRF2005_, epoch=_F(2005), # <http://ITRF.ENSG.IGN.Fr/ITRF_solutions/2008/tp_08-05.php> 

399# xform=_H(_N_0_5, s -0.9, -4.7, 0.94, _0_0, _0_0, _0_0), 

400# rates=_H( 0.3, _0_0, _0_0, _0_0, _0_0, _0_0, _0_0)) 

401_trfX(_ITRF2008_, _ITRF2005_, epoch=_F(2000), 

402 xform=_H( -2.0, -0.9, -4.7, 0.94, _0_0, _0_0, _0_0), 

403 rates=_H( 0.3, _0_0, _0_0, _0_0, _0_0, _0_0, _0_0)) 

404_trfX(_ITRF2008_, _ITRF2000_, epoch=_F(2000), 

405 xform=_H( -1.9, -1.7, -10.5, 1.34, _0_0, _0_0, _0_0), 

406 rates=_H( _0_1, _0_1, -1.8, 0.08, _0_0, _0_0, _0_0)) 

407_trfX(_ITRF2008_, _ITRF97_, epoch=_F(2000), 

408 xform=_H( 4.8, 2.6, -33.2, 2.92, _0_0, _0_0, _0_06), 

409 rates=_H( _0_1, _N_0_5, -3.2, _0_09, _0_0, _0_0, _0_02)) 

410_trfX(_ITRF2008_, _ITRF96_, epoch=_F(2000), 

411 xform=_H( 4.8, 2.6, -33.2, 2.92, _0_0, _0_0, _0_06), 

412 rates=_H( _0_1, _N_0_5, -3.2, _0_09, _0_0, _0_0, _0_02)) 

413_trfX(_ITRF2008_, _ITRF94_, epoch=_F(2000), 

414 xform=_H( 4.8, 2.6, -33.2, 2.92, _0_0, _0_0, _0_06), 

415 rates=_H( _0_1, _N_0_5, -3.2, _0_09, _0_0, _0_0, _0_02)) 

416_trfX(_ITRF2008_, _ITRF93_, epoch=_F(2000), 

417 xform=_H( -24.0, 2.4, -38.6, 3.41, -1.71, -1.48, -0.3), 

418 rates=_H( -2.8, _N_0_1, -2.4, _0_09, -0.11, -0.19, 0.07)) 

419_trfX(_ITRF2008_, _ITRF92_, epoch=_F(2000), 

420 xform=_H( 12.8, 4.6, -41.2, 2.21, _0_0, _0_0, _0_06), 

421 rates=_H( _0_1, _N_0_5, -3.2, _0_09, _0_0, _0_0, _0_02)) 

422_trfX(_ITRF2008_, _ITRF91_, epoch=_F(2000), 

423 xform=_H( 24.8, 18.6, -47.2, 3.61, _0_0, _0_0, _0_06), 

424 rates=_H( _0_1, _N_0_5, -3.2, _0_09, _0_0, _0_0, _0_02)) 

425_trfX(_ITRF2008_, _ITRF90_, epoch=_F(2000), 

426 xform=_H( 22.8, 14.6, -63.2, 3.91, _0_0, _0_0, _0_06), 

427 rates=_H( _0_1, _N_0_5, -3.2, _0_09, _0_0, _0_0, _0_02)) 

428_trfX(_ITRF2008_, _ITRF89_, epoch=_F(2000), 

429 xform=_H( 27.8, 38.6, -101.2, 7.31, _0_0, _0_0, _0_06), 

430 rates=_H( _0_1, _N_0_5, -3.2, _0_09, _0_0, _0_0, _0_02)) 

431_trfX(_ITRF2008_, _ITRF88_, epoch=_F(2000), 

432 xform=_H( 22.8, 2.6, -125.2, 10.41, _0_1, _0_0, _0_06), 

433 rates=_H( _0_1, _N_0_5, -3.2, _0_09, _0_0, _0_0, _0_02)) 

434 

435_trfX(_ITRF2005_, _ITRF2000_, epoch=_F(2000), # <http://ITRF.ENSG.IGN.Fr/ITRF_solutions/2005/tp_05-00.php> 

436 xform=_H( _0_1, -0.8, -5.8, 0.4, _0_0, _0_0, _0_0), 

437 rates=_H( -0.2, _0_1, -1.8, 0.08, _0_0, _0_0, _0_0)) 

438 

439_trfX(_ITRF2000_, _ITRF97_, epoch=_F(1997), 

440 xform=_H( 0.67, 0.61, -1.85, 1.55, _0_0, _0_0, _0_0), 

441 rates=_H( _0_0, _N_0_06, -0.14, _0_01, _0_0, _0_0, _0_02)) 

442_trfX(_ITRF2000_, _ITRF96_, epoch=_F(1997), 

443 xform=_H( 0.67, 0.61, -1.85, 1.55, _0_0, _0_0, _0_0), 

444 rates=_H( _0_0, _N_0_06, -0.14, _0_01, _0_0, _0_0, _0_02)) 

445_trfX(_ITRF2000_, _ITRF94_, epoch=_F(1997), 

446 xform=_H( 0.67, 0.61, -1.85, 1.55, _0_0, _0_0, _0_0), 

447 rates=_H( _0_0, _N_0_06, -0.14, _0_01, _0_0, _0_0, _0_02)) 

448_trfX(_ITRF2000_, _ITRF93_, epoch=_F(1988), 

449 xform=_H( 12.7, 6.5, -20.9, 1.95, -0.39, 0.8, -1.14), 

450 rates=_H( -2.9, -0.2, -0.6, _0_01, -0.11, -0.19, 0.07)) 

451_trfX(_ITRF2000_, _ITRF92_, epoch=_F(1988), 

452 xform=_H( 1.47, 1.35, -1.39, 0.75, _0_0, _0_0, -0.18), 

453 rates=_H( _0_0, _N_0_06, -0.14, _0_01, _0_0, _0_0, _0_02)) 

454_trfX(_ITRF2000_, _ITRF91_, epoch=_F(1988), 

455 xform=_H( 26.7, 27.5, -19.9, 2.15, _0_0, _0_0, -0.18), 

456 rates=_H( _0_0, -0.6, -1.4, _0_01, _0_0, _0_0, _0_02)) 

457_trfX(_ITRF2000_, _ITRF90_, epoch=_F(1988), 

458 xform=_H( 2.47, 2.35, -3.59, 2.45, _0_0, _0_0, -0.18), 

459 rates=_H( _0_0, _N_0_06, -0.14, _0_01, _0_0, _0_0, _0_02)) 

460_trfX(_ITRF2000_, _ITRF89_, epoch=_F(1988), 

461 xform=_H( 2.97, 4.75, -7.39, 5.85, _0_0, _0_0, -0.18), 

462 rates=_H( _0_0, _N_0_06, -0.14, _0_01, _0_0, _0_0, _0_02)) 

463_trfX(_ITRF2000_, _ITRF88_, epoch=_F(1988), 

464 xform=_H( 2.47, 1.15, -9.79, 8.95, _0_1, _0_0, -0.18), 

465 rates=_H( _0_0, _N_0_06, -0.14, _0_01, _0_0, _0_0, _0_02)) 

466 

467# see U{Boucher, C. & Altamimi, Z. "Memo: Specifications for reference frame fixing in the 

468# analysis of a EUREF GPS campaign" (2011) <https://ETRS89.ENSG.IGN.Fr/memo-V8.pdf>} and 

469# Altamimi, Z. U{"Key results of ITRF2014 and implication to ETRS89 realization", EUREF2016 

470# <https://www.EUREF.EU/symposia/2016SanSebastian/01-02-Altamimi.pdf>}. 

471_trfX(_ITRF2014_, _ETRF2000_, epoch=_F(2000), 

472 xform=_H( 53.7, 51.2, -55.1, 1.02, 0.891, 5.39, -8.712), 

473 rates=_H( _0_1, _0_1, -1.9, 0.11, 0.081, 0.49, -0.792)) 

474_trfX(_ITRF2008_, _ETRF2000_, epoch=_F(2000), 

475 xform=_H( 52.1, 49.3, -58.5, 1.34, 0.891, 5.39, -8.712), 

476 rates=_H( _0_1, _0_1, -1.8, 0.08, 0.081, 0.49, -0.792)) 

477_trfX(_ITRF2005_, _ETRF2000_, epoch=_F(2000), 

478 xform=_H( 54.1, 50.2, -53.8, 0.4, 0.891, 5.39, -8.712), 

479 rates=_H( -0.2, _0_1, -1.8, 0.08, 0.081, 0.49, -0.792)) 

480_trfX(_ITRF2000_, _ETRF2000_, epoch=_F(2000), 

481 xform=_H( 54.0, 51.0, -48.0, _0_0, 0.891, 5.39, -8.712), 

482 rates=_H( _0_0, _0_0, _0_0, _0_0, 0.081, 0.49, -0.792)) 

483 

484# GDA2020 "Geocentric Datum of Australia 2020 Technical Manual", v1.5, 2020-12-09, Table 3.3 and 3.4 

485# <https://www.ICSM.gov.AU/sites/default/files/2020-12/GDA2020%20Technical%20Manual%20V1.5_4.pdf> 

486# (the GDA2020 xforms are different but the rates are the same as GDA94, further below) 

487_trfX(_ITRF2014_, _GDA2020_, epoch=_F(2020), 

488 xform=_H_0_0s, 

489 rates=_H( _0_0, _0_0, _0_0, _0_0, 1.50379, 1.18346, 1.20716)) 

490_trfX(_ITRF2008_, _GDA2020_, epoch=_F(2020), 

491 xform=_H( 13.79, 4.55, 15.22, 2.5, 0.2808, 0.2677, -0.4638), 

492 rates=_H( 1.42, 1.34, 0.9, 0.109, 1.5461, 1.182, 1.1551)) 

493_trfX(_ITRF2005_, _GDA2020_, epoch=_F(2020), 

494 xform=_H( 40.32, -33.85, -16.72, 4.286, -1.2893, -0.8492, -0.3342), 

495 rates=_H( 2.25, -0.62, -0.56, 0.294, -1.4707, -1.1443, -1.1701)) 

496_trfX(_ITRF2000_, _GDA2020_, epoch=_F(2020), 

497 xform=_H(-105.52, 51.58, 231.68, 3.55, 4.2175, 6.3941, 0.8617), 

498 rates=_H( -4.66, 3.55, 11.24, 0.249, 1.7454, 1.4868, 1.224)) 

499 

500# see Table 2 in U{Dawson, J. & Woods, A. "ITRF to GDA94 coordinate transformations", Journal of Applied 

501# Geodesy 4 (2010), 189-199<https://www.ResearchGate.net/publication/258401581_ITRF_to_GDA94_coordinate_transformations>} 

502# (note, sign of rotations for GDA94 reversed as "Australia assumes rotation to be of coordinate axes", 

503# rather than the more conventional "position around the coordinate axes") 

504_trfX(_ITRF2008_, _GDA94_, epoch=_F(1994), 

505 xform=_H( -84.68, -19.42, 32.01, 9.71, -0.4254, 2.2578, 2.4015), 

506 rates=_H( 1.42, 1.34, 0.9, 0.109, 1.5461, 1.182, 1.1551)) 

507_trfX(_ITRF2005_, _GDA94_, epoch=_F(1994), 

508 xform=_H( -79.73, -6.86, 38.03, 6.636, 0.0351, -2.1211, -2.1411), 

509 rates=_H( 2.25, -0.62, -0.56, 0.294, -1.4707, -1.1443, -1.1701)) 

510_trfX(_ITRF2000_, _GDA94_, epoch=_F(1994), 

511 xform=_H( -45.91, -29.85, -20.37, 7.07, -1.6705, 0.4594, 1.9356), 

512 rates=_H( -4.66, 3.55, 11.24, 0.249, 1.7454, 1.4868, 1.224)) 

513 

514# see U{Solar, T. & Snay, R.A. "Transforming Positions and Velocities between the 

515# International Terrestrial Reference Frame of 2000 and North American Datum of 1983" 

516# (2004)<https://www.NGS.NOAA.gov/CORS/Articles/SolerSnayASCE.pdf>} 

517_trfX(_ITRF2000_, _NAD83_, epoch=_F(1997), # note NAD83(CORS96) 

518 xform=_H( 995.6, -1901.3, -521.5, 0.615, -25.915, -9.426, -11.599), 

519 rates=_H( 0.7, -0.7, _0_5, -0.182, -0.06667, 0.75744, 0.05133)) 

520 

521# see U{Quinsy QPS<https://confluence.QPS.NL/qinsy/files/latest/en/182618383/182618384/1/1579182881000/ 

522# ITRF_Transformation_Parameters.xlsx>}, sheets ITRF and NAD83 

523_trfX(_ITRF2008_, _NAD83_, epoch=_F(1997), 

524 xform=_H( 993.43, -1903.31, -526.55, 1.71504, -25.91467, -9.42645, -11.59935), 

525 rates=_H( 0.79, -0.6, -1.34, -0.10201, -0.06667, 0.75744, 0.05133)) 

526_trfX(_ITRF2005_, _NAD83_, epoch=_F(1997), 

527 xform=_H( 996.3, -1902.4, -521.9, 0.775, -25.915, -9.426, -11.599), 

528 rates=_H( 0.5, -0.6, -1.3, -0.10201, -0.06667, 0.75744, 0.05133)) 

529_trfX(_ITRF90_, _NAD83_, epoch=_F(1997), 

530 xform=_H( 973.0, -1919.2, -482.9, -0.9, -25.79, -9.65, -11.66), 

531 rates=_H( _0_0, _0_0, _0_0, _0_0, -0.053, 0.742, 0.032)) 

532_trfX(_ITRF90_, _WGS84_, epoch=_F(1984), 

533 xform=_H( 60.0, -517.0, -223.0, -11.0, 18.3, -0.3, 7.0), 

534 rates=_H_0_0s) 

535# del _H, _H_0_0s 

536 

537if __name__ == '__main__': 

538 

539 from pygeodesy.interns import _COMMA_, _NL_, _NLATvar_, _STAR_ 

540 from pygeodesy.lazily import printf 

541 from time import localtime 

542 

543 D = date2epoch.__name__ 

544 E = epoch2date.__name__ 

545 y = localtime()[0] 

546 for m in range(1, 13): 

547 for d in (1, 15, _mDays[m] - 1, _mDays[m]): 

548 f = '%s(%d,%3d,%3d)' % (D, y, m, d) 

549 e = date2epoch(y, m, d) 

550 t = epoch2date(e) 

551 x = NN if t == (y, m, d) else _STAR_ 

552 e = '%.3f' % (e,) 

553 e = '%s, %s(%s)' % (e, E, e) 

554 t = '%d,%3d,%3d' % t 

555 printf('# %s = %s = %s %s', f, e, t, x) 

556 

557 # __doc__ of this file, force all into registery 

558 t = [NN] + RefFrames.toRepr(all=True).split(_NL_) 

559 printf(_NLATvar_.join(i.strip(_COMMA_) for i in t)) 

560 

561# **) MIT License 

562# 

563# Copyright (C) 2016-2023 -- mrJean1 at Gmail -- All Rights Reserved. 

564# 

565# Permission is hereby granted, free of charge, to any person obtaining a 

566# copy of this software and associated documentation files (the "Software"), 

567# to deal in the Software without restriction, including without limitation 

568# the rights to use, copy, modify, merge, publish, distribute, sublicense, 

569# and/or sell copies of the Software, and to permit persons to whom the 

570# Software is furnished to do so, subject to the following conditions: 

571# 

572# The above copyright notice and this permission notice shall be included 

573# in all copies or substantial portions of the Software. 

574# 

575# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS 

576# OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 

577# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL 

578# THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR 

579# OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, 

580# ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR 

581# OTHER DEALINGS IN THE SOFTWARE. 

582 

583# % python -m pygeodesy.trf 

584# 

585# date2epoch(2022, 1, 1) = 2022.003, epoch2date(2022.003) = 2022, 1, 1 

586# date2epoch(2022, 1, 15) = 2022.041, epoch2date(2022.041) = 2022, 1, 15 

587# date2epoch(2022, 1, 30) = 2022.082, epoch2date(2022.082) = 2022, 1, 30 

588# date2epoch(2022, 1, 31) = 2022.085, epoch2date(2022.085) = 2022, 1, 31 

589# date2epoch(2022, 2, 1) = 2022.087, epoch2date(2022.087) = 2022, 2, 2 * 

590# date2epoch(2022, 2, 15) = 2022.126, epoch2date(2022.126) = 2022, 2, 16 * 

591# date2epoch(2022, 2, 28) = 2022.161, epoch2date(2022.161) = 2022, 2, 28 

592# date2epoch(2022, 2, 29) = 2022.164, epoch2date(2022.164) = 2022, 3, 1 * 

593# date2epoch(2022, 3, 1) = 2022.167, epoch2date(2022.167) = 2022, 3, 2 * 

594# date2epoch(2022, 3, 15) = 2022.205, epoch2date(2022.205) = 2022, 3, 16 * 

595# date2epoch(2022, 3, 30) = 2022.246, epoch2date(2022.246) = 2022, 3, 31 * 

596# date2epoch(2022, 3, 31) = 2022.249, epoch2date(2022.249) = 2022, 4, 1 * 

597# date2epoch(2022, 4, 1) = 2022.251, epoch2date(2022.251) = 2022, 4, 1 

598# date2epoch(2022, 4, 15) = 2022.290, epoch2date(2022.290) = 2022, 4, 15 

599# date2epoch(2022, 4, 29) = 2022.328, epoch2date(2022.328) = 2022, 4, 29 

600# date2epoch(2022, 4, 30) = 2022.331, epoch2date(2022.331) = 2022, 4, 30 

601# date2epoch(2022, 5, 1) = 2022.333, epoch2date(2022.333) = 2022, 5, 1 

602# date2epoch(2022, 5, 15) = 2022.372, epoch2date(2022.372) = 2022, 5, 15 

603# date2epoch(2022, 5, 30) = 2022.413, epoch2date(2022.413) = 2022, 5, 30 

604# date2epoch(2022, 5, 31) = 2022.415, epoch2date(2022.415) = 2022, 6, 1 * 

605# date2epoch(2022, 6, 1) = 2022.418, epoch2date(2022.418) = 2022, 6, 2 * 

606# date2epoch(2022, 6, 15) = 2022.456, epoch2date(2022.456) = 2022, 6, 16 * 

607# date2epoch(2022, 6, 29) = 2022.495, epoch2date(2022.495) = 2022, 6, 30 * 

608# date2epoch(2022, 6, 30) = 2022.497, epoch2date(2022.497) = 2022, 7, 1 * 

609# date2epoch(2022, 7, 1) = 2022.500, epoch2date(2022.500) = 2022, 7, 1 

610# date2epoch(2022, 7, 15) = 2022.538, epoch2date(2022.538) = 2022, 7, 16 * 

611# date2epoch(2022, 7, 30) = 2022.579, epoch2date(2022.579) = 2022, 7, 30 

612# date2epoch(2022, 7, 31) = 2022.582, epoch2date(2022.582) = 2022, 7, 31 

613# date2epoch(2022, 8, 1) = 2022.585, epoch2date(2022.585) = 2022, 8, 1 

614# date2epoch(2022, 8, 15) = 2022.623, epoch2date(2022.623) = 2022, 8, 15 

615# date2epoch(2022, 8, 30) = 2022.664, epoch2date(2022.664) = 2022, 8, 31 * 

616# date2epoch(2022, 8, 31) = 2022.667, epoch2date(2022.667) = 2022, 9, 1 * 

617# date2epoch(2022, 9, 1) = 2022.669, epoch2date(2022.669) = 2022, 9, 2 * 

618# date2epoch(2022, 9, 15) = 2022.708, epoch2date(2022.708) = 2022, 9, 16 * 

619# date2epoch(2022, 9, 29) = 2022.746, epoch2date(2022.746) = 2022, 9, 30 * 

620# date2epoch(2022, 9, 30) = 2022.749, epoch2date(2022.749) = 2022, 10, 1 * 

621# date2epoch(2022, 10, 1) = 2022.751, epoch2date(2022.751) = 2022, 10, 1 

622# date2epoch(2022, 10, 15) = 2022.790, epoch2date(2022.790) = 2022, 10, 15 

623# date2epoch(2022, 10, 30) = 2022.831, epoch2date(2022.831) = 2022, 10, 30 

624# date2epoch(2022, 10, 31) = 2022.833, epoch2date(2022.833) = 2022, 10, 31 

625# date2epoch(2022, 11, 1) = 2022.836, epoch2date(2022.836) = 2022, 11, 1 

626# date2epoch(2022, 11, 15) = 2022.874, epoch2date(2022.874) = 2022, 11, 15 

627# date2epoch(2022, 11, 29) = 2022.913, epoch2date(2022.913) = 2022, 11, 29 

628# date2epoch(2022, 11, 30) = 2022.915, epoch2date(2022.915) = 2022, 12, 1 * 

629# date2epoch(2022, 12, 1) = 2022.918, epoch2date(2022.918) = 2022, 12, 2 * 

630# date2epoch(2022, 12, 15) = 2022.956, epoch2date(2022.956) = 2022, 12, 16 * 

631# date2epoch(2022, 12, 30) = 2022.997, epoch2date(2022.997) = 2022, 12, 31 * 

632# date2epoch(2022, 12, 31) = 2023.000, epoch2date(2023.000) = 2023, 1, 1 *