iers 0.1.1 → 0.2.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 +4 -4
- data/CHANGELOG.md +27 -0
- data/README.md +44 -2
- data/Rakefile +28 -0
- data/lib/iers/data.rb +17 -7
- data/lib/iers/data_status.rb +1 -1
- data/lib/iers/leap_second.rb +32 -5
- data/lib/iers/parsers/finals.rb +1 -1
- data/lib/iers/parsers/leap_second.rb +54 -4
- data/lib/iers/tai_utc_drift.rb +144 -0
- data/lib/iers/update_result.rb +1 -1
- data/lib/iers/version.rb +1 -1
- data/lib/iers.rb +1 -0
- metadata +2 -1
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 4132b4709810d88306f0ea6bde33b1664d1fdbfeef985e0191cba65c98c01f8a
|
|
4
|
+
data.tar.gz: 5e3bef64e668f1320bc9c3876e571bf4bb452ea2df52cd09539303bafbf03ea0
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: c2bd5f8b1d3a088bc9aa8d822013a9c04570cea94c6036f080eb4d34d8ad53ac0346e6547092f5ce95fe61f95df6de32006e59371894e3b77768565be15354a1
|
|
7
|
+
data.tar.gz: 8b32049edecc0955026664f02cbf89a1af9d518050374538cbf1b1c3b08d572509b971d522a1d368490d6d07bcbd90bb807bd9fd5c4223d074431a60583244f5
|
data/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,32 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## 0.2.0 - 2026-07-25
|
|
4
|
+
|
|
5
|
+
### Added
|
|
6
|
+
|
|
7
|
+
- `LeapSecond.expires_on`, `LeapSecond.expired?` and
|
|
8
|
+
`LeapSecond.updated_through` expose the expiry date and IERS bulletin that
|
|
9
|
+
`Leap_Second.dat` states in its header. Both were previously discarded with
|
|
10
|
+
the rest of the comment lines. Unlike `Data.status.cache_age`, which measures
|
|
11
|
+
when a file was downloaded, these describe how long the data itself stays
|
|
12
|
+
authoritative.
|
|
13
|
+
- `LeapSecond.at` now answers 1961-01-01 through 1972-01-01, the era when UTC
|
|
14
|
+
was steered by rate adjustments rather than whole leap seconds. It returns
|
|
15
|
+
those values as exact `Rational`s. The coefficients come from the USNO
|
|
16
|
+
`tai-utc.dat` file and are bundled in source, since they are fixed. The
|
|
17
|
+
change is additive: the pre-1972 range previously raised, so no successful
|
|
18
|
+
call returns anything different. `TAI.utc_to_tai` and `TAI.tai_to_utc` gain
|
|
19
|
+
the same range for free.
|
|
20
|
+
|
|
21
|
+
### Changed
|
|
22
|
+
|
|
23
|
+
- `Parsers::LeapSecond.parse` returns a `Table` of `entries` and `metadata`
|
|
24
|
+
instead of an array of entries. `Parsers` is internal; the public API is
|
|
25
|
+
unaffected.
|
|
26
|
+
- `Parsers::LeapSecond.parse` no longer raises `Encoding::CompatibilityError`
|
|
27
|
+
on a file containing invalid UTF-8. Header metadata is read on a best-effort
|
|
28
|
+
basis and an unreadable data row raises `ParseError` as before.
|
|
29
|
+
|
|
3
30
|
## 0.1.1 - 2026-06-27
|
|
4
31
|
|
|
5
32
|
### Changed
|
data/README.md
CHANGED
|
@@ -242,14 +242,28 @@ entries = IERS::EOP.between(
|
|
|
242
242
|
Look up TAI−UTC at a given date:
|
|
243
243
|
|
|
244
244
|
```ruby
|
|
245
|
-
IERS::LeapSecond.at(Time.utc(2017, 1, 1)) # => 37
|
|
245
|
+
IERS::LeapSecond.at(Time.utc(2017, 1, 1)) # => 37 (seconds)
|
|
246
246
|
```
|
|
247
247
|
|
|
248
|
+
`at` covers 1961-01-01 onward. From 1972 it returns a whole number of seconds
|
|
249
|
+
as an `Integer`. Between 1961 and 1972, UTC was steered by small rate
|
|
250
|
+
adjustments rather than whole leap seconds, so TAI−UTC was a fraction of a
|
|
251
|
+
second that changed daily. `at` returns those values as exact `Rational`s:
|
|
252
|
+
|
|
253
|
+
```ruby
|
|
254
|
+
IERS::LeapSecond.at(mjd: 38_900) # => (1910137/500000)
|
|
255
|
+
IERS::LeapSecond.at(mjd: 38_900).to_f # => 3.820274 (seconds, 1965-05-20)
|
|
256
|
+
```
|
|
257
|
+
|
|
258
|
+
The coefficients for that era are fixed and will never change, so they are
|
|
259
|
+
bundled in the gem rather than downloaded. Anything before 1961-01-01 has no
|
|
260
|
+
published UTC and raises `OutOfRangeError`.
|
|
261
|
+
|
|
248
262
|
List all leap seconds:
|
|
249
263
|
|
|
250
264
|
```ruby
|
|
251
265
|
IERS::LeapSecond.all
|
|
252
|
-
# => [#<data IERS::LeapSecond::Entry effective_date=#<Date: 1972-01-01>, tai_utc=10
|
|
266
|
+
# => [#<data IERS::LeapSecond::Entry effective_date=#<Date: 1972-01-01>, tai_utc=10>, ...]
|
|
253
267
|
```
|
|
254
268
|
|
|
255
269
|
Check for a future scheduled leap second:
|
|
@@ -258,6 +272,27 @@ Check for a future scheduled leap second:
|
|
|
258
272
|
IERS::LeapSecond.next_scheduled # => #<data IERS::LeapSecond::Entry ...> or nil
|
|
259
273
|
```
|
|
260
274
|
|
|
275
|
+
#### Leap second file validity
|
|
276
|
+
|
|
277
|
+
`Leap_Second.dat` states when it stops being authoritative and which IERS
|
|
278
|
+
bulletin it was updated through. Both are read from the file that is actually
|
|
279
|
+
loaded, so a custom or cached file reports its own header:
|
|
280
|
+
|
|
281
|
+
```ruby
|
|
282
|
+
IERS::LeapSecond.expires_on # => #<Date: 2026-12-28> or nil
|
|
283
|
+
IERS::LeapSecond.expired? # => false
|
|
284
|
+
IERS::LeapSecond.updated_through # => "IERS Bulletin 71 issued in January 2026"
|
|
285
|
+
```
|
|
286
|
+
|
|
287
|
+
The stated date is itself still valid, so a file expiring on 28 December 2026
|
|
288
|
+
is expired on the 29th. An expired file is not wrong: it means the IERS has
|
|
289
|
+
published a newer one and a leap second may have been announced since.
|
|
290
|
+
|
|
291
|
+
This is unrelated to `Data.status.cache_age`, which measures when the file was
|
|
292
|
+
downloaded rather than how long it stays valid. A bundled snapshot can be a year
|
|
293
|
+
old and still valid while a file downloaded this morning can be a week from
|
|
294
|
+
expiry.
|
|
295
|
+
|
|
261
296
|
### TAI
|
|
262
297
|
|
|
263
298
|
Convert between UTC and TAI time scales:
|
|
@@ -295,6 +330,10 @@ end
|
|
|
295
330
|
|
|
296
331
|
Without `coverage_days_ahead`, the check ensures predictions cover today.
|
|
297
332
|
|
|
333
|
+
`ensure_fresh!` is about prediction coverage in `finals2000A.all` only. Leap
|
|
334
|
+
second file validity is a separate question, answered by
|
|
335
|
+
`IERS::LeapSecond.expired?`.
|
|
336
|
+
|
|
298
337
|
### Data status and cache management
|
|
299
338
|
|
|
300
339
|
```ruby
|
|
@@ -358,6 +397,9 @@ release a new version, update the version number in `version.rb`, and then run
|
|
|
358
397
|
`bundle exec rake release`, which will create a git tag for the version, push
|
|
359
398
|
git commits and the created tag, and push the `.gem` file to [rubygems.org].
|
|
360
399
|
|
|
400
|
+
To refresh the bundled snapshot in `data/`, run `bundle exec rake data:update`.
|
|
401
|
+
A monthly workflow runs this and opens a pull request automatically.
|
|
402
|
+
|
|
361
403
|
## License
|
|
362
404
|
|
|
363
405
|
The gem is available as open source under the terms of the [MIT License].
|
data/Rakefile
CHANGED
|
@@ -10,3 +10,31 @@ require "rubocop/rake_task"
|
|
|
10
10
|
RuboCop::RakeTask.new
|
|
11
11
|
|
|
12
12
|
task default: %i[test rubocop]
|
|
13
|
+
|
|
14
|
+
namespace :data do
|
|
15
|
+
desc "Download the latest IERS data"
|
|
16
|
+
task :update do
|
|
17
|
+
require "iers"
|
|
18
|
+
|
|
19
|
+
data_dir = File.expand_path("data", __dir__)
|
|
20
|
+
IERS.configure do |config|
|
|
21
|
+
config.finals_path = File.join(data_dir, "finals2000A.all")
|
|
22
|
+
config.leap_second_path = File.join(data_dir, "Leap_Second.dat")
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
result = IERS::Data.update!
|
|
26
|
+
|
|
27
|
+
unless result.success?
|
|
28
|
+
result.errors.each do |source, error|
|
|
29
|
+
warn "#{source}: #{error.class} - #{error.message}"
|
|
30
|
+
end
|
|
31
|
+
abort "IERS data update failed"
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
IERS::Data.clear_loaded!
|
|
35
|
+
IERS::Data.ensure_fresh!(coverage_days_ahead: 90)
|
|
36
|
+
IERS::Data.leap_second_entries
|
|
37
|
+
|
|
38
|
+
puts "Updated: #{result.updated_files.join(", ")}"
|
|
39
|
+
end
|
|
40
|
+
end
|
data/lib/iers/data.rb
CHANGED
|
@@ -11,13 +11,13 @@ module IERS
|
|
|
11
11
|
|
|
12
12
|
@mutex = Mutex.new
|
|
13
13
|
@finals = nil
|
|
14
|
-
@
|
|
14
|
+
@leap_second_table = nil
|
|
15
15
|
|
|
16
16
|
module_function
|
|
17
17
|
|
|
18
18
|
# @return [Boolean]
|
|
19
19
|
def loaded?
|
|
20
|
-
!@finals.nil? || !@
|
|
20
|
+
!@finals.nil? || !@leap_second_table.nil?
|
|
21
21
|
end
|
|
22
22
|
|
|
23
23
|
# @param sources [Array<Symbol>] data sources to update (default: all)
|
|
@@ -96,16 +96,26 @@ module IERS
|
|
|
96
96
|
end
|
|
97
97
|
end
|
|
98
98
|
|
|
99
|
-
# @return [
|
|
100
|
-
def
|
|
99
|
+
# @return [Parsers::LeapSecond::Table]
|
|
100
|
+
def leap_second_table
|
|
101
101
|
@mutex.synchronize do
|
|
102
|
-
@
|
|
102
|
+
@leap_second_table ||= begin
|
|
103
103
|
path = resolve_read_path(:leap_seconds)
|
|
104
|
-
Parsers::LeapSecond.parse(path)
|
|
104
|
+
Parsers::LeapSecond.parse(path)
|
|
105
105
|
end
|
|
106
106
|
end
|
|
107
107
|
end
|
|
108
108
|
|
|
109
|
+
# @return [Array<Parsers::LeapSecond::Entry>]
|
|
110
|
+
def leap_second_entries
|
|
111
|
+
leap_second_table.entries
|
|
112
|
+
end
|
|
113
|
+
|
|
114
|
+
# @return [Parsers::LeapSecond::Metadata]
|
|
115
|
+
def leap_second_metadata
|
|
116
|
+
leap_second_table.metadata
|
|
117
|
+
end
|
|
118
|
+
|
|
109
119
|
def resolve_path(source, config = IERS.configuration)
|
|
110
120
|
case source
|
|
111
121
|
when :finals
|
|
@@ -155,7 +165,7 @@ module IERS
|
|
|
155
165
|
def clear_loaded!
|
|
156
166
|
@mutex.synchronize do
|
|
157
167
|
@finals = nil
|
|
158
|
-
@
|
|
168
|
+
@leap_second_table = nil
|
|
159
169
|
end
|
|
160
170
|
end
|
|
161
171
|
|
data/lib/iers/data_status.rb
CHANGED
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
module IERS
|
|
4
4
|
# @attr source [Symbol] +:cached+, +:custom+, or +:bundled+
|
|
5
5
|
# @attr cache_age [Integer, nil] age in seconds, or +nil+
|
|
6
|
-
DataStatus = Data.define(:source, :cache_age) do
|
|
6
|
+
DataStatus = ::Data.define(:source, :cache_age) do
|
|
7
7
|
# @return [Boolean]
|
|
8
8
|
def cached?
|
|
9
9
|
source == :cached
|
data/lib/iers/leap_second.rb
CHANGED
|
@@ -30,17 +30,41 @@ module IERS
|
|
|
30
30
|
end
|
|
31
31
|
end
|
|
32
32
|
|
|
33
|
+
# @return [Date, nil]
|
|
34
|
+
def expires_on
|
|
35
|
+
IERS::Data.leap_second_metadata.expires_on
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
# @param as_of [Date]
|
|
39
|
+
# @return [Boolean]
|
|
40
|
+
def expired?(as_of: Date.today)
|
|
41
|
+
expiry = expires_on
|
|
42
|
+
return false if expiry.nil?
|
|
43
|
+
|
|
44
|
+
as_of > expiry
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
# @return [String, nil]
|
|
48
|
+
def updated_through
|
|
49
|
+
IERS::Data.leap_second_metadata.updated_through
|
|
50
|
+
end
|
|
51
|
+
|
|
33
52
|
# @return [Entry, nil]
|
|
34
53
|
def next_scheduled
|
|
35
54
|
today = Date.today
|
|
36
55
|
all.find { |entry| entry.effective_date > today }
|
|
37
56
|
end
|
|
38
57
|
|
|
58
|
+
# TAI−UTC covers 1961-01-01 onward. From 1972 the value is a whole number
|
|
59
|
+
# of seconds read from Leap_Second.dat and returned as an Integer. Between
|
|
60
|
+
# 1961 and 1972 UTC was steered by rate adjustments rather than whole leap
|
|
61
|
+
# seconds, so the value is a fraction of a second returned as a Rational.
|
|
62
|
+
#
|
|
39
63
|
# @param input [Time, Date, DateTime, nil]
|
|
40
64
|
# @param jd [Float, nil] Julian Date
|
|
41
65
|
# @param mjd [Float, nil] Modified Julian Date
|
|
42
|
-
# @return [Integer] TAI−UTC in seconds
|
|
43
|
-
# @raise [OutOfRangeError]
|
|
66
|
+
# @return [Integer, Rational] TAI−UTC in seconds
|
|
67
|
+
# @raise [OutOfRangeError] before 1961-01-01
|
|
44
68
|
def at(input = nil, jd: nil, mjd: nil)
|
|
45
69
|
query_mjd = TimeScale.to_mjd(input, jd: jd, mjd: mjd)
|
|
46
70
|
parser_entries = IERS::Data.leap_second_entries
|
|
@@ -49,11 +73,14 @@ module IERS
|
|
|
49
73
|
last_mjd = parser_entries.last.mjd
|
|
50
74
|
|
|
51
75
|
if query_mjd < first_mjd
|
|
76
|
+
return TaiUtcDrift.at(query_mjd) if TaiUtcDrift.covers?(query_mjd)
|
|
77
|
+
|
|
52
78
|
raise OutOfRangeError.new(
|
|
53
|
-
"Requested MJD #{query_mjd} is before
|
|
54
|
-
"
|
|
79
|
+
"Requested MJD #{query_mjd} is before TAI−UTC was defined " \
|
|
80
|
+
"(MJD #{TaiUtcDrift::FIRST_MJD}, 1961-01-01; no published UTC " \
|
|
81
|
+
"before then)",
|
|
55
82
|
requested_mjd: query_mjd,
|
|
56
|
-
available_range:
|
|
83
|
+
available_range: TaiUtcDrift::FIRST_MJD..last_mjd
|
|
57
84
|
)
|
|
58
85
|
end
|
|
59
86
|
|
data/lib/iers/parsers/finals.rb
CHANGED
|
@@ -5,10 +5,23 @@ require "date"
|
|
|
5
5
|
module IERS
|
|
6
6
|
module Parsers
|
|
7
7
|
module LeapSecond
|
|
8
|
-
Entry = Data.define(:mjd, :date, :tai_utc)
|
|
8
|
+
Entry = ::Data.define(:mjd, :date, :tai_utc)
|
|
9
|
+
|
|
10
|
+
# @attr expires_on [Date, nil]
|
|
11
|
+
# @attr updated_through [String, nil]
|
|
12
|
+
Metadata = ::Data.define(:expires_on, :updated_through)
|
|
13
|
+
|
|
14
|
+
# @attr entries [Array<Entry>]
|
|
15
|
+
# @attr metadata [Metadata]
|
|
16
|
+
Table = ::Data.define(:entries, :metadata)
|
|
17
|
+
|
|
18
|
+
EXPIRES_ON = /File expires on\s+(\d{1,2})\s+([A-Za-z]+)\s+(\d{4})/i
|
|
19
|
+
UPDATED_THROUGH = /\AUpdated through\s+(.+?)\s*\z/i
|
|
20
|
+
COMMENT_PREFIX = /\A#+\s*/
|
|
9
21
|
|
|
10
22
|
module_function
|
|
11
23
|
|
|
24
|
+
# @return [Table]
|
|
12
25
|
def parse(path)
|
|
13
26
|
path = Pathname(path)
|
|
14
27
|
|
|
@@ -20,14 +33,30 @@ module IERS
|
|
|
20
33
|
end
|
|
21
34
|
|
|
22
35
|
entries = []
|
|
36
|
+
expires_on = nil
|
|
37
|
+
updated_through = nil
|
|
23
38
|
|
|
24
39
|
path.each_line.with_index(1) do |line, line_number|
|
|
25
|
-
|
|
40
|
+
stripped = line.scrub.strip
|
|
41
|
+
next if stripped.empty?
|
|
42
|
+
|
|
43
|
+
if stripped.start_with?("#")
|
|
44
|
+
comment = stripped.sub(COMMENT_PREFIX, "")
|
|
45
|
+
expires_on ||= parse_expiry(comment)
|
|
46
|
+
updated_through ||= parse_updated_through(comment)
|
|
47
|
+
next
|
|
48
|
+
end
|
|
26
49
|
|
|
27
50
|
entries << parse_line(line, path, line_number)
|
|
28
51
|
end
|
|
29
52
|
|
|
30
|
-
|
|
53
|
+
Table.new(
|
|
54
|
+
entries: entries.freeze,
|
|
55
|
+
metadata: Metadata.new(
|
|
56
|
+
expires_on: expires_on,
|
|
57
|
+
updated_through: updated_through
|
|
58
|
+
)
|
|
59
|
+
)
|
|
31
60
|
end
|
|
32
61
|
|
|
33
62
|
def parse_line(line, path, line_number)
|
|
@@ -50,7 +79,28 @@ module IERS
|
|
|
50
79
|
)
|
|
51
80
|
end
|
|
52
81
|
|
|
53
|
-
|
|
82
|
+
# @return [Date, nil]
|
|
83
|
+
def parse_expiry(comment)
|
|
84
|
+
match = EXPIRES_ON.match(comment)
|
|
85
|
+
return nil if match.nil?
|
|
86
|
+
|
|
87
|
+
month = Date::MONTHNAMES.index do |name|
|
|
88
|
+
name&.casecmp?(match[2])
|
|
89
|
+
end
|
|
90
|
+
return nil if month.nil?
|
|
91
|
+
|
|
92
|
+
Date.new(Integer(match[3], 10), month, Integer(match[1], 10))
|
|
93
|
+
rescue ArgumentError
|
|
94
|
+
nil
|
|
95
|
+
end
|
|
96
|
+
|
|
97
|
+
# @return [String, nil]
|
|
98
|
+
def parse_updated_through(comment)
|
|
99
|
+
match = UPDATED_THROUGH.match(comment)
|
|
100
|
+
match && match[1]
|
|
101
|
+
end
|
|
102
|
+
|
|
103
|
+
private_class_method :parse_line, :parse_expiry, :parse_updated_through
|
|
54
104
|
end
|
|
55
105
|
end
|
|
56
106
|
end
|
|
@@ -0,0 +1,144 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module IERS
|
|
4
|
+
# TAI-UTC from 1961-01-01 to 1972-01-01, when UTC was steered by rate
|
|
5
|
+
# adjustments instead of whole leap seconds. In this era TAI-UTC is a linear
|
|
6
|
+
# function of MJD, restarting at each published step, so a value is
|
|
7
|
+
# +offset + (mjd - reference_mjd) * rate+.
|
|
8
|
+
#
|
|
9
|
+
# The table is closed: the last segment ends where Leap_Second.dat begins and
|
|
10
|
+
# no new segment can ever be added.
|
|
11
|
+
#
|
|
12
|
+
# Coefficients from the USNO tai-utc.dat file, the same values ERFA carries in
|
|
13
|
+
# dat.c. They are exact decimals, so they are stored as rationals rather than
|
|
14
|
+
# floats.
|
|
15
|
+
#
|
|
16
|
+
# @api private
|
|
17
|
+
module TaiUtcDrift
|
|
18
|
+
# @attr start_mjd [Integer] first MJD the segment applies to
|
|
19
|
+
# @attr offset [Rational] TAI-UTC in seconds at reference_mjd (not at
|
|
20
|
+
# start_mjd: several segments measure their rate from an earlier MJD)
|
|
21
|
+
# @attr reference_mjd [Integer] the MJD the rate is measured from
|
|
22
|
+
# @attr rate [Rational] seconds of TAI-UTC per day of MJD
|
|
23
|
+
Segment = ::Data.define(:start_mjd, :offset, :reference_mjd, :rate)
|
|
24
|
+
|
|
25
|
+
# 1961-01-01. Below this there is no published UTC, so TAI-UTC is undefined.
|
|
26
|
+
FIRST_MJD = 37_300
|
|
27
|
+
|
|
28
|
+
# 1972-01-01, where Leap_Second.dat takes over. The drift era stops here.
|
|
29
|
+
LAST_MJD = 41_317
|
|
30
|
+
|
|
31
|
+
module_function
|
|
32
|
+
|
|
33
|
+
# @param mjd [Numeric]
|
|
34
|
+
# @return [Rational] TAI-UTC in seconds
|
|
35
|
+
def at(mjd)
|
|
36
|
+
segment = SEGMENTS.reverse_each.find { |s| mjd >= s.start_mjd }
|
|
37
|
+
|
|
38
|
+
segment.offset +
|
|
39
|
+
(Rational(mjd) - segment.reference_mjd) * segment.rate
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
# Whether +at+ has a value for this MJD: 1961-01-01 up to but not including
|
|
43
|
+
# 1972-01-01, where Leap_Second.dat takes over. A caller with a custom leap
|
|
44
|
+
# second file starting after 1972 relies on the upper bound to fall through
|
|
45
|
+
# to its own error rather than reading a stale drift value.
|
|
46
|
+
#
|
|
47
|
+
# @param mjd [Numeric]
|
|
48
|
+
# @return [Boolean]
|
|
49
|
+
def covers?(mjd)
|
|
50
|
+
mjd >= FIRST_MJD && mjd < LAST_MJD
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
SEGMENTS = [
|
|
54
|
+
Segment.new(
|
|
55
|
+
start_mjd: 37_300,
|
|
56
|
+
offset: Rational(14_228_180, 10_000_000),
|
|
57
|
+
reference_mjd: 37_300,
|
|
58
|
+
rate: Rational(1_296, 1_000_000)
|
|
59
|
+
),
|
|
60
|
+
Segment.new(
|
|
61
|
+
start_mjd: 37_512,
|
|
62
|
+
offset: Rational(13_728_180, 10_000_000),
|
|
63
|
+
reference_mjd: 37_300,
|
|
64
|
+
rate: Rational(1_296, 1_000_000)
|
|
65
|
+
),
|
|
66
|
+
Segment.new(
|
|
67
|
+
start_mjd: 37_665,
|
|
68
|
+
offset: Rational(18_458_580, 10_000_000),
|
|
69
|
+
reference_mjd: 37_665,
|
|
70
|
+
rate: Rational(11_232, 10_000_000)
|
|
71
|
+
),
|
|
72
|
+
Segment.new(
|
|
73
|
+
start_mjd: 38_334,
|
|
74
|
+
offset: Rational(19_458_580, 10_000_000),
|
|
75
|
+
reference_mjd: 37_665,
|
|
76
|
+
rate: Rational(11_232, 10_000_000)
|
|
77
|
+
),
|
|
78
|
+
Segment.new(
|
|
79
|
+
start_mjd: 38_395,
|
|
80
|
+
offset: Rational(32_401_300, 10_000_000),
|
|
81
|
+
reference_mjd: 38_761,
|
|
82
|
+
rate: Rational(1_296, 1_000_000)
|
|
83
|
+
),
|
|
84
|
+
Segment.new(
|
|
85
|
+
start_mjd: 38_486,
|
|
86
|
+
offset: Rational(33_401_300, 10_000_000),
|
|
87
|
+
reference_mjd: 38_761,
|
|
88
|
+
rate: Rational(1_296, 1_000_000)
|
|
89
|
+
),
|
|
90
|
+
Segment.new(
|
|
91
|
+
start_mjd: 38_639,
|
|
92
|
+
offset: Rational(34_401_300, 10_000_000),
|
|
93
|
+
reference_mjd: 38_761,
|
|
94
|
+
rate: Rational(1_296, 1_000_000)
|
|
95
|
+
),
|
|
96
|
+
Segment.new(
|
|
97
|
+
start_mjd: 38_761,
|
|
98
|
+
offset: Rational(35_401_300, 10_000_000),
|
|
99
|
+
reference_mjd: 38_761,
|
|
100
|
+
rate: Rational(1_296, 1_000_000)
|
|
101
|
+
),
|
|
102
|
+
Segment.new(
|
|
103
|
+
start_mjd: 38_820,
|
|
104
|
+
offset: Rational(36_401_300, 10_000_000),
|
|
105
|
+
reference_mjd: 38_761,
|
|
106
|
+
rate: Rational(1_296, 1_000_000)
|
|
107
|
+
),
|
|
108
|
+
Segment.new(
|
|
109
|
+
start_mjd: 38_942,
|
|
110
|
+
offset: Rational(37_401_300, 10_000_000),
|
|
111
|
+
reference_mjd: 38_761,
|
|
112
|
+
rate: Rational(1_296, 1_000_000)
|
|
113
|
+
),
|
|
114
|
+
Segment.new(
|
|
115
|
+
start_mjd: 39_004,
|
|
116
|
+
offset: Rational(38_401_300, 10_000_000),
|
|
117
|
+
reference_mjd: 38_761,
|
|
118
|
+
rate: Rational(1_296, 1_000_000)
|
|
119
|
+
),
|
|
120
|
+
Segment.new(
|
|
121
|
+
start_mjd: 39_126,
|
|
122
|
+
offset: Rational(43_131_700, 10_000_000),
|
|
123
|
+
reference_mjd: 39_126,
|
|
124
|
+
rate: Rational(2_592, 1_000_000)
|
|
125
|
+
),
|
|
126
|
+
Segment.new(
|
|
127
|
+
start_mjd: 39_887,
|
|
128
|
+
offset: Rational(42_131_700, 10_000_000),
|
|
129
|
+
reference_mjd: 39_126,
|
|
130
|
+
rate: Rational(2_592, 1_000_000)
|
|
131
|
+
),
|
|
132
|
+
# Reached only by a direct TaiUtcDrift.at(41_317): LeapSecond.at's drift
|
|
133
|
+
# branch requires query_mjd < 41_317. It pins the join to exactly 10,
|
|
134
|
+
# matching Leap_Second.dat's first row; without it the 1968 segment would
|
|
135
|
+
# give ~9.8922 here. Do not remove.
|
|
136
|
+
Segment.new(
|
|
137
|
+
start_mjd: 41_317,
|
|
138
|
+
offset: Rational(10),
|
|
139
|
+
reference_mjd: 41_317,
|
|
140
|
+
rate: Rational(0)
|
|
141
|
+
)
|
|
142
|
+
].freeze
|
|
143
|
+
end
|
|
144
|
+
end
|
data/lib/iers/update_result.rb
CHANGED
data/lib/iers/version.rb
CHANGED
data/lib/iers.rb
CHANGED
|
@@ -14,6 +14,7 @@ require_relative "iers/has_data_quality"
|
|
|
14
14
|
require_relative "iers/interpolation"
|
|
15
15
|
require_relative "iers/eop_lookup"
|
|
16
16
|
require_relative "iers/eop_parameter"
|
|
17
|
+
require_relative "iers/tai_utc_drift"
|
|
17
18
|
require_relative "iers/leap_second"
|
|
18
19
|
require_relative "iers/tai"
|
|
19
20
|
require_relative "iers/ut1"
|
metadata
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: iers
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.
|
|
4
|
+
version: 0.2.0
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Rémy Hannequin
|
|
@@ -174,6 +174,7 @@ files:
|
|
|
174
174
|
- lib/iers/parsers/leap_second.rb
|
|
175
175
|
- lib/iers/polar_motion.rb
|
|
176
176
|
- lib/iers/tai.rb
|
|
177
|
+
- lib/iers/tai_utc_drift.rb
|
|
177
178
|
- lib/iers/terrestrial_rotation.rb
|
|
178
179
|
- lib/iers/time_scale.rb
|
|
179
180
|
- lib/iers/update_result.rb
|