Coverage for pygeodesy/trf.py: 96%

254 statements  

« prev     ^ index     » next       coverage.py v7.2.2, created at 2024-01-26 16:28 -0500

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.ETRF88: RefFrame(name='ETRF88', epoch=1988, datum=Datums.GRS80) 

35@var RefFrames.ETRF89: RefFrame(name='ETRF89', epoch=1989, datum=Datums.GRS80) 

36@var RefFrames.ETRF90: RefFrame(name='ETRF90', epoch=1990, datum=Datums.GRS80) 

37@var RefFrames.ETRF91: RefFrame(name='ETRF91', epoch=1991, datum=Datums.GRS80) 

38@var RefFrames.ETRF92: RefFrame(name='ETRF92', epoch=1992, datum=Datums.GRS80) 

39@var RefFrames.ETRF93: RefFrame(name='ETRF93', epoch=1993, datum=Datums.GRS80) 

40@var RefFrames.ETRF94: RefFrame(name='ETRF94', epoch=1994, datum=Datums.GRS80) 

41@var RefFrames.ETRF96: RefFrame(name='ETRF96', epoch=1996, datum=Datums.GRS80) 

42@var RefFrames.ETRF97: RefFrame(name='ETRF97', epoch=1997, datum=Datums.GRS80) 

43@var RefFrames.ETRF2000: RefFrame(name='ETRF2000', epoch=2005, datum=Datums.GRS80) 

44@var RefFrames.ETRF2005: RefFrame(name='ETRF2005', epoch=2005, datum=Datums.GRS80) 

45@var RefFrames.ETRF2008: RefFrame(name='ETRF2008', epoch=2008, datum=Datums.GRS80) 

46@var RefFrames.ETRF2014: RefFrame(name='ETRF2014', epoch=2014, datum=Datums.GRS80) 

47@var RefFrames.GDA94: RefFrame(name='GDA94', epoch=1994, datum=Datums.GRS80) 

48@var RefFrames.GDA2020: RefFrame(name='GDA2020', epoch=2020, datum=Datums.GRS80) 

49@var RefFrames.ITRF88: RefFrame(name='ITRF88', epoch=1988, datum=Datums.GRS80) 

50@var RefFrames.ITRF89: RefFrame(name='ITRF89', epoch=1989, datum=Datums.GRS80) 

51@var RefFrames.ITRF90: RefFrame(name='ITRF90', epoch=1988, datum=Datums.GRS80) 

52@var RefFrames.ITRF91: RefFrame(name='ITRF91', epoch=1988, datum=Datums.GRS80) 

53@var RefFrames.ITRF92: RefFrame(name='ITRF92', epoch=1988, datum=Datums.GRS80) 

54@var RefFrames.ITRF93: RefFrame(name='ITRF93', epoch=1988, datum=Datums.GRS80) 

55@var RefFrames.ITRF94: RefFrame(name='ITRF94', epoch=1993, datum=Datums.GRS80) 

56@var RefFrames.ITRF96: RefFrame(name='ITRF96', epoch=1997, datum=Datums.GRS80) 

57@var RefFrames.ITRF97: RefFrame(name='ITRF97', epoch=1997, datum=Datums.GRS80) 

58@var RefFrames.ITRF2000: RefFrame(name='ITRF2000', epoch=1997, datum=Datums.GRS80) 

59@var RefFrames.ITRF2005: RefFrame(name='ITRF2005', epoch=2000, datum=Datums.GRS80) 

60@var RefFrames.ITRF2008: RefFrame(name='ITRF2008', epoch=2005, datum=Datums.GRS80) 

61@var RefFrames.ITRF2014: RefFrame(name='ITRF2014', epoch=2010, datum=Datums.GRS80) 

62@var RefFrames.ITRF2020: RefFrame(name='ITRF2020', epoch=2015, datum=Datums.GRS80) 

63@var RefFrames.NAD83: RefFrame(name='NAD83', epoch=1997, datum=Datums.GRS80) 

64@var RefFrames.WGS84: RefFrame(name='WGS84', epoch=1984, datum=Datums.GRS80) 

65@var RefFrames.WGS84g1150: RefFrame(name='WGS84g1150', epoch=2001, datum=Datums.GRS80) 

66@var RefFrames.WGS84g1674: RefFrame(name='WGS84g1674', epoch=2005, datum=Datums.GRS80) 

67@var RefFrames.WGS84g1762: RefFrame(name='WGS84g1762', epoch=2005, datum=Datums.GRS80) 

68''' 

69 

70from pygeodesy.basics import map1, isstr, _xinstanceof, _zip 

71from pygeodesy.constants import _float as _F, _0_0, _0_001, _0_5, _1_0 

72from pygeodesy.datums import Datums, _earth_datum, Transform, _WGS84 

73from pygeodesy.errors import _IsnotError, TRFError 

74from pygeodesy.interns import MISSING, NN, _AT_, _COMMASPACE_, _cartesian_, _conversion_, \ 

75 _datum_, _DOT_, _ellipsoidal_, _exists_, _NAD83_, _no_, \ 

76 _reframe_, _s_, _SPACE_, _sx_, _sy_, _sz_, _to_, _tx_, _ty_, \ 

77 _tz_, _WGS84_, _intern as _i 

78from pygeodesy.lazily import _ALL_LAZY, _ALL_MODS as _MODS 

79from pygeodesy.named import classname, _lazyNamedEnumItem as _lazy, _NamedDict as _XD, \ 

80 _NamedEnum, _NamedEnumItem, _NamedTuple, Fmt 

81from pygeodesy.props import Property_RO, property_RO 

82# from pygeodesy.streprs import Fmt # from .named 

83from pygeodesy.units import Epoch, Float 

84 

85from math import ceil 

86 

87__all__ = _ALL_LAZY.trf 

88__version__ = '24.01.25' 

89 

90_366_0 = 366.0 

91_Forward = _0_001 # mm2m, ppb2ppM, mas2as 

92_GRS80 = Datums.GRS80 

93_Inverse = -_0_001 # same, inverse transforms 

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

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

96 

97_ETRF88_ = _i('ETRF88') 

98_ETRF89_ = _i('ETRF89') 

99_ETRF90_ = _i('ETRF90') 

100_ETRF91_ = _i('ETRF91') 

101_ETRF92_ = _i('ETRF92') 

102_ETRF93_ = _i('ETRF93') 

103_ETRF94_ = _i('ETRF94') 

104_ETRF96_ = _i('ETRF96') 

105_ETRF97_ = _i('ETRF97') 

106_ETRF2000_ = _i('ETRF2000') 

107_ETRF2005_ = _i('ETRF2005') 

108_ETRF2008_ = _i('ETRF2008') 

109_ETRF2014_ = _i('ETRF2014') 

110_GDA94_ = _i('GDA94') 

111_GDA2020_ = _i('GDA2020') 

112_ITRF_ = _i('ITRF') 

113_ITRF88_ = _i('ITRF88') 

114_ITRF89_ = _i('ITRF89') 

115_ITRF90_ = _i('ITRF90') 

116_ITRF91_ = _i('ITRF91') 

117_ITRF92_ = _i('ITRF92') 

118_ITRF93_ = _i('ITRF93') 

119_ITRF94_ = _i('ITRF94') 

120_ITRF96_ = _i('ITRF96') 

121_ITRF97_ = _i('ITRF97') 

122_ITRF2000_ = _i('ITRF2000') 

123_ITRF2005_ = _i('ITRF2005') 

124_ITRF2008_ = _i('ITRF2008') 

125_ITRF2014_ = _i('ITRF2014') 

126_ITRF2020_ = _i('ITRF2020') 

127_WGS84g1150_ = _i('WGS84g1150') 

128_WGS84g1674_ = _i('WGS84g1674') 

129_WGS84g1762_ = _i('WGS84g1762') 

130# del _i 

131 

132 

133class Helmert7Tuple(_NamedTuple): 

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

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

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

137 C{sy} and C{sz} in C{milli-arc-seconds}. 

138 

139 @note: The parameters names are often capitalized and 

140 alternate names are C{(Tx, Ty, Tz, D, Rx, Ry, Rz)} 

141 and C{(T1, T2, T3, D, R1, R2, R3)}. 

142 

143 @see: L{Datum} L{Transform} keyword arguments. 

144 ''' 

145 _Names_ = (_tx_, _ty_, _tz_, _s_, _sx_, _sy_, _sz_) # == kwds 

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

147 

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

149 '''New L{Helmert7Tuple}. 

150 ''' 

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

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

153 

154 

155class RefFrame(_NamedEnumItem): 

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

157 ''' 

158 _datum = _GRS80 # Datums.GRS80 or .WGS84 (L{Datum}) 

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

160 

161 def __init__(self, epoch, datum, name=NN): 

162 '''New L{RefFrame}. 

163 

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

165 @arg datum: Datum or ellipsoid (L{Datum}, {Ellipsoid}, L{Ellipsoid2} 

166 or L{a_f2Tuple}). 

167 @kwarg name: Unique, non-empty name (C{str}). 

168 

169 @raise NameError: A L{RefFrame} with that B{C{name}} already exists. 

170 

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

172 

173 @raise TypeError: Invalid B{C{datum}}. 

174 ''' 

175 if datum is not _GRS80: 

176 _earth_datum(self, datum, raiser=_datum_) 

177 self._epoch = Epoch(epoch) 

178 self._register(RefFrames, name) 

179 

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

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

182 

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

184 ''' 

185 try: # only Cartesian- and LatLonEllipsoidalBase 

186 return other.toRefFrame(self) 

187 except AttributeError: 

188 pass 

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

190 

191 @property_RO 

192 def datum(self): 

193 '''Get this reference frame's datum (L{Datum}). 

194 ''' 

195 return self._datum 

196 

197 @Property_RO 

198 def ellipsoid(self): 

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

200 ''' 

201 return self._datum.ellipsoid 

202 

203 @Property_RO 

204 def epoch(self): 

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

206 ''' 

207 return self._epoch 

208 

209 def toRefFrame(self, point, reframe2, epoch2=None, epoch=None, name=NN): 

210 '''Convert a cartesian or geodetic point from this to another reframe and epoch. 

211 

212 @return: A copy of the B{C{point}}, converted or renamed. 

213 

214 @see: Ellipsoidal methods L{LatLon.toRefFrame<ellipsoidalBase.LatLonEllipsoidalBase.toRefFrame>} 

215 and L{Cartesian.toRefFrame<ellipsoidalBase.CartesianEllipsoidalBase.toRefFrame>} 

216 for more details. 

217 ''' 

218 b = _MODS.ellipsoidalBase 

219 _xinstanceof(b.LatLonEllipsoidalBase, b.CartesianEllipsoidalBase, point=point) 

220 r = point.dup(reframe=self) 

221 return r.toRefFrame(reframe2, epoch2=epoch2, epoch=epoch, name=name or self.name) 

222 

223 def toStr(self, epoch=None, name=NN, **unused): # PYCHOK expected 

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

225 

226 @kwarg epoch: Override this reframe's epoch (C{scalar} or C{str}). 

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

228 reframe's name. 

229 

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

231 ''' 

232 D = self.datum 

233 e = self.epoch if epoch is None else Epoch(epoch) 

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

235 Fmt.EQUAL(epoch=e), 

236 Fmt.EQUAL(datum=_DOT_(classname(D) + _s_, D.name))) 

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

238 

239 

240class RefFrames(_NamedEnum): 

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

242 to accommodate the L{_LazyNamedEnumItem} properties. 

243 ''' 

244 def _Lazy(self, epoch, datum=_GRS80, name=NN): 

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

246 ''' 

247 return RefFrame(epoch, datum, name=name) 

248 

249RefFrames = RefFrames(RefFrame) # PYCHOK singleton 

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

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

252RefFrames._assert( 

253 ETRF88 = _lazy(_ETRF88_, _F(1988)), # epoch? 

254 ETRF89 = _lazy(_ETRF89_, _F(1989)), # epoch? 

255 ETRF90 = _lazy(_ETRF90_, _F(1990)), # epoch? 

256 ETRF91 = _lazy(_ETRF91_, _F(1991)), # epoch? 

257 ETRF92 = _lazy(_ETRF92_, _F(1992)), # epoch? 

258 ETRF93 = _lazy(_ETRF93_, _F(1993)), # epoch? 

259 ETRF94 = _lazy(_ETRF94_, _F(1994)), # epoch? 

260 ETRF96 = _lazy(_ETRF96_, _F(1996)), # epoch? 

261 ETRF97 = _lazy(_ETRF97_, _F(1997)), # epoch? 

262 ETRF2000 = _lazy(_ETRF2000_, _F(2005)), 

263 ETRF2005 = _lazy(_ETRF2005_, _F(2005)), # epoch? 

264 ETRF2008 = _lazy(_ETRF2008_, _F(2008)), # epoch? 

265 ETRF2014 = _lazy(_ETRF2014_, _F(2014)), # epoch? 

266 GDA94 = _lazy(_GDA94_, _F(1994)), # Australia 

267 GDA2020 = _lazy(_GDA2020_, _F(2020)), # Australia 

268 ITRF88 = _lazy(_ITRF88_, _F(1988)), 

269 ITRF89 = _lazy(_ITRF89_, _F(1989)), 

270 ITRF90 = _lazy(_ITRF90_, _F(1988)), 

271 ITRF91 = _lazy(_ITRF91_, _F(1988)), 

272 ITRF92 = _lazy(_ITRF92_, _F(1988)), 

273 ITRF93 = _lazy(_ITRF93_, _F(1988)), 

274 ITRF94 = _lazy(_ITRF94_, _F(1993)), 

275 ITRF96 = _lazy(_ITRF96_, _F(1997)), 

276 ITRF97 = _lazy(_ITRF97_, _F(1997)), 

277 ITRF2000 = _lazy(_ITRF2000_, _F(1997)), 

278 ITRF2005 = _lazy(_ITRF2005_, _F(2000)), 

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

280 ITRF2014 = _lazy(_ITRF2014_, _F(2010)), 

281 ITRF2020 = _lazy(_ITRF2020_, _F(2015)), 

282 NAD83 = _lazy(_NAD83_, _F(1997)), # CORS96 

283 WGS84 = _lazy(_WGS84_, _F(1984), _WGS84), 

284 WGS84g1150 = _lazy(_WGS84g1150_, _F(2001), _WGS84), 

285 WGS84g1674 = _lazy(_WGS84g1674_, _F(2005), _WGS84), 

286 WGS84g1762 = _lazy(_WGS84g1762_, _F(2005), _WGS84)) # same epoch 

287 

288 

289def date2epoch(year, month, day): 

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

291 

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

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

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

295 

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

297 

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

299 

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

301 29 days in February. 

302 ''' 

303 try: 

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

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

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

307 

308 raise ValueError # _invalid_ 

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

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

311 

312 

313def epoch2date(epoch): 

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

315 

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

317 

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

319 

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

321 

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

323 29 days in February. 

324 ''' 

325 e = Epoch(epoch, low=0) 

326 y = int(e) 

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

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

329 if d > n: 

330 d -= n 

331 else: 

332 break 

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

334 

335 

336def _eTsDs4(inst, reframe, epoch, reframe2, epoch2): 

337 '''(INTERNAL) Get epoch, a 0-, 1- or 2-tuple of Helmert L{Transform}s 

338 datum and datum2 to convert B{C{refFrame}} observed at B{C{epoch}} 

339 into B{C{refFrame2}} observed at B{C{epoch2 or epoch}}. 

340 ''' 

341 r = reframe or inst.reframe 

342 if not r: 

343 t = _SPACE_(_DOT_(repr(inst), _reframe_), MISSING) 

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

345 

346 _xinstanceof(RefFrame, reframe2=reframe2, reframe=r) 

347 

348 e1 = Epoch(epoch or inst.epoch or r.epoch) 

349 e2 = e1 if epoch2 is None else Epoch(epoch2=epoch2) 

350 

351 xs = _2Transforms(r.name, e1, reframe2.name, e2) 

352 if xs is None: 

353 t = _SPACE_(RefFrame.__name__, repr(r.name), _AT_, e1, 

354 _to_, repr(reframe2.name), _AT_, e2) 

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

356 

357 return e2, xs, r.datum, reframe2.datum 

358 

359 

360def _intermediate(n1, n2): 

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

362 ''' 

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

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

365 n = f.intersection(t) 

366 return n.pop() if n else NN 

367 

368 

369def _2Transform(n1, n2, e, _Forward_Inverse): 

370 '''(INTERNAL) Combine the TRF C{xform} and C{rates} from a 

371 conversion C{_trfXs[(n1, n2)]} observed at C{B{e}poch} 

372 into a single I{datum} L{Transform}. 

373 

374 @note: Translations are converted from C{millimeter} to C{meter}, 

375 rotations from C{milliarcseconds} to C{arcseconds} and 

376 scale from C{ppb} to C{ppM}. 

377 ''' 

378 X = _trfXs[(n1, n2)] 

379 e -= X.epoch # delta in fractional years 

380 d = dict((n, (x + e * r) * _Forward_Inverse) for n, x, r in 

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

382 return Transform(**d) 

383 

384 

385def _2Transforms(n1, e1, n2, e2): 

386 '''(INTERNAL) Get 0-, 1- or 2-tuple of Helmert L{Transform}s or C{None}. 

387 ''' 

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

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

390 return () # PYCHOK returns 

391 if (n1, n2) in _trfXs: 

392 return (_2Transform(n1, n2, e1, _Forward), # PYCHOK returns 

393 _2Transform(n1, n2, e2, _Forward)) if e2 != e1 else \ 

394 (_2Transform(n1, n2, e2, _Forward),) 

395 if (n2, n1) in _trfXs: 

396 return (_2Transform(n2, n1, e1, _Inverse), # PYCHOK returns 

397 _2Transform(n2, n1, e2, _Inverse)) if e2 != e1 else \ 

398 (_2Transform(n2, n1, e2, _Inverse),) 

399 n = _intermediate(n1, n2) 

400 if n: 

401 return (_2Transform(n1, n, e1, _Forward), # PYCHOK returns 

402 _2Transform(n, n2, e2, _Forward)) 

403 n = _intermediate(n2, n1) 

404 if n: 

405 return (_2Transform(n, n1, e1, _Inverse), # PYCHOK returns 

406 _2Transform(n2, n, e2, _Inverse)) 

407 return None 

408 

409 

410def trfTransforms(reframe, epoch, reframe2, epoch2): 

411 '''Get the L{Transform}(s) to convert reframe at epoch to reframe2 at epoch2. 

412 

413 @arg reframe: Reference frame to convert I{from} (L{RefFrame} or C{str}). 

414 @arg epoch: Epoch to observe I{from} (L{Epoch}, C{scalar} or C{str}). 

415 @arg reframe2: Reference frame to convert I{to} (L{RefFrame} or C{str}). 

416 @arg epoch2: Epoch to observe to observe I{to} (L{Epoch}, C{scalar} or C{str}). 

417 

418 @return: A 0-, 1- or 2-tuple of Helmert L{Transform}s or C{None} if no 

419 conversion exists. 

420 ''' 

421 _xinstanceof(RefFrame, str, reframe=reframe, reframe2=reframe2) 

422 n1 = reframe.upper() if isstr(reframe) else reframe.name 

423 n2 = reframe2.upper() if isstr(reframe2) else reframe2.name 

424 return _2Transforms(n1, Epoch(epoch), n2, Epoch(epoch2=epoch2)) 

425 

426 

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

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

429 

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

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

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

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

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

435 @kwarg rates: Helmert I{rate} parameters (C{Helmert7Tuple}), like 

436 B{C{xform}}, but in C{units per year}. 

437 

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

439 ''' 

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

441 e = reframe2.epoch if epoch is None else Epoch(epoch=epoch) 

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

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

444 

445 

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

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

448 ''' 

449 n1_n2 = n1, n2 

450 if n1_n2 in _trfXs: 

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

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

453 

454 

455def _H(*ps): 

456 h = Helmert7Tuple(*ps) 

457 return _Hs.setdefault(h, h) # PYCHOK del 

458 

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

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

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

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

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

464_Hs = dict() # PYCHOK unique HelmertTuples, temporary 

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

466_trfX(_ITRF2020_, _ITRF2014_, epoch=_F(2015), # <https://ITRF.IGN.Fr/docs/solutions/itrf2020/Transfo-ITRF2020_TRFs.txt> 

467 xform=_H( -1.4, -0.9, 1.4, -0.42, _0_0, _0_0, _0_0), 

468 rates=_H( _0_0, -0.1, 0.2, _0_0, _0_0, _0_0, _0_0)) 

469_trfX(_ITRF2020_, _ITRF2008_, epoch=_F(2015), 

470 xform=_H( 0.2, 1.0, 3.3, -0.29, _0_0, _0_0, _0_0), 

471 rates=_H( _0_0, -0.1, 0.1, 0.03, _0_0, _0_0, _0_0)) 

472_trfX(_ITRF2020_, _ITRF2005_, epoch=_F(2015), 

473 xform=_H( 2.7, 0.1, -1.4, 0.65, _0_0, _0_0, _0_0), 

474 rates=_H( 0.3, -0.1, 0.1, 0.03, _0_0, _0_0, _0_0)) 

475_trfX(_ITRF2020_, _ITRF2000_, epoch=_F(2015), 

476 xform=_H( -0.2, 0.8, -34.2, 2.25, _0_0, _0_0, _0_0), 

477 rates=_H( 0.1, _0_0, -1.7, 0.11, _0_0, _0_0, _0_0)) 

478_trfX(_ITRF2020_, _ITRF97_, epoch=_F(2015), 

479 xform=_H( 6.5, -3.9, -77.9, 3.98, _0_0, _0_0, 0.36), 

480 rates=_H( 0.1, -0.6, -3.1, 0.12, _0_0, _0_0, 0.02)) 

481_trfX(_ITRF2020_, _ITRF96_, epoch=_F(2015), 

482 xform=_H( 6.5, -3.9, -77.9, 3.98, _0_0, _0_0, 0.36), 

483 rates=_H( 0.1, -0.6, -3.1, 0.12, _0_0, _0_0, 0.02)) 

484_trfX(_ITRF2020_, _ITRF94_, epoch=_F(2015), 

485 xform=_H( 6.5, -3.9, -77.9, 3.98, _0_0, _0_0, 0.36), 

486 rates=_H( 0.1, -0.6, -3.1, 0.12, _0_0, _0_0, 0.02)) 

487_trfX(_ITRF2020_, _ITRF93_, epoch=_F(2015), 

488 xform=_H( -65.8, 1.9, -71.3, 4.47, -3.36, -4.33, 0.75), 

489 rates=_H( -2.8, -0.2, -2.3, 0.12, -0.11, -0.19, 0.07)) 

490_trfX(_ITRF2020_, _ITRF92_, epoch=_F(2015), 

491 xform=_H( 14.5, -1.9, -85.9, 3.27, _0_0, _0_0, 0.36), 

492 rates=_H( 0.1, -0.6, -3.1, 0.12, _0_0, _0_0, 0.02)) 

493_trfX(_ITRF2020_, _ITRF91_, epoch=_F(2015), 

494 xform=_H( 26.5, 12.1, -91.9, 4.67, _0_0, _0_0, 0.36), 

495 rates=_H( 0.1, -0.6, -3.1, 0.12, _0_0, _0_0, 0.02)) 

496_trfX(_ITRF2020_, _ITRF90_, epoch=_F(2015), 

497 xform=_H( 24.5, 8.1, -107.9, 4.97, _0_0, _0_0, 0.36), 

498 rates=_H( 0.1, -0.6, -3.1, 0.12, _0_0, _0_0, 0.02)) 

499_trfX(_ITRF2020_, _ITRF89_, epoch=_F(2015), 

500 xform=_H( 29.5, 32.1, -145.9, 8.37, _0_0, _0_0, 0.36), 

501 rates=_H( 0.1, -0.6, -3.1, 0.12, _0_0, _0_0, 0.02)) 

502_trfX(_ITRF2020_, _ITRF88_, epoch=_F(2015), 

503 xform=_H( 24.5, -3.9, -169.9, 11.47, 0.1, _0_0, 0.36), 

504 rates=_H( 0.1, -0.6, -3.1, 0.12, _0_0, _0_0, 0.02)) 

505 

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

507# Altamimi, Z. U{"EUREF Technical Note 1: Relationship and Transformation between the International and 

508# the European Terrestrial Reference Systems"<https://ERTS89.ENSG,IFN.Fr/pub/EUREF-TN-1.pdf>} Appendix A. 

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

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

511 rates=_H( _0_0, _0_0, -0.1, 0.03, _0_0, _0_0, _0_0)) 

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

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

514 rates=_H( 0.3, _0_0, -0.1, 0.03, _0_0, _0_0, _0_0)) 

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

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

517 rates=_H( 0.1, 0.1, -1.9, 0.11, _0_0, _0_0, _0_0)) 

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

519 xform=_H( 7.4, -0.5, -62.8, 3.8, _0_0, _0_0, 0.26), 

520 rates=_H( 0.1, -0.5, -3.3, 0.12, _0_0, _0_0, 0.02)) 

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

522 xform=_H( 7.4, -0.5, -62.8, 3.8, _0_0, _0_0, 0.26), 

523 rates=_H( 0.1, -0.5, -3.3, 0.12, _0_0, _0_0, 0.02)) 

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

525 xform=_H( 7.4, -0.5, -62.8, 3.8, _0_0, _0_0, 0.26), 

526 rates=_H( 0.1, -0.5, -3.3, 0.12, _0_0, _0_0, 0.02)) 

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

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

529 rates=_H( -2.8, -0.1, -2.5, 0.12, -0.11, -0.19, 0.07)) 

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

531 xform=_H( 15.4, 1.5, -70.8, 3.09, _0_0, _0_0, 0.26), 

532 rates=_H( 0.1, -0.5, -3.3, 0.12, _0_0, _0_0, 0.02)) 

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

534 xform=_H( 27.4, 15.5, -76.8, 4.49, _0_0, _0_0, 0.26), 

535 rates=_H( 0.1, -0.5, -3.3, 0.12, _0_0, _0_0, 0.02)) 

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

537 xform=_H( 25.4, 11.5, -92.8, 4.79, _0_0, _0_0, 0.26), 

538 rates=_H( 0.1, -0.5, -3.3, 0.12, _0_0, _0_0, 0.02)) 

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

540 xform=_H( 30.4, 35.5, -130.8, 8.19, _0_0, _0_0, 0.26), 

541 rates=_H( 0.1, -0.5, -3.3, 0.12, _0_0, _0_0, 0.02)) 

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

543 xform=_H( 25.4, -0.5, -154.8, 11.29, 0.1, _0_0, 0.26), 

544 rates=_H( 0.1, -0.5, -3.3, 0.12, _0_0, _0_0, 0.02)) 

545 

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

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

548# xform=_H( -0.5, s -0.9, -4.7, 0.94, _0_0, _0_0, _0_0), 

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

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

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

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

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

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

555 rates=_H( 0.1, 0.1, -1.8, 0.08, _0_0, _0_0, _0_0)) 

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

557 xform=_H( 4.8, 2.6, -33.2, 2.92, _0_0, _0_0, 0.06), 

558 rates=_H( 0.1, -0.5, -3.2, 0.09, _0_0, _0_0, 0.02)) 

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

560 xform=_H( 4.8, 2.6, -33.2, 2.92, _0_0, _0_0, 0.06), 

561 rates=_H( 0.1, -0.5, -3.2, 0.09, _0_0, _0_0, 0.02)) 

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

563 xform=_H( 4.8, 2.6, -33.2, 2.92, _0_0, _0_0, 0.06), 

564 rates=_H( 0.1, -0.5, -3.2, 0.09, _0_0, _0_0, 0.02)) 

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

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

567 rates=_H( -2.8, -0.1, -2.4, 0.09, -0.11, -0.19, 0.07)) 

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

569 xform=_H( 12.8, 4.6, -41.2, 2.21, _0_0, _0_0, 0.06), 

570 rates=_H( 0.1, -0.5, -3.2, 0.09, _0_0, _0_0, 0.02)) 

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

572 xform=_H( 24.8, 18.6, -47.2, 3.61, _0_0, _0_0, 0.06), 

573 rates=_H( 0.1, -0.5, -3.2, 0.09, _0_0, _0_0, 0.02)) 

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

575 xform=_H( 22.8, 14.6, -63.2, 3.91, _0_0, _0_0, 0.06), 

576 rates=_H( 0.1, -0.5, -3.2, 0.09, _0_0, _0_0, 0.02)) 

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

578 xform=_H( 27.8, 38.6, -101.2, 7.31, _0_0, _0_0, 0.06), 

579 rates=_H( 0.1, -0.5, -3.2, 0.09, _0_0, _0_0, 0.02)) 

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

581 xform=_H( 22.8, 2.6, -125.2, 10.41, 0.1, _0_0, 0.06), 

582 rates=_H( 0.1, -0.5, -3.2, 0.09, _0_0, _0_0, 0.02)) 

583 

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

585 xform=_H( 0.1, -0.8, -5.8, 0.4, _0_0, _0_0, _0_0), 

586 rates=_H( -0.2, 0.1, -1.8, 0.08, _0_0, _0_0, _0_0)) 

587 

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

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

590 rates=_H( _0_0, -0.06, -0.14, 0.01, _0_0, _0_0, 0.02)) 

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

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

593 rates=_H( _0_0, -0.06, -0.14, 0.01, _0_0, _0_0, 0.02)) 

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

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

596 rates=_H( _0_0, -0.06, -0.14, 0.01, _0_0, _0_0, 0.02)) 

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

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

599 rates=_H( -2.9, -0.2, -0.6, 0.01, -0.11, -0.19, 0.07)) 

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

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

602 rates=_H( _0_0, -0.06, -0.14, 0.01, _0_0, _0_0, 0.02)) 

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

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

605 rates=_H( _0_0, -0.6, -1.4, 0.01, _0_0, _0_0, 0.02)) 

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

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

608 rates=_H( _0_0, -0.06, -0.14, 0.01, _0_0, _0_0, 0.02)) 

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

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

611 rates=_H( _0_0, -0.06, -0.14, 0.01, _0_0, _0_0, 0.02)) 

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

613 xform=_H( 2.47, 1.15, -9.79, 8.95, 0.1, _0_0, -0.18), 

614 rates=_H( _0_0, -0.06, -0.14, 0.01, _0_0, _0_0, 0.02)) 

615 

616# see Altamimi, Z. U{"EUREF Technical Note 1: Relationship and Transformation between the International and 

617# the European Terrestrial Reference Systems"<https://ERTS89.ENSG,IFN.Fr/pub/EUREF-TN-1.pdf>} Table 1. 

618# _trfX(_ITRF2014_, _ETRF2014_, epoch=_F(1989), 

619# xform=_H( _0_0, _0_0, _0_0, _0_0, _0_0, _0_0, _0_0), 

620# rates=_H( _0_0, _0_0, _0_0, _0_0, 0.085, 0.531, -0.77)) 

621_trfX(_ITRF2005_, _ETRF2005_, epoch=_F(1989), 

622 xform=_H( 56.0, 48.0, -37.0, _0_0, _0_0, _0_0, _0_0), 

623 rates=_H( _0_0, _0_0, _0_0, _0_0, 0.054, 0.518, -0.781)) 

624# _trfX(_ITRF2000_, _ETRF2000_, epoch=_F(1989), 

625# xform=_H( 54.0, 51.0, -48.0, _0_0, _0_0, _0_0, _0_0), 

626# rates=_H( _0_0, _0_0, _0_0, _0_0, 0.081, 0.49, -0.792)) 

627_trfX(_ITRF97_, _ETRF97_, epoch=_F(1989), 

628 xform=_H( 41.0, 41.0, -49.0, _0_0, _0_0, _0_0, _0_0), 

629 rates=_H( _0_0, _0_0, _0_0, _0_0, 0.2, 0.5, -0.65)) 

630_trfX(_ITRF96_, _ETRF96_, epoch=_F(1989), 

631 xform=_H( 41.0, 41.0, -49.0, _0_0, _0_0, _0_0, _0_0), 

632 rates=_H( _0_0, _0_0, _0_0, _0_0, 0.2, 0.5, -0.65)) 

633_trfX(_ITRF94_, _ETRF94_, epoch=_F(1989), 

634 xform=_H( 41.0, 41.0, -49.0, _0_0, _0_0, _0_0, _0_0), 

635 rates=_H( _0_0, _0_0, _0_0, _0_0, 0.2, 0.5, -0.65)) 

636_trfX(_ITRF93_, _ETRF93_, epoch=_F(1989), 

637 xform=_H( 19.0, 53.0, -21.0, _0_0, _0_0, _0_0, _0_0), 

638 rates=_H( _0_0, _0_0, _0_0, _0_0, 0.32, 0.78, -0.67)) 

639_trfX(_ITRF92_, _ETRF92_, epoch=_F(1989), 

640 xform=_H( 38.0, 40.0, -37.0, 0.0, 0.0, 0.0, 0.0), 

641 rates=_H( _0_0, _0_0, _0_0, _0_0, 0.21, 0.52, -0.68)) 

642_trfX(_ITRF91_, _ETRF91_, epoch=_F(1989), 

643 xform=_H( 21.0, 25.0, -37.0, _0_0, _0_0, _0_0, _0_0), 

644 rates=_H( _0_0, _0_0, _0_0, _0_0, 0.21, 0.52, -0.68)) 

645_trfX(_ITRF90_, _ETRF90_, epoch=_F(1989), 

646 xform=_H( 19.0, 28.0, -23.0, _0_0, _0_0, _0_0, _0_0), 

647 rates=_H( _0_0, _0_0, _0_0, _0_0, 0.11, 0.57, -0.71)) 

648_trfX(_ITRF89_, _ETRF89_, epoch=_F(1989), 

649 xform=_H( _0_0, _0_0, _0_0, _0_0, _0_0, _0_0, _0_0), 

650 rates=_H( _0_0, _0_0, _0_0, _0_0, 0.11, 0.57, -0.71)) 

651 

652# see Altamimi, Z. U{"EUREF Technical Note 1: Relationship and Transformation between the International and 

653# the European Terrestrial Reference Systems"<https://ERTS89.ENSG,IFN.Fr/pub/EUREF-TN-1.pdf>} Table 2. 

654_trfX(_ITRF2014_, _ETRF2014_, epoch=_F(2010), 

655 xform=_H( _0_0, _0_0, _0_0, _0_0, 1.785, 11.151, -16.17), 

656 rates=_H( _0_0, _0_0, _0_0, _0_0, 0.085, 0.531, -0.77)) 

657_trfX(_ITRF2008_, _ETRF2014_, epoch=_F(2010), 

658 xform=_H( -1.6, -1.9, -2.4, 0.02, 1.785, 11.151, -16.17), 

659 rates=_H( _0_0, _0_0, 0.1, -0.03, 0.085, 0.531, -0.77)) 

660_trfX(_ITRF2005_, _ETRF2014_, epoch=_F(2010), 

661 xform=_H( -2.6, -1.0, 2.3, -0.92, 1.785, 11.151, -16.17), 

662 rates=_H( -0.3, _0_0, 0.1, -0.03, 0.085, 0.531, -0.77)) 

663_trfX(_ITRF2000_, _ETRF2014_, epoch=_F(2010), 

664 xform=_H( -0.7, -1.2, 26.1, -2.12, 1.785, 11.151, -16.17), 

665 rates=_H( -0.1, -0.1, 1.9, -0.11, 0.085, 0.531, -0.77)) 

666_trfX(_ITRF97_, _ETRF2014_, epoch=_F(2010), 

667 xform=_H( -7.4, 0.5, 62.8, -3.8, 1.785, 11.151, -16.43), 

668 rates=_H( -0.1, 0.5, 3.3, -0.12, 0.085, 0.531, -0.79)) 

669_trfX(_ITRF96_, _ETRF2014_, epoch=_F(2010), 

670 xform=_H( -7.4, 0.5, 62.8, -3.8, 1.785, 11.151, -16.43), 

671 rates=_H( -0.1, 0.5, 3.3, -0.12, 0.085, 0.531, -0.79)) 

672_trfX(_ITRF94_, _ETRF2014_, epoch=_F(2010), 

673 xform=_H( -7.4, 0.5, 62.8, -3.8, 1.785, 11.151, -16.43), 

674 rates=_H( -0.1, 0.5, 3.3, -0.12, 0.085, 0.531, -0.79)) 

675_trfX(_ITRF93_, _ETRF2014_, epoch=_F(2010), 

676 xform=_H( 50.4, -3.3, 60.2, -4.29, 4.595, 14.531, -16.57), 

677 rates=_H( 2.8, 0.1, 2.5, -0.12, 0.195, 0.721, -0.84)) 

678_trfX(_ITRF92_, _ETRF2014_, epoch=_F(2010), 

679 xform=_H( -15.4, -1.5, 70.8, -3.09, 1.785, 11.151, -16.43), 

680 rates=_H( -0.1, 0.5, 3.3, -0.12, 0.085, 0.531, -0.79)) 

681_trfX(_ITRF91_, _ETRF2014_, epoch=_F(2010), 

682 xform=_H( -27.4, -15.5, 76.8, -4.49, 1.785, 11.151, -16.43), 

683 rates=_H( -0.1, 0.5, 3.3, -0.12, 0.085, 0.531, -0.79)) 

684_trfX(_ITRF90_, _ETRF2014_, epoch=_F(2010), 

685 xform=_H( -25.4, -11.5, 92.8, -4.79, 1.785, 11.151, -16.43), 

686 rates=_H( -0.1, 0.5, 3.3, -0.12, 0.085, 0.531, -0.79)) 

687_trfX(_ITRF89_, _ETRF2014_, epoch=_F(2010), 

688 xform=_H( -30.4, -35.5, 130.8, -8.19, 1.785, 11.151, -16.43), 

689 rates=_H( -0.1, 0.5, 3.3, -0.12, 0.085, 0.531, -0.79)) 

690 

691# see U{Altamimi, Z. "EUREF Technical Note 1: Relationship and Transformation between the International and 

692# the European Terrestrial Reference Systems"<https://ERTS89.ENSG,IFN.Fr/pub/EUREF-TN-1.pdf>} Table 3, 

693# U{Boucher, C. & Altamimi, Z. "Memo: Specifications for reference frame fixing in the analysis of a EUREF GPS 

694# campaign" (2011) <https://ETRS89.ENSG.IGN.Fr/memo-V8.pdf>} and U{Altamimi, Z. "Key results of ITRF2014 and 

695# implication to ETRS89 realization", EUREF2016<https://www.EUREF.EU/symposia/2016SanSebastian/01-02-Altamimi.pdf>}. 

696# _trfX(_ITRF2014_, _ETRF2000_, epoch=_F(2000), 

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

698# rates=_H( 0.1, 0.1, -1.9, 0.11, 0.081, 0.49, -0.792)) 

699_trfX(_ITRF2014_, _ETRF2000_, epoch=_F(2010), 

700 xform=_H( 54.7, 52.2, -74.1, 2.12, 1.701, 10.29, -16.632), 

701 rates=_H( 0.1, 0.1, -1.9, 0.11, 0.081, 0.49, -0.792)) 

702# _trfX(_ITRF2008_, _ETRF2000_, epoch=_F(2000), 

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

704# rates=_H( 0.1, 0.1, -1.8, 0.08, 0.081, 0.49, -0.792)) 

705_trfX(_ITRF2008_, _ETRF2000_, epoch=_F(2010), 

706 xform=_H( 53.1, 50.3, -76.5, 2.14, 1.701, 10.29, -16.632), 

707 rates=_H( 0.1, 0.1, -1.8, 0.08, 0.081, 0.49, -0.792)) 

708# _trfX(_ITRF2005_, _ETRF2000_, epoch=_F(2000), 

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

710# rates=_H( -0.2, 0.1, -1.8, 0.08, 0.081, 0.49, -0.792)) 

711_trfX(_ITRF2005_, _ETRF2000_, epoch=_F(2010), 

712 xform=_H( 52.1, 51.2, -71.8, 1.2, 1.701, 10.29, -16.632), 

713 rates=_H( -0.2, 0.1, -1.8, 0.08, 0.081, 0.49, -0.792)) 

714# _trfX(_ITRF2000_, _ETRF2000_, epoch=_F(2000), 

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

716# rates=_H( _0_0, _0_0, _0_0, _0_0, 0.081, 0.49, -0.792)) 

717_trfX(_ITRF2000_, _ETRF2000_, epoch=_F(2010), 

718 xform=_H( 54.0, 51.0, -48.0, _0_0, 1.701, 10.29, -16.632), 

719 rates=_H( _0_0, _0_0, _0_0, _0_0, 0.081, 0.49, -0.812)) 

720_trfX(_ITRF97_, _ETRF2000_, epoch=_F(2010), 

721 xform=_H( 54.0, 51.0, -48.0, -1.68, 1.701, 10.29, -16.892), 

722 rates=_H( _0_0, 0.6, 1.4, -0.01, 0.081, 0.49, -0.812)) 

723_trfX(_ITRF96_, _ETRF2000_, epoch=_F(2010), 

724 xform=_H( 54.0, 51.0, -48.0, -1.68, 1.701, 10.29, -16.892), 

725 rates=_H( _0_0, 0.6, 1.4, -0.01, 0.081, 0.49, -0.812)) 

726_trfX(_ITRF94_, _ETRF2000_, epoch=_F(2010), 

727 xform=_H( 47.3, 52.7, -11.3, -1.68, 1.701, 10.29, -16.892), 

728 rates=_H( _0_0, 0.6, 1.4, -0.01, 0.081, 0.49, -0.812)) 

729_trfX(_ITRF93_, _ETRF2000_, epoch=_F(2010), 

730 xform=_H( 105.1, 48.9, -13.9, -2.17, 4.511, 13.67, -17.032), 

731 rates=_H( 2.9, 0.2, 0.6, -0.01, 0.191, 0.68, -0.862)) 

732_trfX(_ITRF92_, _ETRF2000_, epoch=_F(2010), 

733 xform=_H( 39.3, 50.7, -3.3, -0.97, 1.701, 10.29, -16.892), 

734 rates=_H( _0_0, 0.6, 1.4, -0.01, 0.081, 0.49, -0.812)) 

735_trfX(_ITRF91_, _ETRF2000_, epoch=_F(2010), 

736 xform=_H( 27.3, 36.7, 2.7, -2.37, 1.701, 10.29, -16.892), 

737 rates=_H( _0_0, 0.6, 1.4, -0.01, 0.081, 0.49, -0.812)) 

738_trfX(_ITRF90_, _ETRF2000_, epoch=_F(2010), 

739 xform=_H( 29.3, 40.7, 18.7, -2.67, 1.701, 10.29, -16.892), 

740 rates=_H( _0_0, 0.6, 1.4, -0.01, 0.081, 0.49, -0.812)) 

741_trfX(_ITRF89_, _ETRF2000_, epoch=_F(2010), 

742 xform=_H( 24.3, 16.7, 56.7, -6.07, 1.701, 10.29, -16.892), 

743 rates=_H( _0_0, 0.6, 1.4, -0.01, 0.081, 0.49, -0.812)) 

744 

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

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

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

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

749 xform=_H( _0_0, _0_0, _0_0, _0_0, _0_0, _0_0, _0_0), 

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

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

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

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

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

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

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

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

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

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

760 

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

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

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

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

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

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

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

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

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

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

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

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

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

774 

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

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

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

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

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

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

781 

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

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

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

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

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

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

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

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

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

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

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

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

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

795 rates=_H( _0_0, _0_0, _0_0, _0_0, _0_0, _0_0, _0_0)) 

796del _H, _Hs 

797 

798if __name__ == '__main__': 

799 

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

801 from pygeodesy.lazily import printf 

802 from time import localtime 

803 

804 D = date2epoch.__name__ 

805 E = epoch2date.__name__ 

806 y = localtime()[0] 

807 for m in range(1, 13): 

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

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

810 e = date2epoch(y, m, d) 

811 t = epoch2date(e) 

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

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

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

815 t = '%d,%3d,%3d' % t 

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

817 

818 # __doc__ of this file, force all into registery 

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

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

821 

822# **) MIT License 

823# 

824# Copyright (C) 2016-2024 -- mrJean1 at Gmail -- All Rights Reserved. 

825# 

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

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

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

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

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

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

832# 

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

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

835# 

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

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

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

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

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

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

842# OTHER DEALINGS IN THE SOFTWARE. 

843 

844# % python -m pygeodesy.trf 

845# 

846# date2epoch(2024, 1, 1) = 2024.003, epoch2date(2024.003) = 2024, 1, 1 

847# date2epoch(2024, 1, 15) = 2024.041, epoch2date(2024.041) = 2024, 1, 15 

848# date2epoch(2024, 1, 30) = 2024.082, epoch2date(2024.082) = 2024, 1, 30 

849# date2epoch(2024, 1, 31) = 2024.085, epoch2date(2024.085) = 2024, 1, 31 

850# date2epoch(2024, 2, 1) = 2024.087, epoch2date(2024.087) = 2024, 2, 2 * 

851# date2epoch(2024, 2, 15) = 2024.126, epoch2date(2024.126) = 2024, 2, 16 * 

852# date2epoch(2024, 2, 28) = 2024.161, epoch2date(2024.161) = 2024, 2, 28 

853# date2epoch(2024, 2, 29) = 2024.164, epoch2date(2024.164) = 2024, 3, 1 * 

854# date2epoch(2024, 3, 1) = 2024.167, epoch2date(2024.167) = 2024, 3, 2 * 

855# date2epoch(2024, 3, 15) = 2024.205, epoch2date(2024.205) = 2024, 3, 16 * 

856# date2epoch(2024, 3, 30) = 2024.246, epoch2date(2024.246) = 2024, 3, 31 * 

857# date2epoch(2024, 3, 31) = 2024.249, epoch2date(2024.249) = 2024, 4, 1 * 

858# date2epoch(2024, 4, 1) = 2024.251, epoch2date(2024.251) = 2024, 4, 1 

859# date2epoch(2024, 4, 15) = 2024.290, epoch2date(2024.290) = 2024, 4, 15 

860# date2epoch(2024, 4, 29) = 2024.328, epoch2date(2024.328) = 2024, 4, 29 

861# date2epoch(2024, 4, 30) = 2024.331, epoch2date(2024.331) = 2024, 4, 30 

862# date2epoch(2024, 5, 1) = 2024.333, epoch2date(2024.333) = 2024, 5, 1 

863# date2epoch(2024, 5, 15) = 2024.372, epoch2date(2024.372) = 2024, 5, 15 

864# date2epoch(2024, 5, 30) = 2024.413, epoch2date(2024.413) = 2024, 5, 30 

865# date2epoch(2024, 5, 31) = 2024.415, epoch2date(2024.415) = 2024, 6, 1 * 

866# date2epoch(2024, 6, 1) = 2024.418, epoch2date(2024.418) = 2024, 6, 2 * 

867# date2epoch(2024, 6, 15) = 2024.456, epoch2date(2024.456) = 2024, 6, 16 * 

868# date2epoch(2024, 6, 29) = 2024.495, epoch2date(2024.495) = 2024, 6, 30 * 

869# date2epoch(2024, 6, 30) = 2024.497, epoch2date(2024.497) = 2024, 7, 1 * 

870# date2epoch(2024, 7, 1) = 2024.500, epoch2date(2024.500) = 2024, 7, 1 

871# date2epoch(2024, 7, 15) = 2024.538, epoch2date(2024.538) = 2024, 7, 16 * 

872# date2epoch(2024, 7, 30) = 2024.579, epoch2date(2024.579) = 2024, 7, 30 

873# date2epoch(2024, 7, 31) = 2024.582, epoch2date(2024.582) = 2024, 7, 31 

874# date2epoch(2024, 8, 1) = 2024.585, epoch2date(2024.585) = 2024, 8, 1 

875# date2epoch(2024, 8, 15) = 2024.623, epoch2date(2024.623) = 2024, 8, 15 

876# date2epoch(2024, 8, 30) = 2024.664, epoch2date(2024.664) = 2024, 8, 31 * 

877# date2epoch(2024, 8, 31) = 2024.667, epoch2date(2024.667) = 2024, 9, 1 * 

878# date2epoch(2024, 9, 1) = 2024.669, epoch2date(2024.669) = 2024, 9, 2 * 

879# date2epoch(2024, 9, 15) = 2024.708, epoch2date(2024.708) = 2024, 9, 16 * 

880# date2epoch(2024, 9, 29) = 2024.746, epoch2date(2024.746) = 2024, 9, 30 * 

881# date2epoch(2024, 9, 30) = 2024.749, epoch2date(2024.749) = 2024, 10, 1 * 

882# date2epoch(2024, 10, 1) = 2024.751, epoch2date(2024.751) = 2024, 10, 1 

883# date2epoch(2024, 10, 15) = 2024.790, epoch2date(2024.790) = 2024, 10, 15 

884# date2epoch(2024, 10, 30) = 2024.831, epoch2date(2024.831) = 2024, 10, 30 

885# date2epoch(2024, 10, 31) = 2024.833, epoch2date(2024.833) = 2024, 10, 31 

886# date2epoch(2024, 11, 1) = 2024.836, epoch2date(2024.836) = 2024, 11, 1 

887# date2epoch(2024, 11, 15) = 2024.874, epoch2date(2024.874) = 2024, 11, 15 

888# date2epoch(2024, 11, 29) = 2024.913, epoch2date(2024.913) = 2024, 11, 29 

889# date2epoch(2024, 11, 30) = 2024.915, epoch2date(2024.915) = 2024, 12, 1 * 

890# date2epoch(2024, 12, 1) = 2024.918, epoch2date(2024.918) = 2024, 12, 2 * 

891# date2epoch(2024, 12, 15) = 2024.956, epoch2date(2024.956) = 2024, 12, 16 * 

892# date2epoch(2024, 12, 30) = 2024.997, epoch2date(2024.997) = 2024, 12, 31 * 

893# date2epoch(2024, 12, 31) = 2025.000, epoch2date(2025.000) = 2025, 1, 1 *