Source code for coordio.time

#!/usr/bin/env python
# -*- coding: utf-8 -*-
#
# @Author: José Sánchez-Gallego (gallegoj@uw.edu)
# @Date: 2020-08-16
# @Filename: time.py
# @License: BSD 3-clause (http://www.opensource.org/licenses/BSD-3-Clause)

import ctypes
import datetime
import time

import numpy

from . import sofa
from .exceptions import CoordIOError
from .iers import IERS


[docs]def utc_to_tai(jd): """Convert UTC to TAI. Wrapper around iauUtctai.""" utc1 = int(jd) utc2 = jd - utc1 tai1 = ctypes.c_double() tai2 = ctypes.c_double() res = sofa.iauUtctai(utc1, utc2, tai1, tai2) if res != 0: raise ValueError(f'iauUtctai return with error code {res}.') return tai1.value + tai2.value
[docs]class Time: """A time storage and conversion class. All times, regardless of the input format and scale are converted and stored as TAI Julian dates. Parameters ---------- value The input time value. If `None`, the current `datetime.datetime.now` system time will be used. format : str The format of the input time. scale : str The time scale of the input value. Valid values are ``UTC`` and ``TAI``. Attributes ---------- jd : float The Julian date in the TAI scale. """ def __init__(self, value=None, format='jd', scale='UTC'): if isinstance(value, datetime.datetime) or value is None: jd1, jd2 = sofa.get_internal_date(value, scale) self.jd = jd1 + jd2 elif format == 'jd': self.jd = value else: raise NotImplementedError('Not implemented format.') if scale == 'UTC': self.jd = utc_to_tai(self.jd) elif scale == 'TAI': pass else: raise CoordIOError('Invalid scale.') self._iers = None # Store the current time as a UNIX time. We'll use this later to know # how much time has passed and do a quick update to now. self.__internal_time = time.time() if value is None else None def __repr__(self): return f'<Time (JD={self.jd})>' @property def iers(self): """Gets the IERS table.""" if self._iers is None: self._iers = IERS() return self._iers @property def jd1(self): """Returns the integer part of the Julian data.""" return int(self.jd) @property def jd2(self): """Returns the fractional part of the Julian data.""" return self.jd - self.jd1 @property def mjd(self): """Returns the modified Julian date ``(JD-2400000.5)``.""" return self.jd - 2400000.5
[docs] def to_now(self, scale='UTC'): """Updates the internal value to the current TAI date. Parameters ---------- scale : str The scale of the system clock. Assumes it's UTC but it can be changed if the internal clock is synced to TAI or other timescale. Returns ------- now : `float` The new TAI JD. Also updates the internal value. """ # If __internal_time is set this means we initialised this # time as now(). We can simply add how long it has passed # since then. if self.__internal_time: new_now = time.time() delta_jd = (new_now - self.__internal_time) / 86400. self.jd += delta_jd self.__internal_time = new_now return self.jd # Otherwise reinitialise the object. self.__init__(scale=scale) return self.jd
[docs] def get_dut1(self): """Returns the delta UT1-UTC.""" if not self.iers: raise CoordIOError('IERS table not loaded.') return self.iers.get_delta_ut1_utc(jd=self.jd)
[docs] def to_utc(self): """Returns the date converted to JD in the UTC scale.""" utc1 = ctypes.c_double() utc2 = ctypes.c_double() res = sofa.iauTaiutc(self.jd1, self.jd2, utc1, utc2) if res != 0: raise ValueError(f'iauTaiutc return with error code {res}.') return utc1.value + utc2.value
[docs] def to_ut1(self): """Returns the date converted to JD in the UT1 scale.""" # DTA is UT1-UTC, which can be expressed as delta_UT1-delta_AT. # delta_UT1=UT1-UTC and is given by the IERS tables, in seconds. # This will update the table if it's out of date. delta_ut1 = self.iers.get_delta_ut1_utc(self.jd) # delta_AT=TAI-UTC and can be obtained with the iauDat function but # it's easier to calculate it using iauTaiutc and subtracting TAI. delta_at = self.jd - self.to_utc() # delta_UT1-TAI, in seconds, for iauTaiut1 dta = delta_ut1 - delta_at * 86400. ut1 = ctypes.c_double() ut2 = ctypes.c_double() res = sofa.iauTaiut1(self.jd1, self.jd2, dta, ut1, ut2) if res != 0: raise ValueError(f'iauTaiut1 return with error code {res}.') return ut1.value + ut2.value
[docs] def to_tt(self): """Returns the date converted to JD in the TT scale.""" return self.jd + 32.184 / 86400.
[docs] def to_tdb(self, longitude=0.0, latitude=0.0, altitude=0.0): """Returns the date converted to JD in the TDB scale. Parameters ---------- longitude : float The East-positive longitude of the site, in degrees. latitude : float The site latitude in degrees. altitude : float The altitude (elevation) of the site above see level, in meters. Defaults to zero meters. """ # We need to call iauDtdb that models the delta between TT and TDB. # It depends on the location of the observer on the Earth, and the # fractional part of the UT1 time. # Earth radius in km RE = 6378.1 rlong = numpy.radians(longitude) rlat = numpy.radians(latitude) # Distance from Earth spin axis (km) u = (RE + altitude / 1000.) * numpy.cos(rlat) # Distance north of equatorial plane (km) v = (RE + altitude / 1000.) * numpy.sin(rlat) ut1 = self.to_ut1() ut1_1 = ut1 - int(ut1) tt = self.to_tt() tt_1 = int(tt) tt_2 = tt - tt_1 delta_tdb_tt = sofa.iauDtdb(tt_1, tt_2, ut1_1, rlong, u, v) return tt + delta_tdb_tt / 86400.