diff --git a/doc/source/getting_started/Getting-Started.ipynb b/doc/source/getting_started/Getting-Started.ipynb index 27e289f..af353d7 100644 --- a/doc/source/getting_started/Getting-Started.ipynb +++ b/doc/source/getting_started/Getting-Started.ipynb @@ -2,7 +2,7 @@ "cells": [ { "cell_type": "markdown", - "id": "2e01086c", + "id": "382d354c", "metadata": {}, "source": [ "# Getting Started\n", @@ -21,8 +21,111 @@ "- **TAI time**: International Atomic Time which is computed as the weighted average of several hundred atomic clocks.\n", "- **UTC time**: Coordinated Universal Time which is [periodically adjusted](https://www.nist.gov/pml/time-and-frequency-division/leap-seconds-faqs) to account for the difference between the definition of the second and the rotation of Earth.\n", "- **GPS time**: Atomic timing system for the Global Positioning System constellation of satellites monitored by the United States Naval Observatory (USNO). GPS time and UTC time were equal on January 6, 1980. TAI time is ahead of GPS time by 19 seconds.\n", - "- **LORAN time**: Atomic timing system for the Loran-C chain transmitter sites used in terrestrial radionavigation. LORAN time and UTC time were equal on January 1, 1958. TAI time is ahead of LORAN time by 10 seconds.\n", - "\n", + "- **LORAN time**: Atomic timing system for the Loran-C chain transmitter sites used in terrestrial radionavigation. LORAN time and UTC time were equal on January 1, 1958. TAI time is ahead of LORAN time by 10 seconds." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "5ccc6e80", + "metadata": { + "tags": [ + "remove-input" + ] + }, + "outputs": [], + "source": [ + "import datetime\n", + "import timescale\n", + "import numpy as np\n", + "import matplotlib.pyplot as plt\n", + "import matplotlib.dates as mdates\n", + "\n", + "# adjust style\n", + "facecolor = \"#fcfcfc\"\n", + "plt.rcParams[\"figure.facecolor\"] = facecolor\n", + "plt.rcParams[\"font.family\"] = \"sans-serif\"\n", + "plt.rcParams[\"font.sans-serif\"] = [\"Lato\"]\n", + "\n", + "# build a monthly time axis for plotting\n", + "today = datetime.date.today().isoformat()\n", + "ts = timescale.from_range(\"1972-01-01\", today, 1, \"D\")\n", + "dt = ts.to_datetime()\n", + "\n", + "# create figure\n", + "fig, ax = plt.subplots(figsize=(8, 4.5))\n", + "# add differences between timescales\n", + "ax.plot(\n", + " dt,\n", + " np.zeros_like(ts.utc),\n", + " color=\"0.4\",\n", + " lw=1.5,\n", + " label=\"UTC\",\n", + " zorder=0,\n", + ")\n", + "ax.plot(\n", + " dt,\n", + " ts.ut1_utc,\n", + " color=\"red\",\n", + " lw=1.5,\n", + " label=\"UT1\\u2013UTC\",\n", + " zorder=2,\n", + ")\n", + "ax.plot(\n", + " dt,\n", + " ts.gps_utc,\n", + " color=\"dodgerblue\",\n", + " lw=1.5,\n", + " label=\"GPS\\u2013UTC\",\n", + " zorder=1,\n", + ")\n", + "ax.plot(\n", + " dt,\n", + " ts.loran_utc,\n", + " color=\"mediumseagreen\",\n", + " lw=1.5,\n", + " label=\"LORAN\\u2013UTC\",\n", + ")\n", + "ax.plot(\n", + " dt,\n", + " ts.tai_utc,\n", + " color=\"darkorchid\",\n", + " lw=1.5,\n", + " label=\"TAI\\u2013UTC\",\n", + ")\n", + "# show maximum range of UT1 variability\n", + "ax.axhline(0.9, color=\"k\", lw=0.7, ls=\"--\")\n", + "ax.axhline(-0.9, color=\"k\", lw=0.7, ls=\"--\")\n", + "ax.text(\n", + " dt[-100],\n", + " 1.0,\n", + " r\"$\\pm 0.9$s DUT1 limit\",\n", + " color=\"k\",\n", + " ha=\"right\",\n", + " va=\"bottom\",\n", + " fontsize=9,\n", + ")\n", + "# add legend\n", + "lgd = ax.legend(frameon=False)\n", + "for line in lgd.get_lines():\n", + " line.set_linewidth(6)\n", + "# add labels\n", + "ax.set_ylabel(\"Offset relative to UTC [s]\")\n", + "ax.set_xlabel(\"Time [yr]\")\n", + "# set limits and tick locations\n", + "ax.set_xlim(dt[0], dt[-1])\n", + "ax.xaxis.set_major_locator(mdates.YearLocator(5))\n", + "ax.xaxis.set_major_formatter(mdates.DateFormatter(\"%Y\"))\n", + "# set layout\n", + "fig.tight_layout()\n", + "plt.show()" + ] + }, + { + "cell_type": "markdown", + "id": "2e01086c", + "metadata": {}, + "source": [ "### Dynamic Time\n", "\n", "`timescale` also keeps updated tables of delta times for converting between dynamic (TT) and universal (UT1) times.\n", @@ -47,22 +150,6 @@ "5. Julian dates " ] }, - { - "cell_type": "code", - "execution_count": null, - "id": "67db92dc", - "metadata": { - "tags": [ - "remove-input" - ] - }, - "outputs": [], - "source": [ - "import datetime\n", - "import numpy as np\n", - "import timescale.time" - ] - }, { "cell_type": "code", "execution_count": null, diff --git a/timescale/time.py b/timescale/time.py index 1a56e2c..2305446 100755 --- a/timescale/time.py +++ b/timescale/time.py @@ -17,6 +17,7 @@ UPDATE HISTORY: Updated 07/2026: add HTML representations of Timescale and Calendar + added attributes for tai_utc and loran_utc Updated 05/2026: added functions to update delta time files from project updated CDDIS ftp login options for encrypted connections Updated 04/2026: added endpoint option (defaults to True) to date_range @@ -1104,6 +1105,12 @@ def gps_week(self): """GPS week number since 1980-01-06T00:00:00""" return (self.gps / (self.day * 7)).astype(np.int64) + @timescale.utilities.reify + def loran_utc(self): + """Leap seconds between LORAN and UTC time""" + # LORAN time is behind of TAI time by 10 seconds + return self.tai_utc - 10.0 + @timescale.utilities.reify def J2000(self): """Seconds (Terrestrial Time) since 2000-01-01T12:00:00""" @@ -1127,6 +1134,21 @@ def st(self): np.mod(ST + self.era * self.deg2asec, self.turnasec) / self.turnasec ) + @timescale.utilities.reify + def tai_utc(self): + """Leap seconds between TAI and UTC time""" + # dynamic time is ahead of TAI by 32.184 seconds + _tt_tai = 32.184 + # TAI time is ahead of GPS by 19 seconds + _tai_gps = 19.0 + # number of leap seconds at the start of the GPS epoch + _leaps_gps = 10.0 + # convert from dynamic time to TAI + TAI = np.atleast_1d(self.tt - _jd_gps) * self.day - _tt_tai + # calculate the number of leap seconds + leaps = count_leap_seconds(TAI - _tai_gps, truncate=False) + return leaps - _leaps_gps + _tai_gps + @timescale.utilities.reify def tdb(self): """Approximate Barycentric Dynamical Time (TDB) as Julian Days""" @@ -1178,12 +1200,10 @@ def ut1_utc(self): """ # dynamic time is ahead of TAI by 32.184 seconds _tt_tai = 32.184 - # TAI time is ahead of GPS by 19 seconds - _tai_gps = 19.0 # convert from delta times back to seconds _tt_ut1 = self.day * self.tt_ut1 # recalculate UT1-UTC (seconds) - return _tt_tai + _tai_gps + self.gps_utc - _tt_ut1 + return _tt_tai + self.tai_utc - _tt_ut1 @timescale.utilities.reify def T(self):