_R = (b'(?m)^......(.........) . '
      b'(.\\d.......)......... '
      b'(.\\d.......).........  '
      b'.(.\\d........)')
def parse_x_y_dut1_from_finals_all(f):
def install_polar_motion_table(ts, finals_data):
def build_timescale_arrays(utc_mjd, dut1):
def parse_dut1_from_finals_all(f): class Splines(object):
    def __init__(self, table):
        table = _to_array(table)
        if len(table.shape) < 2:  # Let caller provide a single row.
            table = table.reshape(table.shape + (1,))
        self.table = table
        self.lower = lower = table[0]
        self.upper = upper = table[1]
        self._width = upper - lower
        self._n = arange(len(lower))
        self.coefficients = table[2:]

    def __call__(self, x):
        i = interp(x, self.lower, self._n)
        i = i.astype(int)
        t = (x - self.lower[i]) / self._width[i]
        coefficients = iter(self.coefficients)
        value = next(coefficients)[i]
        for c in coefficients:
            value *= t
            value += c[i]
        return value

    @reify
    def derivative(self):
        columns = [self.table[0], self.table[1]]
        coefficients = self.table[2:-1]
        for i, c in enumerate(coefficients):
            n = len(coefficients) - i
            columns.append(n * c / self._width)
        return Splines(columns) def load_bundled_npy(filename):
    """Load a binary NumPy array file that is bundled with Skyfield."""
    data = get_data('skyfield', 'data/{0}'.format(filename))
    return load(BytesIO(data)) def _build_legacy_data(deltat_data, deltat_preds, leap_second_dat):
    data_end_time = deltat_data[0, -1]
    i = np.searchsorted(deltat_preds[0], data_end_time, side='right')
    delta_t_recent = np.concatenate([deltat_data, deltat_preds[:,i:]], axis=1)
    leap_dates, leap_offsets = leap_second_dat
    return delta_t_recent, leap_dates, leap_offsets def parse_deltat_data(fileobj):
    """Parse the United States Naval Observatory ``deltat.data`` file.

    Each line file gives the date and the value of Delta T::

    2016  2  1  68.1577

    This function returns a 2xN array of raw Julian dates and matching
    Delta T values.

    """
    array = np.loadtxt(fileobj)
    year, month, day, delta_t = array.T
    return np.array((julian_date(year, month, day), delta_t)) def parse_deltat_preds(fileobj):
    """Parse the United States Naval Observatory ``deltat.preds`` file.

    The old format supplies a floating point year, the value of Delta T,
    and one or two other fields::

    2015.75      67.97               0.210         0.02

    The new format adds a modified Julian day as the first field:

    58484.000  2019.00   69.34      -0.152       0.117

    This function returns a 2xN array of raw Julian dates and matching
    Delta T values.

    """
    lines = iter(fileobj)
    header = next(lines)

    if header.startswith(b'YEAR'):
        # Format in use until 2019 February
        next(lines)             # discard blank line
        year_float, delta_t = np.loadtxt(lines, usecols=[0, 1]).T
    else:
        # Format in use since 2019 February
        year_float, delta_t = np.loadtxt(lines, usecols=[1, 2]).T

    year = year_float.astype(int)
    month = 1 + (year_float * 12.0).astype(int) % 12
    return np.array((julian_date(year, month, 1), delta_t)) def parse_leap_seconds(fileobj):
    """Parse the IERS file ``Leap_Second.dat``.

    The leap dates array can be searched with::

        index = np.searchsorted(leap_dates, jd, 'right')

    The resulting index allows (TAI - UTC) to be fetched with::

        offset = leap_offsets[index]

    """
    lines = iter(fileobj)
    for line in lines:
        if line.startswith(b'#  File expires on'):
            break
    else:
        raise ValueError('Leap_Second.dat is missing its expiration date')
    line = line.decode('ascii')

    with _lock:
        original_locale = locale.setlocale(locale.LC_ALL)
        locale.setlocale(locale.LC_ALL, 'C')
        try:
            dt = datetime.strptime(line, '#  File expires on %d %B %Y\n')
        finally:
            locale.setlocale(locale.LC_ALL, original_locale)

    # The file went out of date at the beginning of July 2016, and kept
    # downloading every time a user ran a Skyfield program.  So we now
    # build in a grace period:
    grace_period = timedelta(days=30)
    expiration_date = dt.date() + grace_period
    mjd, day, month, year, offsets = np.loadtxt(lines).T
    leap_dates = np.ndarray(len(mjd) + 2)
    leap_dates[0] = '-inf'
    leap_dates[1:-1] = mjd + 2400000.5
    leap_dates[-1] = 'inf'
    leap_offsets = np.ndarray(len(mjd) + 2)
    leap_offsets[0] = leap_offsets[1] = offsets[0]
    leap_offsets[2:] = offsets
    return expiration_date, (leap_dates, leap_offsets) class SpiceKernel(object):
    """Ephemeris file in NASA .bsp format.

    A "Spacecraft and Planet Kernel" (SPK) file from NASA provides
    |xyz| coordinates for bodies in the Solar System like the Sun,
    planets, moons, and spacecraft.

    You can download a .bsp file yourself and use this class to open it,
    or use the Skyfield ``load()`` function to automatically download a
    popular ephemeris.  Once loaded, you can print this object to the
    screen to see a report on the segments that it includes:

    >>> planets = load('de421.bsp')
    >>> print(planets)
    SPICE kernel file 'de421.bsp' has 15 segments
      JD 2414864.50 - JD 2471184.50  (1899-07-28 through 2053-10-08)
          0 -> 1    SOLAR SYSTEM BARYCENTER -> MERCURY BARYCENTER
          0 -> 2    SOLAR SYSTEM BARYCENTER -> VENUS BARYCENTER
          0 -> 3    SOLAR SYSTEM BARYCENTER -> EARTH BARYCENTER
          0 -> 4    SOLAR SYSTEM BARYCENTER -> MARS BARYCENTER
          0 -> 5    SOLAR SYSTEM BARYCENTER -> JUPITER BARYCENTER
          0 -> 6    SOLAR SYSTEM BARYCENTER -> SATURN BARYCENTER
          0 -> 7    SOLAR SYSTEM BARYCENTER -> URANUS BARYCENTER
          0 -> 8    SOLAR SYSTEM BARYCENTER -> NEPTUNE BARYCENTER
          0 -> 9    SOLAR SYSTEM BARYCENTER -> PLUTO BARYCENTER
          0 -> 10   SOLAR SYSTEM BARYCENTER -> SUN
          3 -> 301  EARTH BARYCENTER -> MOON
          3 -> 399  EARTH BARYCENTER -> EARTH
          1 -> 199  MERCURY BARYCENTER -> MERCURY
          2 -> 299  VENUS BARYCENTER -> VENUS
          4 -> 499  MARS BARYCENTER -> MARS

    To retrieve the one or more vectors necessary to compute the
    position of a body relative to the Solar System barycenter, look up
    the body by its name or official SPICE identifying integer:

    >>> planets['earth']
    <VectorSum of 2 vectors:
     'de421.bsp' segment 0 SOLAR SYSTEM BARYCENTER -> 3 EARTH BARYCENTER
     'de421.bsp' segment 3 EARTH BARYCENTER -> 399 EARTH>
    >>> planets[499]
    <VectorSum of 2 vectors:
     'de421.bsp' segment 0 SOLAR SYSTEM BARYCENTER -> 4 MARS BARYCENTER
     'de421.bsp' segment 4 MARS BARYCENTER -> 499 MARS>

    The result will be a :class:`~skyfield.vectorlib.VectorFunction`
    instance that you can ask for a position at a given input time.

    """
    def __init__(self, path):
        self.path = path
        self.filename = os.path.basename(path)
        self.spk = SPK.open(path)
        self.segments = [SPICESegment(self, s) for s in self.spk.segments]
        self.codes = set(s.center for s in self.segments).union(
                         s.target for s in self.segments)

    def __repr__(self):
        return '<{0} {1!r}>'.format(type(self).__name__, self.path)

    def __str__(self):
        segments = self.spk.segments
        lines = ['SPICE kernel file {0!r} has {1} segments'
                 .format(self.filename, len(segments))]
        format_date = '{0}-{1:02}-{2:02}'.format
        start = end = None
        for s in segments:
            if start != s.start_jd or end != s.end_jd:
                start, end = s.start_jd, s.end_jd
                starts = format_date(*compute_calendar_date(int(start)))
                ends = format_date(*compute_calendar_date(int(end)))
                lines.append('  JD {0:.2f} - JD {1:.2f}  ({2} through {3})'
                             .format(start, end, starts, ends))
            lines.append(_format_segment(s))
        return '\n'.join(lines)

    def close(self):
        """Close this ephemeris file."""
        self.spk.close()

        # In practice, users are not confident the file is really closed
        # unless the metadata also disappears.
        del self.segments[:]
        self.codes.clear()

    def comments(self):
        """Return the comments string of this kernel.

        The resulting string often contains embedded newlines, and is
        formatted for a human reader.

        >>> print(planets.comments())
        ; de421.bsp LOG FILE
        ;
        ; Created 2008-02-12/11:33:34.00.
        ...
        LEAPSECONDS_FILE    = naif0007.tls
        SPK_FILE            = de421.bsp
        ...

        """
        return self.spk.comments()

    def names(self):
        """Return all target names that are valid with this kernel.

        >>> pprint(planets.names())
        {0: ['SOLAR_SYSTEM_BARYCENTER', 'SSB', 'SOLAR SYSTEM BARYCENTER'],
         1: ['MERCURY_BARYCENTER', 'MERCURY BARYCENTER'],
         2: ['VENUS_BARYCENTER', 'VENUS BARYCENTER'],
         3: ['EARTH_BARYCENTER',
             'EMB',
         ...

        The result is a dictionary with target code keys and name lists
        as values.  The last name in each list is the one that Skyfield
        uses when printing information about a body.

        """
        d = defaultdict(list)
        for code, name in target_name_pairs:
            if code in self.codes:
                d[code].append(name)
        return dict(d)

    def decode(self, name):
        """Translate a target name into its integer code.

        >>> planets.decode('Venus')
        299

        Raises ``ValueError`` if you supply an unknown name, or
        ``KeyError`` if the target is missing from this kernel.  You can
        supply an integer code if you already have one and just want to
        check whether it is present in this kernel.

        """
        if isinstance(name, int):
            code = name
        else:
            name = name.upper()
            code = _jpl_name_code_dict.get(name)
            if code is None:
                raise ValueError('unknown SPICE target {0!r}'.format(name))
        if code not in self.codes:
            targets = ', '.join(_format_code_and_name(c) for c in self.codes)
            raise KeyError('kernel {0!r} is missing {1!r} -'
                           ' the targets it supports are: {2}'
                           .format(self.filename, name, targets))
        return code

    def __getitem__(self, target):
        """Return a vector function for computing the location of `target`."""
        target = self.decode(target)
        segments = self.segments
        segment_dict = dict((segment.target, segment) for segment in segments)
        chain = tuple(_center(target, segment_dict))
        if len(chain) == 1:
            return chain[0]
        chain = chain[::-1]
        center = chain[0].center
        target = chain[-1].target
        return VectorSum(center, target, chain)

    def __contains__(self, name_or_code):
        if isinstance(name_or_code, int):
            code = name_or_code
        else:
            code = _jpl_name_code_dict.get(name_or_code.upper())
        return code in self.codes class EarthSatellite(VectorFunction):
    """An Earth satellite loaded from a TLE file and propagated with SGP4.

    An earth satellite object is a Skyfield vector function, so you can
    either call its ``at()`` method to generate its position in the sky
    or else use addition and subtraction to combine it with other
    vectors.

    Satellite parameters are generally only accurate for a week or two
    around the *epoch* of the parameters, the date for which they were
    generated, which is available as an attribute:

    ``epoch``
        A Skyfield :class:`~skyfield.timelib.Time` giving the exact
        epoch moment for these satellite orbit parameters.
    ``name``
        Satellite name

    When building a satellite, use the arguments ``line1`` and ``line2``
    to provide the two data lines from a TLE file as separate strings.
    Optional ``name`` lets you give a name to the satellite, accessible
    later through the ``name`` attribute.  ``ts`` is a
    :class:`~skyfield.timelib.Timescale` object, used to generate the
    ``epoch`` value; if it is not provided, the satellite will use a
    built in ``Timescale`` object.

    If you are interested in the catalog entry details, the SGP4 model
    parameters for a particular satellite can be accessed through its
    ``model`` attribute:

    ``model.satnum``
        The unique satellite NORAD catalog number given in the TLE file.
    ``model.classification``
        Satellite classification, or else ``'U'`` for “Unknown”
    ``model.intldesg``
        International designator
    ``model.epochyr``
        Full four-digit year of this element set's epoch moment.
    ``model.epochdays``
        Fractional days into the year of the epoch moment.
    ``model.jdsatepoch``
        Julian date of the epoch (computed from ``epochyr`` and ``epochdays``).
    ``model.ndot``
        First time derivative of the mean motion (ignored by SGP4).
    ``model.nddot``
        Second time derivative of the mean motion (ignored by SGP4).
    ``model.bstar``
        Ballistic drag coefficient B* in inverse earth radii.
    ``model.ephtype``
        Ephemeris type (ignored by SGP4 as determination now automatic)
    ``model.elnum``
        Element number
    ``model.inclo``
        Inclination in radians.
    ``model.nodeo``
        Right ascension of ascending node in radians.
    ``model.ecco``
        Eccentricity.
    ``model.argpo``
        Argument of perigee in radians.
    ``model.mo``
        Mean anomaly in radians.
    ``model.no_kozai``
        Mean motion in radians per minute.
    ``model.revnum``
        Revolution number at epoch [Revs]

    """
    center = 399
    ts = None  # see __init__()

    def __init__(self, line1, line2, name=None, ts=None):
        if ts is None:
            ts = self.ts
            if ts is None:
                from .api import load  # avoid import loop
                ts = EarthSatellite.ts = load.timescale()

        self.name = None if name is None else name.strip()
        satrec = Satrec.twoline2rv(line1, line2)
        self.model = satrec

        two_digit_year = satrec.epochyr
        if two_digit_year < 57:
            year = two_digit_year + 2000
        else:
            year = two_digit_year + 1900

        self.epoch = ts.utc(year, 1, satrec.epochdays)

        self._setup(satrec)

    def _setup(self, satrec):
        # If only I had not made __init__() specific to TLE lines, but
        # had put them in an alternate construtor instead, this would
        # simply have lived in __init__().  Alas!  I was so young then.

        self.target = -100000 - satrec.satnum

    @classmethod
    def from_satrec(cls, satrec, ts):
        """Build an EarthSatellite from a raw sgp4 Satrec object.

        This lets you provide raw numeric orbital elements instead of
        the text of a TLE set.  See :ref:`from-satrec` for detais.

        """
        self = cls.__new__(cls)
        self.model = satrec
        self.name = None

        # TODO: once sgp4 starts filling in epochyr and epochdays in
        # sgp4init(), the separate epoch code here and in __init__() can
        # be unified to always use epochyr and epochdays.
        whole, fraction = divmod(satrec.jdsatepoch, 1.0)
        year, month, day = compute_calendar_date(whole)
        day += 0.5  # convert integer Julian day into Julian date float
        self.epoch = ts.utc(year, month, day + fraction + satrec.jdsatepochF)

        self._setup(satrec)
        return self

    def __str__(self):
        return self.target_name

    @property
    def target_name(self):
        return '{0}{1}catalog #{2} epoch {3}'.format(
            self.name or '',
            ' ' if self.name else '',
            self.model.satnum,
            self.epoch.utc_strftime(),
        )

    def _position_and_velocity_TEME_km(self, t):
        """Return the raw true equator mean equinox (TEME) vectors from SGP4.

        Returns a tuple of NumPy arrays ``([x y z], [xdot ydot zdot])``
        expressed in kilometers and kilometers per second.  Note that we
        assume the TLE epoch to be a UTC date, per AIAA 2006-6753.

        """
        sat = self.model
        jd = t.whole
        fraction = t.tai_fraction - t._leap_seconds() / DAY_S

        if getattr(jd, 'shape', None):
            e, r, v = sat.sgp4_array(jd, fraction)
            messages = [SGP4_ERRORS[error] if error else None for error in e]
            return r.T, v.T, messages
        else:
            error, position, velocity = sat.sgp4(jd, fraction)
            message = SGP4_ERRORS[error] if error else None
            return array(position), array(velocity), message

    def ITRF_position_velocity_error(self, t):
        """Deprecated: use the TEME and ITRS frame objects instead."""
        # TODO: can we teach frame objects to figure out that the
        # transform TEME -> ITRS can not only skip the t.M rotation, but
        # can also subtract the angles of their two competing z-axis
        # rotations and call rot_z() only once instead of twice?
        rTEME, vTEME, error = self._position_and_velocity_TEME_km(t)
        rTEME /= AU_KM
        vTEME /= AU_KM
        vTEME *= DAY_S
        rITRF, vITRF = TEME_to_ITRF(t.whole, rTEME, vTEME, 0.0, 0.0,
                                    t.ut1_fraction)
        return rITRF, vITRF, error

    def _at(self, t):
        """Compute this satellite's GCRS position and velocity at time `t`."""
        r, v, error = self._position_and_velocity_TEME_km(t)
        r /= AU_KM
        v /= AU_KM
        v *= DAY_S
        R = _T(TEME.rotation_at(t))
        r = mxv(R, r)
        v = mxv(R, v)
        return r, v, None, error

    def find_events(self, topos, t0, t1, altitude_degrees=0.0):
        """Return the times at which the satellite rises, culminates, and sets.

        Searches between ``t0`` and ``t1``, which should each be a
        Skyfield :class:`~skyfield.timelib.Time` object, for passes of
        this satellite above the location ``topos`` that reach at least
        ``altitude_degrees`` above the horizon.

        Returns a tuple ``(t, events)`` whose first element is a
        :class:`~skyfield.timelib.Time` array and whose second element
        is an array of events:

        * 0 — Satellite rose above ``altitude_degrees``.
        * 1 — Satellite culminated and started to descend again.
        * 2 — Satellite fell below ``altitude_degrees``.

        Note that multiple culminations in a row are possible when,
        without setting, the satellite reaches a second peak altitude
        after descending partway down the sky from the first one.

        """
        # First, we find the moments of maximum altitude over the time
        # period.  Some of these maxima will be negative, meaning the
        # satellite failed to crest the horizon.

        ts = t0.ts
        at = (self - topos).at
        half_second = 0.5 / DAY_S
        orbits_per_minute = self.model.no_kozai / tau
        orbits_per_day = 24 * 60 * orbits_per_minute

        # Note the protection against zero orbits_per_day.
        # TODO: why isn't 3 samples per orbit enough?
        step_days = 0.05 / max(orbits_per_day, 1.0)

        # Long-period satellites might rise each day not because of
        # their own motion, but because the Earth rotates under them, so
        # check position at least each quarter-day.  We might need to
        # tighten this even further if experience someday shows it
        # missing a pass of a particular satellite.
        if step_days > 0.25:
            step_days = 0.25

        def cheat(t):
            """Avoid computing expensive values that cancel out anyway."""
            t.gast = t.tt * 0.0
            t.M = t.MT = _identity

        def altitude_at(t):
            cheat(t)
            return at(t).altaz()[0].degrees

        altitude_at.step_days = step_days
        tmax, altitude = find_maxima(t0, t1, altitude_at, half_second, 12)
        if not tmax:
            return tmax, ones_like(tmax)

        # Next, filter out the maxima that are not high enough.

        keepers = altitude >= altitude_degrees
        jdmax = tmax.tt[keepers]
        ones = ones_like(jdmax, 'uint8')

        # Finally, find the rising and setting that bracket each maximum
        # altitude.  We guess that the satellite will be back below the
        # horizon in between each pair of adjancent maxima.

        def below_horizon_at(t):
            cheat(t)
            return at(t).altaz()[0].degrees < altitude_degrees

        # The `jdo` array are the times of maxima, with their averages
        # in between them.  The start and end times are thrown in too,
        # in case a rising or setting is lingering out between a maxima
        # and the ends of our range.  Could this perhaps still miss a
        # stubborn rising or setting near the ends?
        doublets = repeat(concatenate(((t0.tt,), jdmax, (t1.tt,))), 2)
        jdo = (doublets[:-1] + doublets[1:]) / 2.0

        trs, rs = _find_discrete(t0.ts, jdo, below_horizon_at, half_second, 8)

        jd = concatenate((jdmax, trs.tt))
        v = concatenate((ones, rs * 2))

        i = jd.argsort()
        return ts.tt_jd(jd[i]), v[i] class Timescale(object):
    """The data necessary to express dates in different timescales.

    A `Timescale` provides time objects with the data tables they need
    to translate between different time scales: the schedule of UTC leap
    seconds, and the value of ∆T over time.  Most programs create a
    single `Timescale` which they use to build their `Time` objects:

    >>> from skyfield.api import load
    >>> ts = load.timescale()
    >>> t = ts.utc(1980, 3, 1, 9, 30)
    >>> t
    <Time tt=2444299.896425741>

    See :ref:`downloading-timescale-files` if you are interested in
    checking how recent the data is in the files loaded by the
    timescale.

    """
    _utcnow = datetime.utcnow
    polar_motion_table = None

    def __init__(self, delta_t_recent, leap_dates, leap_offsets):
        if callable(delta_t_recent):
            # Let a caller completely override our approach to ∆T by
            # passing a function of their own.
            self.delta_t_function = delta_t_recent
        else:
            self.delta_t_table = delta_t_recent  # so users can see it
            self.delta_t_function = build_delta_t(delta_t_recent)

        self.leap_dates, self.leap_offsets = leap_dates, leap_offsets
        self.J2000 = Time(self, float_(T0))
        self.B1950 = Time(self, float_(B1950))
        self.julian_calendar_cutoff = None

        # Our internal leap-second table has three columns:
        #
        # _leap_utc      Integer UTC seconds since JD 0 w/leap seconds missing.
        # _leap_offsets  Integer offset between UTC and TAI.
        # _leap_tai      Integer TAI seconds since JD 0.

        is_legacy_table = isinf(leap_dates[-1])
        if is_legacy_table:
            leap_dates = leap_dates[2:-1]
            leap_offsets = leap_offsets[3:]

        one_zero = [[-1,0]]
        self._leap_utc = (leap_dates[:,None] * DAY_S + one_zero).flatten()
        self._leap_offsets = (leap_offsets[:,None] + one_zero).flatten()
        self._leap_tai = self._leap_utc + self._leap_offsets

    def now(self):
        """Return the current date and time as a `Time` object.

        For the return value to be correct, your operating system time
        and timezone settings must be set so that the Python Standard
        Library constructor ``datetime.datetime.utcnow()`` returns a
        correct UTC date and time.

        """
        return self.from_datetime(self._utcnow().replace(tzinfo=utc))

    def from_datetime(self, datetime):
        """Return a `Time` for a Python ``datetime``.

        The ``datetime`` must be “timezone-aware”: it must have a time
        zone object as its ``tzinfo`` attribute instead of ``None``.

        .. versionadded:: 1.24

        """
        return self._utc(_datetime_to_utc_tuple(datetime))

    def from_datetimes(self, datetime_list):
        """Return a `Time` for a list of Python ``datetime`` objects.

        The ``datetime`` objects must each be “timezone-aware”: they
        must each have a time zone object as their ``tzinfo`` attribute
        instead of ``None``.

        .. versionadded:: 1.24

        """
        tuples = (_datetime_to_utc_tuple(d) for d in datetime_list)
        return self._utc(array(value) for value in zip(*tuples))

    def utc(self, year, month=1, day=1, hour=0, minute=0, second=0.0):
        """Build a `Time` from a UTC `calendar date`.

        .. versionadded:: 1.24
           Passing a Python ``datetime`` or a list of datetimes as the
           first argument has been deprecated (and was never supported
           for the other time scale methods).  Instead, use the methods
           :meth:`~skyfield.timelib.Timescale.from_datetime()` and
           :meth:`~skyfield.timelib.Timescale.from_datetimes()`.

        """
        # TODO: someday deprecate passing datetime objects here, as
        # there are now separate constructors for them.
        if isinstance(year, datetime):
            return self.from_datetime(year)
        if isinstance(year, date):
            return self.from_datetime(datetime.combine(year, _time_zero))
        if hasattr(year, '__len__') and isinstance(year[0], datetime):
            return self.from_datetimes(year)

        tup = year, month, day, hour, minute, second
        return self._utc(tup)

    def _utc(self, tup):
        # Build a Time from a UTC tuple, carefully preserving its exact
        # second number in the Time's hidden TAI seconds field.
        year, month, day, hour, minute, second = tup
        a = _to_array
        cutoff = self.julian_calendar_cutoff

        # Figure out exactly the TAI second number.
        seconds = (julian_day(a(year), a(month), a(day), cutoff) - 0.5) * DAY_S
        seconds, sfr = divmod(seconds, 1.0)  # in case there were any fractions
        seconds += interp(seconds, self._leap_utc, self._leap_offsets)
        more = a(hour) * 3600.0 + a(minute) * 60.0 + a(second)
        seconds2, sfr = divmod(sfr + more, 1.0)
        seconds += seconds2

        # For the other timescales, use the usual Julian date + fraction.
        whole, fraction = divmod(seconds, DAY_S)
        fraction += sfr
        fraction /= DAY_S

        t = Time(self, whole, fraction + tt_minus_tai)
        t.tai_fraction = fraction
        t._tai_seconds = seconds, sfr
        return t

    def _jd(self, year, month, day, hour, minute, second):
        a = _to_array
        cutoff = self.julian_calendar_cutoff
        whole = julian_day(a(year), a(month), a(day), cutoff) - 0.5
        fraction = (a(second) + a(minute) * 60.0 + a(hour) * 3600.0) / DAY_S
        return _reconcile(whole, fraction)

    def _cal(self, whole, fraction):
        return calendar_tuple(whole, fraction, self.julian_calendar_cutoff)

    def _strftime(self, format, jd, fraction, seconds_bump=None):
        # Python forces an unhappy choice upon us: either use the faster
        # time.strftime() and lose support for '%f', or use the slower
        # datetime.strftime() and crash if years are negative.  We take the
        # first option, but then patch '%f' support back in by secretly
        # passing the microseconds string as the time zone name.  After all,
        # the routines supported by this function never use time zones.
        # What could go wrong?

        offset, ms = _strftime_offset_seconds(format)
        fraction = fraction + offset / DAY_S
        year, month, day, hour, minute, second = self._cal(jd, fraction)
        z = year * 0

        # TODO: will this always produce the same whole number that
        # calendar_tuple() produces internally?  Or should we make a private
        # version of calendar_tuple() that returns it to us for use here?
        weekday = (fraction + 0.5 + _to_array(jd)).astype(int) % 7

        if _format_uses_day_of_year(format):
            start_of_year = julian_day(year, 1, 1, self.julian_calendar_cutoff)
            yday = (jd + fraction + 1.5 - start_of_year).astype(int)
        else:
            yday = z

        if ms:
            format = format[:ms.start()] + '%Z' + format[ms.end():]
            second = (second * 1e6).astype(int)
            second, usec = divmod(second, 1000000)
            if seconds_bump is not None:
                second += seconds_bump
            if getattr(jd, 'ndim', 0):
                u = ['%06d' % u for u in usec]
                tup = (year, month, day, hour, minute, second,
                       weekday, yday, z, u)
                return [strftime(format, struct_time(t)) for t in zip(*tup)]
            u = '%06d' % usec
            tup = year, month, day, hour, minute, second, weekday, yday, z, u
            return strftime(format, struct_time(tup))
        else:
            second = second.astype(int)
            if seconds_bump is not None:
                second += seconds_bump
            tup = year, month, day, hour, minute, second, weekday, yday, z
            if getattr(jd, 'ndim', 0):
                return [strftime(format, item) for item in zip(*tup)]
            return strftime(format, tup)

    def tai(self, year=None, month=1, day=1, hour=0, minute=0, second=0.0,
            jd=None):
        """Build a `Time` from an International Atomic Time `calendar date`.

        .. versionadded:: 1.6
           Passing a Julian date with ``jd=`` has been deprecated;
           instead, use :meth:`~skyfield.timelib.Timescale.tai_jd()`.

        """
        if jd is not None:
            return self.tai_jd(jd)  # deprecate someday
        whole, fraction = self._jd(year, month, day, hour, minute, second)
        t = Time(self, whole, fraction + tt_minus_tai)
        t.tai_fraction = fraction
        return t

    def tai_jd(self, jd, fraction=None):
        """Build a `Time` from an International Atomic Time Julian date."""
        jd, fraction = _normalize_jd_and_fraction(jd, fraction)
        t = Time(self, jd, fraction + tt_minus_tai)
        t.tai_fraction = fraction
        return t

    def tt(self, year=None, month=1, day=1, hour=0, minute=0, second=0.0,
           jd=None):
        """Build a `Time` from a Terrestrial Time `calendar date`.

        .. versionadded:: 1.6
           Passing a Julian date with ``jd=`` has been deprecated;
           instead, use :meth:`~skyfield.timelib.Timescale.tt_jd()`.

        """
        if jd is not None:
            return self.tt_jd(jd)  # deprecate someday
        whole, fraction = self._jd(year, month, day, hour, minute, second)
        return Time(self, whole, fraction)

    def tt_jd(self, jd, fraction=None):
        """Build a `Time` from a Terrestrial Time Julian date."""
        jd, fraction = _normalize_jd_and_fraction(jd, fraction)
        return Time(self, jd, fraction)

    def J(self, year):
        """Build a `Time` from a Terrestrial Time Julian year or array.

        Julian years are convenient uniform periods of exactly 365.25
        days of Terrestrial Time, centered on 2000 January 1 12h TT =
        Julian year 2000.0.

        """
        tt = _to_array(year) * 365.25 + 1721045.0
        return Time(self, tt, 0.0)

    def tdb(self, year=None, month=1, day=1, hour=0, minute=0, second=0.0,
            jd=None):
        """Build a `Time` from a Barycentric Dynamical Time `calendar date`.

        .. versionadded:: 1.6
           Passing a Julian date with ``jd=`` has been deprecated;
           instead, use :meth:`~skyfield.timelib.Timescale.tdb_jd()`.

        """
        if jd is not None:
            return self.tdb_jd(jd)  # deprecate someday
        whole, fraction = self._jd(year, month, day, hour, minute, second)
        jd = whole + fraction  # TODO: why do tests break if we pass separately
        return Time(self, jd, - tdb_minus_tt(jd) / DAY_S)

    def tdb_jd(self, jd, fraction=None):
        """Build a `Time` from a Barycentric Dynamical Time Julian date."""
        jd, fraction = _normalize_jd_and_fraction(jd, fraction)
        t = Time(self, jd, fraction - tdb_minus_tt(jd, fraction) / DAY_S)
        t.tdb_fraction = fraction
        return t

    def ut1(self, year=None, month=1, day=1, hour=0, minute=0, second=0.0,
            jd=None):
        """Build a `Time` from a UT1 Universal Time `calendar date`.

        .. versionadded:: 1.6
           Passing a Julian date with ``jd=`` has been deprecated;
           instead, use :meth:`~skyfield.timelib.Timescale.ut1_jd()`.

        """
        if jd is None:  # TODO: deprecate the jd parameter to this method
            whole, fraction = self._jd(year, month, day, hour, minute, second)
            jd = whole + fraction  # TODO: can we pass high precision on?
        return self.ut1_jd(jd)

    def ut1_jd(self, jd):
        """Build a `Time` from a UT1 Universal Time Julian date."""
        ut1 = _to_array(jd)

        # Estimate TT = UT1, to get a rough Delta T estimate.
        tt_approx = ut1
        delta_t_approx = self.delta_t_function(tt_approx)

        # Use the rough Delta T to make a much better estimate of TT,
        # then generate an even better Delta T.
        tt_approx = ut1 + delta_t_approx / DAY_S
        delta_t_approx = self.delta_t_function(tt_approx)

        # We can now estimate TT with an error of < 1e-9 seconds within
        # 10 centuries of either side of the present; for details, see:
        # https://github.com/skyfielders/astronomy-notebooks
        # and look for the notebook "error-in-timescale-ut1.ipynb".
        delta_t_approx /= DAY_S
        t = Time(self, ut1, delta_t_approx)
        t.ut1_fraction = 0.0 * ut1
        return t

    def from_astropy(self, t):
        """Build a Skyfield `Time` from an AstroPy time object."""
        return self.tt(jd=t.tt.jd)

    def linspace(self, t0, t1, num=50):
        """Return ``num`` times spaced uniformly between ``t0`` to ``t1``.

        This routine is named after, and powered by, the NumPy routine
        `linspace()`_.

        .. _linspace(): https://numpy.org/doc/stable/reference/generated/numpy.linspace.html

        """
        whole0 = t0.whole
        frac0 = t0.tt_fraction
        whole1 = t1.whole
        frac1 = t1.tt_fraction
        return Time(
            self,
            linspace(whole0, whole1, num),
            linspace(frac0, frac1, num),
        )
import itertools
import os
import errno
import sys
import certifi
import numpy as np
from fnmatch import fnmatch
from pkgutil import get_data
from time import time
from .data import iers
from .curvelib import Splines
from .functions import load_bundled_npy
from .io_timescale import (
    _build_legacy_data,
    parse_deltat_data, parse_deltat_preds, parse_leap_seconds,
)
from .jpllib import SpiceKernel
from .sgp4lib import EarthSatellite
from .timelib import Timescale
    from io import BytesIO
    from StringIO import StringIO as BytesIO
    from ssl import create_default_context
    from urllib.parse import urlparse
    from urllib.request import urlopen
    from urlparse import urlparse
    from urllib2 import urlopen

    def tle(self, url, reload=False, filename=None):
        """Load and parse a satellite TLE file.

        DEPRECATED: in a misguided attempt to be overly convenient, this
        routine builds an unweildy dictionary of satellites with keys of
        two different Python types: integer keys for satellite numbers,
        and string keys for satellite names. It even lists satellites
        like ``ISS (ZARYA)`` twice, in case the user wants to look them
        up by a single name like ``ZARYA``.  What a mess.  Users should
        instead call the simple ``tle_file()`` method, and themselves
        build any dictionaries they need.

        See the :meth:`~skyfield.iokit.Loader.open()` documentation for
        the meaning of the ``reload`` and ``filename`` parameters.

        """
        d = {}
        with self.open(url, reload=reload, filename=filename) as f:
            for names, sat in parse_tle(f):
                d[sat.model.satnum] = sat
                for name in names:
                    d[name] = sat
        return d

    def tle_file(self, url, reload=False, filename=None,
                 ts=None, skip_names=False):
        """Load and parse a TLE file, returning a list of Earth satellites.

        Given a URL or local path to an ASCII text file, this loads a
        series of TLE “Two-Line Element” sets and returns a list of
        :class:`~skyfield.sgp4lib.EarthSatellite` objects for them.
        See :doc:`earth-satellites`.

        See the :meth:`~skyfield.iokit.Loader.open()` method for the
        meaning of the ``reload`` and ``filename`` parameters.

        See the :meth:`parse_tle_file()` function for the meaning of the
        ``ts`` and ``skip_names`` parameters.

        """
        with self.open(url, reload=reload, filename=filename) as f:
            return list(parse_tle_file(f, ts, skip_names))

    def download(self, url, filename=None, backup=False):
        """Download a file, even if it’s already on disk; return its path.

        You can specify the local ``filename`` to which the file will be
        saved; the default is to use the final component of ``url``.
        Set ``backup`` to ``True`` if you want an already-existing file
        moved out of the way instead of overwritten.

        Your operating system may raise any of several errors during a
        download: hostname lookup failure (this is the usual symptom if
        you are disconnected from the Internet); the server refusing the
        connection; and the connection closing mid-download.  Skyfield
        makes no attempt to intercept or interpret these errors — which
        vary by operating system — so your application itself should
        catch network errors if it needs to avoid printing raw Python
        exceptions, or if you want to retry failed downloads.

        """
        if '://' not in url:
            url = self.build_url(url)
        if filename is None:
            filename = urlparse(url).path.split('/')[-1]
        path = self.path_to(filename)
        download(url, path, self.verbose, backup=backup)
        return path

    def open(self, url, mode='rb', reload=False, filename=None, backup=False):
        """Open a file, downloading it first if it does not yet exist.

        Unlike when you call a loader directly like ``my_loader()``,
        this ``my_loader.open()`` method does not attempt to parse or
        interpret the file; it simply returns an open file object.

        The ``url`` can be either an external URL, or else the path to a
        file on the current filesystem.  A relative path will be assumed
        to be relative to the base directory of this loader object.

        If a URL was provided and the ``reload`` parameter is true, then
        any existing file will be removed before the download starts.

        The ``filename`` parameter lets you specify an alternative local
        filename instead of having the filename extracted from the final
        component of the URL.

        """
        if '://' not in url:
            path_that_might_be_relative = url
            path = os.path.join(self.directory, path_that_might_be_relative)
            return open(path, mode)

        if filename is None:
            filename = urlparse(url).path.split('/')[-1]

        path = self._assure(url, filename, reload, backup)
        return open(path, mode)

    def timescale(self, delta_t=None, builtin=True):
        """Return a `Timescale` built using official Earth rotation data.

        ``delta_t`` — Lets you override the standard ∆T tables by
        providing your own ∆T offset in seconds.  For details, see
        :ref:`custom-delta-t`.

        ``builtin`` — By default, Skyfield uses ∆T and leap second
        tables that it carries internally; to instead load this data
        from files, set this option to ``False``.  For compatibility
        with Skyfield ≤ 1.30, if you have on disk the three files
        ``deltat.data``, ``deltat.preds``, and ``Leap_Second.dat``, then
        Skyfield will load them.  Otherwise, Skyfield will download and
        use ``finals2000A.all`` from the International Earth Rotation
        Service.  For details, see :ref:`downloading-timescale-files`.

        """
        e = self._exists
        if builtin:
            # See "build_arrays.py" for a notes on how these are stored.

            arrays = load_bundled_npy('iers.npz')