Nutze Skyfield fuer praezisere Daemmerungsberechnung
This commit is contained in:
Vendored
+205
@@ -0,0 +1,205 @@
|
||||
"""The Satellite class."""
|
||||
|
||||
from sgp4.alpha5 import from_alpha5
|
||||
from sgp4.earth_gravity import wgs72old, wgs72, wgs84
|
||||
from sgp4.ext import invjday, jday
|
||||
from sgp4.io import twoline2rv
|
||||
from sgp4.propagation import sgp4, sgp4init
|
||||
|
||||
WGS72OLD = 0
|
||||
WGS72 = 1
|
||||
WGS84 = 2
|
||||
gravity_constants = wgs72old, wgs72, wgs84 # indexed using enum values above
|
||||
minutes_per_day = 1440.
|
||||
|
||||
class Satrec(object):
|
||||
"""Slow Python-only version of the satellite object."""
|
||||
|
||||
# Approximate the behavior of the C-accelerated class by locking
|
||||
# down attribute access, to avoid folks accidentally writing code
|
||||
# against this class and adding extra attributes, then moving to a
|
||||
# computer where the C-accelerated class is used and having their
|
||||
# code suddenly produce errors.
|
||||
__slots__ = (
|
||||
'Om', 'a', 'alta', 'altp', 'am', 'argpdot', 'argpo', 'atime', 'aycof',
|
||||
'bstar', 'cc1', 'cc4', 'cc5', 'classification', 'con41', 'd2', 'd2201',
|
||||
'd2211', 'd3', 'd3210', 'd3222', 'd4', 'd4410', 'd4422', 'd5220',
|
||||
'd5232', 'd5421', 'd5433', 'dedt', 'del1', 'del2', 'del3', 'delmo',
|
||||
'didt', 'dmdt', 'dnodt', 'domdt', 'e3', 'ecco', 'ee2', 'elnum', 'em',
|
||||
'ephtype', 'epoch', 'epochdays', 'epochyr', 'error', 'error_message',
|
||||
'eta', 'gsto', 'im', 'inclo', 'init', 'intldesg', 'irez', 'isimp',
|
||||
'j2', 'j3', 'j3oj2', 'j4', 'jdsatepoch', 'mdot', 'method', 'mm', 'mo',
|
||||
'mu', 'nddot', 'ndot', 'nm', 'no_kozai', 'no_unkozai', 'nodecf',
|
||||
'nodedot', 'nodeo', 'om', 'omgcof', 'operationmode', 'peo', 'pgho',
|
||||
'pho', 'pinco', 'plo', 'radiusearthkm', 'revnum', 'satnum_str', 'se2',
|
||||
'se3', 'sgh2', 'sgh3', 'sgh4', 'sh2', 'sh3', 'si2', 'si3', 'sinmao',
|
||||
'sl2', 'sl3', 'sl4', 't', 't2cof', 't3cof', 't4cof', 't5cof', 'tumin',
|
||||
'x1mth2', 'x7thm1', 'xfact', 'xgh2', 'xgh3', 'xgh4',
|
||||
'xh2', 'xh3', 'xi2', 'xi3', 'xke', 'xl2', 'xl3', 'xl4', 'xlamo',
|
||||
'xlcof', 'xli', 'xmcof', 'xni', 'zmol', 'zmos',
|
||||
'jdsatepochF'
|
||||
)
|
||||
|
||||
array = None # replaced, if needed, with NumPy array()
|
||||
|
||||
def __init__(self):
|
||||
self.revnum = 0 # for consistency, since sgp4init() leaves this unset
|
||||
|
||||
@property
|
||||
def no(self):
|
||||
return self.no_kozai
|
||||
|
||||
@property
|
||||
def satnum(self):
|
||||
return from_alpha5(self.satnum_str)
|
||||
|
||||
@classmethod
|
||||
def twoline2rv(cls, line1, line2, whichconst=WGS72):
|
||||
whichconst = gravity_constants[whichconst]
|
||||
self = cls()
|
||||
twoline2rv(line1, line2, whichconst, 'i', self)
|
||||
|
||||
# Expose the same attribute types as the C++ code.
|
||||
self.ephtype = int(self.ephtype.strip() or '0')
|
||||
self.revnum = int(self.revnum)
|
||||
|
||||
# Install a fancy split JD of the kind the C++ natively supports.
|
||||
# We rebuild it from the TLE year and day to maintain precision.
|
||||
year = self.epochyr
|
||||
days, fraction = divmod(self.epochdays, 1.0)
|
||||
self.jdsatepoch = year * 365 + (year - 1) // 4 + days + 1721044.5
|
||||
self.jdsatepochF = round(fraction, 8) # exact number of digits in TLE
|
||||
|
||||
# Remove the legacy datetime "epoch", which is not provided by
|
||||
# the C++ version of the object.
|
||||
del self.epoch
|
||||
|
||||
# Undo my non-standard 4-digit year
|
||||
self.epochyr %= 100
|
||||
return self
|
||||
|
||||
def sgp4init(self, whichconst, opsmode, satnum, epoch, bstar,
|
||||
ndot, nddot, ecco, argpo, inclo, mo, no_kozai, nodeo):
|
||||
whichconst = gravity_constants[whichconst]
|
||||
whole, fraction = divmod(epoch, 1.0)
|
||||
whole_jd = whole + 2433281.5
|
||||
|
||||
# Go out on a limb: if `epoch` has no decimal digits past the 8
|
||||
# decimal places stored in a TLE, then assume the user is trying
|
||||
# to specify an exact decimal fraction.
|
||||
if round(epoch, 8) == epoch:
|
||||
fraction = round(fraction, 8)
|
||||
|
||||
self.jdsatepoch = whole_jd
|
||||
self.jdsatepochF = fraction
|
||||
|
||||
y, m, d, H, M, S = invjday(whole_jd)
|
||||
jan0 = jday(y, 1, 0, 0, 0, 0.0)
|
||||
self.epochyr = y % 100
|
||||
self.epochdays = whole_jd - jan0 + fraction
|
||||
|
||||
self.classification = 'U'
|
||||
|
||||
sgp4init(whichconst, opsmode, satnum, epoch, bstar, ndot, nddot,
|
||||
ecco, argpo, inclo, mo, no_kozai, nodeo, self)
|
||||
|
||||
def sgp4(self, jd, fr):
|
||||
tsince = ((jd - self.jdsatepoch) * minutes_per_day +
|
||||
(fr - self.jdsatepochF) * minutes_per_day)
|
||||
r, v = sgp4(self, tsince)
|
||||
return self.error, r, v
|
||||
|
||||
def sgp4_tsince(self, tsince):
|
||||
r, v = sgp4(self, tsince)
|
||||
return self.error, r, v
|
||||
|
||||
def sgp4_array(self, jd, fr):
|
||||
"""Compute positions and velocities for the times in a NumPy array.
|
||||
|
||||
Given NumPy arrays ``jd`` and ``fr`` of the same length that
|
||||
supply the whole part and the fractional part of one or more
|
||||
Julian dates, return a tuple ``(e, r, v)`` of three vectors:
|
||||
|
||||
* ``e``: nonzero for any dates that produced errors, 0 otherwise.
|
||||
* ``r``: position vectors in kilometers.
|
||||
* ``v``: velocity vectors in kilometers per second.
|
||||
|
||||
"""
|
||||
# Import NumPy the first time sgp4_array() is called.
|
||||
array = self.array
|
||||
if array is None:
|
||||
from numpy import array
|
||||
Satrec.array = array
|
||||
|
||||
results = []
|
||||
z = list(zip(jd, fr))
|
||||
for jd_i, fr_i in z:
|
||||
results.append(self.sgp4(jd_i, fr_i))
|
||||
elist, rlist, vlist = zip(*results)
|
||||
|
||||
e = array(elist)
|
||||
r = array(rlist)
|
||||
v = array(vlist)
|
||||
|
||||
r.shape = v.shape = len(jd), 3
|
||||
return e, r, v
|
||||
|
||||
class SatrecArray(object):
|
||||
"""Slow Python-only version of the satellite array."""
|
||||
|
||||
__slots__ = ('_satrecs',)
|
||||
|
||||
array = None # replaced with NumPy array(), if the user tries calling
|
||||
|
||||
def __init__(self, satrecs):
|
||||
self._satrecs = satrecs
|
||||
# Import NumPy the first time a SatrecArray is instantiated.
|
||||
if self.array is None:
|
||||
from numpy import array
|
||||
SatrecArray.array = array
|
||||
|
||||
def sgp4(self, jd, fr):
|
||||
"""Compute positions and velocities for the satellites in this array.
|
||||
|
||||
Given NumPy scalars or arrays ``jd`` and ``fr`` supplying the
|
||||
whole part and the fractional part of one or more Julian dates,
|
||||
return a tuple ``(e, r, v)`` of three vectors that are each as
|
||||
long as ``jd`` and ``fr``:
|
||||
|
||||
* ``e``: nonzero for any dates that produced errors, 0 otherwise.
|
||||
* ``r``: (x,y,z) position vector in kilometers.
|
||||
* ``v``: (dx,dy,dz) velocity vector in kilometers per second.
|
||||
|
||||
"""
|
||||
results = []
|
||||
z = list(zip(jd, fr))
|
||||
for satrec in self._satrecs:
|
||||
for jd_i, fr_i in z:
|
||||
results.append(satrec.sgp4(jd_i, fr_i))
|
||||
elist, rlist, vlist = zip(*results)
|
||||
|
||||
e = self.array(elist)
|
||||
r = self.array(rlist)
|
||||
v = self.array(vlist)
|
||||
|
||||
jdlen = len(jd)
|
||||
mylen = len(self._satrecs)
|
||||
e.shape = (mylen, jdlen)
|
||||
r.shape = v.shape = (mylen, jdlen, 3)
|
||||
|
||||
return e, r, v
|
||||
|
||||
class Satellite(object):
|
||||
"""The old Satellite object, for compatibility with sgp4 1.x."""
|
||||
jdsatepochF = 0.0 # for compatibility with new Satrec; makes tests simpler
|
||||
|
||||
def propagate(self, year, month=1, day=1, hour=0, minute=0, second=0.0):
|
||||
"""Return a position and velocity vector for a given date and time."""
|
||||
|
||||
j = jday(year, month, day, hour, minute, second)
|
||||
m = (j - self.jdsatepoch) * minutes_per_day
|
||||
r, v = sgp4(self, m)
|
||||
return r, v
|
||||
|
||||
no = Satrec.no
|
||||
satnum = Satrec.satnum
|
||||
Reference in New Issue
Block a user