Sidereon.GNSS.Time (Sidereon v0.13.0)

Copy Markdown View Source

Epoch conversions shared by the GNSS correction wrappers.

These helpers turn an Elixir NaiveDateTime or a {{year, month, day}, {hour, minute, second}} tuple into the two representations the sidereon-core crate consumes:

  • a split Julian date {jd_whole, fraction} where jd_whole is the *.5 midnight boundary of the civil day and fraction is the within-day part (the same convention the SP3 reader uses);
  • integer or continuous seconds since the J2000 epoch (JD 2451545.0), used by NIF calls that consume either exact product epoch axes or fractional receive times.

No leap-second shifting is applied: the epoch stays in the time scale the caller supplied it in (typically GPS time for these models).

Summary

Types

A time scale, named either by atom (:gpst, :utc, :glonasst, ...) or by its uppercase abbreviation string ("GPST", "UTC", ...).

Functions

Fractional day-of-year of the epoch, as the float the Niell troposphere seasonal term consumes.

Convert an epoch to integer seconds since the J2000 epoch (JD 2451545.0).

Convert an epoch to continuous floating-point seconds since J2000.

Convert an epoch to the split Julian date {jd_whole, fraction}.

GPS minus UTC, in seconds, in effect at a UTC calendar date.

Provenance and coverage of the embedded leap-second table.

TAI minus UTC, in seconds, in effect at a UTC calendar date.

TAI minus UTC for a list of UTC calendar dates.

Seconds-of-day in [0, 86400), formed from the epoch's clock fields.

TAI minus UTC, in seconds, in effect at a UTC calendar date.

The supported time-scale atoms.

Fixed inter-system time offset to - from, in seconds.

Leap-aware inter-system time offset to - from, in seconds, at utc_jd.

Provenance and coverage of the embedded UT1/EOP table.

Validated UTC instant for an epoch, as the split Julian date {jd_whole, fraction}.

Types

leap_second_table()

@type leap_second_table() :: %{
  source: String.t(),
  first_mjd: integer(),
  last_mjd: integer(),
  entries: non_neg_integer()
}

time_scale()

@type time_scale() :: atom() | String.t()

A time scale, named either by atom (:gpst, :utc, :glonasst, ...) or by its uppercase abbreviation string ("GPST", "UTC", ...).

ut1_coverage()

@type ut1_coverage() :: %{
  source: String.t(),
  first_mjd: integer(),
  last_mjd: integer(),
  first_jd_tt: float(),
  last_jd_tt: float(),
  entries: non_neg_integer()
}

Functions

day_of_year(epoch)

@spec day_of_year(NaiveDateTime.t() | tuple()) :: float()

Fractional day-of-year of the epoch, as the float the Niell troposphere seasonal term consumes.

January 1 00:00 is 1.0. The continuous day-of-year comes from sidereon_core::astro::time::civil::day_of_year, matching the crate's fractional SolveInputs.day_of_year convention, so the SPP troposphere and Sidereon.GNSS.Troposphere agree for the same epoch.

epoch_to_j2000_seconds(ndt)

@spec epoch_to_j2000_seconds(NaiveDateTime.t() | tuple()) ::
  {:ok, integer()} | {:error, term()}

Convert an epoch to integer seconds since the J2000 epoch (JD 2451545.0).

A whole-second epoch yields an exact integer (the core returns the exact whole-second value, which is converted back to an integer here). Returns {:ok, seconds} or {:error, :non_integer_second_epoch} if the epoch carries a sub-second part. The continuous seconds come from sidereon_core::astro::time::civil::j2000_seconds.

epoch_to_j2000_seconds_fractional(epoch)

@spec epoch_to_j2000_seconds_fractional(NaiveDateTime.t() | tuple()) ::
  {:ok, float()} | {:error, term()}

Convert an epoch to continuous floating-point seconds since J2000.

Unlike epoch_to_j2000_seconds/1, this accepts sub-second NaiveDateTime values and tuple epochs with a floating-point seconds field. Delegates to sidereon_core::astro::time::civil::j2000_seconds.

epoch_to_split_jd(epoch)

@spec epoch_to_split_jd(NaiveDateTime.t() | tuple()) :: {float(), float()}

Convert an epoch to the split Julian date {jd_whole, fraction}.

The calendar arithmetic lives in sidereon-core (sidereon_core::astro::time::civil::split_julian_date); this module only marshals the epoch into civil (year, month, day, hour, minute, second) fields.

gps_utc_offset_s(year, month, day)

@spec gps_utc_offset_s(integer(), integer(), integer()) :: float()

GPS minus UTC, in seconds, in effect at a UTC calendar date.

This is the IS-GPS-200 quantity broadcast in the navigation message (the leap seconds a GPS receiver applies): 18 s from 2017. It is leap_seconds/3 minus the constant 19 s TAI - GPST, so it is the value to use whenever you mean "GPS - UTC", not leap_seconds/3 (which is TAI - UTC).

leap_second_table_info()

@spec leap_second_table_info() :: leap_second_table()

Provenance and coverage of the embedded leap-second table.

leap_seconds(year, month, day)

@spec leap_seconds(integer(), integer(), integer()) :: float()

TAI minus UTC, in seconds, in effect at a UTC calendar date.

Delegates to sidereon_core::astro::time::scales::julian_day_number and sidereon_core::astro::time::scales::find_leap_seconds.

leap_seconds_batch(dates)

@spec leap_seconds_batch([{integer(), integer(), integer()}]) :: [float()]

TAI minus UTC for a list of UTC calendar dates.

Each date is {year, month, day} and each result delegates to the same core functions as leap_seconds/3.

second_of_day(epoch)

@spec second_of_day(NaiveDateTime.t() | tuple()) :: float()

Seconds-of-day in [0, 86400), formed from the epoch's clock fields.

Used by the Klobuchar diurnal term, which takes the GPS second-of-day directly. The arithmetic delegates to sidereon_core::astro::time::civil::second_of_day.

tai_utc_offset_s(year, month, day)

@spec tai_utc_offset_s(integer(), integer(), integer()) :: float()

TAI minus UTC, in seconds, in effect at a UTC calendar date.

The unambiguously named alias of leap_seconds/3 (the IERS Bulletin C quantity, 37 s from 2017); it returns the identical value. Use gps_utc_offset_s/3 for the GNSS "GPS - UTC" offset instead.

time_scales()

@spec time_scales() :: [atom()]

The supported time-scale atoms.

timescale_offset(from, to)

@spec timescale_offset(time_scale(), time_scale()) ::
  {:ok, float()} | {:error, term()}

Fixed inter-system time offset to - from, in seconds.

Returns the value that, added to a reading in the from scale, yields the to-scale reading of the same instant. Defined only for the atomic scales (TAI/TT/GPST/GST/QZSST/BDT) whose mutual offset is a constant.

Returns {:error, {:epoch_required, scale}} for the UTC-based scales (UTC and GLONASST) whose offset carries the leap-second count (use timescale_offset_at/3 with an epoch), and {:error, {:unsupported, "TDB"}} for TDB (its offset from TT is an epoch-dependent periodic term).

iex> Sidereon.GNSS.Time.timescale_offset(:gpst, :tai)
{:ok, 19.0}

iex> Sidereon.GNSS.Time.timescale_offset(:gpst, :utc)
{:error, {:epoch_required, "UTC"}}

timescale_offset_at(from, to, utc_jd)

@spec timescale_offset_at(time_scale(), time_scale(), number()) ::
  {:ok, float()} | {:error, term()}

Leap-aware inter-system time offset to - from, in seconds, at utc_jd.

utc_jd is the UTC Julian date of the instant; it only affects the result when from or to is UTC-based (UTC/GLONASST), resolving the leap-second count. For purely atomic pairs it is ignored and the result matches timescale_offset/2.

iex> {:ok, off} = Sidereon.GNSS.Time.timescale_offset_at(:glonasst, :utc, 2_451_545.0)
iex> Float.round(off, 1)
-10800.0

ut1_coverage_info()

@spec ut1_coverage_info() :: ut1_coverage()

Provenance and coverage of the embedded UT1/EOP table.

utc_instant_split(epoch)

@spec utc_instant_split(NaiveDateTime.t() | tuple()) ::
  {:ok, {float(), float()}} | {:error, term()}

Validated UTC instant for an epoch, as the split Julian date {jd_whole, fraction}.

Delegates to sidereon_core::astro::time::model::Instant::from_utc_civil, the entry the ionosphere/troposphere delay dispatchers build their epoch argument from. Unlike epoch_to_split_jd/1, this runs the core's JulianDateSplit guard, so an out-of-day clock field is rejected as {:error, :invalid_instant} rather than producing an out-of-range fraction.

iex> {:ok, {jd_whole, _fraction}} =
...>   Sidereon.GNSS.Time.utc_instant_split({{2020, 6, 25}, {12, 0, 0}})
iex> jd_whole
2_459_025.5