Script 'mail_helper' called by obssrc Hello community, here is the log from the commit of package python-sgp4 for openSUSE:Factory checked in at 2021-07-04 22:10:50 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Comparing /work/SRC/openSUSE:Factory/python-sgp4 (Old) and /work/SRC/openSUSE:Factory/.python-sgp4.new.2625 (New) ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Package is "python-sgp4" Sun Jul 4 22:10:50 2021 rev:6 rq:903954 version:2.20 Changes: -------- --- /work/SRC/openSUSE:Factory/python-sgp4/python-sgp4.changes 2021-02-18 20:53:02.323474754 +0100 +++ /work/SRC/openSUSE:Factory/.python-sgp4.new.2625/python-sgp4.changes 2021-07-04 22:10:56.089139981 +0200 @@ -1,0 +2,32 @@ +Sun Jul 4 12:03:54 UTC 2021 - Ben Greiner <[email protected]> + +- Update to version 2.20 + * Taught sgp4init() to round both epochdays and jdsatepochF to + the same 8 decimal places used for the date fraction in a TLE, + if the user-supplied epoch itself has 8 or fewer digits behind + the decimal point. This should make it easier to build + satellites that round-trip to TLE format with perfect accuracy. + * Fixed how export_tle() formats the BSTAR field when its value, + if written in scientific notation, has a positive exponent. + * Fixed the epochyr assigned by sgp4init() so years before 2000 + have two digits instead of three (for example, so that 1980 + produces an epochyr of 80 instead of 980). +- Release 2.19 + * Extended the documentation on the Python Package Index and in + the module docstring so it lists every Satrec attribute that + this library exposes; even the more obscure ones might be + useful to folks working to analyze satellite orbits. +- Release 2.18 + * If a TLE satellite number lacks the required 5 digits, + twoline2rv() now gives the underlying C++ library a little + help so it can still parse the classification and + international designator correctly. + * The Satrec attributes jdsatepoch, jdsatepochF, epochyr, and + epochdays are now writeable, so users can adjust their values + manually ??? which should make up for the fact that the sgp4init + () method can???t set them with full floating point precision. +- Release 2.17 + * Fixed where in the output array the sgp4_array() method writes + NaN values when an SGP4 propagation fails. + +------------------------------------------------------------------- Old: ---- sgp4-2.16.tar.gz New: ---- sgp4-2.20.tar.gz ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Other differences: ------------------ ++++++ python-sgp4.spec ++++++ --- /var/tmp/diff_new_pack.Va7Thc/_old 2021-07-04 22:10:56.509136732 +0200 +++ /var/tmp/diff_new_pack.Va7Thc/_new 2021-07-04 22:10:56.509136732 +0200 @@ -20,7 +20,7 @@ %define skip_python2 1 %define skip_python36 1 Name: python-sgp4 -Version: 2.16 +Version: 2.20 Release: 0 Summary: Track earth satellite TLE orbits using up-to-date 2010 version of SGP4 License: MIT ++++++ sgp4-2.16.tar.gz -> sgp4-2.20.tar.gz ++++++ diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/sgp4-2.16/PKG-INFO new/sgp4-2.20/PKG-INFO --- old/sgp4-2.16/PKG-INFO 2021-02-12 22:28:18.969517500 +0100 +++ new/sgp4-2.20/PKG-INFO 2021-07-01 11:36:12.217999700 +0200 @@ -1,7 +1,7 @@ Metadata-Version: 1.1 Name: sgp4 -Version: 2.16 -Summary: Track earth satellite TLE orbits using up-to-date 2020 version of SGP4 +Version: 2.20 +Summary: Track Earth satellites given TLE data, using up-to-date 2020 SGP4 routines. Home-page: https://github.com/brandon-rhodes/python-sgp4 Author: Brandon Rhodes Author-email: [email protected] @@ -17,14 +17,17 @@ satellites themselves deviate from the ideal orbits described in TLE files. - * If your platform supports it, this package compiles the verbatim - source code from the official C++ version of SGP4. You can call the - routine directly, or through an array API that loops over arrays of - satellites and arrays of times with machine code instead of Python. + * If your platform supports it, this package compiles and uses the + verbatim source code from the official C++ version of SGP4. * Otherwise, a slower but reliable Python implementation of SGP4 is used instead. + * If, instead of asking for the position of a single satellite at a + single time, you supply this library with an array of satellites and + an array of times, then the arrays can be processed using machine code + instead of requiring you to run a slow Python loop over them. + Note that the SGP4 propagator returns raw *x,y,z* Cartesian coordinates in a ???True Equator Mean Equinox??? (TEME) reference frame that???s centered on the Earth but does not rotate with it ??? an ???Earth centered inertial??? @@ -276,46 +279,6 @@ [-3.85 6.28 -1.85] [-3.91 6.25 -1.83]]] - Attributes - ---------- - - The attributes of a ``Satrec`` object carry the data loaded from the TLE - entry. - 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_kozai`` - Mean motion in radians per minute. - - The parameters are also listed briefly, along with their units, in the - ???Providing your own elements??? section below. - Export ------ @@ -381,9 +344,9 @@ Providing your own elements --------------------------- - If instead of parsing a TLE you want to provide your own orbital - elements, you can call the ``sgp4init()`` method of any existing - satellite object to reset it to those new elements. + If instead of parsing a TLE you want to specify orbital elements + directly, you can call a satellite object???s ``sgp4init()`` method with + the new elements: >>> sat = Satrec() >>> sat.sgp4init( @@ -391,7 +354,7 @@ ... 'i', # 'a' = old AFSPC mode, 'i' = improved mode ... 5, # satnum: Satellite number ... 18441.785, # epoch: days since 1949 December 31 00:00 UT - ... 2.8098e-05, # bstar: drag coefficient (/earth radii) + ... 2.8098e-05, # bstar: drag coefficient (1/earth radii) ... 6.969196665e-13, # ndot (NOT USED): ballistic coefficient (revs/day) ... 0.0, # nddot (NOT USED): mean motion 2nd derivative (revs/day^3) ... 0.1859667, # ecco: eccentricity @@ -402,27 +365,153 @@ ... 6.0863854713832, # nodeo: right ascension of ascending node (radians) ... ) - The two parameters marked ???NOT USED??? above, ``ndot`` and ``nddot``, do - get saved to the satellite object, and do get written out if you write - the parameters to a TLE or OMM file. But they are ignored by SGP4 when - doing propagation, so you can leave them ``0.0`` without any effect on - the resulting satellite positions. - - To compute the ???epoch??? value, you can simply take a normal Julian date - and subtract ``2433281.5`` days. - - In addition to setting the attributes natively set by the underlying - ``sgp4init()`` routine, this library also goes ahead and sets the date - fields ``epochyr``, ``epochdays``, ``jdsatepoch``, and ``jdsatepochF``. - - The character provided as the second argument can be ``'a'`` to run the - computations so that they are compatible with the old Air Force Space - Command edition of the library, or ``'i'`` to run the new and improved - version of the SGP4 algorithm. - - You can also directly access a satellite???s orbital parameters by asking - for the attributes ``sat.epoch``, ``sat.bstar``, and so forth, using the - names given in the comments above. + * The two parameters marked ???NOT USED??? above, ``ndot`` and ``nddot``, do + get saved to the satellite object, and do get written out if you write + the parameters to a TLE or OMM file. But they are ignored by SGP4 + when doing propagation, so you can leave them ``0.0`` without any + effect on the resulting satellite positions. + + * To compute the ???epoch??? argument, you can take a normal Julian date and + subtract ``2433281.5`` days. + + * Once the underlying C++ routine is finished, this Python library ??? as + a convenience for callers ??? goes ahead and sets four time attributes + that ``sgp4init()`` leaves unset: the date fields ``epochyr``, + ``epochdays``, ``jdsatepoch``, and ``jdsatepochF``. + + See the next section for the complete list of attributes that are + available from the satellite record once it has been initialized. + + Attributes + ---------- + + There are several dozen ``Satrec`` attributes + that expose data from the underlying C++ SGP4 record. + They fall into several categories. + + *Identification* + + These are copied directly from the TLE record but aren???t used by the + propagation math. + + | ``satnum`` ??? Unique number assigned to the satellite. + | ``classification`` ??? ``'U'``, ``'C'``, or ``'S'`` + indicating the element set is Unclassified, Classified, or Secret. + | ``ephtype`` ??? Integer ???ephemeris type???, used internally by space + agencies to mark element sets that are not ready for publication; + this field should always be ``0`` in published TLEs. + | ``elnum`` ??? Element set number. + | ``revnum`` ??? Satellite???s revolution number at the moment of the epoch, + presumably counting from 1 following launch. + + *The Orbital Elements* + + These are the orbital parameters, copied verbatim from the text of the + TLE record. They describe the orbit at the moment of the TLE???s epoch + and so remain constant even as the satellite record is used over and + over again to propagate positions for different times. + + | ``epochyr`` ??? Epoch date: the last two digits of the year. + | ``epochdays`` ??? Epoch date: the number of days into the year, + including a decimal fraction for the UTC time of day. + | ``ndot`` ??? First time derivative of the mean motion + (loaded from the TLE, but otherwise ignored). + | ``nddot`` ??? Second time derivative of the mean motion + (loaded from the TLE, but otherwise ignored). + | ``bstar`` ??? Ballistic drag coefficient B* (1/earth??radii). + | ``inclo`` ??? Inclination (radians). + | ``nodeo`` ??? Right ascension of ascending node (radians). + | ``ecco`` ??? Eccentricity. + | ``argpo`` ??? Argument of perigee (radians). + | ``mo`` ??? Mean anomaly (radians). + | ``no_kozai`` ??? Mean motion (radians/minute). + | ``no`` ??? Alias for ``no_kozai``, for compatibility with old code. + + You can also access the epoch as a Julian date: + + | ``jdsatepoch`` ??? Whole part of the epoch???s Julian date. + | ``jdsatepochF`` ??? Fractional part of the epoch???s Julian date. + + *Derived Orbit Properties* + + These are computed when the satellite is first loaded, + as a convenience for callers who might be interested in them. + They aren???t used by the SGP4 propagator itself. + + | ``a`` ??? Semi-major axis (earth??radii). + | ``altp`` ??? Altitude of the satellite at perigee + (earth??radii, assuming a spherical Earth). + | ``alta`` ??? Altitude of the satellite at apogee + (earth??radii, assuming a spherical Earth). + | ``argpdot`` ??? Rate at which the argument of perigee is changing + (radians/minute). + | ``gsto`` ??? Greenwich Sidereal Time at the satellite???s epoch (radians). + | ``mdot`` ??? Rate at which the mean anomaly is changing (radians/minute) + | ``nodedot`` ??? Rate at which the right ascension of the ascending node + is changing (radians/minute). + + *Propagator Mode* + + | ``operationmode`` ??? A single character that directs SGP4 + to either operate in its modern ``'i'`` improved mode + or in its legacy ``'a'`` AFSPC mode. + | ``method`` ??? A single character, chosen automatically + when the orbital elements were loaded, that indicates whether SGP4 + has chosen to use its built-in ``'n'`` Near Earth + or ``'d'`` Deep Space mode for this satellite. + + *Results From the Most Recent Call* + + | ``t`` ??? + The time you gave when you most recently asked SGP4 + to compute this satellite???s position, + measured in minutes before (negative) or after (position) + the satellite???s epoch. + | ``error`` ??? + Error code produced by the most recent SGP4 propagation + you performed with this element set. + + The possible ``error`` codes are: + + 0. No error. + 1. Mean eccentricity is outside the range 0???????e??<??1. + 2. Mean motion has fallen below zero. + 3. Perturbed eccentricity is outside the range 0???????e???????1. + 4. Length of the orbit???s semi-latus rectum has fallen below zero. + 5. (No longer used.) + 6. Orbit has decayed: the computed position is underground. + (The position is still returned, in case the vector is helpful + to software that might be searching for the moment of re-entry.) + + Partway through each propagation, the SGP4 routine saves a set of + ???singly averaged mean elements??? that describe the orbit???s shape at the + moment for which a position is being computed. They are averaged with + respect to the mean anomaly and include the effects of secular gravity, + atmospheric drag, and ??? in Deep Space mode ??? of those pertubations from + the Sun and Moon that SGP4 averages over an entire revolution of each of + those bodies. They omit both the shorter-term and longer-term periodic + pertubations from the Sun and Moon that SGP4 applies right before + computing each position. + + | ``am`` ??? Average semi-major axis (earth??radii). + | ``em`` ??? Average eccentricity. + | ``im`` ??? Average inclination (radians). + | ``Om`` ??? Average right ascension of ascending node (radians). + | ``om`` ??? Average argument of perigee (radians). + | ``mm`` ??? Average mean anomaly (radians). + | ``nm`` ??? Average mean motion (radians/minute). + + *Gravity Model Parameters* + + When the satellite record is initialized, your choice of gravity model + results in a slate of eight constants being copied in: + + | ``tumin`` ??? Minutes in one ???time unit???. + | ``xke`` ??? The reciprocal of ``tumin``. + | ``mu`` ??? Earth???s gravitational parameter (km??/s??). + | ``radiusearthkm`` ??? Radius of the earth (km). + | ``j2``, ``j3``, ``j4`` ??? Un-normalized zonal harmonic values J???, J???, and J???. + | ``j3oj2`` ??? The ratio J???/J???. Validation against the official algorithm ----------------------------------------- @@ -470,6 +559,42 @@ Changelog --------- + 2021-07-01 ??? 2.20 + + * Taught ``sgp4init()`` to round both ``epochdays`` and ``jdsatepochF`` + to the same 8 decimal places used for the date fraction in a TLE, if + the user-supplied ``epoch`` itself has 8 or fewer digits behind the + decimal point. This should make it easier to build satellites that + round-trip to TLE format with perfect accuracy. + + * Fixed how ``export_tle()`` formats the BSTAR field when its value, if + written in scientific notation, has a positive exponent. + + * Fixed the ``epochyr`` assigned by ``sgp4init()`` so years before 2000 + have two digits instead of three (for example, so that 1980 produces + an ``epochyr`` of 80 instead of 980). + + 2021-04-22 ??? 2.19 + + * Extended the documentation on the Python Package Index and in the + module docstring so it lists every ``Satrec`` attribute that this + library exposes; even the more obscure ones might be useful to folks + working to analyze satellite orbits. + + 2021-03-08 ??? 2.18 + + * If a TLE satellite number lacks the required 5 digits, + ``twoline2rv()`` now gives the underlying C++ library a little help so + it can still parse the classification and international designator + correctly. + + * The ``Satrec`` attributes ``jdsatepoch``, ``jdsatepochF``, + ``epochyr``, and ``epochdays`` are now writeable, so users can adjust + their values manually ??? which should make up for the fact that the + ``sgp4init()`` method can???t set them with full floating point + precision. + + | 2021-02-17 ??? 2.17 ??? Fixed where in the output array the ``sgp4_array()`` method writes NaN values when an SGP4 propagation fails. | 2021-02-12 ??? 2.16 ??? Fixed ``days2mdhms()`` rounding to always match TLE epoch. | 2021-01-08 ??? 2.15 ??? Fixed parsing of the ``satnum`` TLE field in the Python fallback code, when the field has a leading space; added OMM export routine. | 2020-12-16 ??? 2.14 ??? New data formats: added OMM message support for both XML and CSV, and added support for the new Alpha-5 extension to TLE files. diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/sgp4-2.16/extension/wrapper.cpp new/sgp4-2.20/extension/wrapper.cpp --- old/sgp4-2.16/extension/wrapper.cpp 2021-02-12 22:28:12.257153000 +0100 +++ new/sgp4-2.20/extension/wrapper.cpp 2021-07-01 11:36:07.177992600 +0200 @@ -74,12 +74,13 @@ for (Py_ssize_t j=0; j < jmax; j++) { double t = (jd[j] - satrec.jdsatepoch) * 1440.0 + (fr[j] - satrec.jdsatepochF) * 1440.0; - Py_ssize_t k = i * jmax + j; - SGP4Funcs::sgp4(satrec, t, r + k*3, v + k*3); + Py_ssize_t k1 = i * jmax + j; + Py_ssize_t k3 = 3 * k1; + SGP4Funcs::sgp4(satrec, t, r + k3, v + k3); + e[k1] = (uint8_t) satrec.error; if (satrec.error && satrec.error < 6) { - r[k] = r[k+1] = r[k+2] = v[k] = v[k+1] = v[k+2] = NAN; + r[k3] = r[k3+1] = r[k3+2] = v[k3] = v[k3+1] = v[k3+2] = NAN; } - e[k] = (uint8_t) satrec.error; } } } @@ -119,6 +120,11 @@ line1[68] = '\0'; line2[68] = '\0'; + /* Allocate the new object. */ + SatrecObject *self = (SatrecObject*) cls->tp_alloc(cls, 0); + if (!self) + return NULL; + /* Correct for locales that use a comma as the decimal point, since users report that the scanf() function on macOS is sensitive to locale when parsing floats. This operation is not thread-safe, @@ -131,10 +137,18 @@ if (switch_locale) old_locale = setlocale(LC_NUMERIC, "C"); - SatrecObject *self = (SatrecObject*) cls->tp_alloc(cls, 0); - if (!self) - return NULL; + /* Leading spaces in a catalog number make scanf() in the official + code consume the Classification letter as part of the catalog + number. (The first character of the International Designator + then gets consumed as the Classification instead.) But no + parsing error is reported, which is bad for users, so let's avoid + the situation by adding leading zeros ourselves. */ + for (int i=2; i<7; i++) { + if (line1[i] == ' ') line1[i] = '0'; + if (line2[i] == ' ') line2[i] = '0'; + } + /* Call the official routine. */ SGP4Funcs::twoline2rv(line1, line2, ' ', ' ', 'i', whichconst, dummy, dummy, dummy, self->satrec); @@ -192,14 +206,27 @@ nodeo, satrec); /* Populate date fields that SGP4Funcs::twoline2rv would set. */ + double whole; + double fraction = modf(epoch, &whole); + double whole_jd = whole + 2433281.5; + + /* Go out on a limb: if `epoch` has no decimal digits past the 8 + decimal places stored in a TLE, then assume the user is trying + to specify an exact decimal fraction. */ + double epoch8 = epoch * 1e8; + if (round(epoch8) == epoch8) { + fraction = round(fraction * 1e8) / 1e8; + } + + satrec.jdsatepoch = whole_jd; + satrec.jdsatepochF = fraction; + int y, m, d, H, M; double S, jan0jd, jan0fr /* always comes out 0.0 */; - SGP4Funcs::invjday_SGP4(2433281.5, epoch, y, m, d, H, M, S); + SGP4Funcs::invjday_SGP4(2433281.5, whole, y, m, d, H, M, S); SGP4Funcs::jday_SGP4(y, 1, 0, 0, 0, 0.0, jan0jd, jan0fr); - satrec.epochyr = y % 1000; - satrec.epochdays = 2433281.5 - jan0jd + epoch; - satrec.jdsatepochF = modf(epoch, &satrec.jdsatepoch); - satrec.jdsatepoch += 2433281.5; + satrec.epochyr = y % 100; + satrec.epochdays = whole_jd - jan0jd + fraction; /* Return true as sgp4init does, satrec.error contains any error codes */ @@ -268,16 +295,16 @@ {"operationmode", T_CHAR, O(operationmode), READONLY, PyDoc_STR("Operation mode: 'a' legacy AFSPC, or 'i' improved.")}, - {"jdsatepoch", T_DOUBLE, O(jdsatepoch), READONLY, + {"jdsatepoch", T_DOUBLE, O(jdsatepoch), 0, PyDoc_STR("Julian date of epoch, day number (see jdsatepochF).")}, - {"jdsatepochF", T_DOUBLE, O(jdsatepochF), READONLY, + {"jdsatepochF", T_DOUBLE, O(jdsatepochF), 0, PyDoc_STR("Julian date of epoch, fraction of day (see jdsatepoch).")}, {"classification", T_CHAR, O(classification), 0, "Usually U=Unclassified, C=Classified, or S=Secret."}, /* intldesg: inline character array; see Satrec_getset. */ - {"epochyr", T_INT, O(epochyr), READONLY, + {"epochyr", T_INT, O(epochyr), 0, PyDoc_STR("Year of this element set's epoch (see epochdays). Not set by sgp4init().")}, - {"epochdays", T_DOUBLE, O(epochdays), READONLY, + {"epochdays", T_DOUBLE, O(epochdays), 0, PyDoc_STR("Day of the year of this element set's epoch (see epochyr). Not set by sgp4init().")}, {"ndot", T_DOUBLE, O(ndot), READONLY, PyDoc_STR("Ballistic Coefficient in revs/day.")}, diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/sgp4-2.16/setup.py new/sgp4-2.20/setup.py --- old/sgp4-2.16/setup.py 2021-02-12 22:28:12.257153000 +0100 +++ new/sgp4-2.20/setup.py 2021-07-01 11:36:07.177992600 +0200 @@ -1,19 +1,9 @@ import os -import re import sys from distutils.core import setup, Extension -from textwrap import dedent - -import sgp4, sgp4.model +import sgp4 description, long_description = sgp4.__doc__.split('\n', 1) -satdoc = dedent(sgp4.model.Satellite.__doc__.split('\n', 1)[1]) -long_description = re.sub( - r'\s+\[INSERT CLASS DOCSTRING HERE.\]\s+', - satdoc, - long_description, - re.M, -) # Force compilation on Travis CI + Python 3 to make sure it keeps working. optional = True @@ -40,8 +30,16 @@ extra_compile_args=['-ffloat-store'], )) +# Read the package's "__version__" without importing it. +path = 'sgp4/__init__.py' +with open(path, 'rb') as f: + text = f.read().decode('utf-8') +text = text.replace('-*- coding: utf-8 -*-', '') # for Python 2.7 +namespace = {} +eval(compile(text, path, 'exec'), namespace) + setup(name = 'sgp4', - version = '2.16', + version = namespace['__version__'], description = description, long_description = long_description, license = 'MIT', diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/sgp4-2.16/sgp4/__init__.py new/sgp4-2.20/sgp4/__init__.py --- old/sgp4-2.16/sgp4/__init__.py 2021-02-12 22:28:12.257153000 +0100 +++ new/sgp4-2.20/sgp4/__init__.py 2021-07-01 11:36:07.177992600 +0200 @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -"""Track earth satellite TLE orbits using up-to-date 2020 version of SGP4 +"""Track Earth satellites given TLE data, using up-to-date 2020 SGP4 routines. This Python package computes the position and velocity of an earth-orbiting satellite, given the satellite's TLE orbital elements @@ -11,14 +11,17 @@ satellites themselves deviate from the ideal orbits described in TLE files. -* If your platform supports it, this package compiles the verbatim - source code from the official C++ version of SGP4. You can call the - routine directly, or through an array API that loops over arrays of - satellites and arrays of times with machine code instead of Python. +* If your platform supports it, this package compiles and uses the + verbatim source code from the official C++ version of SGP4. * Otherwise, a slower but reliable Python implementation of SGP4 is used instead. +* If, instead of asking for the position of a single satellite at a + single time, you supply this library with an array of satellites and + an array of times, then the arrays can be processed using machine code + instead of requiring you to run a slow Python loop over them. + Note that the SGP4 propagator returns raw *x,y,z* Cartesian coordinates in a ???True Equator Mean Equinox??? (TEME) reference frame that???s centered on the Earth but does not rotate with it ??? an ???Earth centered inertial??? @@ -270,15 +273,6 @@ [-3.85 6.28 -1.85] [-3.91 6.25 -1.83]]] -Attributes ----------- - -The attributes of a ``Satrec`` object carry the data loaded from the TLE -entry. [INSERT CLASS DOCSTRING HERE.] - -The parameters are also listed briefly, along with their units, in the -???Providing your own elements??? section below. - Export ------ @@ -344,9 +338,9 @@ Providing your own elements --------------------------- -If instead of parsing a TLE you want to provide your own orbital -elements, you can call the ``sgp4init()`` method of any existing -satellite object to reset it to those new elements. +If instead of parsing a TLE you want to specify orbital elements +directly, you can call a satellite object???s ``sgp4init()`` method with +the new elements: >>> sat = Satrec() >>> sat.sgp4init( @@ -354,7 +348,7 @@ ... 'i', # 'a' = old AFSPC mode, 'i' = improved mode ... 5, # satnum: Satellite number ... 18441.785, # epoch: days since 1949 December 31 00:00 UT -... 2.8098e-05, # bstar: drag coefficient (/earth radii) +... 2.8098e-05, # bstar: drag coefficient (1/earth radii) ... 6.969196665e-13, # ndot (NOT USED): ballistic coefficient (revs/day) ... 0.0, # nddot (NOT USED): mean motion 2nd derivative (revs/day^3) ... 0.1859667, # ecco: eccentricity @@ -365,27 +359,153 @@ ... 6.0863854713832, # nodeo: right ascension of ascending node (radians) ... ) -The two parameters marked ???NOT USED??? above, ``ndot`` and ``nddot``, do -get saved to the satellite object, and do get written out if you write -the parameters to a TLE or OMM file. But they are ignored by SGP4 when -doing propagation, so you can leave them ``0.0`` without any effect on -the resulting satellite positions. - -To compute the ???epoch??? value, you can simply take a normal Julian date -and subtract ``2433281.5`` days. - -In addition to setting the attributes natively set by the underlying -``sgp4init()`` routine, this library also goes ahead and sets the date -fields ``epochyr``, ``epochdays``, ``jdsatepoch``, and ``jdsatepochF``. - -The character provided as the second argument can be ``'a'`` to run the -computations so that they are compatible with the old Air Force Space -Command edition of the library, or ``'i'`` to run the new and improved -version of the SGP4 algorithm. - -You can also directly access a satellite???s orbital parameters by asking -for the attributes ``sat.epoch``, ``sat.bstar``, and so forth, using the -names given in the comments above. +* The two parameters marked ???NOT USED??? above, ``ndot`` and ``nddot``, do + get saved to the satellite object, and do get written out if you write + the parameters to a TLE or OMM file. But they are ignored by SGP4 + when doing propagation, so you can leave them ``0.0`` without any + effect on the resulting satellite positions. + +* To compute the ???epoch??? argument, you can take a normal Julian date and + subtract ``2433281.5`` days. + +* Once the underlying C++ routine is finished, this Python library ??? as + a convenience for callers ??? goes ahead and sets four time attributes + that ``sgp4init()`` leaves unset: the date fields ``epochyr``, + ``epochdays``, ``jdsatepoch``, and ``jdsatepochF``. + +See the next section for the complete list of attributes that are +available from the satellite record once it has been initialized. + +Attributes +---------- + +There are several dozen ``Satrec`` attributes +that expose data from the underlying C++ SGP4 record. +They fall into several categories. + +*Identification* + +These are copied directly from the TLE record but aren???t used by the +propagation math. + +| ``satnum`` ??? Unique number assigned to the satellite. +| ``classification`` ??? ``'U'``, ``'C'``, or ``'S'`` + indicating the element set is Unclassified, Classified, or Secret. +| ``ephtype`` ??? Integer ???ephemeris type???, used internally by space + agencies to mark element sets that are not ready for publication; + this field should always be ``0`` in published TLEs. +| ``elnum`` ??? Element set number. +| ``revnum`` ??? Satellite???s revolution number at the moment of the epoch, + presumably counting from 1 following launch. + +*The Orbital Elements* + +These are the orbital parameters, copied verbatim from the text of the +TLE record. They describe the orbit at the moment of the TLE???s epoch +and so remain constant even as the satellite record is used over and +over again to propagate positions for different times. + +| ``epochyr`` ??? Epoch date: the last two digits of the year. +| ``epochdays`` ??? Epoch date: the number of days into the year, + including a decimal fraction for the UTC time of day. +| ``ndot`` ??? First time derivative of the mean motion + (loaded from the TLE, but otherwise ignored). +| ``nddot`` ??? Second time derivative of the mean motion + (loaded from the TLE, but otherwise ignored). +| ``bstar`` ??? Ballistic drag coefficient B* (1/earth??radii). +| ``inclo`` ??? Inclination (radians). +| ``nodeo`` ??? Right ascension of ascending node (radians). +| ``ecco`` ??? Eccentricity. +| ``argpo`` ??? Argument of perigee (radians). +| ``mo`` ??? Mean anomaly (radians). +| ``no_kozai`` ??? Mean motion (radians/minute). +| ``no`` ??? Alias for ``no_kozai``, for compatibility with old code. + +You can also access the epoch as a Julian date: + +| ``jdsatepoch`` ??? Whole part of the epoch???s Julian date. +| ``jdsatepochF`` ??? Fractional part of the epoch???s Julian date. + +*Derived Orbit Properties* + +These are computed when the satellite is first loaded, +as a convenience for callers who might be interested in them. +They aren???t used by the SGP4 propagator itself. + +| ``a`` ??? Semi-major axis (earth??radii). +| ``altp`` ??? Altitude of the satellite at perigee + (earth??radii, assuming a spherical Earth). +| ``alta`` ??? Altitude of the satellite at apogee + (earth??radii, assuming a spherical Earth). +| ``argpdot`` ??? Rate at which the argument of perigee is changing + (radians/minute). +| ``gsto`` ??? Greenwich Sidereal Time at the satellite???s epoch (radians). +| ``mdot`` ??? Rate at which the mean anomaly is changing (radians/minute) +| ``nodedot`` ??? Rate at which the right ascension of the ascending node + is changing (radians/minute). + +*Propagator Mode* + +| ``operationmode`` ??? A single character that directs SGP4 + to either operate in its modern ``'i'`` improved mode + or in its legacy ``'a'`` AFSPC mode. +| ``method`` ??? A single character, chosen automatically + when the orbital elements were loaded, that indicates whether SGP4 + has chosen to use its built-in ``'n'`` Near Earth + or ``'d'`` Deep Space mode for this satellite. + +*Results From the Most Recent Call* + +| ``t`` ??? + The time you gave when you most recently asked SGP4 + to compute this satellite???s position, + measured in minutes before (negative) or after (position) + the satellite???s epoch. +| ``error`` ??? + Error code produced by the most recent SGP4 propagation + you performed with this element set. + +The possible ``error`` codes are: + +0. No error. +1. Mean eccentricity is outside the range 0???????e??<??1. +2. Mean motion has fallen below zero. +3. Perturbed eccentricity is outside the range 0???????e???????1. +4. Length of the orbit???s semi-latus rectum has fallen below zero. +5. (No longer used.) +6. Orbit has decayed: the computed position is underground. + (The position is still returned, in case the vector is helpful + to software that might be searching for the moment of re-entry.) + +Partway through each propagation, the SGP4 routine saves a set of +???singly averaged mean elements??? that describe the orbit???s shape at the +moment for which a position is being computed. They are averaged with +respect to the mean anomaly and include the effects of secular gravity, +atmospheric drag, and ??? in Deep Space mode ??? of those pertubations from +the Sun and Moon that SGP4 averages over an entire revolution of each of +those bodies. They omit both the shorter-term and longer-term periodic +pertubations from the Sun and Moon that SGP4 applies right before +computing each position. + +| ``am`` ??? Average semi-major axis (earth??radii). +| ``em`` ??? Average eccentricity. +| ``im`` ??? Average inclination (radians). +| ``Om`` ??? Average right ascension of ascending node (radians). +| ``om`` ??? Average argument of perigee (radians). +| ``mm`` ??? Average mean anomaly (radians). +| ``nm`` ??? Average mean motion (radians/minute). + +*Gravity Model Parameters* + +When the satellite record is initialized, your choice of gravity model +results in a slate of eight constants being copied in: + +| ``tumin`` ??? Minutes in one ???time unit???. +| ``xke`` ??? The reciprocal of ``tumin``. +| ``mu`` ??? Earth???s gravitational parameter (km??/s??). +| ``radiusearthkm`` ??? Radius of the earth (km). +| ``j2``, ``j3``, ``j4`` ??? Un-normalized zonal harmonic values J???, J???, and J???. +| ``j3oj2`` ??? The ratio J???/J???. Validation against the official algorithm ----------------------------------------- @@ -433,6 +553,42 @@ Changelog --------- +2021-07-01 ??? 2.20 + +* Taught ``sgp4init()`` to round both ``epochdays`` and ``jdsatepochF`` + to the same 8 decimal places used for the date fraction in a TLE, if + the user-supplied ``epoch`` itself has 8 or fewer digits behind the + decimal point. This should make it easier to build satellites that + round-trip to TLE format with perfect accuracy. + +* Fixed how ``export_tle()`` formats the BSTAR field when its value, if + written in scientific notation, has a positive exponent. + +* Fixed the ``epochyr`` assigned by ``sgp4init()`` so years before 2000 + have two digits instead of three (for example, so that 1980 produces + an ``epochyr`` of 80 instead of 980). + +2021-04-22 ??? 2.19 + +* Extended the documentation on the Python Package Index and in the + module docstring so it lists every ``Satrec`` attribute that this + library exposes; even the more obscure ones might be useful to folks + working to analyze satellite orbits. + +2021-03-08 ??? 2.18 + +* If a TLE satellite number lacks the required 5 digits, + ``twoline2rv()`` now gives the underlying C++ library a little help so + it can still parse the classification and international designator + correctly. + +* The ``Satrec`` attributes ``jdsatepoch``, ``jdsatepochF``, + ``epochyr``, and ``epochdays`` are now writeable, so users can adjust + their values manually ??? which should make up for the fact that the + ``sgp4init()`` method can???t set them with full floating point + precision. + +| 2021-02-17 ??? 2.17 ??? Fixed where in the output array the ``sgp4_array()`` method writes NaN values when an SGP4 propagation fails. | 2021-02-12 ??? 2.16 ??? Fixed ``days2mdhms()`` rounding to always match TLE epoch. | 2021-01-08 ??? 2.15 ??? Fixed parsing of the ``satnum`` TLE field in the Python fallback code, when the field has a leading space; added OMM export routine. | 2020-12-16 ??? 2.14 ??? New data formats: added OMM message support for both XML and CSV, and added support for the new Alpha-5 extension to TLE files. @@ -457,3 +613,4 @@ | 2012-08-27 ??? 1.0 ??? Initial release """ +__version__ = '2.20' diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/sgp4-2.16/sgp4/exporter.py new/sgp4-2.20/sgp4/exporter.py --- old/sgp4-2.16/sgp4/exporter.py 2021-02-12 22:28:12.257153000 +0100 +++ new/sgp4-2.20/sgp4/exporter.py 2021-07-01 11:36:07.177992600 +0200 @@ -42,14 +42,11 @@ # Add First Time Derivative of the Mean Motion (don't use "+") append("{0: 8.8f}".format(satrec.ndot * (_xpdotp * 1440.0)).replace("0", "", 1) + " ") - # Add Second Time Derivative of Mean Motion (don't use "+") - # Multiplication with 10 is a hack to get the exponent right - append("{0: 4.4e}".format((satrec.nddot * (_xpdotp * 1440.0 * 1440)) * 10).replace(".", "") - .replace("e+00", "-0").replace("e-0", "-") + " ") + # Add Second Time Derivative of Mean Motion + append(_abbreviate_rate(satrec.nddot * _xpdotp * 20736000.0, '-0')) # Add BSTAR - # Multiplication with 10 is a hack to get the exponent right - append("{0: 4.4e}".format(satrec.bstar * 10).replace(".", "").replace("e+00", "+0").replace("e-0", "-") + " ") + append(_abbreviate_rate(satrec.bstar * 10.0, '+0')) # Add Ephemeris Type and Element Number ephtype = getattr(satrec, 'ephtype', 0) @@ -105,6 +102,15 @@ return line1, line2 +def _abbreviate_rate(value, zero_exponent_string): + return ( + '{0: 4.4e} '.format(value) + .replace('.', '') + .replace('e+00', zero_exponent_string) + .replace('e-0', '-') + .replace('e+0', '+') + ) + def export_omm(satrec, object_name): launch_year = int(satrec.intldesg[:2]) launch_year += 1900 + (launch_year < 57) * 100 diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/sgp4-2.16/sgp4/model.py new/sgp4-2.20/sgp4/model.py --- old/sgp4-2.16/sgp4/model.py 2021-02-12 22:28:12.257153000 +0100 +++ new/sgp4-2.20/sgp4/model.py 2021-07-01 11:36:07.177992600 +0200 @@ -73,14 +73,22 @@ def sgp4init(self, whichconst, opsmode, satnum, epoch, bstar, ndot, nddot, ecco, argpo, inclo, mo, no_kozai, nodeo): whichconst = gravity_constants[whichconst] + whole, fraction = divmod(epoch, 1.0) + whole_jd = whole + 2433281.5 - y, m, d, H, M, S = invjday(epoch + 2433281.5) - jan0epoch = jday(y, 1, 0, 0, 0, 0.0) - 2433281.5 - - self.epochyr = y % 1000 - self.epochdays = epoch - jan0epoch - self.jdsatepoch, self.jdsatepochF = divmod(epoch, 1.0) - self.jdsatepoch += 2433281.5 + # Go out on a limb: if `epoch` has no decimal digits past the 8 + # decimal places stored in a TLE, then assume the user is trying + # to specify an exact decimal fraction. + if round(epoch, 8) == epoch: + fraction = round(fraction, 8) + + self.jdsatepoch = whole_jd + self.jdsatepochF = fraction + + y, m, d, H, M, S = invjday(whole_jd) + jan0 = jday(y, 1, 0, 0, 0, 0.0) + self.epochyr = y % 100 + self.epochdays = whole_jd - jan0 + fraction sgp4init(whichconst, opsmode, satnum, epoch, bstar, ndot, nddot, ecco, argpo, inclo, mo, no_kozai, nodeo, self) @@ -172,41 +180,7 @@ return e, r, v class Satellite(object): - """The old Satellite object for compatibility with sgp4 1.x. - - 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_kozai`` - Mean motion in radians per minute. - - """ + """The old Satellite object, for compatibility with sgp4 1.x.""" jdsatepochF = 0.0 # for compatibility with new Satrec; makes tests simpler # TODO: only offer this on legacy class we no longer document diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/sgp4-2.16/sgp4/tests.py new/sgp4-2.20/sgp4/tests.py --- old/sgp4-2.16/sgp4/tests.py 2021-02-12 22:28:12.261153200 +0100 +++ new/sgp4-2.20/sgp4/tests.py 2021-07-01 11:36:07.181992800 +0200 @@ -19,6 +19,8 @@ except ImportError: from StringIO import StringIO +import numpy as np + from sgp4.api import WGS72OLD, WGS72, WGS84, Satrec, jday, accelerated from sgp4.earth_gravity import wgs72 from sgp4.ext import invjday, newtonnu, rv2coe @@ -47,7 +49,9 @@ 'operationmode': 'i', # Time 'epochyr': 0, + 'epochdays': 179.78495062, 'jdsatepoch': 2451722.5, + 'jdsatepochF': 0.78495062, # Orbit 'bstar': 2.8098e-05, 'ndot': 6.96919666594958e-13, @@ -59,7 +63,7 @@ 'no_kozai': 0.04722944544077857, 'nodeo': 6.08638547138321, } -VANGUARD_EPOCH = 18441.7849506199999894 +VANGUARD_EPOCH = 18441.78495062 # Handle deprecated assertRaisesRegexp, but allow its use Python 2.6 and 2.7 if sys.version_info[:2] == (2, 7) or sys.version_info[:2] == (2, 6): @@ -78,7 +82,6 @@ verify_vanguard_1(sat, legacy=True) def test_satrec_initialized_with_sgp4init(): - # epochyr and epochdays are not set by sgp4init sat = Satrec() sat.sgp4init( WGS72, @@ -109,6 +112,21 @@ verify_vanguard_1(sat, legacy=True) # ------------------------------------------------------------------------ +# Test array API + +def test_whether_array_logic_writes_nan_values_to_correct_row(): + # https://github.com/brandon-rhodes/python-sgp4/issues/87 + l1 = "1 44160U 19006AX 20162.79712247 +.00816806 +19088-3 +34711-2 0 9997" + l2 = "2 44160 095.2472 272.0808 0216413 032.6694 328.7739 15.58006382062511" + sat = Satrec.twoline2rv(l1, l2) + jd0 = np.array([2459054.5, 2459055.5]) + jd1 = np.array([0.79712247, 0.79712247]) + e, r, v = sat.sgp4_array(jd0, jd1) + assert list(e) == [6, 1] + assert np.isnan(r).tolist() == [[False, False, False], [True, True, True]] + assert np.isnan(v).tolist() == [[False, False, False], [True, True, True]] + +# ------------------------------------------------------------------------ # Other Officially Supported Routines # @@ -224,6 +242,16 @@ ) assertRaises(ValueError, export_tle, sat) +def test_tle_import_export_round_trips(): + for line1, line2 in [( + '1 44542U 19061A 21180.78220369 -.00000015 00000-0 -66561+1 0 9997', + '2 44542 54.7025 244.1098 0007981 318.8601 283.5781 1.86231125 12011', + )]: + sat = Satrec.twoline2rv(line1, line2) + outline1, outline2 = export_tle(sat) + assertEqual(line1, outline1) + assertEqual(line2, outline2) + def test_all_three_gravity_models_with_twoline2rv(): # The numbers below are those produced by Vallado's C++ code. # (Why does the Python version not produce the same values to @@ -293,10 +321,13 @@ def test_satnum_leading_spaces(): # https://github.com/brandon-rhodes/python-sgp4/issues/81 + # https://github.com/brandon-rhodes/python-sgp4/issues/90 l1 = '1 4859U 21001A 21007.63955392 .00000000 00000+0 00000+0 0 9990' l2 = '2 4859 000.0000 000.0000 0000000 000.0000 000.0000 01.00000000 09' sat = Satrec.twoline2rv(l1, l2) assertEqual(sat.satnum, 4859) + assertEqual(sat.classification, 'U') + assertEqual(sat.intldesg, '21001A') def test_satnum_alpha5_encoding(): def make_sat(satnum_string): @@ -333,6 +364,17 @@ ) assertEqual(sat.intldesg, '13066AE') +def test_1990s_satrec_initialized_with_sgp4init(): + sat = Satrec() + sat.sgp4init( + WGS72, + 'i', + VANGUARD_ATTRS['satnum'], + VANGUARD_EPOCH - 365.0, # change year 2000 to 1999 + *sgp4init_args(VANGUARD_ATTRS) + ) + assertEqual(sat.epochyr, 99) + def test_setters(): sat = Satrec() @@ -459,7 +501,9 @@ if legacy: attrs = attrs.copy() del attrs['epochyr'] + del attrs['epochdays'] del attrs['jdsatepoch'] + del attrs['jdsatepochF'] for name, value in attrs.items(): try: @@ -469,10 +513,6 @@ e.args = ('for attribute %s, %s' % (name, message),) raise e - if not legacy: - assertAlmostEqual(sat.epochdays, 179.78495062, delta=3e-14) - assertAlmostEqual(sat.jdsatepochF, 0.78495062, delta=1e-13) - def sgp4init_args(d): """Given a dict of orbital parameters, return them in sgp4init order.""" return (d['bstar'], d['ndot'], d['nddot'], d['ecco'], d['argpo'], @@ -723,7 +763,6 @@ assert_satellites_match(sat1, sat2) def assert_satellites_match(sat1, sat2): - julian_fractions = {'epochdays', 'jdsatepochF'} todo = {'whichconst'} for attr in dir(sat1): @@ -737,9 +776,6 @@ if callable(value1): continue value2 = getattr(sat2, attr) - if attr in julian_fractions: - value1 = round(value1, 10) - value2 = round(value2, 10) assertEqual(value1, value2, '%s %r != %r' % (attr, value1, value2)) # Live example of OMM:
