Project

iers

0.0
No release in over 3 years
Access to Earth orientation parameters and time scale values from the International Earth Rotation and Reference Systems Service (IERS).
2005
2006
2007
2008
2009
2010
2011
2012
2013
2014
2015
2016
2017
2018
2019
2020
2021
2022
2023
2024
2025
2026
 Dependencies

Development

 Project Readme

IERS

Tests

Access to Earth orientation parameters and time scale values from the International Earth Rotation and Reference Systems Service (IERS).

About IERS and Earth Orientation Parameters

The IERS is an international service that monitors the irregularities of Earth's rotation and orientation in space. Because Earth's rotation is not perfectly uniform, precise timekeeping, satellite navigation, and telescope pointing all depend on regularly updated measurements.

The key quantities tracked by the IERS are known as Earth Orientation Parameters (EOP):

  • Polar motion (x, y) — the position of Earth's rotational pole relative to its crust, expressed in arcseconds. The pole wanders in a roughly circular path of a few tenths of an arcsecond over ~14 months (the Chandler wobble).
  • UT1−UTC — the difference between astronomical time (UT1, tied to Earth's actual rotation angle) and coordinated universal time (UTC, maintained by atomic clocks). This difference drifts by up to ~0.9 s before a leap second is introduced to keep them close.
  • Leap seconds — occasional one-second adjustments applied to UTC so that it stays within 0.9 s of UT1. Since 1972, 27 leap seconds have been added.

Data files

This library works with two data files published by the IERS:

  • finals2000A — a daily table spanning from 1973 to the present (plus predictions ~1 year ahead). Each row contains polar motion, UT1−UTC, and other EOP for one Modified Julian Date. Recent rows carry rapid "Series A" values; older rows also include refined "Bulletin B" values.
  • Leap_Second.dat — the complete history of leap seconds with their effective dates and the cumulative TAI−UTC offset.

Both files are downloaded automatically by IERS::Data.update! and cached locally.

Installation

Install the gem and add to the application's Gemfile by executing:

$ bundle add iers

If Bundler is not being used to manage dependencies, install the gem by executing:

$ gem install iers

Usage

Bundled data

The gem ships with a snapshot of the IERS data files taken at release time, so queries work immediately without any download:

require "iers"

IERS::UT1.at(Time.utc(2020, 6, 15))  # works out of the box

The bundled snapshot includes predictions roughly one year into the future from the release date. As time passes those predictions expire, so you should download fresh data periodically:

result = IERS::Data.update!
result.success? # => true

You can also update a single source:

IERS::Data.update!(:finals)
IERS::Data.update!(:leap_seconds)

Downloaded files are cached in ~/.cache/iers/ by default and take precedence over the bundled snapshot.

The bundled data files are sourced from the IERS Earth Orientation Center and the USNO Rapid Service/Prediction Center.

Polar motion

Query the pole position at any point in time:

pm = IERS::PolarMotion.at(Time.utc(2020, 6, 15))
pm.x          # => 0.070979... (arcseconds)
pm.y          # => 0.456571... (arcseconds)
pm.observed?  # => true

Retrieve daily grid values over a date range. between returns a lazy Enumerator, so entries are computed on demand:

entries = IERS::PolarMotion.between(
  Date.new(2020, 1, 1),
  Date.new(2020, 1, 31)
)
entries.count  # => 31

Rotation matrix

Compute the polar motion rotation matrix W (IERS Conventions 2010, §5.4.1):

w = IERS::PolarMotion.rotation_matrix_at(Time.utc(2020, 6, 15))
w.length     # => 3
w[0].length  # => 3

Returns a nested Array (3×3, row-major).

The matrix is also available on any PolarMotion::Entry:

pm = IERS::PolarMotion.at(Time.utc(2020, 6, 15))
pm.rotation_matrix  # => same nested Array

UT1−UTC

Query the difference between UT1 and UTC:

entry = IERS::UT1.at(Time.utc(2020, 6, 15))
entry.ut1_utc    # => -0.178182...
entry.observed?  # => true

Daily grid values:

entries = IERS::UT1.between(
  Date.new(2020, 1, 1),
  Date.new(2020, 1, 31)
)

Celestial pole offsets

Query the celestial pole offset corrections (dX, dY):

cpo = IERS::CelestialPoleOffset.at(Time.utc(2020, 6, 15))
cpo.x  # => dX correction (milliarcseconds)
cpo.y  # => dY correction (milliarcseconds)

Length of day

Query the excess length of day:

entry = IERS::LengthOfDay.at(Time.utc(2020, 6, 15))
entry.length_of_day  # => excess LOD (seconds)
entry.observed?      # => true

Delta T

Compute Delta T (TT − UT1). From 1972 onward the value is derived from IERS data; before 1972 (back to 1800) it uses Espenak & Meeus polynomial approximations:

entry = IERS::DeltaT.at(Time.utc(2020, 6, 15))
entry.delta_t    # => ~69.36 (seconds)
entry.measured?  # => true

entry = IERS::DeltaT.at(Time.utc(1900, 6, 15))
entry.delta_t    # => ~-2.12 (seconds)
entry.estimated? # => true

Earth Rotation Angle

Compute ERA (IERS Conventions 2010, eq. 5.15). The UT1-UTC correction is looked up internally:

IERS::EarthRotationAngle.at(Time.utc(2020, 6, 15))  # => radians, in [0, 2π)

Greenwich Mean Sidereal Time

Compute GMST (IERS Conventions 2010, eq. 5.32). Uses ERA internally and adds the equinox-based polynomial evaluated at TT:

IERS::GMST.at(Time.utc(2020, 6, 15))  # => radians, in [0, 2π)

Terrestrial rotation matrix

Compute R(ERA) × W, the rotation from ITRS to TIRS (IERS Conventions 2010, eq. 5.1), combining the Earth Rotation Angle and the polar motion matrix (W):

r = IERS::TerrestrialRotation.at(Time.utc(2020, 6, 15))
r.length     # => 3
r[0].length  # => 3

Returns a 3×3 nested Array (row-major).

Earth Orientation Parameters (unified)

Query all EOP components at once:

eop = IERS::EOP.at(Time.utc(2020, 6, 15))
eop.polar_motion_x   # => arcseconds
eop.polar_motion_y   # => arcseconds
eop.ut1_utc          # => seconds
eop.length_of_day    # => seconds
eop.celestial_pole_x # => milliarcseconds
eop.celestial_pole_y # => milliarcseconds
eop.observed?        # => true
eop.date             # => #<Date: 2020-06-15>

Retrieve daily EOP over a date range:

entries = IERS::EOP.between(
  Date.new(2020, 1, 1),
  Date.new(2020, 1, 31)
)

Leap seconds

Look up TAI−UTC at a given date:

IERS::LeapSecond.at(Time.utc(2017, 1, 1))  # => 37.0 (seconds)

List all leap seconds:

IERS::LeapSecond.all
# => [#<data IERS::LeapSecond::Entry effective_date=#<Date: 1972-01-01>, tai_utc=10.0>, ...]

Check for a future scheduled leap second:

IERS::LeapSecond.next_scheduled  # => #<data IERS::LeapSecond::Entry ...> or nil

TAI

Convert between UTC and TAI time scales:

tai_mjd = IERS::TAI.utc_to_tai(Time.utc(2017, 1, 1))  # => MJD in TAI
utc_mjd = IERS::TAI.tai_to_utc(mjd: tai_mjd)           # => MJD in UTC

Time input

All query methods accept Ruby Time, Date, and DateTime objects as positional arguments. You can also use keyword arguments for numeric Julian Dates:

IERS::UT1.at(mjd: 58849.0)        # Modified Julian Date
IERS::UT1.at(jd: 2458849.5)       # Julian Date
IERS::UT1.at(Time.utc(2020, 1, 1)) # Ruby Time

Data freshness

Check that predictions cover enough of the future before relying on query results:

begin
  IERS::Data.ensure_fresh!(coverage_days_ahead: 90)
rescue IERS::StaleDataError => e
  puts "Predictions end #{e.predicted_until}, need #{e.required_until}"
  IERS::Data.update!
end

Without coverage_days_ahead, the check ensures predictions cover today.

Data status and cache management

status = IERS::Data.status
status.cached?    # => true if downloaded data exists
status.cache_age  # => age in seconds, or nil

IERS::Data.clear_cache!  # remove downloaded files

Custom data paths

Point the gem at your own copies of the IERS data files:

IERS.configure do |config|
  config.finals_path = "/path/to/finals2000A.all"
  config.leap_second_path = "/path/to/Leap_Second.dat"
end

Configuration

IERS.configure do |config|
  config.cache_dir = "/path/to/cache"
  config.interpolation = :linear   # default: :lagrange
  config.lagrange_order = 6        # default: 4
  config.download_timeout = 60     # default: 30 (seconds)
end

To fully reset configuration and cached data:

IERS.reset!

Error handling

All errors inherit from IERS::Error:

  • IERS::DataError: base for data-related errors
    • IERS::ParseError: malformed data file
    • IERS::FileNotFoundError: data file not found
    • IERS::StaleDataError: predictions don't extend far enough
  • IERS::DownloadError: base for download-related errors
    • IERS::NetworkError: HTTP or connection failure
    • IERS::ValidationError: downloaded file failed validation
  • IERS::OutOfRangeError — query outside data coverage
  • IERS::ConfigurationError — invalid configuration

Development

After checking out the repo, run bin/setup to install dependencies. Then, run rake test to run the tests. You can also run bin/console for an interactive prompt that will allow you to experiment.

To install this gem onto your local machine, run bundle exec rake install. To release a new version, update the version number in version.rb, and then run bundle exec rake release, which will create a git tag for the version, push git commits and the created tag, and push the .gem file to rubygems.org.

License

The gem is available as open source under the terms of the MIT License.

Code of Conduct

Everyone interacting in the IERS Ruby project's codebases, issue trackers, chat rooms and mailing lists is expected to follow the code of conduct.