iers 0.0.1 → 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: fc31ab2d8d7c623f16a33f1092ae9e0713ab9a9f785d0c86b4ffec022d2ab779
4
- data.tar.gz: 5a1d2262a36a36d488f553f66ef5e7a79053aa4c319dbc678cb61d672f00f7a8
3
+ metadata.gz: 4977c2dce8a969f0590cabc8c9c2a992e946b14d5def3fd9e7b6635c76a5cd83
4
+ data.tar.gz: fd23f09386c87acbc6f4b25207d023198a41c9f55502c341859188496fc6f66b
5
5
  SHA512:
6
- metadata.gz: b669b6e70cb16c915d0c23323a900306164c3da7c7b7c3abe8d9863872c0fdd4b89860fa6d84e0c85b6ad47002c0b9bb35eb5c0166ec6d0b096f9b23a63941df
7
- data.tar.gz: 13f68ed59f48f60de2ce141cf630d4086c565e482062025bf6c104cb31f3167097975ae0f0c036c94a553103f76e9a70b4d77a58b900552bedde8a9efbb249ee
6
+ metadata.gz: c219975975bd14d24c47c45062868592999f4da0f19d254d73a01676b71b5d881faebd8f73afbb3d3155ef5da341459414846165e4f2540570f78e9487bba435
7
+ data.tar.gz: 0b13b6a3dc0daa6d2012e112662c3605b59de6c4d6b1437f4162912095bb424772cd13fb1c0cddb7cb3e234e85bfd0658ecf63ecf977a8281f063e13da05ceaa
data/.yardopts ADDED
@@ -0,0 +1,3 @@
1
+ --markup markdown
2
+ --no-private
3
+ lib/**/*.rb
data/CHANGELOG.md CHANGED
@@ -1,5 +1,44 @@
1
1
  # Changelog
2
2
 
3
- ## 0.0.1 - 2026-02-24
3
+ ## 0.1.0 - 2026-02-27
4
4
 
5
- - Initial release
5
+ Initial public release.
6
+
7
+ ### Features
8
+
9
+ - **Polar motion** - `PolarMotion.at` returns x/y coordinates with Lagrange or
10
+ linear interpolation; `PolarMotion.rotation_matrix_at` builds the full W
11
+ rotation matrix; `PolarMotion.between` for daily range queries
12
+ - **UT1-UTC** - `UT1.at` with automatic UT1-TAI normalization across leap second
13
+ boundaries; Bulletin B preference over Series A when available
14
+ - **Celestial pole offsets** - `CelestialPoleOffset.at` for dX/dY corrections
15
+ - **Length of day** - `LengthOfDay.at` for LOD excess
16
+ - **Delta T** - `DeltaT.at` for TT - UT1, extended back to 1800 with
17
+ Espenak & Meeus polynomials
18
+ - **Earth Rotation Angle** - `EarthRotationAngle.at` per IERS Conventions 2010
19
+ - **Greenwich Mean Sidereal Time** - `GMST.at` via ERA + polynomial
20
+ - **Terrestrial rotation** - `TerrestrialRotation.at` for the R(ERA) × W matrix
21
+ - **Unified EOP** - `EOP.at` and `EOP.between` composing all individual
22
+ parameters
23
+ - **Leap seconds** - `LeapSecond.at` for TAI-UTC lookup with binary search;
24
+ `LeapSecond.next_scheduled` for the next known transition
25
+ - **TAI utilities** - `TAI.utc_to_tai` and `TAI.tai_to_utc` for UTC↔TAI
26
+ conversion
27
+ - **Bundled data** - ships a `finals2000A.all` and `Leap_Second.dat` snapshot
28
+ for out-of-the-box usage with no network calls required
29
+ - **Data management** - `Data.update!` downloads fresh files with atomic writes
30
+ and redirect handling; `Data.ensure_fresh!` raises `StaleDataError` when
31
+ coverage is insufficient; `DataStatus` reports bundled/cached/custom state
32
+ - **Configuration** - `IERS.configure` for data paths, interpolation method
33
+ (`:lagrange` or `:linear`), Lagrange order, freshness thresholds, and
34
+ download URLs; per-query `interpolation:` override on all `.at` methods
35
+ - **Time inputs** - all query methods accept `Time`, `Date`, `DateTime`,
36
+ Modified Julian Date (float), or Julian Date (float)
37
+ - **Data quality** - every `Entry` exposes `observed?` and `predicted?`
38
+ predicates via the `HasDataQuality` mixin; `date` via `HasDate`
39
+ - **Lazy enumerators** - `between` returns a lazy `Enumerator` for
40
+ memory-efficient iteration over large date ranges
41
+ - **Thread safety** - mutex-protected lazy caching in `Data` and `LeapSecond`
42
+ - **Error hierarchy** - `IERS::Error` base with `ConfigurationError`,
43
+ `DataError`, `DownloadError`, `ParseError`, `FileNotFoundError`,
44
+ `OutOfRangeError`, and `StaleDataError`
data/README.md CHANGED
@@ -5,6 +5,40 @@
5
5
  Access to Earth orientation parameters and time scale values from the
6
6
  International Earth Rotation and Reference Systems Service (IERS).
7
7
 
8
+ ## About IERS and Earth Orientation Parameters
9
+
10
+ The [IERS] is an international service that monitors the irregularities of
11
+ Earth's rotation and orientation in space. Because Earth's rotation is not
12
+ perfectly uniform, precise timekeeping, satellite navigation, and telescope
13
+ pointing all depend on regularly updated measurements.
14
+
15
+ The key quantities tracked by the IERS are known as **Earth Orientation
16
+ Parameters** (EOP):
17
+
18
+ - **Polar motion (x, y)** — the position of Earth's rotational pole relative
19
+ to its crust, expressed in arcseconds. The pole wanders in a roughly circular
20
+ path of a few tenths of an arcsecond over ~14 months (the Chandler wobble).
21
+ - **UT1−UTC** — the difference between astronomical time (UT1, tied to Earth's
22
+ actual rotation angle) and coordinated universal time (UTC, maintained by
23
+ atomic clocks). This difference drifts by up to ~0.9 s before a leap second
24
+ is introduced to keep them close.
25
+ - **Leap seconds** — occasional one-second adjustments applied to UTC so that
26
+ it stays within 0.9 s of UT1. Since 1972, 27 leap seconds have been added.
27
+
28
+ ### Data files
29
+
30
+ This library works with two data files published by the IERS:
31
+
32
+ - **finals2000A** — a daily table spanning from 1973 to the present (plus
33
+ predictions ~1 year ahead). Each row contains polar motion, UT1−UTC, and
34
+ other EOP for one Modified Julian Date. Recent rows carry rapid "Series A"
35
+ values; older rows also include refined "Bulletin B" values.
36
+ - **Leap_Second.dat** — the complete history of leap seconds with their
37
+ effective dates and the cumulative TAI−UTC offset.
38
+
39
+ Both files are downloaded automatically by `IERS::Data.update!` and cached
40
+ locally.
41
+
8
42
  ## Installation
9
43
 
10
44
  Install the gem and add to the application's Gemfile by executing:
@@ -18,7 +52,300 @@ executing:
18
52
 
19
53
  ## Usage
20
54
 
21
- Coming soon.
55
+ ### Bundled data
56
+
57
+ The gem ships with a snapshot of the IERS data files taken at release time, so
58
+ queries work immediately without any download:
59
+
60
+ ```ruby
61
+ require "iers"
62
+
63
+ IERS::UT1.at(Time.utc(2020, 6, 15)) # works out of the box
64
+ ```
65
+
66
+ The bundled snapshot includes predictions roughly one year into the future from
67
+ the release date. As time passes those predictions expire, so you should
68
+ download fresh data periodically:
69
+
70
+ ```ruby
71
+ result = IERS::Data.update!
72
+ result.success? # => true
73
+ ```
74
+
75
+ You can also update a single source:
76
+
77
+ ```ruby
78
+ IERS::Data.update!(:finals)
79
+ IERS::Data.update!(:leap_seconds)
80
+ ```
81
+
82
+ Downloaded files are cached in `~/.cache/iers/` by default and take precedence
83
+ over the bundled snapshot.
84
+
85
+ The bundled data files are sourced from the [IERS Earth Orientation Center] and
86
+ the [USNO Rapid Service/Prediction Center].
87
+
88
+ ### Polar motion
89
+
90
+ Query the pole position at any point in time:
91
+
92
+ ```ruby
93
+ pm = IERS::PolarMotion.at(Time.utc(2020, 6, 15))
94
+ pm.x # => 0.070979... (arcseconds)
95
+ pm.y # => 0.456571... (arcseconds)
96
+ pm.observed? # => true
97
+ ```
98
+
99
+ Retrieve daily grid values over a date range. `between` returns a lazy
100
+ `Enumerator`, so entries are computed on demand:
101
+
102
+ ```ruby
103
+ entries = IERS::PolarMotion.between(
104
+ Date.new(2020, 1, 1),
105
+ Date.new(2020, 1, 31)
106
+ )
107
+ entries.count # => 31
108
+ ```
109
+
110
+ #### Rotation matrix
111
+
112
+ Compute the polar motion rotation matrix W (IERS Conventions 2010, §5.4.1):
113
+
114
+ ```ruby
115
+ w = IERS::PolarMotion.rotation_matrix_at(Time.utc(2020, 6, 15))
116
+ w.length # => 3
117
+ w[0].length # => 3
118
+ ```
119
+
120
+ Returns a nested `Array` (3×3, row-major).
121
+
122
+ The matrix is also available on any `PolarMotion::Entry`:
123
+
124
+ ```ruby
125
+ pm = IERS::PolarMotion.at(Time.utc(2020, 6, 15))
126
+ pm.rotation_matrix # => same nested Array
127
+ ```
128
+
129
+ ### UT1−UTC
130
+
131
+ Query the difference between UT1 and UTC:
132
+
133
+ ```ruby
134
+ entry = IERS::UT1.at(Time.utc(2020, 6, 15))
135
+ entry.ut1_utc # => -0.178182...
136
+ entry.observed? # => true
137
+ ```
138
+
139
+ Daily grid values:
140
+
141
+ ```ruby
142
+ entries = IERS::UT1.between(
143
+ Date.new(2020, 1, 1),
144
+ Date.new(2020, 1, 31)
145
+ )
146
+ ```
147
+
148
+ ### Celestial pole offsets
149
+
150
+ Query the celestial pole offset corrections (dX, dY):
151
+
152
+ ```ruby
153
+ cpo = IERS::CelestialPoleOffset.at(Time.utc(2020, 6, 15))
154
+ cpo.x # => dX correction (milliarcseconds)
155
+ cpo.y # => dY correction (milliarcseconds)
156
+ ```
157
+
158
+ ### Length of day
159
+
160
+ Query the excess length of day:
161
+
162
+ ```ruby
163
+ entry = IERS::LengthOfDay.at(Time.utc(2020, 6, 15))
164
+ entry.length_of_day # => excess LOD (seconds)
165
+ entry.observed? # => true
166
+ ```
167
+
168
+ ### Delta T
169
+
170
+ Compute Delta T (TT − UT1). From 1972 onward the value is derived from IERS
171
+ data; before 1972 (back to 1800) it uses Espenak & Meeus polynomial
172
+ approximations:
173
+
174
+ ```ruby
175
+ entry = IERS::DeltaT.at(Time.utc(2020, 6, 15))
176
+ entry.delta_t # => ~69.36 (seconds)
177
+ entry.measured? # => true
178
+
179
+ entry = IERS::DeltaT.at(Time.utc(1900, 6, 15))
180
+ entry.delta_t # => ~-2.12 (seconds)
181
+ entry.estimated? # => true
182
+ ```
183
+
184
+ ### Earth Rotation Angle
185
+
186
+ Compute ERA (IERS Conventions 2010, eq. 5.15). The UT1-UTC correction is
187
+ looked up internally:
188
+
189
+ ```ruby
190
+ IERS::EarthRotationAngle.at(Time.utc(2020, 6, 15)) # => radians, in [0, 2π)
191
+ ```
192
+
193
+ ### Greenwich Mean Sidereal Time
194
+
195
+ Compute GMST (IERS Conventions 2010, eq. 5.32). Uses ERA internally and adds
196
+ the equinox-based polynomial evaluated at TT:
197
+
198
+ ```ruby
199
+ IERS::GMST.at(Time.utc(2020, 6, 15)) # => radians, in [0, 2π)
200
+ ```
201
+
202
+ ### Terrestrial rotation matrix
203
+
204
+ Compute R(ERA) × W, the rotation from ITRS to TIRS (IERS Conventions 2010,
205
+ eq. 5.1), combining the Earth Rotation Angle and the polar motion matrix (W):
206
+
207
+ ```ruby
208
+ r = IERS::TerrestrialRotation.at(Time.utc(2020, 6, 15))
209
+ r.length # => 3
210
+ r[0].length # => 3
211
+ ```
212
+
213
+ Returns a 3×3 nested `Array` (row-major).
214
+
215
+ ### Earth Orientation Parameters (unified)
216
+
217
+ Query all EOP components at once:
218
+
219
+ ```ruby
220
+ eop = IERS::EOP.at(Time.utc(2020, 6, 15))
221
+ eop.polar_motion_x # => arcseconds
222
+ eop.polar_motion_y # => arcseconds
223
+ eop.ut1_utc # => seconds
224
+ eop.length_of_day # => seconds
225
+ eop.celestial_pole_x # => milliarcseconds
226
+ eop.celestial_pole_y # => milliarcseconds
227
+ eop.observed? # => true
228
+ eop.date # => #<Date: 2020-06-15>
229
+ ```
230
+
231
+ Retrieve daily EOP over a date range:
232
+
233
+ ```ruby
234
+ entries = IERS::EOP.between(
235
+ Date.new(2020, 1, 1),
236
+ Date.new(2020, 1, 31)
237
+ )
238
+ ```
239
+
240
+ ### Leap seconds
241
+
242
+ Look up TAI−UTC at a given date:
243
+
244
+ ```ruby
245
+ IERS::LeapSecond.at(Time.utc(2017, 1, 1)) # => 37.0 (seconds)
246
+ ```
247
+
248
+ List all leap seconds:
249
+
250
+ ```ruby
251
+ IERS::LeapSecond.all
252
+ # => [#<data IERS::LeapSecond::Entry effective_date=#<Date: 1972-01-01>, tai_utc=10.0>, ...]
253
+ ```
254
+
255
+ Check for a future scheduled leap second:
256
+
257
+ ```ruby
258
+ IERS::LeapSecond.next_scheduled # => #<data IERS::LeapSecond::Entry ...> or nil
259
+ ```
260
+
261
+ ### TAI
262
+
263
+ Convert between UTC and TAI time scales:
264
+
265
+ ```ruby
266
+ tai_mjd = IERS::TAI.utc_to_tai(Time.utc(2017, 1, 1)) # => MJD in TAI
267
+ utc_mjd = IERS::TAI.tai_to_utc(mjd: tai_mjd) # => MJD in UTC
268
+ ```
269
+
270
+ ### Time input
271
+
272
+ All query methods accept Ruby `Time`, `Date`, and `DateTime` objects as
273
+ positional arguments. You can also use keyword arguments for numeric Julian
274
+ Dates:
275
+
276
+ ```ruby
277
+ IERS::UT1.at(mjd: 58849.0) # Modified Julian Date
278
+ IERS::UT1.at(jd: 2458849.5) # Julian Date
279
+ IERS::UT1.at(Time.utc(2020, 1, 1)) # Ruby Time
280
+ ```
281
+
282
+ ### Data freshness
283
+
284
+ Check that predictions cover enough of the future before relying on
285
+ query results:
286
+
287
+ ```ruby
288
+ begin
289
+ IERS::Data.ensure_fresh!(coverage_days_ahead: 90)
290
+ rescue IERS::StaleDataError => e
291
+ puts "Predictions end #{e.predicted_until}, need #{e.required_until}"
292
+ IERS::Data.update!
293
+ end
294
+ ```
295
+
296
+ Without `coverage_days_ahead`, the check ensures predictions cover today.
297
+
298
+ ### Data status and cache management
299
+
300
+ ```ruby
301
+ status = IERS::Data.status
302
+ status.cached? # => true if downloaded data exists
303
+ status.cache_age # => age in seconds, or nil
304
+
305
+ IERS::Data.clear_cache! # remove downloaded files
306
+ ```
307
+
308
+ ### Custom data paths
309
+
310
+ Point the gem at your own copies of the IERS data files:
311
+
312
+ ```ruby
313
+ IERS.configure do |config|
314
+ config.finals_path = "/path/to/finals2000A.all"
315
+ config.leap_second_path = "/path/to/Leap_Second.dat"
316
+ end
317
+ ```
318
+
319
+ ### Configuration
320
+
321
+ ```ruby
322
+ IERS.configure do |config|
323
+ config.cache_dir = "/path/to/cache"
324
+ config.interpolation = :linear # default: :lagrange
325
+ config.lagrange_order = 6 # default: 4
326
+ config.download_timeout = 60 # default: 30 (seconds)
327
+ end
328
+ ```
329
+
330
+ To fully reset configuration and cached data:
331
+
332
+ ```ruby
333
+ IERS.reset!
334
+ ```
335
+
336
+ ### Error handling
337
+
338
+ All errors inherit from `IERS::Error`:
339
+
340
+ - `IERS::DataError`: base for data-related errors
341
+ - `IERS::ParseError`: malformed data file
342
+ - `IERS::FileNotFoundError`: data file not found
343
+ - `IERS::StaleDataError`: predictions don't extend far enough
344
+ - `IERS::DownloadError`: base for download-related errors
345
+ - `IERS::NetworkError`: HTTP or connection failure
346
+ - `IERS::ValidationError`: downloaded file failed validation
347
+ - `IERS::OutOfRangeError` — query outside data coverage
348
+ - `IERS::ConfigurationError` — invalid configuration
22
349
 
23
350
  ## Development
24
351
 
@@ -40,6 +367,9 @@ The gem is available as open source under the terms of the [MIT License].
40
367
  Everyone interacting in the IERS Ruby project's codebases, issue trackers, chat
41
368
  rooms and mailing lists is expected to follow the [code of conduct].
42
369
 
370
+ [IERS]: https://www.iers.org
371
+ [IERS Earth Orientation Center]: https://hpiers.obspm.fr/eop-pc/
372
+ [USNO Rapid Service/Prediction Center]: https://maia.usno.navy.mil/ser7/
43
373
  [Bundler]: https://bundler.io
44
374
  [rubygems.org]: https://rubygems.org
45
375
  [MIT License]: https://opensource.org/licenses/MIT
@@ -0,0 +1,41 @@
1
+ # Value of TAI-UTC in second valid beetween the initial value until
2
+ # the epoch given on the next line. The last line reads that NO
3
+ # leap second was introduced since the corresponding date
4
+ # Updated through IERS Bulletin 71 issued in January 2026
5
+ #
6
+ #
7
+ # File expires on 28 December 2026
8
+ #
9
+ #
10
+ # MJD Date TAI-UTC (s)
11
+ # day month year
12
+ # --- -------------- ------
13
+ #
14
+ 41317.0 1 1 1972 10
15
+ 41499.0 1 7 1972 11
16
+ 41683.0 1 1 1973 12
17
+ 42048.0 1 1 1974 13
18
+ 42413.0 1 1 1975 14
19
+ 42778.0 1 1 1976 15
20
+ 43144.0 1 1 1977 16
21
+ 43509.0 1 1 1978 17
22
+ 43874.0 1 1 1979 18
23
+ 44239.0 1 1 1980 19
24
+ 44786.0 1 7 1981 20
25
+ 45151.0 1 7 1982 21
26
+ 45516.0 1 7 1983 22
27
+ 46247.0 1 7 1985 23
28
+ 47161.0 1 1 1988 24
29
+ 47892.0 1 1 1990 25
30
+ 48257.0 1 1 1991 26
31
+ 48804.0 1 7 1992 27
32
+ 49169.0 1 7 1993 28
33
+ 49534.0 1 7 1994 29
34
+ 50083.0 1 1 1996 30
35
+ 50630.0 1 7 1997 31
36
+ 51179.0 1 1 1999 32
37
+ 53736.0 1 1 2006 33
38
+ 54832.0 1 1 2009 34
39
+ 56109.0 1 7 2012 35
40
+ 57204.0 1 7 2015 36
41
+ 57754.0 1 1 2017 37