123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227 |
- """Epoch module."""
- import operator
- import math
- import datetime as DT
- from matplotlib import cbook
- from matplotlib.dates import date2num
- class Epoch:
- # Frame conversion offsets in seconds
- # t(TO) = t(FROM) + allowed[ FROM ][ TO ]
- allowed = {
- "ET": {
- "UTC": +64.1839,
- },
- "UTC": {
- "ET": -64.1839,
- },
- }
- def __init__(self, frame, sec=None, jd=None, daynum=None, dt=None):
- """Create a new Epoch object.
- Build an epoch 1 of 2 ways:
- Using seconds past a Julian date:
- # Epoch('ET', sec=1e8, jd=2451545)
- or using a matplotlib day number
- # Epoch('ET', daynum=730119.5)
- = ERROR CONDITIONS
- - If the input units are not in the allowed list, an error is thrown.
- = INPUT VARIABLES
- - frame The frame of the epoch. Must be 'ET' or 'UTC'
- - sec The number of seconds past the input JD.
- - jd The Julian date of the epoch.
- - daynum The matplotlib day number of the epoch.
- - dt A python datetime instance.
- """
- if ((sec is None and jd is not None) or
- (sec is not None and jd is None) or
- (daynum is not None and
- (sec is not None or jd is not None)) or
- (daynum is None and dt is None and
- (sec is None or jd is None)) or
- (daynum is not None and dt is not None) or
- (dt is not None and (sec is not None or jd is not None)) or
- ((dt is not None) and not isinstance(dt, DT.datetime))):
- raise ValueError(
- "Invalid inputs. Must enter sec and jd together, "
- "daynum by itself, or dt (must be a python datetime).\n"
- "Sec = %s\n"
- "JD = %s\n"
- "dnum= %s\n"
- "dt = %s" % (sec, jd, daynum, dt))
- cbook._check_in_list(self.allowed, frame=frame)
- self._frame = frame
- if dt is not None:
- daynum = date2num(dt)
- if daynum is not None:
- # 1-JAN-0001 in JD = 1721425.5
- jd = float(daynum) + 1721425.5
- self._jd = math.floor(jd)
- self._seconds = (jd - self._jd) * 86400.0
- else:
- self._seconds = float(sec)
- self._jd = float(jd)
- # Resolve seconds down to [ 0, 86400)
- deltaDays = math.floor(self._seconds / 86400)
- self._jd += deltaDays
- self._seconds -= deltaDays * 86400.0
- def convert(self, frame):
- if self._frame == frame:
- return self
- offset = self.allowed[self._frame][frame]
- return Epoch(frame, self._seconds + offset, self._jd)
- def frame(self):
- return self._frame
- def julianDate(self, frame):
- t = self
- if frame != self._frame:
- t = self.convert(frame)
- return t._jd + t._seconds / 86400.0
- def secondsPast(self, frame, jd):
- t = self
- if frame != self._frame:
- t = self.convert(frame)
- delta = t._jd - jd
- return t._seconds + delta * 86400
- def __eq__(self, rhs):
- return self._cmp(rhs, operator.eq)
- def __ne__(self, rhs):
- return self._cmp(rhs, operator.ne)
- def __lt__(self, rhs):
- return self._cmp(rhs, operator.lt)
- def __le__(self, rhs):
- return self._cmp(rhs, operator.le)
- def __gt__(self, rhs):
- return self._cmp(rhs, operator.gt)
- def __ge__(self, rhs):
- return self._cmp(rhs, operator.ge)
- def _cmp(self, rhs, op):
- """Compare two Epoch's.
- = INPUT VARIABLES
- - rhs The Epoch to compare against.
- - op The function to do the comparison
- = RETURN VALUE
- - Returns op(self, rhs)
- """
- t = self
- if self._frame != rhs._frame:
- t = self.convert(rhs._frame)
- if t._jd != rhs._jd:
- return op(t._jd, rhs._jd)
- return op(t._seconds, rhs._seconds)
- def __add__(self, rhs):
- """Add a duration to an Epoch.
- = INPUT VARIABLES
- - rhs The Epoch to subtract.
- = RETURN VALUE
- - Returns the difference of ourselves and the input Epoch.
- """
- t = self
- if self._frame != rhs.frame():
- t = self.convert(rhs._frame)
- sec = t._seconds + rhs.seconds()
- return Epoch(t._frame, sec, t._jd)
- def __sub__(self, rhs):
- """Subtract two Epoch's or a Duration from an Epoch.
- Valid:
- Duration = Epoch - Epoch
- Epoch = Epoch - Duration
- = INPUT VARIABLES
- - rhs The Epoch to subtract.
- = RETURN VALUE
- - Returns either the duration between to Epoch's or the a new
- Epoch that is the result of subtracting a duration from an epoch.
- """
- # Delay-load due to circular dependencies.
- import matplotlib.testing.jpl_units as U
- # Handle Epoch - Duration
- if isinstance(rhs, U.Duration):
- return self + -rhs
- t = self
- if self._frame != rhs._frame:
- t = self.convert(rhs._frame)
- days = t._jd - rhs._jd
- sec = t._seconds - rhs._seconds
- return U.Duration(rhs._frame, days*86400 + sec)
- def __str__(self):
- """Print the Epoch."""
- return "%22.15e %s" % (self.julianDate(self._frame), self._frame)
- def __repr__(self):
- """Print the Epoch."""
- return str(self)
- @staticmethod
- def range(start, stop, step):
- """Generate a range of Epoch objects.
- Similar to the Python range() method. Returns the range [
- start, stop) at the requested step. Each element will be a
- Epoch object.
- = INPUT VARIABLES
- - start The starting value of the range.
- - stop The stop value of the range.
- - step Step to use.
- = RETURN VALUE
- - Returns a list containing the requested Epoch values.
- """
- elems = []
- i = 0
- while True:
- d = start + i * step
- if d >= stop:
- break
- elems.append(d)
- i += 1
- return elems
|