sgp4-1.4/0000750000175000017500000000000012456126333013323 5ustar brandonbrandon00000000000000sgp4-1.4/PKG-INFO0000640000175000017500000001277312456126333014433 0ustar brandonbrandon00000000000000Metadata-Version: 1.1 Name: sgp4 Version: 1.4 Summary: Track earth satellite TLE orbits using up-to-date 2010 version of SGP4 Home-page: https://github.com/brandon-rhodes/python-sgp4 Author: Brandon Rhodes Author-email: brandon@rhodesmill.org License: MIT Description: This Python package computes the position and velocity of an earth-orbiting satellite, given the satellite's TLE orbital elements from a source like `Celestrak `_. It implements the most recent version of SGP4, and is regularly run against the SGP4 test suite to make sure that its satellite position predictions **agree to within 0.1 mm** of the predictions of the standard C++ implementation of the algorithm. This error is far less than the 1–3 km/day by which satellites themselves deviate from the ideal orbits described in TLE files. The C++ function names have been retained, since users may already be familiar with this library in other languages. Here is how to compute the x,y,z position and velocity for Vanguard 1 at 12:50:19 on 29 June 2000: >>> from sgp4.earth_gravity import wgs72 >>> from sgp4.io import twoline2rv >>> >>> line1 = ('1 00005U 58002B 00179.78495062 ' ... '.00000023 00000-0 28098-4 0 4753') >>> line2 = ('2 00005 34.2682 348.7242 1859667 ' ... '331.7664 19.3264 10.82419157413667') >>> >>> satellite = twoline2rv(line1, line2, wgs72) >>> position, velocity = satellite.propagate( ... 2000, 6, 29, 12, 50, 19) >>> >>> print(satellite.error) # nonzero on error 0 >>> print(satellite.error_message) None >>> print(position) (5576.056952..., -3999.371134..., -1521.957159...) >>> print(velocity) (4.772627..., 5.119817..., 4.275553...) The position vector measures the satellite position in **kilometers** from the center of the earth. The velocity is the rate at which those three parameters are changing, expressed in **kilometers per second**. There are three gravity models available that you can import from the ``earth_gravity`` module: * ``wgs72`` * ``wgs72old`` * ``wgs84`` The ``wgs72`` model seems to be the most commonly used in the satellite tracking community, and is probably the model behind most TLE elements that are available for download. The ``twoline2rv()`` function returns a ``Satellite`` object whose attributes carry the data loaded from the TLE entry: * Unique satellite number, as given in the TLE file. >>> satellite.satnum 5 * The epoch of the element set, expressed three ways: as the integer year plus the floating point number of days into the year; as a floating-point Julian date; and as Python ``datetime`` object. >>> satellite.epochyr 2000 >>> satellite.epochdays 179.78495062 >>> satellite.jdsatepoch 2451723.28495062 >>> satellite.epoch datetime.datetime(2000, 6, 27, 18, 50, 19, 733567) This implementation passes all of the automated tests in the August 2010 release of the reference implementation of SGP4 by Vallado et al., who originally published their revision of SGP4 in 2006: Vallado, David A., Paul Crawford, Richard Hujsak, and T.S. Kelso, “Revisiting Spacetrack Report #3,” presented at the AIAA/AAS Astrodynamics Specialist Conference, Keystone, CO, 2006 August 21–24. If you would like to review the paper, it is `available online `_. You can always download the latest version of their code for comparison against this Python module (or other implementations) at `AIAA-2006-6753.zip `_. This module was adapted from Vallado's C++ code since its revision date was the most recently updated SGP4 implementation in their zip file: * C++, August 2010 * Fortran, August 2008 * Pascal, August 2008 * Matlab, May 2008 * Java, July 2005 Changelog --------- | 2015-01-15 — 1.4 — Display detailed help when TLE input does not match format. | 2014-06-26 — 1.3 — Return ``(NaN,NaN,NaN)`` vectors on error and set ``.error_message`` | 2013-11-29 — 1.2 — Made ``epochyr`` 4 digits; add ``datetime`` for ``.epoch`` | 2012-11-22 — 1.1 — Python 3 compatibility; more documentation | 2012-08-27 — 1.0 — Initial release Platform: UNKNOWN Classifier: Development Status :: 5 - Production/Stable Classifier: Intended Audience :: Science/Research Classifier: License :: OSI Approved :: MIT License Classifier: Programming Language :: Python :: 2 Classifier: Programming Language :: Python :: 2.6 Classifier: Programming Language :: Python :: 2.7 Classifier: Programming Language :: Python :: 3 Classifier: Programming Language :: Python :: 3.2 Classifier: Programming Language :: Python :: 3.3 Classifier: Programming Language :: Python :: 3.4 Classifier: Topic :: Scientific/Engineering :: Astronomy sgp4-1.4/sgp4/0000750000175000017500000000000012456126333014200 5ustar brandonbrandon00000000000000sgp4-1.4/sgp4/io.py0000640000175000017500000002110212456125333015155 0ustar brandonbrandon00000000000000"""Read the TLE earth satellite file format. This is a minimally-edited copy of "sgp4io.cpp". """ import re from datetime import datetime from math import pi, pow from sgp4.ext import days2mdhms, jday from sgp4.model import Satellite from sgp4.propagation import sgp4init INT_RE = re.compile(r'[+-]?\d*') FLOAT_RE = re.compile(r'[+-]?\d*(\.\d*)?') LINE1 = '1 NNNNNC NNNNNAAA NNNNN.NNNNNNNN +.NNNNNNNN +NNNNN-N +NNNNN-N N NNNNN' LINE2 = '2 NNNNN NNN.NNNN NNN.NNNN NNNNNNN NNN.NNNN NNN.NNNN NN.NNNNNNNNNNNNNN' error_message = """TLE format error The Two-Line Element (TLE) format was designed for punch cards and is therefore very strict about the position of every space and digit in a TLE line. Your line does not quite match. Here is the official format for line {} followed by the line you provided: {} {}""" """ /* ---------------------------------------------------------------- * * sgp4io.cpp * * this file contains a function to read two line element sets. while * not formerly part of the sgp4 mathematical theory, it is * required for practical implemenation. * * companion code for * fundamentals of astrodynamics and applications * 2007 * by david vallado * * (w) 719-573-2600, email dvallado@agi.com * * current : * 27 Aug 10 david vallado * fix input format and delete unused variables in twoline2rv * changes : * 3 sep 08 david vallado * add operationmode for afspc (a) or improved (i) * 9 may 07 david vallado * fix year correction to 57 * 27 mar 07 david vallado * misc fixes to manual inputs * 14 aug 06 david vallado * original baseline * ---------------------------------------------------------------- */ """ """ /* ----------------------------------------------------------------------------- * * function twoline2rv * * this function converts the two line element set character string data to * variables and initializes the sgp4 variables. several intermediate varaibles * and quantities are determined. note that the result is a structure so multiple * satellites can be processed simultaneously without having to reinitialize. the * verification mode is an important option that permits quick checks of any * changes to the underlying technical theory. this option works using a * modified tle file in which the start, stop, and delta time values are * included at the end of the second line of data. this only works with the * verification mode. the catalog mode simply propagates from -1440 to 1440 min * from epoch and is useful when performing entire catalog runs. * * author : david vallado 719-573-2600 1 mar 2001 * * inputs : * longstr1 - first line of the tle * longstr2 - second line of the tle * typerun - type of run verification 'v', catalog 'c', * manual 'm' * typeinput - type of manual input mfe 'm', epoch 'e', dayofyr 'd' * opsmode - mode of operation afspc or improved 'a', 'i' * whichconst - which set of constants to use 72, 84 * * outputs : * satrec - structure containing all the sgp4 satellite information * * coupling : * getgravconst- * days2mdhms - conversion of days to month, day, hour, minute, second * jday - convert day month year hour minute second into julian date * sgp4init - initialize the sgp4 variables * * references : * norad spacetrack report #3 * vallado, crawford, hujsak, kelso 2006 --------------------------------------------------------------------------- */ """ def twoline2rv(longstr1, longstr2, whichconst, afspc_mode=False): """Return a Satellite imported from two lines of TLE data. Provide the two TLE lines as strings `longstr1` and `longstr2`, and select which standard set of gravitational constants you want by providing `gravity_constants`: `sgp4.earth_gravity.wgs72` - Standard WGS 72 model `sgp4.earth_gravity.wgs84` - More recent WGS 84 model `sgp4.earth_gravity.wgs72old` - Legacy support for old SGP4 behavior Normally, computations are made using various recent improvements to the algorithm. If you want to turn some of these off and go back into "afspc" mode, then set `afspc_mode` to `True`. """ opsmode = 'a' if afspc_mode else 'i' deg2rad = pi / 180.0; # 0.0174532925199433 xpdotp = 1440.0 / (2.0 *pi); # 229.1831180523293 tumin = whichconst.tumin satrec = Satellite() satrec.error = 0; satrec.whichconst = whichconst # Python extension: remembers its consts line = longstr1.rstrip() try: assert line.startswith('1 ') satrec.satnum = int(line[2:7]) # classification = line[7] or 'U' assert line[8] == ' ' # intldesg = line[9:17] two_digit_year = int(line[18:20]) assert line[23] == '.' satrec.epochdays = float(line[20:32]) assert line[32] == ' ' assert line[34] == '.' satrec.ndot = float(line[33:43]) assert line[43] == ' ' satrec.nddot = float(line[44] + '.' + line[45:50]) nexp = int(line[50:52]) assert line[52] == ' ' satrec.bstar = float(line[53] + '.' + line[54:59]) ibexp = int(line[59:61]) assert line[61] == ' ' assert line[63] == ' ' # numb = int(line[62]) # elnum = int(line[64:68]) except (AssertionError, IndexError, ValueError): raise ValueError(error_message.format(1, LINE1, line)) line = longstr2.rstrip() try: assert line.startswith('2 ') satrec.satnum = int(line[2:7]) # TODO: check it matches line 1? assert line[7] == ' ' assert line[11] == '.' satrec.inclo = float(line[8:16]) assert line[16] == ' ' assert line[20] == '.' satrec.nodeo = float(line[17:25]) assert line[25] == ' ' satrec.ecco = float('0.' + line[26:33].replace(' ', '0')) assert line[33] == ' ' assert line[37] == '.' satrec.argpo = float(line[34:42]) assert line[42] == ' ' assert line[46] == '.' satrec.mo = float(line[43:51]) assert line[51] == ' ' satrec.no = float(line[52:63]) #revnum = line[63:68] except (AssertionError, IndexError, ValueError): raise ValueError(error_message.format(2, LINE2, line)) # ---- find no, ndot, nddot ---- satrec.no = satrec.no / xpdotp; # rad/min satrec.nddot= satrec.nddot * pow(10.0, nexp); satrec.bstar= satrec.bstar * pow(10.0, ibexp); # ---- convert to sgp4 units ---- satrec.a = pow( satrec.no*tumin , (-2.0/3.0) ); satrec.ndot = satrec.ndot / (xpdotp*1440.0); # ? * minperday satrec.nddot= satrec.nddot / (xpdotp*1440.0*1440); # ---- find standard orbital elements ---- satrec.inclo = satrec.inclo * deg2rad; satrec.nodeo = satrec.nodeo * deg2rad; satrec.argpo = satrec.argpo * deg2rad; satrec.mo = satrec.mo * deg2rad; satrec.alta = satrec.a*(1.0 + satrec.ecco) - 1.0; satrec.altp = satrec.a*(1.0 - satrec.ecco) - 1.0; """ // ---------------------------------------------------------------- // find sgp4epoch time of element set // remember that sgp4 uses units of days from 0 jan 1950 (sgp4epoch) // and minutes from the epoch (time) // ---------------------------------------------------------------- // ---------------- temp fix for years from 1957-2056 ------------------- // --------- correct fix will occur when year is 4-digit in tle --------- """ if two_digit_year < 57: year = two_digit_year + 2000; else: year = two_digit_year + 1900; mon,day,hr,minute,sec = days2mdhms(year, satrec.epochdays); sec_whole, sec_fraction = divmod(sec, 1.0) satrec.epochyr = year satrec.jdsatepoch = jday(year,mon,day,hr,minute,sec); satrec.epoch = datetime(year, mon, day, hr, minute, int(sec_whole), int(sec_fraction * 1000000.0 // 1.0)) # ---------------- initialize the orbit at sgp4epoch ------------------- sgp4init(whichconst, opsmode, satrec.satnum, satrec.jdsatepoch-2433281.5, satrec.bstar, satrec.ecco, satrec.argpo, satrec.inclo, satrec.mo, satrec.no, satrec.nodeo, satrec) return satrec sgp4-1.4/sgp4/earth_gravity.py0000640000175000017500000000056712245716260017433 0ustar brandonbrandon00000000000000"""Three earth-gravity models for use with SGP4.""" from collections import namedtuple from sgp4.propagation import getgravconst EarthGravity = namedtuple( 'EarthGravity', 'tumin mu radiusearthkm xke j2 j3 j4 j3oj2', ) wgs72old = EarthGravity(*getgravconst('wgs72old')) wgs72 = EarthGravity(*getgravconst('wgs72')) wgs84 = EarthGravity(*getgravconst('wgs84')) sgp4-1.4/sgp4/propagation.py0000640000175000017500000021420712423537774017115 0ustar brandonbrandon00000000000000"""The sgp4 procedures for analytical propagation of a satellite. I have made the rather unorthodox decision to leave as much of this C++ code alone as possible: if a line of code would run without change in Python, then I refused to re-indent it or remove its terminal semicolon, so that in the future it will be easier to keep updating this file as the original author's C++ continues to improve. Thus, 5-space indentation (!) prevails in this file. I have even kept all of the C++ block comments (by turning them into Python string constants) to make this easier to navigate and maintain, as well as to make it more informative for people who encounter this code for the first time here in its Python form. | - Brandon Rhodes | Common Grounds Coffee House, Bluffton, Ohio | On a very hot August day in 2012 """ from math import atan2, cos, fabs, fmod, pi, sin, sqrt deg2rad = pi / 180.0; _nan = float('NaN') false = (_nan, _nan, _nan) true = True twopi = 2.0 * pi """ /* ---------------------------------------------------------------- * * sgp4unit.cpp * * this file contains the sgp4 procedures for analytical propagation * of a satellite. the code was originally released in the 1980 and 1986 * spacetrack papers. a detailed discussion of the theory and history * may be found in the 2006 aiaa paper by vallado, crawford, hujsak, * and kelso. * * companion code for * fundamentals of astrodynamics and applications * 2007 * by david vallado * * (w) 719-573-2600, email dvallado@agi.com * * current : * 30 Aug 10 david vallado * delete unused variables in initl * replace pow inetger 2, 3 with multiplies for speed * changes : * 3 Nov 08 david vallado * put returns in for error codes * 29 sep 08 david vallado * fix atime for faster operation in dspace * add operationmode for afspc (a) or improved (i) * performance mode * 16 jun 08 david vallado * update small eccentricity check * 16 nov 07 david vallado * misc fixes for better compliance * 20 apr 07 david vallado * misc fixes for constants * 11 aug 06 david vallado * chg lyddane choice back to strn3, constants, misc doc * 15 dec 05 david vallado * misc fixes * 26 jul 05 david vallado * fixes for paper * note that each fix is preceded by a * comment with "sgp4fix" and an explanation of * what was changed * 10 aug 04 david vallado * 2nd printing baseline working * 14 may 01 david vallado * 2nd edition baseline * 80 norad * original baseline * ---------------------------------------------------------------- */ """ """ /* ----------------------------------------------------------------------------- * * procedure dpper * * this procedure provides deep space long period periodic contributions * to the mean elements. by design, these periodics are zero at epoch. * this used to be dscom which included initialization, but it's really a * recurring function. * * author : david vallado 719-573-2600 28 jun 2005 * * inputs : * e3 - * ee2 - * peo - * pgho - * pho - * pinco - * plo - * se2 , se3 , sgh2, sgh3, sgh4, sh2, sh3, si2, si3, sl2, sl3, sl4 - * t - * xh2, xh3, xi2, xi3, xl2, xl3, xl4 - * zmol - * zmos - * ep - eccentricity 0.0 - 1.0 * inclo - inclination - needed for lyddane modification * nodep - right ascension of ascending node * argpp - argument of perigee * mp - mean anomaly * * outputs : * ep - eccentricity 0.0 - 1.0 * inclp - inclination * nodep - right ascension of ascending node * argpp - argument of perigee * mp - mean anomaly * * locals : * alfdp - * betdp - * cosip , sinip , cosop , sinop , * dalf - * dbet - * dls - * f2, f3 - * pe - * pgh - * ph - * pinc - * pl - * sel , ses , sghl , sghs , shl , shs , sil , sinzf , sis , * sll , sls * xls - * xnoh - * zf - * zm - * * coupling : * none. * * references : * hoots, roehrich, norad spacetrack report #3 1980 * hoots, norad spacetrack report #6 1986 * hoots, schumacher and glover 2004 * vallado, crawford, hujsak, kelso 2006 ----------------------------------------------------------------------------*/ """ def _dpper(satrec, inclo, init, ep, inclp, nodep, argpp, mp, opsmode): # Copy satellite attributes into local variables for convenience # and symmetry in writing formulae. e3 = satrec.e3 ee2 = satrec.ee2 peo = satrec.peo pgho = satrec.pgho pho = satrec.pho pinco = satrec.pinco plo = satrec.plo se2 = satrec.se2 se3 = satrec.se3 sgh2 = satrec.sgh2 sgh3 = satrec.sgh3 sgh4 = satrec.sgh4 sh2 = satrec.sh2 sh3 = satrec.sh3 si2 = satrec.si2 si3 = satrec.si3 sl2 = satrec.sl2 sl3 = satrec.sl3 sl4 = satrec.sl4 t = satrec.t xgh2 = satrec.xgh2 xgh3 = satrec.xgh3 xgh4 = satrec.xgh4 xh2 = satrec.xh2 xh3 = satrec.xh3 xi2 = satrec.xi2 xi3 = satrec.xi3 xl2 = satrec.xl2 xl3 = satrec.xl3 xl4 = satrec.xl4 zmol = satrec.zmol zmos = satrec.zmos # ---------------------- constants ----------------------------- zns = 1.19459e-5; zes = 0.01675; znl = 1.5835218e-4; zel = 0.05490; # --------------- calculate time varying periodics ----------- zm = zmos + zns * t; # be sure that the initial call has time set to zero if init == 'y': zm = zmos; zf = zm + 2.0 * zes * sin(zm); sinzf = sin(zf); f2 = 0.5 * sinzf * sinzf - 0.25; f3 = -0.5 * sinzf * cos(zf); ses = se2* f2 + se3 * f3; sis = si2 * f2 + si3 * f3; sls = sl2 * f2 + sl3 * f3 + sl4 * sinzf; sghs = sgh2 * f2 + sgh3 * f3 + sgh4 * sinzf; shs = sh2 * f2 + sh3 * f3; zm = zmol + znl * t; if init == 'y': zm = zmol; zf = zm + 2.0 * zel * sin(zm); sinzf = sin(zf); f2 = 0.5 * sinzf * sinzf - 0.25; f3 = -0.5 * sinzf * cos(zf); sel = ee2 * f2 + e3 * f3; sil = xi2 * f2 + xi3 * f3; sll = xl2 * f2 + xl3 * f3 + xl4 * sinzf; sghl = xgh2 * f2 + xgh3 * f3 + xgh4 * sinzf; shll = xh2 * f2 + xh3 * f3; pe = ses + sel; pinc = sis + sil; pl = sls + sll; pgh = sghs + sghl; ph = shs + shll; if init == 'n': pe = pe - peo; pinc = pinc - pinco; pl = pl - plo; pgh = pgh - pgho; ph = ph - pho; inclp = inclp + pinc; ep = ep + pe; sinip = sin(inclp); cosip = cos(inclp); """ /* ----------------- apply periodics directly ------------ */ // sgp4fix for lyddane choice // strn3 used original inclination - this is technically feasible // gsfc used perturbed inclination - also technically feasible // probably best to readjust the 0.2 limit value and limit discontinuity // 0.2 rad = 11.45916 deg // use next line for original strn3 approach and original inclination // if (inclo >= 0.2) // use next line for gsfc version and perturbed inclination """ if inclp >= 0.2: ph /= sinip pgh -= cosip * ph argpp += pgh nodep += ph mp += pl else: # ---- apply periodics with lyddane modification ---- sinop = sin(nodep); cosop = cos(nodep); alfdp = sinip * sinop; betdp = sinip * cosop; dalf = ph * cosop + pinc * cosip * sinop; dbet = -ph * sinop + pinc * cosip * cosop; alfdp = alfdp + dalf; betdp = betdp + dbet; nodep = fmod(nodep, twopi); # sgp4fix for afspc written intrinsic functions # nodep used without a trigonometric function ahead if nodep < 0.0 and opsmode == 'a': nodep = nodep + twopi; xls = mp + argpp + pl + pgh + (cosip - pinc * sinip) * nodep xnoh = nodep; nodep = atan2(alfdp, betdp); # sgp4fix for afspc written intrinsic functions # nodep used without a trigonometric function ahead if nodep < 0.0 and opsmode == 'a': nodep = nodep + twopi; if fabs(xnoh - nodep) > pi: if nodep < xnoh: nodep = nodep + twopi; else: nodep = nodep - twopi; mp += pl argpp = xls - mp - cosip * nodep; return ep, inclp, nodep, argpp, mp """ /*----------------------------------------------------------------------------- * * procedure dscom * * this procedure provides deep space common items used by both the secular * and periodics subroutines. input is provided as shown. this routine * used to be called dpper, but the functions inside weren't well organized. * * author : david vallado 719-573-2600 28 jun 2005 * * inputs : * epoch - * ep - eccentricity * argpp - argument of perigee * tc - * inclp - inclination * nodep - right ascension of ascending node * np - mean motion * * outputs : * sinim , cosim , sinomm , cosomm , snodm , cnodm * day - * e3 - * ee2 - * em - eccentricity * emsq - eccentricity squared * gam - * peo - * pgho - * pho - * pinco - * plo - * rtemsq - * se2, se3 - * sgh2, sgh3, sgh4 - * sh2, sh3, si2, si3, sl2, sl3, sl4 - * s1, s2, s3, s4, s5, s6, s7 - * ss1, ss2, ss3, ss4, ss5, ss6, ss7, sz1, sz2, sz3 - * sz11, sz12, sz13, sz21, sz22, sz23, sz31, sz32, sz33 - * xgh2, xgh3, xgh4, xh2, xh3, xi2, xi3, xl2, xl3, xl4 - * nm - mean motion * z1, z2, z3, z11, z12, z13, z21, z22, z23, z31, z32, z33 - * zmol - * zmos - * * locals : * a1, a2, a3, a4, a5, a6, a7, a8, a9, a10 - * betasq - * cc - * ctem, stem - * x1, x2, x3, x4, x5, x6, x7, x8 - * xnodce - * xnoi - * zcosg , zsing , zcosgl , zsingl , zcosh , zsinh , zcoshl , zsinhl , * zcosi , zsini , zcosil , zsinil , * zx - * zy - * * coupling : * none. * * references : * hoots, roehrich, norad spacetrack report #3 1980 * hoots, norad spacetrack report #6 1986 * hoots, schumacher and glover 2004 * vallado, crawford, hujsak, kelso 2006 ----------------------------------------------------------------------------*/ """ def _dscom( epoch, ep, argpp, tc, inclp, nodep, np, e3, ee2, peo, pgho, pho, pinco, plo, se2, se3, sgh2, sgh3, sgh4, sh2, sh3, si2, si3, sl2, sl3, sl4, xgh2, xgh3, xgh4, xh2, xh3, xi2, xi3, xl2, xl3, xl4, zmol, zmos, ): # -------------------------- constants ------------------------- zes = 0.01675; zel = 0.05490; c1ss = 2.9864797e-6; c1l = 4.7968065e-7; zsinis = 0.39785416; zcosis = 0.91744867; zcosgs = 0.1945905; zsings = -0.98088458; # --------------------- local variables ------------------------ nm = np; em = ep; snodm = sin(nodep); cnodm = cos(nodep); sinomm = sin(argpp); cosomm = cos(argpp); sinim = sin(inclp); cosim = cos(inclp); emsq = em * em; betasq = 1.0 - emsq; rtemsq = sqrt(betasq); # ----------------- initialize lunar solar terms --------------- peo = 0.0; pinco = 0.0; plo = 0.0; pgho = 0.0; pho = 0.0; day = epoch + 18261.5 + tc / 1440.0; xnodce = (4.5236020 - 9.2422029e-4 * day) % twopi stem = sin(xnodce); ctem = cos(xnodce); zcosil = 0.91375164 - 0.03568096 * ctem; zsinil = sqrt(1.0 - zcosil * zcosil); zsinhl = 0.089683511 * stem / zsinil; zcoshl = sqrt(1.0 - zsinhl * zsinhl); gam = 5.8351514 + 0.0019443680 * day; zx = 0.39785416 * stem / zsinil; zy = zcoshl * ctem + 0.91744867 * zsinhl * stem; zx = atan2(zx, zy); zx = gam + zx - xnodce; zcosgl = cos(zx); zsingl = sin(zx); # ------------------------- do solar terms --------------------- zcosg = zcosgs; zsing = zsings; zcosi = zcosis; zsini = zsinis; zcosh = cnodm; zsinh = snodm; cc = c1ss; xnoi = 1.0 / nm; for lsflg in 1, 2: a1 = zcosg * zcosh + zsing * zcosi * zsinh; a3 = -zsing * zcosh + zcosg * zcosi * zsinh; a7 = -zcosg * zsinh + zsing * zcosi * zcosh; a8 = zsing * zsini; a9 = zsing * zsinh + zcosg * zcosi * zcosh; a10 = zcosg * zsini; a2 = cosim * a7 + sinim * a8; a4 = cosim * a9 + sinim * a10; a5 = -sinim * a7 + cosim * a8; a6 = -sinim * a9 + cosim * a10; x1 = a1 * cosomm + a2 * sinomm; x2 = a3 * cosomm + a4 * sinomm; x3 = -a1 * sinomm + a2 * cosomm; x4 = -a3 * sinomm + a4 * cosomm; x5 = a5 * sinomm; x6 = a6 * sinomm; x7 = a5 * cosomm; x8 = a6 * cosomm; z31 = 12.0 * x1 * x1 - 3.0 * x3 * x3; z32 = 24.0 * x1 * x2 - 6.0 * x3 * x4; z33 = 12.0 * x2 * x2 - 3.0 * x4 * x4; z1 = 3.0 * (a1 * a1 + a2 * a2) + z31 * emsq; z2 = 6.0 * (a1 * a3 + a2 * a4) + z32 * emsq; z3 = 3.0 * (a3 * a3 + a4 * a4) + z33 * emsq; z11 = -6.0 * a1 * a5 + emsq * (-24.0 * x1 * x7-6.0 * x3 * x5); z12 = -6.0 * (a1 * a6 + a3 * a5) + emsq * \ (-24.0 * (x2 * x7 + x1 * x8) - 6.0 * (x3 * x6 + x4 * x5)); z13 = -6.0 * a3 * a6 + emsq * (-24.0 * x2 * x8 - 6.0 * x4 * x6); z21 = 6.0 * a2 * a5 + emsq * (24.0 * x1 * x5 - 6.0 * x3 * x7); z22 = 6.0 * (a4 * a5 + a2 * a6) + emsq * \ (24.0 * (x2 * x5 + x1 * x6) - 6.0 * (x4 * x7 + x3 * x8)); z23 = 6.0 * a4 * a6 + emsq * (24.0 * x2 * x6 - 6.0 * x4 * x8); z1 = z1 + z1 + betasq * z31; z2 = z2 + z2 + betasq * z32; z3 = z3 + z3 + betasq * z33; s3 = cc * xnoi; s2 = -0.5 * s3 / rtemsq; s4 = s3 * rtemsq; s1 = -15.0 * em * s4; s5 = x1 * x3 + x2 * x4; s6 = x2 * x3 + x1 * x4; s7 = x2 * x4 - x1 * x3; # ----------------------- do lunar terms ------------------- if lsflg == 1: ss1 = s1; ss2 = s2; ss3 = s3; ss4 = s4; ss5 = s5; ss6 = s6; ss7 = s7; sz1 = z1; sz2 = z2; sz3 = z3; sz11 = z11; sz12 = z12; sz13 = z13; sz21 = z21; sz22 = z22; sz23 = z23; sz31 = z31; sz32 = z32; sz33 = z33; zcosg = zcosgl; zsing = zsingl; zcosi = zcosil; zsini = zsinil; zcosh = zcoshl * cnodm + zsinhl * snodm; zsinh = snodm * zcoshl - cnodm * zsinhl; cc = c1l; zmol = (4.7199672 + 0.22997150 * day - gam) % twopi zmos = (6.2565837 + 0.017201977 * day) % twopi # ------------------------ do solar terms ---------------------- se2 = 2.0 * ss1 * ss6; se3 = 2.0 * ss1 * ss7; si2 = 2.0 * ss2 * sz12; si3 = 2.0 * ss2 * (sz13 - sz11); sl2 = -2.0 * ss3 * sz2; sl3 = -2.0 * ss3 * (sz3 - sz1); sl4 = -2.0 * ss3 * (-21.0 - 9.0 * emsq) * zes; sgh2 = 2.0 * ss4 * sz32; sgh3 = 2.0 * ss4 * (sz33 - sz31); sgh4 = -18.0 * ss4 * zes; sh2 = -2.0 * ss2 * sz22; sh3 = -2.0 * ss2 * (sz23 - sz21); # ------------------------ do lunar terms ---------------------- ee2 = 2.0 * s1 * s6; e3 = 2.0 * s1 * s7; xi2 = 2.0 * s2 * z12; xi3 = 2.0 * s2 * (z13 - z11); xl2 = -2.0 * s3 * z2; xl3 = -2.0 * s3 * (z3 - z1); xl4 = -2.0 * s3 * (-21.0 - 9.0 * emsq) * zel; xgh2 = 2.0 * s4 * z32; xgh3 = 2.0 * s4 * (z33 - z31); xgh4 = -18.0 * s4 * zel; xh2 = -2.0 * s2 * z22; xh3 = -2.0 * s2 * (z23 - z21); return ( snodm, cnodm, sinim, cosim, sinomm, cosomm,day, e3, ee2, em, emsq, gam, peo, pgho, pho, pinco, plo, rtemsq, se2, se3, sgh2, sgh3, sgh4, sh2, sh3, si2, si3, sl2, sl3, sl4, s1, s2, s3, s4, s5, s6, s7, ss1, ss2, ss3, ss4, ss5, ss6, ss7, sz1, sz2, sz3, sz11, sz12, sz13, sz21, sz22, sz23, sz31, sz32, sz33, xgh2, xgh3, xgh4, xh2, xh3, xi2, xi3, xl2, xl3, xl4, nm, z1, z2, z3, z11, z12, z13, z21, z22, z23, z31, z32, z33, zmol, zmos ) """ /*----------------------------------------------------------------------------- * * procedure dsinit * * this procedure provides deep space contributions to mean motion dot due * to geopotential resonance with half day and one day orbits. * * author : david vallado 719-573-2600 28 jun 2005 * * inputs : * cosim, sinim- * emsq - eccentricity squared * argpo - argument of perigee * s1, s2, s3, s4, s5 - * ss1, ss2, ss3, ss4, ss5 - * sz1, sz3, sz11, sz13, sz21, sz23, sz31, sz33 - * t - time * tc - * gsto - greenwich sidereal time rad * mo - mean anomaly * mdot - mean anomaly dot (rate) * no - mean motion * nodeo - right ascension of ascending node * nodedot - right ascension of ascending node dot (rate) * xpidot - * z1, z3, z11, z13, z21, z23, z31, z33 - * eccm - eccentricity * argpm - argument of perigee * inclm - inclination * mm - mean anomaly * xn - mean motion * nodem - right ascension of ascending node * * outputs : * em - eccentricity * argpm - argument of perigee * inclm - inclination * mm - mean anomaly * nm - mean motion * nodem - right ascension of ascending node * irez - flag for resonance 0-none, 1-one day, 2-half day * atime - * d2201, d2211, d3210, d3222, d4410, d4422, d5220, d5232, d5421, d5433 - * dedt - * didt - * dmdt - * dndt - * dnodt - * domdt - * del1, del2, del3 - * ses , sghl , sghs , sgs , shl , shs , sis , sls * theta - * xfact - * xlamo - * xli - * xni * * locals : * ainv2 - * aonv - * cosisq - * eoc - * f220, f221, f311, f321, f322, f330, f441, f442, f522, f523, f542, f543 - * g200, g201, g211, g300, g310, g322, g410, g422, g520, g521, g532, g533 - * sini2 - * temp - * temp1 - * theta - * xno2 - * * coupling : * getgravconst * * references : * hoots, roehrich, norad spacetrack report #3 1980 * hoots, norad spacetrack report #6 1986 * hoots, schumacher and glover 2004 * vallado, crawford, hujsak, kelso 2006 ----------------------------------------------------------------------------*/ """ def _dsinit( whichconst, cosim, emsq, argpo, s1, s2, s3, s4, s5, sinim, ss1, ss2, ss3, ss4, ss5, sz1, sz3, sz11, sz13, sz21, sz23, sz31, sz33, t, tc, gsto, mo, mdot, no, nodeo, nodedot, xpidot, z1, z3, z11, z13, z21, z23, z31, z33, ecco, eccsq, em, argpm, inclm, mm, nm, nodem, irez, atime, d2201, d2211, d3210, d3222, d4410, d4422, d5220, d5232, d5421, d5433, dedt, didt, dmdt, dnodt, domdt, del1, del2, del3, xfact, xlamo, xli, xni, ): q22 = 1.7891679e-6; q31 = 2.1460748e-6; q33 = 2.2123015e-7; root22 = 1.7891679e-6; root44 = 7.3636953e-9; root54 = 2.1765803e-9; rptim = 4.37526908801129966e-3; # equates to 7.29211514668855e-5 rad/sec root32 = 3.7393792e-7; root52 = 1.1428639e-7; x2o3 = 2.0 / 3.0; znl = 1.5835218e-4; zns = 1.19459e-5; # sgp4fix identify constants and allow alternate values xke = whichconst.xke # -------------------- deep space initialization ------------ irez = 0; if 0.0034906585 < nm < 0.0052359877: irez = 1; if 8.26e-3 <= nm <= 9.24e-3 and em >= 0.5: irez = 2; # ------------------------ do solar terms ------------------- ses = ss1 * zns * ss5; sis = ss2 * zns * (sz11 + sz13); sls = -zns * ss3 * (sz1 + sz3 - 14.0 - 6.0 * emsq); sghs = ss4 * zns * (sz31 + sz33 - 6.0); shs = -zns * ss2 * (sz21 + sz23); # sgp4fix for 180 deg incl if inclm < 5.2359877e-2 or inclm > pi - 5.2359877e-2: shs = 0.0; if sinim != 0.0: shs = shs / sinim; sgs = sghs - cosim * shs; # ------------------------- do lunar terms ------------------ dedt = ses + s1 * znl * s5; didt = sis + s2 * znl * (z11 + z13); dmdt = sls - znl * s3 * (z1 + z3 - 14.0 - 6.0 * emsq); sghl = s4 * znl * (z31 + z33 - 6.0); shll = -znl * s2 * (z21 + z23); # sgp4fix for 180 deg incl if inclm < 5.2359877e-2 or inclm > pi - 5.2359877e-2: shll = 0.0; domdt = sgs + sghl; dnodt = shs; if sinim != 0.0: domdt = domdt - cosim / sinim * shll; dnodt = dnodt + shll / sinim; # ----------- calculate deep space resonance effects -------- dndt = 0.0; theta = (gsto + tc * rptim) % twopi em = em + dedt * t; inclm = inclm + didt * t; argpm = argpm + domdt * t; nodem = nodem + dnodt * t; mm = mm + dmdt * t; """ // sgp4fix for negative inclinations // the following if statement should be commented out //if (inclm < 0.0) // { // inclm = -inclm; // argpm = argpm - pi; // nodem = nodem + pi; // } """ # -------------- initialize the resonance terms ------------- if irez != 0: aonv = pow(nm / xke, x2o3); # ---------- geopotential resonance for 12 hour orbits ------ if irez == 2: cosisq = cosim * cosim; emo = em; em = ecco; emsqo = emsq; emsq = eccsq; eoc = em * emsq; g201 = -0.306 - (em - 0.64) * 0.440; if em <= 0.65: g211 = 3.616 - 13.2470 * em + 16.2900 * emsq; g310 = -19.302 + 117.3900 * em - 228.4190 * emsq + 156.5910 * eoc; g322 = -18.9068 + 109.7927 * em - 214.6334 * emsq + 146.5816 * eoc; g410 = -41.122 + 242.6940 * em - 471.0940 * emsq + 313.9530 * eoc; g422 = -146.407 + 841.8800 * em - 1629.014 * emsq + 1083.4350 * eoc; g520 = -532.114 + 3017.977 * em - 5740.032 * emsq + 3708.2760 * eoc; else: g211 = -72.099 + 331.819 * em - 508.738 * emsq + 266.724 * eoc; g310 = -346.844 + 1582.851 * em - 2415.925 * emsq + 1246.113 * eoc; g322 = -342.585 + 1554.908 * em - 2366.899 * emsq + 1215.972 * eoc; g410 = -1052.797 + 4758.686 * em - 7193.992 * emsq + 3651.957 * eoc; g422 = -3581.690 + 16178.110 * em - 24462.770 * emsq + 12422.520 * eoc; if em > 0.715: g520 =-5149.66 + 29936.92 * em - 54087.36 * emsq + 31324.56 * eoc; else: g520 = 1464.74 - 4664.75 * em + 3763.64 * emsq; if em < 0.7: g533 = -919.22770 + 4988.6100 * em - 9064.7700 * emsq + 5542.21 * eoc; g521 = -822.71072 + 4568.6173 * em - 8491.4146 * emsq + 5337.524 * eoc; g532 = -853.66600 + 4690.2500 * em - 8624.7700 * emsq + 5341.4 * eoc; else: g533 =-37995.780 + 161616.52 * em - 229838.20 * emsq + 109377.94 * eoc; g521 =-51752.104 + 218913.95 * em - 309468.16 * emsq + 146349.42 * eoc; g532 =-40023.880 + 170470.89 * em - 242699.48 * emsq + 115605.82 * eoc; sini2= sinim * sinim; f220 = 0.75 * (1.0 + 2.0 * cosim+cosisq); f221 = 1.5 * sini2; f321 = 1.875 * sinim * (1.0 - 2.0 * cosim - 3.0 * cosisq); f322 = -1.875 * sinim * (1.0 + 2.0 * cosim - 3.0 * cosisq); f441 = 35.0 * sini2 * f220; f442 = 39.3750 * sini2 * sini2; f522 = 9.84375 * sinim * (sini2 * (1.0 - 2.0 * cosim- 5.0 * cosisq) + 0.33333333 * (-2.0 + 4.0 * cosim + 6.0 * cosisq) ); f523 = sinim * (4.92187512 * sini2 * (-2.0 - 4.0 * cosim + 10.0 * cosisq) + 6.56250012 * (1.0+2.0 * cosim - 3.0 * cosisq)); f542 = 29.53125 * sinim * (2.0 - 8.0 * cosim+cosisq * (-12.0 + 8.0 * cosim + 10.0 * cosisq)); f543 = 29.53125 * sinim * (-2.0 - 8.0 * cosim+cosisq * (12.0 + 8.0 * cosim - 10.0 * cosisq)); xno2 = nm * nm; ainv2 = aonv * aonv; temp1 = 3.0 * xno2 * ainv2; temp = temp1 * root22; d2201 = temp * f220 * g201; d2211 = temp * f221 * g211; temp1 = temp1 * aonv; temp = temp1 * root32; d3210 = temp * f321 * g310; d3222 = temp * f322 * g322; temp1 = temp1 * aonv; temp = 2.0 * temp1 * root44; d4410 = temp * f441 * g410; d4422 = temp * f442 * g422; temp1 = temp1 * aonv; temp = temp1 * root52; d5220 = temp * f522 * g520; d5232 = temp * f523 * g532; temp = 2.0 * temp1 * root54; d5421 = temp * f542 * g521; d5433 = temp * f543 * g533; xlamo = (mo + nodeo + nodeo-theta - theta) % twopi xfact = mdot + dmdt + 2.0 * (nodedot + dnodt - rptim) - no; em = emo; emsq = emsqo; # ---------------- synchronous resonance terms -------------- if irez == 1: g200 = 1.0 + emsq * (-2.5 + 0.8125 * emsq); g310 = 1.0 + 2.0 * emsq; g300 = 1.0 + emsq * (-6.0 + 6.60937 * emsq); f220 = 0.75 * (1.0 + cosim) * (1.0 + cosim); f311 = 0.9375 * sinim * sinim * (1.0 + 3.0 * cosim) - 0.75 * (1.0 + cosim); f330 = 1.0 + cosim; f330 = 1.875 * f330 * f330 * f330; del1 = 3.0 * nm * nm * aonv * aonv; del2 = 2.0 * del1 * f220 * g200 * q22; del3 = 3.0 * del1 * f330 * g300 * q33 * aonv; del1 = del1 * f311 * g310 * q31 * aonv; xlamo = (mo + nodeo + argpo - theta) % twopi xfact = mdot + xpidot - rptim + dmdt + domdt + dnodt - no; # ------------ for sgp4, initialize the integrator ---------- xli = xlamo; xni = no; atime = 0.0; nm = no + dndt; return ( em, argpm, inclm, mm, nm, nodem, irez, atime, d2201, d2211, d3210, d3222, d4410, d4422, d5220, d5232, d5421, d5433, dedt, didt, dmdt, dndt, dnodt, domdt, del1, del2, del3, xfact, xlamo, xli, xni, ) """ /*----------------------------------------------------------------------------- * * procedure dspace * * this procedure provides deep space contributions to mean elements for * perturbing third body. these effects have been averaged over one * revolution of the sun and moon. for earth resonance effects, the * effects have been averaged over no revolutions of the satellite. * (mean motion) * * author : david vallado 719-573-2600 28 jun 2005 * * inputs : * d2201, d2211, d3210, d3222, d4410, d4422, d5220, d5232, d5421, d5433 - * dedt - * del1, del2, del3 - * didt - * dmdt - * dnodt - * domdt - * irez - flag for resonance 0-none, 1-one day, 2-half day * argpo - argument of perigee * argpdot - argument of perigee dot (rate) * t - time * tc - * gsto - gst * xfact - * xlamo - * no - mean motion * atime - * em - eccentricity * ft - * argpm - argument of perigee * inclm - inclination * xli - * mm - mean anomaly * xni - mean motion * nodem - right ascension of ascending node * * outputs : * atime - * em - eccentricity * argpm - argument of perigee * inclm - inclination * xli - * mm - mean anomaly * xni - * nodem - right ascension of ascending node * dndt - * nm - mean motion * * locals : * delt - * ft - * theta - * x2li - * x2omi - * xl - * xldot - * xnddt - * xndt - * xomi - * * coupling : * none - * * references : * hoots, roehrich, norad spacetrack report #3 1980 * hoots, norad spacetrack report #6 1986 * hoots, schumacher and glover 2004 * vallado, crawford, hujsak, kelso 2006 ----------------------------------------------------------------------------*/ """ def _dspace( irez, d2201, d2211, d3210, d3222, d4410, d4422, d5220, d5232, d5421, d5433, dedt, del1, del2, del3, didt, dmdt, dnodt, domdt, argpo, argpdot, t, tc, gsto, xfact, xlamo, no, atime, em, argpm, inclm, xli, mm, xni, nodem, nm, ): fasx2 = 0.13130908; fasx4 = 2.8843198; fasx6 = 0.37448087; g22 = 5.7686396; g32 = 0.95240898; g44 = 1.8014998; g52 = 1.0508330; g54 = 4.4108898; rptim = 4.37526908801129966e-3; # equates to 7.29211514668855e-5 rad/sec stepp = 720.0; stepn = -720.0; step2 = 259200.0; # ----------- calculate deep space resonance effects ----------- dndt = 0.0; theta = (gsto + tc * rptim) % twopi em = em + dedt * t; inclm = inclm + didt * t; argpm = argpm + domdt * t; nodem = nodem + dnodt * t; mm = mm + dmdt * t; """ // sgp4fix for negative inclinations // the following if statement should be commented out // if (inclm < 0.0) // { // inclm = -inclm; // argpm = argpm - pi; // nodem = nodem + pi; // } /* - update resonances : numerical (euler-maclaurin) integration - */ /* ------------------------- epoch restart ---------------------- */ // sgp4fix for propagator problems // the following integration works for negative time steps and periods // the specific changes are unknown because the original code was so convoluted // sgp4fix take out atime = 0.0 and fix for faster operation """ ft = 0.0; if irez != 0: # sgp4fix streamline check if atime == 0.0 or t * atime <= 0.0 or fabs(t) < fabs(atime): atime = 0.0; xni = no; xli = xlamo; # sgp4fix move check outside loop if t > 0.0: delt = stepp; else: delt = stepn; iretn = 381; # added for do loop iret = 0; # added for loop while iretn == 381: # ------------------- dot terms calculated ------------- # ----------- near - synchronous resonance terms ------- if irez != 2: xndt = del1 * sin(xli - fasx2) + del2 * sin(2.0 * (xli - fasx4)) + \ del3 * sin(3.0 * (xli - fasx6)); xldot = xni + xfact; xnddt = del1 * cos(xli - fasx2) + \ 2.0 * del2 * cos(2.0 * (xli - fasx4)) + \ 3.0 * del3 * cos(3.0 * (xli - fasx6)); xnddt = xnddt * xldot; else: # --------- near - half-day resonance terms -------- xomi = argpo + argpdot * atime; x2omi = xomi + xomi; x2li = xli + xli; xndt = (d2201 * sin(x2omi + xli - g22) + d2211 * sin(xli - g22) + d3210 * sin(xomi + xli - g32) + d3222 * sin(-xomi + xli - g32)+ d4410 * sin(x2omi + x2li - g44)+ d4422 * sin(x2li - g44) + d5220 * sin(xomi + xli - g52) + d5232 * sin(-xomi + xli - g52)+ d5421 * sin(xomi + x2li - g54) + d5433 * sin(-xomi + x2li - g54)); xldot = xni + xfact; xnddt = (d2201 * cos(x2omi + xli - g22) + d2211 * cos(xli - g22) + d3210 * cos(xomi + xli - g32) + d3222 * cos(-xomi + xli - g32) + d5220 * cos(xomi + xli - g52) + d5232 * cos(-xomi + xli - g52) + 2.0 * (d4410 * cos(x2omi + x2li - g44) + d4422 * cos(x2li - g44) + d5421 * cos(xomi + x2li - g54) + d5433 * cos(-xomi + x2li - g54))); xnddt = xnddt * xldot; # ----------------------- integrator ------------------- # sgp4fix move end checks to end of routine if fabs(t - atime) >= stepp: iret = 0; iretn = 381; else: ft = t - atime; iretn = 0; if iretn == 381: xli = xli + xldot * delt + xndt * step2; xni = xni + xndt * delt + xnddt * step2; atime = atime + delt; nm = xni + xndt * ft + xnddt * ft * ft * 0.5; xl = xli + xldot * ft + xndt * ft * ft * 0.5; if irez != 1: mm = xl - 2.0 * nodem + 2.0 * theta; dndt = nm - no; else: mm = xl - nodem - argpm + theta; dndt = nm - no; nm = no + dndt; return ( atime, em, argpm, inclm, xli, mm, xni, nodem, dndt, nm, ) """ /*----------------------------------------------------------------------------- * * procedure initl * * this procedure initializes the spg4 propagator. all the initialization is * consolidated here instead of having multiple loops inside other routines. * * author : david vallado 719-573-2600 28 jun 2005 * * inputs : * ecco - eccentricity 0.0 - 1.0 * epoch - epoch time in days from jan 0, 1950. 0 hr * inclo - inclination of satellite * no - mean motion of satellite * satn - satellite number * * outputs : * ainv - 1.0 / a * ao - semi major axis * con41 - * con42 - 1.0 - 5.0 cos(i) * cosio - cosine of inclination * cosio2 - cosio squared * eccsq - eccentricity squared * method - flag for deep space 'd', 'n' * omeosq - 1.0 - ecco * ecco * posq - semi-parameter squared * rp - radius of perigee * rteosq - square root of (1.0 - ecco*ecco) * sinio - sine of inclination * gsto - gst at time of observation rad * no - mean motion of satellite * * locals : * ak - * d1 - * del - * adel - * po - * * coupling : * getgravconst * gstime - find greenwich sidereal time from the julian date * * references : * hoots, roehrich, norad spacetrack report #3 1980 * hoots, norad spacetrack report #6 1986 * hoots, schumacher and glover 2004 * vallado, crawford, hujsak, kelso 2006 ----------------------------------------------------------------------------*/ """ def _initl( satn, whichconst, ecco, epoch, inclo, no, method, opsmode, ): # sgp4fix use old way of finding gst # ----------------------- earth constants ---------------------- # sgp4fix identify constants and allow alternate values tumin, mu, radiusearthkm, xke, j2, j3, j4, j3oj2 = whichconst x2o3 = 2.0 / 3.0; # ------------- calculate auxillary epoch quantities ---------- eccsq = ecco * ecco; omeosq = 1.0 - eccsq; rteosq = sqrt(omeosq); cosio = cos(inclo); cosio2 = cosio * cosio; # ------------------ un-kozai the mean motion ----------------- ak = pow(xke / no, x2o3); d1 = 0.75 * j2 * (3.0 * cosio2 - 1.0) / (rteosq * omeosq); del_ = d1 / (ak * ak); adel = ak * (1.0 - del_ * del_ - del_ * (1.0 / 3.0 + 134.0 * del_ * del_ / 81.0)); del_ = d1/(adel * adel); no = no / (1.0 + del_); ao = pow(xke / no, x2o3); sinio = sin(inclo); po = ao * omeosq; con42 = 1.0 - 5.0 * cosio2; con41 = -con42-cosio2-cosio2; ainv = 1.0 / ao; posq = po * po; rp = ao * (1.0 - ecco); method = 'n'; # sgp4fix modern approach to finding sidereal time if opsmode == 'a': # sgp4fix use old way of finding gst # count integer number of days from 0 jan 1970 ts70 = epoch - 7305.0; ds70 = (ts70 + 1.0e-8) // 1.0; tfrac = ts70 - ds70; # find greenwich location at epoch c1 = 1.72027916940703639e-2; thgr70= 1.7321343856509374; fk5r = 5.07551419432269442e-15; c1p2p = c1 + twopi; gsto = (thgr70 + c1*ds70 + c1p2p*tfrac + ts70*ts70*fk5r) % twopi if gsto < 0.0: gsto = gsto + twopi; else: gsto = _gstime(epoch + 2433281.5); return ( no, method, ainv, ao, con41, con42, cosio, cosio2,eccsq, omeosq, posq, rp, rteosq,sinio , gsto, ) """ /*----------------------------------------------------------------------------- * * procedure sgp4init * * this procedure initializes variables for sgp4. * * author : david vallado 719-573-2600 28 jun 2005 * * inputs : * opsmode - mode of operation afspc or improved 'a', 'i' * whichconst - which set of constants to use 72, 84 * satn - satellite number * bstar - sgp4 type drag coefficient kg/m2er * ecco - eccentricity * epoch - epoch time in days from jan 0, 1950. 0 hr * argpo - argument of perigee (output if ds) * inclo - inclination * mo - mean anomaly (output if ds) * no - mean motion * nodeo - right ascension of ascending node * * outputs : * satrec - common values for subsequent calls * return code - non-zero on error. * 1 - mean elements, ecc >= 1.0 or ecc < -0.001 or a < 0.95 er * 2 - mean motion less than 0.0 * 3 - pert elements, ecc < 0.0 or ecc > 1.0 * 4 - semi-latus rectum < 0.0 * 5 - epoch elements are sub-orbital * 6 - satellite has decayed * * locals : * cnodm , snodm , cosim , sinim , cosomm , sinomm * cc1sq , cc2 , cc3 * coef , coef1 * cosio4 - * day - * dndt - * em - eccentricity * emsq - eccentricity squared * eeta - * etasq - * gam - * argpm - argument of perigee * nodem - * inclm - inclination * mm - mean anomaly * nm - mean motion * perige - perigee * pinvsq - * psisq - * qzms24 - * rtemsq - * s1, s2, s3, s4, s5, s6, s7 - * sfour - * ss1, ss2, ss3, ss4, ss5, ss6, ss7 - * sz1, sz2, sz3 * sz11, sz12, sz13, sz21, sz22, sz23, sz31, sz32, sz33 - * tc - * temp - * temp1, temp2, temp3 - * tsi - * xpidot - * xhdot1 - * z1, z2, z3 - * z11, z12, z13, z21, z22, z23, z31, z32, z33 - * * coupling : * getgravconst- * initl - * dscom - * dpper - * dsinit - * sgp4 - * * references : * hoots, roehrich, norad spacetrack report #3 1980 * hoots, norad spacetrack report #6 1986 * hoots, schumacher and glover 2004 * vallado, crawford, hujsak, kelso 2006 ----------------------------------------------------------------------------*/ """ def sgp4init( whichconst, opsmode, satn, epoch, xbstar, xecco, xargpo, xinclo, xmo, xno, xnodeo, satrec, ): """ /* ------------------------ initialization --------------------- */ // sgp4fix divisor for divide by zero check on inclination // the old check used 1.0 + cos(pi-1.0e-9), but then compared it to // 1.5 e-12, so the threshold was changed to 1.5e-12 for consistency """ temp4 = 1.5e-12; # ----------- set all near earth variables to zero ------------ satrec.isimp = 0; satrec.method = 'n'; satrec.aycof = 0.0; satrec.con41 = 0.0; satrec.cc1 = 0.0; satrec.cc4 = 0.0; satrec.cc5 = 0.0; satrec.d2 = 0.0; satrec.d3 = 0.0; satrec.d4 = 0.0; satrec.delmo = 0.0; satrec.eta = 0.0; satrec.argpdot = 0.0; satrec.omgcof = 0.0; satrec.sinmao = 0.0; satrec.t = 0.0; satrec.t2cof = 0.0; satrec.t3cof = 0.0; satrec.t4cof = 0.0; satrec.t5cof = 0.0; satrec.x1mth2 = 0.0; satrec.x7thm1 = 0.0; satrec.mdot = 0.0; satrec.nodedot = 0.0; satrec.xlcof = 0.0; satrec.xmcof = 0.0; satrec.nodecf = 0.0; # ----------- set all deep space variables to zero ------------ satrec.irez = 0; satrec.d2201 = 0.0; satrec.d2211 = 0.0; satrec.d3210 = 0.0; satrec.d3222 = 0.0; satrec.d4410 = 0.0; satrec.d4422 = 0.0; satrec.d5220 = 0.0; satrec.d5232 = 0.0; satrec.d5421 = 0.0; satrec.d5433 = 0.0; satrec.dedt = 0.0; satrec.del1 = 0.0; satrec.del2 = 0.0; satrec.del3 = 0.0; satrec.didt = 0.0; satrec.dmdt = 0.0; satrec.dnodt = 0.0; satrec.domdt = 0.0; satrec.e3 = 0.0; satrec.ee2 = 0.0; satrec.peo = 0.0; satrec.pgho = 0.0; satrec.pho = 0.0; satrec.pinco = 0.0; satrec.plo = 0.0; satrec.se2 = 0.0; satrec.se3 = 0.0; satrec.sgh2 = 0.0; satrec.sgh3 = 0.0; satrec.sgh4 = 0.0; satrec.sh2 = 0.0; satrec.sh3 = 0.0; satrec.si2 = 0.0; satrec.si3 = 0.0; satrec.sl2 = 0.0; satrec.sl3 = 0.0; satrec.sl4 = 0.0; satrec.gsto = 0.0; satrec.xfact = 0.0; satrec.xgh2 = 0.0; satrec.xgh3 = 0.0; satrec.xgh4 = 0.0; satrec.xh2 = 0.0; satrec.xh3 = 0.0; satrec.xi2 = 0.0; satrec.xi3 = 0.0; satrec.xl2 = 0.0; satrec.xl3 = 0.0; satrec.xl4 = 0.0; satrec.xlamo = 0.0; satrec.zmol = 0.0; satrec.zmos = 0.0; satrec.atime = 0.0; satrec.xli = 0.0; satrec.xni = 0.0; """ // sgp4fix - note the following variables are also passed directly via satrec. // it is possible to streamline the sgp4init call by deleting the "x" // variables, but the user would need to set the satrec.* values first. we // include the additional assignments in case twoline2rv is not used. """ satrec.bstar = xbstar; satrec.ecco = xecco; satrec.argpo = xargpo; satrec.inclo = xinclo; satrec.mo = xmo; satrec.no = xno; satrec.nodeo = xnodeo; # sgp4fix add opsmode satrec.operationmode = opsmode; # ------------------------ earth constants ----------------------- # sgp4fix identify constants and allow alternate values tumin, mu, radiusearthkm, xke, j2, j3, j4, j3oj2 = whichconst ss = 78.0 / radiusearthkm + 1.0; # sgp4fix use multiply for speed instead of pow qzms2ttemp = (120.0 - 78.0) / radiusearthkm; qzms2t = qzms2ttemp * qzms2ttemp * qzms2ttemp * qzms2ttemp; x2o3 = 2.0 / 3.0; satrec.init = 'y'; satrec.t = 0.0; ( satrec.no, method, ainv, ao, satrec.con41, con42, cosio, cosio2,eccsq, omeosq, posq, rp, rteosq,sinio , satrec.gsto, ) = _initl( satn, whichconst, satrec.ecco, epoch, satrec.inclo, satrec.no, satrec.method, satrec.operationmode ); satrec.error = 0; """ // sgp4fix remove this check as it is unnecessary // the mrt check in sgp4 handles decaying satellite cases even if the starting // condition is below the surface of te earth // if (rp < 1.0) // { // printf("# *** satn%d epoch elts sub-orbital ***\n", satn); // satrec.error = 5; // } """ if omeosq >= 0.0 or satrec.no >= 0.0: satrec.isimp = 0; if rp < 220.0 / radiusearthkm + 1.0: satrec.isimp = 1; sfour = ss; qzms24 = qzms2t; perige = (rp - 1.0) * radiusearthkm; # - for perigees below 156 km, s and qoms2t are altered - if perige < 156.0: sfour = perige - 78.0; if perige < 98.0: sfour = 20.0; # sgp4fix use multiply for speed instead of pow qzms24temp = (120.0 - sfour) / radiusearthkm; qzms24 = qzms24temp * qzms24temp * qzms24temp * qzms24temp; sfour = sfour / radiusearthkm + 1.0; pinvsq = 1.0 / posq; tsi = 1.0 / (ao - sfour); satrec.eta = ao * satrec.ecco * tsi; etasq = satrec.eta * satrec.eta; eeta = satrec.ecco * satrec.eta; psisq = fabs(1.0 - etasq); coef = qzms24 * pow(tsi, 4.0); coef1 = coef / pow(psisq, 3.5); cc2 = coef1 * satrec.no * (ao * (1.0 + 1.5 * etasq + eeta * (4.0 + etasq)) + 0.375 * j2 * tsi / psisq * satrec.con41 * (8.0 + 3.0 * etasq * (8.0 + etasq))); satrec.cc1 = satrec.bstar * cc2; cc3 = 0.0; if satrec.ecco > 1.0e-4: cc3 = -2.0 * coef * tsi * j3oj2 * satrec.no * sinio / satrec.ecco; satrec.x1mth2 = 1.0 - cosio2; satrec.cc4 = 2.0* satrec.no * coef1 * ao * omeosq * \ (satrec.eta * (2.0 + 0.5 * etasq) + satrec.ecco * (0.5 + 2.0 * etasq) - j2 * tsi / (ao * psisq) * (-3.0 * satrec.con41 * (1.0 - 2.0 * eeta + etasq * (1.5 - 0.5 * eeta)) + 0.75 * satrec.x1mth2 * (2.0 * etasq - eeta * (1.0 + etasq)) * cos(2.0 * satrec.argpo))); satrec.cc5 = 2.0 * coef1 * ao * omeosq * (1.0 + 2.75 * (etasq + eeta) + eeta * etasq); cosio4 = cosio2 * cosio2; temp1 = 1.5 * j2 * pinvsq * satrec.no; temp2 = 0.5 * temp1 * j2 * pinvsq; temp3 = -0.46875 * j4 * pinvsq * pinvsq * satrec.no; satrec.mdot = satrec.no + 0.5 * temp1 * rteosq * satrec.con41 + 0.0625 * \ temp2 * rteosq * (13.0 - 78.0 * cosio2 + 137.0 * cosio4); satrec.argpdot = (-0.5 * temp1 * con42 + 0.0625 * temp2 * (7.0 - 114.0 * cosio2 + 395.0 * cosio4) + temp3 * (3.0 - 36.0 * cosio2 + 49.0 * cosio4)); xhdot1 = -temp1 * cosio; satrec.nodedot = xhdot1 + (0.5 * temp2 * (4.0 - 19.0 * cosio2) + 2.0 * temp3 * (3.0 - 7.0 * cosio2)) * cosio; xpidot = satrec.argpdot+ satrec.nodedot; satrec.omgcof = satrec.bstar * cc3 * cos(satrec.argpo); satrec.xmcof = 0.0; if satrec.ecco > 1.0e-4: satrec.xmcof = -x2o3 * coef * satrec.bstar / eeta; satrec.nodecf = 3.5 * omeosq * xhdot1 * satrec.cc1; satrec.t2cof = 1.5 * satrec.cc1; # sgp4fix for divide by zero with xinco = 180 deg if fabs(cosio+1.0) > 1.5e-12: satrec.xlcof = -0.25 * j3oj2 * sinio * (3.0 + 5.0 * cosio) / (1.0 + cosio); else: satrec.xlcof = -0.25 * j3oj2 * sinio * (3.0 + 5.0 * cosio) / temp4; satrec.aycof = -0.5 * j3oj2 * sinio; # sgp4fix use multiply for speed instead of pow delmotemp = 1.0 + satrec.eta * cos(satrec.mo); satrec.delmo = delmotemp * delmotemp * delmotemp; satrec.sinmao = sin(satrec.mo); satrec.x7thm1 = 7.0 * cosio2 - 1.0; # --------------- deep space initialization ------------- if 2*pi / satrec.no >= 225.0: satrec.method = 'd'; satrec.isimp = 1; tc = 0.0; inclm = satrec.inclo; ( snodm, cnodm, sinim, cosim, sinomm, cosomm,day, satrec.e3, satrec.ee2, em, emsq, gam, satrec.peo, satrec.pgho, satrec.pho, satrec.pinco, satrec.plo, rtemsq, satrec.se2, satrec.se3, satrec.sgh2, satrec.sgh3, satrec.sgh4, satrec.sh2, satrec.sh3, satrec.si2, satrec.si3, satrec.sl2, satrec.sl3, satrec.sl4, s1, s2, s3, s4, s5, s6, s7, ss1, ss2, ss3, ss4, ss5, ss6, ss7, sz1, sz2, sz3, sz11, sz12, sz13, sz21, sz22, sz23, sz31, sz32, sz33, satrec.xgh2, satrec.xgh3, satrec.xgh4, satrec.xh2, satrec.xh3, satrec.xi2, satrec.xi3, satrec.xl2, satrec.xl3, satrec.xl4, nm, z1, z2, z3, z11, z12, z13, z21, z22, z23, z31, z32, z33, satrec.zmol, satrec.zmos ) = _dscom( epoch, satrec.ecco, satrec.argpo, tc, satrec.inclo, satrec.nodeo, satrec.no, satrec.e3, satrec.ee2, satrec.peo, satrec.pgho, satrec.pho, satrec.pinco, satrec.plo, satrec.se2, satrec.se3, satrec.sgh2, satrec.sgh3, satrec.sgh4, satrec.sh2, satrec.sh3, satrec.si2, satrec.si3, satrec.sl2, satrec.sl3, satrec.sl4, satrec.xgh2, satrec.xgh3, satrec.xgh4, satrec.xh2, satrec.xh3, satrec.xi2, satrec.xi3, satrec.xl2, satrec.xl3, satrec.xl4, satrec.zmol, satrec.zmos ); (satrec.ecco, satrec.inclo, satrec.nodeo, satrec.argpo, satrec.mo ) = _dpper( satrec, inclm, satrec.init, satrec.ecco, satrec.inclo, satrec.nodeo, satrec.argpo, satrec.mo, satrec.operationmode ); argpm = 0.0; nodem = 0.0; mm = 0.0; ( em, argpm, inclm, mm, nm, nodem, satrec.irez, satrec.atime, satrec.d2201, satrec.d2211, satrec.d3210, satrec.d3222, satrec.d4410, satrec.d4422, satrec.d5220, satrec.d5232, satrec.d5421, satrec.d5433, satrec.dedt, satrec.didt, satrec.dmdt, dndt, satrec.dnodt, satrec.domdt, satrec.del1, satrec.del2, satrec.del3, satrec.xfact, satrec.xlamo, satrec.xli, satrec.xni ) = _dsinit( whichconst, cosim, emsq, satrec.argpo, s1, s2, s3, s4, s5, sinim, ss1, ss2, ss3, ss4, ss5, sz1, sz3, sz11, sz13, sz21, sz23, sz31, sz33, satrec.t, tc, satrec.gsto, satrec.mo, satrec.mdot, satrec.no, satrec.nodeo, satrec.nodedot, xpidot, z1, z3, z11, z13, z21, z23, z31, z33, satrec.ecco, eccsq, em, argpm, inclm, mm, nm, nodem, satrec.irez, satrec.atime, satrec.d2201, satrec.d2211, satrec.d3210, satrec.d3222 , satrec.d4410, satrec.d4422, satrec.d5220, satrec.d5232, satrec.d5421, satrec.d5433, satrec.dedt, satrec.didt, satrec.dmdt, satrec.dnodt, satrec.domdt, satrec.del1, satrec.del2, satrec.del3, satrec.xfact, satrec.xlamo, satrec.xli, satrec.xni ); #----------- set variables if not deep space ----------- if satrec.isimp != 1: cc1sq = satrec.cc1 * satrec.cc1; satrec.d2 = 4.0 * ao * tsi * cc1sq; temp = satrec.d2 * tsi * satrec.cc1 / 3.0; satrec.d3 = (17.0 * ao + sfour) * temp; satrec.d4 = 0.5 * temp * ao * tsi * (221.0 * ao + 31.0 * sfour) * \ satrec.cc1; satrec.t3cof = satrec.d2 + 2.0 * cc1sq; satrec.t4cof = 0.25 * (3.0 * satrec.d3 + satrec.cc1 * (12.0 * satrec.d2 + 10.0 * cc1sq)); satrec.t5cof = 0.2 * (3.0 * satrec.d4 + 12.0 * satrec.cc1 * satrec.d3 + 6.0 * satrec.d2 * satrec.d2 + 15.0 * cc1sq * (2.0 * satrec.d2 + cc1sq)); """ /* finally propogate to zero epoch to initialize all others. */ // sgp4fix take out check to let satellites process until they are actually below earth surface // if(satrec.error == 0) """ sgp4(satrec, 0.0); satrec.init = 'n'; # sgp4fix return boolean. satrec.error contains any error codes return true; """ /*----------------------------------------------------------------------------- * * procedure sgp4 * * this procedure is the sgp4 prediction model from space command. this is an * updated and combined version of sgp4 and sdp4, which were originally * published separately in spacetrack report #3. this version follows the * methodology from the aiaa paper (2006) describing the history and * development of the code. * * author : david vallado 719-573-2600 28 jun 2005 * * inputs : * satrec - initialised structure from sgp4init() call. * tsince - time eince epoch (minutes) * * outputs : * r - position vector km * v - velocity km/sec * return code - non-zero on error. * 1 - mean elements, ecc >= 1.0 or ecc < -0.001 or a < 0.95 er * 2 - mean motion less than 0.0 * 3 - pert elements, ecc < 0.0 or ecc > 1.0 * 4 - semi-latus rectum < 0.0 * 5 - epoch elements are sub-orbital * 6 - satellite has decayed * * locals : * am - * axnl, aynl - * betal - * cosim , sinim , cosomm , sinomm , cnod , snod , cos2u , * sin2u , coseo1 , sineo1 , cosi , sini , cosip , sinip , * cosisq , cossu , sinsu , cosu , sinu * delm - * delomg - * dndt - * eccm - * emsq - * ecose - * el2 - * eo1 - * eccp - * esine - * argpm - * argpp - * omgadf - * pl - * r - * rtemsq - * rdotl - * rl - * rvdot - * rvdotl - * su - * t2 , t3 , t4 , tc * tem5, temp , temp1 , temp2 , tempa , tempe , templ * u , ux , uy , uz , vx , vy , vz * inclm - inclination * mm - mean anomaly * nm - mean motion * nodem - right asc of ascending node * xinc - * xincp - * xl - * xlm - * mp - * xmdf - * xmx - * xmy - * nodedf - * xnode - * nodep - * np - * * coupling : * getgravconst- * dpper * dpspace * * references : * hoots, roehrich, norad spacetrack report #3 1980 * hoots, norad spacetrack report #6 1986 * hoots, schumacher and glover 2004 * vallado, crawford, hujsak, kelso 2006 ----------------------------------------------------------------------------*/ """ def sgp4(satrec, tsince, whichconst=None): mrt = 0.0 if whichconst is None: whichconst = satrec.whichconst """ /* ------------------ set mathematical constants --------------- */ // sgp4fix divisor for divide by zero check on inclination // the old check used 1.0 + cos(pi-1.0e-9), but then compared it to // 1.5 e-12, so the threshold was changed to 1.5e-12 for consistency """ temp4 = 1.5e-12; twopi = 2.0 * pi; x2o3 = 2.0 / 3.0; # sgp4fix identify constants and allow alternate values tumin, mu, radiusearthkm, xke, j2, j3, j4, j3oj2 = whichconst vkmpersec = radiusearthkm * xke/60.0; # --------------------- clear sgp4 error flag ----------------- satrec.t = tsince; satrec.error = 0; satrec.error_message = None # ------- update for secular gravity and atmospheric drag ----- xmdf = satrec.mo + satrec.mdot * satrec.t; argpdf = satrec.argpo + satrec.argpdot * satrec.t; nodedf = satrec.nodeo + satrec.nodedot * satrec.t; argpm = argpdf; mm = xmdf; t2 = satrec.t * satrec.t; nodem = nodedf + satrec.nodecf * t2; tempa = 1.0 - satrec.cc1 * satrec.t; tempe = satrec.bstar * satrec.cc4 * satrec.t; templ = satrec.t2cof * t2; if satrec.isimp != 1: delomg = satrec.omgcof * satrec.t; # sgp4fix use mutliply for speed instead of pow delmtemp = 1.0 + satrec.eta * cos(xmdf); delm = satrec.xmcof * \ (delmtemp * delmtemp * delmtemp - satrec.delmo); temp = delomg + delm; mm = xmdf + temp; argpm = argpdf - temp; t3 = t2 * satrec.t; t4 = t3 * satrec.t; tempa = tempa - satrec.d2 * t2 - satrec.d3 * t3 - \ satrec.d4 * t4; tempe = tempe + satrec.bstar * satrec.cc5 * (sin(mm) - satrec.sinmao); templ = templ + satrec.t3cof * t3 + t4 * (satrec.t4cof + satrec.t * satrec.t5cof); nm = satrec.no; em = satrec.ecco; inclm = satrec.inclo; if satrec.method == 'd': tc = satrec.t; ( atime, em, argpm, inclm, xli, mm, xni, nodem, dndt, nm, ) = _dspace( satrec.irez, satrec.d2201, satrec.d2211, satrec.d3210, satrec.d3222, satrec.d4410, satrec.d4422, satrec.d5220, satrec.d5232, satrec.d5421, satrec.d5433, satrec.dedt, satrec.del1, satrec.del2, satrec.del3, satrec.didt, satrec.dmdt, satrec.dnodt, satrec.domdt, satrec.argpo, satrec.argpdot, satrec.t, tc, satrec.gsto, satrec.xfact, satrec.xlamo, satrec.no, satrec.atime, em, argpm, inclm, satrec.xli, mm, satrec.xni, nodem, nm ); if nm <= 0.0: satrec.error_message = ('mean motion {0:f} is less than zero' .format(nm)) satrec.error = 2; # sgp4fix add return return false, false; am = pow((xke / nm),x2o3) * tempa * tempa; nm = xke / pow(am, 1.5); em = em - tempe; # fix tolerance for error recognition # sgp4fix am is fixed from the previous nm check if em >= 1.0 or em < -0.001: # || (am < 0.95) satrec.error_message = ('mean eccentricity {0:f} not within' ' range 0.0 <= e < 1.0'.format(em)) satrec.error = 1; # sgp4fix to return if there is an error in eccentricity return false, false; # sgp4fix fix tolerance to avoid a divide by zero if em < 1.0e-6: em = 1.0e-6; mm = mm + satrec.no * templ; xlm = mm + argpm + nodem; emsq = em * em; temp = 1.0 - emsq; nodem = fmod(nodem, twopi); argpm = argpm % twopi xlm = xlm % twopi mm = (xlm - argpm - nodem) % twopi # ----------------- compute extra mean quantities ------------- sinim = sin(inclm); cosim = cos(inclm); # -------------------- add lunar-solar periodics -------------- ep = em; xincp = inclm; argpp = argpm; nodep = nodem; mp = mm; sinip = sinim; cosip = cosim; if satrec.method == 'd': ep, xincp, nodep, argpp, mp = _dpper( satrec, satrec.inclo, 'n', ep, xincp, nodep, argpp, mp, satrec.operationmode ); if xincp < 0.0: xincp = -xincp; nodep = nodep + pi; argpp = argpp - pi; if ep < 0.0 or ep > 1.0: satrec.error_message = ('perturbed eccentricity {0:f} not within' ' range 0.0 <= e <= 1.0'.format(ep)) satrec.error = 3; # sgp4fix add return return false, false; # -------------------- long period periodics ------------------ if satrec.method == 'd': sinip = sin(xincp); cosip = cos(xincp); satrec.aycof = -0.5*j3oj2*sinip; # sgp4fix for divide by zero for xincp = 180 deg if fabs(cosip+1.0) > 1.5e-12: satrec.xlcof = -0.25 * j3oj2 * sinip * (3.0 + 5.0 * cosip) / (1.0 + cosip); else: satrec.xlcof = -0.25 * j3oj2 * sinip * (3.0 + 5.0 * cosip) / temp4; axnl = ep * cos(argpp); temp = 1.0 / (am * (1.0 - ep * ep)); aynl = ep* sin(argpp) + temp * satrec.aycof; xl = mp + argpp + nodep + temp * satrec.xlcof * axnl; # --------------------- solve kepler's equation --------------- u = (xl - nodep) % twopi eo1 = u; tem5 = 9999.9; ktr = 1; # sgp4fix for kepler iteration # the following iteration needs better limits on corrections while fabs(tem5) >= 1.0e-12 and ktr <= 10: sineo1 = sin(eo1); coseo1 = cos(eo1); tem5 = 1.0 - coseo1 * axnl - sineo1 * aynl; tem5 = (u - aynl * coseo1 + axnl * sineo1 - eo1) / tem5; if fabs(tem5) >= 0.95: tem5 = 0.95 if tem5 > 0.0 else -0.95; eo1 = eo1 + tem5; ktr = ktr + 1; # ------------- short period preliminary quantities ----------- ecose = axnl*coseo1 + aynl*sineo1; esine = axnl*sineo1 - aynl*coseo1; el2 = axnl*axnl + aynl*aynl; pl = am*(1.0-el2); if pl < 0.0: satrec.error_message = ('semilatus rectum {0:f} is less than zero' .format(pl)) satrec.error = 4; # sgp4fix add return return false, false; else: rl = am * (1.0 - ecose); rdotl = sqrt(am) * esine/rl; rvdotl = sqrt(pl) / rl; betal = sqrt(1.0 - el2); temp = esine / (1.0 + betal); sinu = am / rl * (sineo1 - aynl - axnl * temp); cosu = am / rl * (coseo1 - axnl + aynl * temp); su = atan2(sinu, cosu); sin2u = (cosu + cosu) * sinu; cos2u = 1.0 - 2.0 * sinu * sinu; temp = 1.0 / pl; temp1 = 0.5 * j2 * temp; temp2 = temp1 * temp; # -------------- update for short period periodics ------------ if satrec.method == 'd': cosisq = cosip * cosip; satrec.con41 = 3.0*cosisq - 1.0; satrec.x1mth2 = 1.0 - cosisq; satrec.x7thm1 = 7.0*cosisq - 1.0; mrt = rl * (1.0 - 1.5 * temp2 * betal * satrec.con41) + \ 0.5 * temp1 * satrec.x1mth2 * cos2u; su = su - 0.25 * temp2 * satrec.x7thm1 * sin2u; xnode = nodep + 1.5 * temp2 * cosip * sin2u; xinc = xincp + 1.5 * temp2 * cosip * sinip * cos2u; mvt = rdotl - nm * temp1 * satrec.x1mth2 * sin2u / xke; rvdot = rvdotl + nm * temp1 * (satrec.x1mth2 * cos2u + 1.5 * satrec.con41) / xke; # --------------------- orientation vectors ------------------- sinsu = sin(su); cossu = cos(su); snod = sin(xnode); cnod = cos(xnode); sini = sin(xinc); cosi = cos(xinc); xmx = -snod * cosi; xmy = cnod * cosi; ux = xmx * sinsu + cnod * cossu; uy = xmy * sinsu + snod * cossu; uz = sini * sinsu; vx = xmx * cossu - cnod * sinsu; vy = xmy * cossu - snod * sinsu; vz = sini * cossu; # --------- position and velocity (in km and km/sec) ---------- _mr = mrt * radiusearthkm r = (_mr * ux, _mr * uy, _mr * uz) v = ((mvt * ux + rvdot * vx) * vkmpersec, (mvt * uy + rvdot * vy) * vkmpersec, (mvt * uz + rvdot * vz) * vkmpersec) # sgp4fix for decaying satellites if mrt < 1.0: satrec.error_message = ('mrt {0:f} is less than 1.0 indicating' ' the satellite has decayed'.format(mrt)) satrec.error = 6; return false, false; return r, v; """ /* ----------------------------------------------------------------------------- * * function gstime * * this function finds the greenwich sidereal time. * * author : david vallado 719-573-2600 1 mar 2001 * * inputs description range / units * jdut1 - julian date in ut1 days from 4713 bc * * outputs : * gstime - greenwich sidereal time 0 to 2pi rad * * locals : * temp - temporary variable for doubles rad * tut1 - julian centuries from the * jan 1, 2000 12 h epoch (ut1) * * coupling : * none * * references : * vallado 2004, 191, eq 3-45 * --------------------------------------------------------------------------- */ """ def _gstime(jdut1): tut1 = (jdut1 - 2451545.0) / 36525.0; temp = -6.2e-6* tut1 * tut1 * tut1 + 0.093104 * tut1 * tut1 + \ (876600.0*3600 + 8640184.812866) * tut1 + 67310.54841; # sec temp = (temp * deg2rad / 240.0) % twopi # 360/86400 = 1/240, to deg, to rad # ------------------------ check quadrants --------------------- if temp < 0.0: temp += twopi; return temp; """ /* ----------------------------------------------------------------------------- * * function getgravconst * * this function gets constants for the propagator. note that mu is identified to * facilitiate comparisons with newer models. the common useage is wgs72. * * author : david vallado 719-573-2600 21 jul 2006 * * inputs : * whichconst - which set of constants to use wgs72old, wgs72, wgs84 * * outputs : * tumin - minutes in one time unit * mu - earth gravitational parameter * radiusearthkm - radius of the earth in km * xke - reciprocal of tumin * j2, j3, j4 - un-normalized zonal harmonic values * j3oj2 - j3 divided by j2 * * locals : * * coupling : * none * * references : * norad spacetrack report #3 * vallado, crawford, hujsak, kelso 2006 --------------------------------------------------------------------------- */ """ def getgravconst(whichconst): if whichconst == 'wgs72old': mu = 398600.79964; # in km3 / s2 radiusearthkm = 6378.135; # km xke = 0.0743669161; tumin = 1.0 / xke; j2 = 0.001082616; j3 = -0.00000253881; j4 = -0.00000165597; j3oj2 = j3 / j2; # ------------ wgs-72 constants ------------ elif whichconst == 'wgs72': mu = 398600.8; # in km3 / s2 radiusearthkm = 6378.135; # km xke = 60.0 / sqrt(radiusearthkm*radiusearthkm*radiusearthkm/mu); tumin = 1.0 / xke; j2 = 0.001082616; j3 = -0.00000253881; j4 = -0.00000165597; j3oj2 = j3 / j2; elif whichconst == 'wgs84': # ------------ wgs-84 constants ------------ mu = 398600.5; # in km3 / s2 radiusearthkm = 6378.137; # km xke = 60.0 / sqrt(radiusearthkm*radiusearthkm*radiusearthkm/mu); tumin = 1.0 / xke; j2 = 0.00108262998905; j3 = -0.00000253215306; j4 = -0.00000161098761; j3oj2 = j3 / j2; return tumin, mu, radiusearthkm, xke, j2, j3, j4, j3oj2 sgp4-1.4/sgp4/__init__.py0000640000175000017500000000773112456126124016320 0ustar brandonbrandon00000000000000# -*- coding: utf-8 -*- """Track earth satellite TLE orbits using up-to-date 2010 version of SGP4 This Python package computes the position and velocity of an earth-orbiting satellite, given the satellite's TLE orbital elements from a source like `Celestrak `_. It implements the most recent version of SGP4, and is regularly run against the SGP4 test suite to make sure that its satellite position predictions **agree to within 0.1 mm** of the predictions of the standard C++ implementation of the algorithm. This error is far less than the 1–3 km/day by which satellites themselves deviate from the ideal orbits described in TLE files. The C++ function names have been retained, since users may already be familiar with this library in other languages. Here is how to compute the x,y,z position and velocity for Vanguard 1 at 12:50:19 on 29 June 2000: >>> from sgp4.earth_gravity import wgs72 >>> from sgp4.io import twoline2rv >>> >>> line1 = ('1 00005U 58002B 00179.78495062 ' ... '.00000023 00000-0 28098-4 0 4753') >>> line2 = ('2 00005 34.2682 348.7242 1859667 ' ... '331.7664 19.3264 10.82419157413667') >>> >>> satellite = twoline2rv(line1, line2, wgs72) >>> position, velocity = satellite.propagate( ... 2000, 6, 29, 12, 50, 19) >>> >>> print(satellite.error) # nonzero on error 0 >>> print(satellite.error_message) None >>> print(position) (5576.056952..., -3999.371134..., -1521.957159...) >>> print(velocity) (4.772627..., 5.119817..., 4.275553...) The position vector measures the satellite position in **kilometers** from the center of the earth. The velocity is the rate at which those three parameters are changing, expressed in **kilometers per second**. There are three gravity models available that you can import from the ``earth_gravity`` module: * ``wgs72`` * ``wgs72old`` * ``wgs84`` The ``wgs72`` model seems to be the most commonly used in the satellite tracking community, and is probably the model behind most TLE elements that are available for download. The ``twoline2rv()`` function returns a ``Satellite`` object whose attributes carry the data loaded from the TLE entry: * Unique satellite number, as given in the TLE file. >>> satellite.satnum 5 * The epoch of the element set, expressed three ways: as the integer year plus the floating point number of days into the year; as a floating-point Julian date; and as Python ``datetime`` object. >>> satellite.epochyr 2000 >>> satellite.epochdays 179.78495062 >>> satellite.jdsatepoch 2451723.28495062 >>> satellite.epoch datetime.datetime(2000, 6, 27, 18, 50, 19, 733567) This implementation passes all of the automated tests in the August 2010 release of the reference implementation of SGP4 by Vallado et al., who originally published their revision of SGP4 in 2006: Vallado, David A., Paul Crawford, Richard Hujsak, and T.S. Kelso, “Revisiting Spacetrack Report #3,” presented at the AIAA/AAS Astrodynamics Specialist Conference, Keystone, CO, 2006 August 21–24. If you would like to review the paper, it is `available online `_. You can always download the latest version of their code for comparison against this Python module (or other implementations) at `AIAA-2006-6753.zip `_. This module was adapted from Vallado's C++ code since its revision date was the most recently updated SGP4 implementation in their zip file: * C++, August 2010 * Fortran, August 2008 * Pascal, August 2008 * Matlab, May 2008 * Java, July 2005 Changelog --------- | 2015-01-15 — 1.4 — Display detailed help when TLE input does not match format. | 2014-06-26 — 1.3 — Return ``(NaN,NaN,NaN)`` vectors on error and set ``.error_message`` | 2013-11-29 — 1.2 — Made ``epochyr`` 4 digits; add ``datetime`` for ``.epoch`` | 2012-11-22 — 1.1 — Python 3 compatibility; more documentation | 2012-08-27 — 1.0 — Initial release """ sgp4-1.4/sgp4/model.py0000640000175000017500000000315212245716260015654 0ustar brandonbrandon00000000000000"""The Satellite class.""" from sgp4.ext import jday from sgp4.propagation import sgp4 minutes_per_day = 1440. class Satellite(object): """An earth-orbiting satellite as represented by the SGP4 model. Most of this class's hundred-plus attributes are intermediate values of interest only to the propagation algorithm itself. Here are the attributes set by ``sgp4.io.twoline2rv()`` in which users are likely to be interested: ``satnum`` Unique satellite number given in the TLE file. ``epochyr`` Full four-digit year of this element set's epoch moment. ``epochdays`` Fractional days into the year of the epoch moment. ``jdsatepoch`` Julian date of the epoch (computed from ``epochyr`` and ``epochdays``). ``ndot`` First time derivative of the mean motion (ignored by SGP4). ``nddot`` Second time derivative of the mean motion (ignored by SGP4). ``bstar`` Ballistic drag coefficient B* in inverse earth radii. ``inclo`` Inclination in radians. ``nodeo`` Right ascension of ascending node in radians. ``ecco`` Eccentricity. ``argpo`` Argument of perigee in radians. ``mo`` Mean anomaly in radians. ``no`` Mean motion in radians per minute. """ 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 sgp4-1.4/sgp4/tests.py0000640000175000017500000002076212456123665015731 0ustar brandonbrandon00000000000000"""Test suite for SGP4.""" import os import re import sys from doctest import DocTestSuite, ELLIPSIS from unittest import TestCase from math import pi, isnan from sgp4.earth_gravity import wgs72 from sgp4.ext import invjday, newtonnu, rv2coe from sgp4.io import twoline2rv from sgp4.propagation import sgp4 thisdir = os.path.dirname(__file__) error = 2e-7 rad = 180.0 / pi class Tests(TestCase): def test_tle_verify(self): # Check whether a test run produces the output in tcppver.out whichconst = 'wgs72' error_list = [] actual = generate_test_output(whichconst, error_list) previous_data_line = None # Iterate across "tcppver.out", making sure that we ourselves # produce a line that looks very much like the corresponding # line in that file. tcppath = os.path.join(thisdir, 'tcppver.out') with open(tcppath) as tcpfile: for i, expected_line in enumerate(tcpfile, start = 1): try: actual_line = next(actual) except StopIteration: raise ValueError( 'WARNING: our output ended early, on line %d' % (i,)) if actual_line == '(Use previous data line)': actual_line = (' 0.00000000' + previous_data_line[17:107]) # Compare the lines. The first seven fields are printed # to very high precision, so we allow a small error due # to rounding differences; the rest are printed to lower # precision, and so can be compared textually. if 'xx' in actual_line: similar = (actual_line == expected_line) else: afields = actual_line.split() efields = expected_line.split() actual7 = [ float(a) for a in afields[:7] ] expected7 = [ float(e) for e in efields[:7] ] similar = ( len(actual7) == len(expected7) and all( -error < (a - e) < error for a, e in zip(actual7, expected7) ) and afields[7:] == efields[7:] # just compare text ) if not similar: raise ValueError( 'Line %d of output does not match:\n' '\n' 'Expect: %s' 'Actual: %s' % (i, expected_line, actual_line)) if 'xx' not in actual_line: previous_data_line = actual_line # Make sure the test file is not missing lines. missing_count = 0 for actual_line in actual: missing_count += 1 if missing_count > 0: raise ValueError('we produced %d extra lines' % (missing_count,)) self.assertEqual(error_list, [ (1, 'mean eccentricity -0.001329' ' not within range 0.0 <= e < 1.0'), (1, 'mean eccentricity -0.001208' ' not within range 0.0 <= e < 1.0'), (6, 'mrt 0.996159 is less than 1.0' ' indicating the satellite has decayed'), (6, 'mrt 0.996252 is less than 1.0' ' indicating the satellite has decayed'), (4, 'semilatus rectum -0.103223 is less than zero'), (3, 'perturbed eccentricity -122.217193' ' not within range 0.0 <= e <= 1.0'), (6, 'mrt 0.830534 is less than 1.0' ' indicating the satellite has decayed'), ]) def test_hyperbolic_orbit(self): # Exercise the newtonnu() code path with asinh() to see whether # we can replace it with the one from Python's math module. self.assertEqual(newtonnu(1.0, 2.9), # parabolic (8.238092752965605, 194.60069989482898)) self.assertEqual(newtonnu(1.1, 2.7), # hyperbolic (4.262200676156417, 34.76134082028372)) def test_bad_first_line(self): with self.assertRaisesRegexp(ValueError, re.escape("""TLE format error The Two-Line Element (TLE) format was designed for punch cards and is therefore very strict about the position of every space and digit in a TLE line. Your line does not quite match. Here is the official format for line 1 followed by the line you provided: 1 NNNNNC NNNNNAAA NNNNN.NNNNNNNN +.NNNNNNNN +NNNNN-N +NNNNN-N N NNNNN 1 00005U 58002B 00179.78495062 .000000234 00000-0 28098-4 0 4753""")): twoline2rv(good1.replace('23 ', '234'), good2, wgs72) def test_bad_second_line(self): with self.assertRaisesRegexp(ValueError, re.escape("""TLE format error The Two-Line Element (TLE) format was designed for punch cards and is therefore very strict about the position of every space and digit in a TLE line. Your line does not quite match. Here is the official format for line 2 followed by the line you provided: 2 NNNNN NNN.NNNN NNN.NNNN NNNNNNN NNN.NNNN NNN.NNNN NN.NNNNNNNNNNNNNN 2 00005 34 .268234 8.7242 1859667 331.7664 19.3264 10.82419157413667""")): twoline2rv(good1, good2.replace(' 34', '34 '), wgs72) good1 = '1 00005U 58002B 00179.78495062 .00000023 00000-0 28098-4 0 4753' good2 = '2 00005 34.2682 348.7242 1859667 331.7664 19.3264 10.82419157413667' def generate_test_output(whichconst, error_list): """Generate lines like those in the test file tcppver.out. This iterates through the satellites in "SGP4-VER.TLE", which are each supplemented with a time start/stop/step over which we are supposed to print results. """ whichconst = wgs72 tlepath = os.path.join(thisdir, 'SGP4-VER.TLE') with open(tlepath) as tlefile: tlelines = iter(tlefile.readlines()) for line1 in tlelines: if not line1.startswith('1'): continue line2 = next(tlelines) satrec = twoline2rv(line1, line2, whichconst) yield '%ld xx\n' % (satrec.satnum,) for line in generate_satellite_output(satrec, line2, error_list): yield line def generate_satellite_output(satrec, line2, error_list): """Print a data line for each time in line2's start/stop/step field.""" mu = satrec.whichconst.mu r, v = sgp4(satrec, 0.0) if isnan(r[0]) and isnan(r[1]) and isnan(r[2]): error_list.append((satrec.error, satrec.error_message)) yield '(Use previous data line)' return yield format_short_line(satrec, r, v) tstart, tend, tstep = (float(field) for field in line2[69:].split()) tsince = tstart while tsince <= tend: if tsince == tstart == 0.0: tsince += tstep continue # avoid duplicating the first line r, v = sgp4(satrec, tsince) if isnan(r[0]) and isnan(r[1]) and isnan(r[2]): error_list.append((satrec.error, satrec.error_message)) return yield format_long_line(satrec, mu, r, v) tsince += tstep if tsince - tend < tstep - 1e-6: # do not miss last line! r, v = sgp4(satrec, tend) if isnan(r[0]) and isnan(r[1]) and isnan(r[2]): error_list.append((satrec.error, satrec.error_message)) return yield format_long_line(satrec, mu, r, v) def format_short_line(satrec, r, v): """Short line, using the same format string that testcpp.cpp uses.""" return ' %16.8f %16.8f %16.8f %16.8f %12.9f %12.9f %12.9f\n' % ( satrec.t, r[0], r[1], r[2], v[0], v[1], v[2]) def format_long_line(satrec, mu, r, v): """Long line, using the same format string that testcpp.cpp uses.""" short = format_short_line(satrec, r, v).strip('\n') jd = satrec.jdsatepoch + satrec.t / 1440.0 year, mon, day, hr, minute, sec = invjday(jd) (p, a, ecc, incl, node, argp, nu, m, arglat, truelon, lonper ) = rv2coe(r, v, mu) return short + (' %14.6f %8.6f %10.5f %10.5f %10.5f %10.5f %10.5f' ' %5i%3i%3i %2i:%2i:%9.6f\n') % ( a, ecc, incl*rad, node*rad, argp*rad, nu*rad, m*rad, year, mon, day, hr, minute, sec) def load_tests(loader, tests, ignore): """Run our main documentation as a test.""" # Python 2.6 formats floating-point numbers a bit differently and # breaks the doctest. if sys.version_info >= (2, 7): tests.addTests(DocTestSuite('sgp4', optionflags=ELLIPSIS)) return tests sgp4-1.4/sgp4/ext.py0000640000175000017500000004613312423537774015373 0ustar brandonbrandon00000000000000# -*- coding: utf-8 -*- """Utility routines from "sgp4ext.cpp".""" from math import (acos, asinh, atan2, copysign, cos, fabs, fmod, pi, sin, sinh, sqrt, tan) undefined = None """ /* ----------------------------------------------------------------------------- * * function mag * * this procedure finds the magnitude of a vector. the tolerance is set to * 0.000001, thus the 1.0e-12 for the squared test of underflows. * * author : david vallado 719-573-2600 1 mar 2001 * * inputs description range / units * vec - vector * * outputs : * vec - answer stored in fourth component * * locals : * none. * * coupling : * none. * --------------------------------------------------------------------------- */ """ def mag(x): return sqrt(x[0]*x[0] + x[1]*x[1] + x[2]*x[2]); """ /* ----------------------------------------------------------------------------- * * procedure cross * * this procedure crosses two vectors. * * author : david vallado 719-573-2600 1 mar 2001 * * inputs description range / units * vec1 - vector number 1 * vec2 - vector number 2 * * outputs : * outvec - vector result of a x b * * locals : * none. * * coupling : * mag magnitude of a vector ---------------------------------------------------------------------------- */ """ def cross(vec1, vec2, outvec): outvec[0]= vec1[1]*vec2[2] - vec1[2]*vec2[1]; outvec[1]= vec1[2]*vec2[0] - vec1[0]*vec2[2]; outvec[2]= vec1[0]*vec2[1] - vec1[1]*vec2[0]; """ /* ----------------------------------------------------------------------------- * * function dot * * this function finds the dot product of two vectors. * * author : david vallado 719-573-2600 1 mar 2001 * * inputs description range / units * vec1 - vector number 1 * vec2 - vector number 2 * * outputs : * dot - result * * locals : * none. * * coupling : * none. * * --------------------------------------------------------------------------- */ """ def dot(x, y): return (x[0]*y[0] + x[1]*y[1] + x[2]*y[2]); """ /* ----------------------------------------------------------------------------- * * procedure angle * * this procedure calculates the angle between two vectors. the output is * set to 999999.1 to indicate an undefined value. be sure to check for * this at the output phase. * * author : david vallado 719-573-2600 1 mar 2001 * * inputs description range / units * vec1 - vector number 1 * vec2 - vector number 2 * * outputs : * theta - angle between the two vectors -pi to pi * * locals : * temp - temporary real variable * * coupling : * dot dot product of two vectors * --------------------------------------------------------------------------- */ """ def angle(vec1, vec2): small = 0.00000001; undefined = 999999.1; magv1 = mag(vec1); magv2 = mag(vec2); if magv1*magv2 > small*small: temp= dot(vec1,vec2) / (magv1*magv2); if fabs(temp) > 1.0: temp = copysign(1.0, temp) return acos( temp ); else: return undefined; """ /* ----------------------------------------------------------------------------- * * function newtonnu * * this function solves keplers equation when the true anomaly is known. * the mean and eccentric, parabolic, or hyperbolic anomaly is also found. * the parabolic limit at 168° is arbitrary. the hyperbolic anomaly is also * limited. the hyperbolic sine is used because it's not double valued. * * author : david vallado 719-573-2600 27 may 2002 * * revisions * vallado - fix small 24 sep 2002 * * inputs description range / units * ecc - eccentricity 0.0 to * nu - true anomaly -2pi to 2pi rad * * outputs : * e0 - eccentric anomaly 0.0 to 2pi rad 153.02 ° * m - mean anomaly 0.0 to 2pi rad 151.7425 ° * * locals : * e1 - eccentric anomaly, next value rad * sine - sine of e * cose - cosine of e * ktr - index * * coupling : * asinh - arc hyperbolic sine * * references : * vallado 2007, 85, alg 5 * --------------------------------------------------------------------------- */ """ def newtonnu(ecc, nu): # --------------------- implementation --------------------- e0= 999999.9; m = 999999.9; small = 0.00000001; # --------------------------- circular ------------------------ if fabs(ecc) < small: m = nu; e0= nu; else: # ---------------------- elliptical ----------------------- if ecc < 1.0-small: sine= ( sqrt( 1.0 -ecc*ecc ) * sin(nu) ) / ( 1.0 +ecc*cos(nu) ); cose= ( ecc + cos(nu) ) / ( 1.0 + ecc*cos(nu) ); e0 = atan2( sine,cose ); m = e0 - ecc*sin(e0); else: # -------------------- hyperbolic -------------------- if ecc > 1.0 + small: if ecc > 1.0 and fabs(nu)+0.00001 < pi-acos(1.0 /ecc): sine= ( sqrt( ecc*ecc-1.0 ) * sin(nu) ) / ( 1.0 + ecc*cos(nu) ); e0 = asinh( sine ); m = ecc*sinh(e0) - e0; else: # ----------------- parabolic --------------------- if fabs(nu) < 168.0*pi/180.0: e0= tan( nu*0.5 ); m = e0 + (e0*e0*e0)/3.0; if ecc < 1.0: m = fmod( m,2.0 *pi ); if m < 0.0: m = m + 2.0 *pi; e0 = fmod( e0,2.0 *pi ); return e0, m """ /* ----------------------------------------------------------------------------- * * function rv2coe * * this function finds the classical orbital elements given the geocentric * equatorial position and velocity vectors. * * author : david vallado 719-573-2600 21 jun 2002 * * revisions * vallado - fix special cases 5 sep 2002 * vallado - delete extra check in inclination code 16 oct 2002 * vallado - add constant file use 29 jun 2003 * vallado - add mu 2 apr 2007 * * inputs description range / units * r - ijk position vector km * v - ijk velocity vector km / s * mu - gravitational parameter km3 / s2 * * outputs : * p - semilatus rectum km * a - semimajor axis km * ecc - eccentricity * incl - inclination 0.0 to pi rad * omega - longitude of ascending node 0.0 to 2pi rad * argp - argument of perigee 0.0 to 2pi rad * nu - true anomaly 0.0 to 2pi rad * m - mean anomaly 0.0 to 2pi rad * arglat - argument of latitude (ci) 0.0 to 2pi rad * truelon - true longitude (ce) 0.0 to 2pi rad * lonper - longitude of periapsis (ee) 0.0 to 2pi rad * * locals : * hbar - angular momentum h vector km2 / s * ebar - eccentricity e vector * nbar - line of nodes n vector * c1 - v**2 - u/r * rdotv - r dot v * hk - hk unit vector * sme - specfic mechanical energy km2 / s2 * i - index * e - eccentric, parabolic, * hyperbolic anomaly rad * temp - temporary variable * typeorbit - type of orbit ee, ei, ce, ci * * coupling : * mag - magnitude of a vector * cross - cross product of two vectors * angle - find the angle between two vectors * newtonnu - find the mean anomaly * * references : * vallado 2007, 126, alg 9, ex 2-5 * --------------------------------------------------------------------------- */ """ def rv2coe(r, v, mu): hbar = [None, None, None] nbar = [None, None, None] ebar = [None, None, None] typeorbit = [None, None, None]; twopi = 2.0 * pi; halfpi = 0.5 * pi; small = 0.00000001; undefined = 999999.1; infinite = 999999.9; # ------------------------- implementation ----------------- magr = mag( r ); magv = mag( v ); # ------------------ find h n and e vectors ---------------- cross( r,v, hbar ); magh = mag( hbar ); if magh > small: nbar[0]= -hbar[1]; nbar[1]= hbar[0]; nbar[2]= 0.0; magn = mag( nbar ); c1 = magv*magv - mu /magr; rdotv = dot( r,v ); for i in range(0, 3): ebar[i]= (c1*r[i] - rdotv*v[i])/mu; ecc = mag( ebar ); # ------------ find a e and semi-latus rectum ---------- sme= ( magv*magv*0.5 ) - ( mu /magr ); if fabs( sme ) > small: a= -mu / (2.0 *sme); else: a= infinite; p = magh*magh/mu; # ----------------- find inclination ------------------- hk= hbar[2]/magh; incl= acos( hk ); # -------- determine type of orbit for later use -------- # ------ elliptical, parabolic, hyperbolic inclined ------- typeorbit = 'ei' if ecc < small: # ---------------- circular equatorial --------------- if incl < small or fabs(incl-pi) < small: typeorbit = 'ce' else: # -------------- circular inclined --------------- typeorbit = 'ci' else: # - elliptical, parabolic, hyperbolic equatorial -- if incl < small or fabs(incl-pi) < small: typeorbit = 'ee' # ---------- find longitude of ascending node ------------ if magn > small: temp= nbar[0] / magn; if fabs(temp) > 1.0: temp = copysign(1.0, temp) omega= acos( temp ); if nbar[1] < 0.0: omega= twopi - omega; else: omega= undefined; # ---------------- find argument of perigee --------------- if typeorbit == 'ei': argp = angle( nbar,ebar); if ebar[2] < 0.0: argp= twopi - argp; else: argp= undefined; # ------------ find true anomaly at epoch ------------- if typeorbit[0] == 'e': nu = angle( ebar,r); if rdotv < 0.0: nu= twopi - nu; else: nu= undefined; # ---- find argument of latitude - circular inclined ----- if typeorbit == 'ci': arglat = angle( nbar,r ); if r[2] < 0.0: arglat= twopi - arglat; m = arglat; else: arglat= undefined; # -- find longitude of perigee - elliptical equatorial ---- if ecc > small and typeorbit == 'ee': temp= ebar[0]/ecc; if fabs(temp) > 1.0: temp = copysign(1.0, temp) lonper= acos( temp ); if ebar[1] < 0.0: lonper= twopi - lonper; if incl > halfpi: lonper= twopi - lonper; else: lonper= undefined; # -------- find true longitude - circular equatorial ------ if magr > small and typeorbit == 'ce': temp= r[0]/magr; if fabs(temp) > 1.0: temp = copysign(1.0, temp) truelon= acos( temp ); if r[1] < 0.0: truelon= twopi - truelon; if incl > halfpi: truelon= twopi - truelon; m = truelon; else: truelon= undefined; # ------------ find mean anomaly for all orbits ----------- if typeorbit[0] == 'e': e, m = newtonnu(ecc, nu); else: p = undefined; a = undefined; ecc = undefined; incl = undefined; omega= undefined; argp = undefined; nu = undefined; m = undefined; arglat = undefined; truelon= undefined; lonper = undefined; return p, a, ecc, incl, omega, argp, nu, m, arglat, truelon, lonper """ /* ----------------------------------------------------------------------------- * * procedure jday * * this procedure finds the julian date given the year, month, day, and time. * the julian date is defined by each elapsed day since noon, jan 1, 4713 bc. * * algorithm : calculate the answer in one step for efficiency * * author : david vallado 719-573-2600 1 mar 2001 * * inputs description range / units * year - year 1900 .. 2100 * mon - month 1 .. 12 * day - day 1 .. 28,29,30,31 * hr - universal time hour 0 .. 23 * min - universal time min 0 .. 59 * sec - universal time sec 0.0 .. 59.999 * * outputs : * jd - julian date days from 4713 bc * * locals : * none. * * coupling : * none. * * references : * vallado 2007, 189, alg 14, ex 3-14 * * --------------------------------------------------------------------------- */ """ def jday(year, mon, day, hr, minute, sec): return (367.0 * year - 7.0 * (year + ((mon + 9.0) // 12.0)) * 0.25 // 1.0 + 275.0 * mon // 9.0 + day + 1721013.5 + ((sec / 60.0 + minute) / 60.0 + hr) / 24.0 # ut in days # - 0.5*sgn(100.0*year + mon - 190002.5) + 0.5; ) """ /* ----------------------------------------------------------------------------- * * procedure days2mdhms * * this procedure converts the day of the year, days, to the equivalent month * day, hour, minute and second. * * algorithm : set up array for the number of days per month * find leap year - use 1900 because 2000 is a leap year * loop through a temp value while the value is < the days * perform int conversions to the correct day and month * convert remainder into h m s using type conversions * * author : david vallado 719-573-2600 1 mar 2001 * * inputs description range / units * year - year 1900 .. 2100 * days - julian day of the year 0.0 .. 366.0 * * outputs : * mon - month 1 .. 12 * day - day 1 .. 28,29,30,31 * hr - hour 0 .. 23 * min - minute 0 .. 59 * sec - second 0.0 .. 59.999 * * locals : * dayofyr - day of year * temp - temporary extended values * inttemp - temporary int value * i - index * lmonth[12] - int array containing the number of days per month * * coupling : * none. * --------------------------------------------------------------------------- */ """ def days2mdhms(year, days): lmonth = (31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31); dayofyr = int(days // 1.0); # ----------------- find month and day of month ---------------- if (year % 4) == 0: lmonth = (31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31); i = 1; inttemp = 0; while dayofyr > inttemp + lmonth[i-1] and i < 12: inttemp = inttemp + lmonth[i-1]; i += 1; mon = i; day = dayofyr - inttemp; # ----------------- find hours minutes and seconds ------------- temp = (days - dayofyr) * 24.0; hr = int(temp // 1.0); temp = (temp - hr) * 60.0; minute = int(temp // 1.0); sec = (temp - minute) * 60.0; return mon, day, hr, minute, sec """ /* ----------------------------------------------------------------------------- * * procedure invjday * * this procedure finds the year, month, day, hour, minute and second * given the julian date. tu can be ut1, tdt, tdb, etc. * * algorithm : set up starting values * find leap year - use 1900 because 2000 is a leap year * find the elapsed days through the year in a loop * call routine to find each individual value * * author : david vallado 719-573-2600 1 mar 2001 * * inputs description range / units * jd - julian date days from 4713 bc * * outputs : * year - year 1900 .. 2100 * mon - month 1 .. 12 * day - day 1 .. 28,29,30,31 * hr - hour 0 .. 23 * min - minute 0 .. 59 * sec - second 0.0 .. 59.999 * * locals : * days - day of year plus fractional * portion of a day days * tu - julian centuries from 0 h * jan 0, 1900 * temp - temporary double values * leapyrs - number of leap years from 1900 * * coupling : * days2mdhms - finds month, day, hour, minute and second given days and year * * references : * vallado 2007, 208, alg 22, ex 3-13 * --------------------------------------------------------------------------- */ """ def invjday(jd): # --------------- find year and days of the year --------------- temp = jd - 2415019.5; tu = temp / 365.25; year = 1900 + int(tu // 1.0); leapyrs = int(((year - 1901) * 0.25) // 1.0); # optional nudge by 8.64x10-7 sec to get even outputs days = temp - ((year - 1900) * 365.0 + leapyrs) + 0.00000000001; # ------------ check for case of beginning of a year ----------- if (days < 1.0): year = year - 1; leapyrs = int(((year - 1901) * 0.25) // 1.0); days = temp - ((year - 1900) * 365.0 + leapyrs); # ----------------- find remaing data ------------------------- mon, day, hr, minute, sec = days2mdhms(year, days); sec = sec - 0.00000086400; return year, mon, day, hr, minute, sec sgp4-1.4/setup.py0000640000175000017500000000224212456126055015037 0ustar brandonbrandon00000000000000from distutils.core import setup from textwrap import dedent import sgp4, sgp4.model description, long_description = sgp4.__doc__.split('\n', 1) satdoc = dedent(sgp4.model.Satellite.__doc__.split('\n', 1)[1]) long_description = long_description.replace('entry.', 'entry.' + satdoc) setup(name = 'sgp4', version = '1.4', description = description, long_description = long_description, license = 'MIT', author = 'Brandon Rhodes', author_email = 'brandon@rhodesmill.org', url = 'https://github.com/brandon-rhodes/python-sgp4', classifiers = [ 'Development Status :: 5 - Production/Stable', 'Intended Audience :: Science/Research', 'License :: OSI Approved :: MIT License', 'Programming Language :: Python :: 2', 'Programming Language :: Python :: 2.6', 'Programming Language :: Python :: 2.7', 'Programming Language :: Python :: 3', 'Programming Language :: Python :: 3.2', 'Programming Language :: Python :: 3.3', 'Programming Language :: Python :: 3.4', 'Topic :: Scientific/Engineering :: Astronomy', ], packages = ['sgp4'], )