Skip to content
Permalink

Comparing changes

Choose two branches to see what’s changed or to start a new pull request. If you need to, you can also or learn more about diff comparisons.

Open a pull request

Create a new pull request by comparing changes across two branches. If you need to, you can also . Learn more about diff comparisons here.
base repository: skyfielders/python-skyfield
Failed to load repositories. Confirm that selected base ref is valid, then try again.
Loading
base: 1.50
Choose a base ref
...
head repository: skyfielders/python-skyfield
Failed to load repositories. Confirm that selected head ref is valid, then try again.
Loading
compare: 1.51
Choose a head ref

Commits on Feb 16, 2025

  1. Fix front-page claim about NumPy binary dependency

    Because `sgp4` has also turned into a binary dependency.
    brandon-rhodes committed Feb 16, 2025
    Copy the full SHA
    c76c109 View commit details

Commits on Feb 19, 2025

  1. Diagram of which Time methods use which others

    brandon-rhodes committed Feb 19, 2025
    Copy the full SHA
    3dd850d View commit details

Commits on Feb 22, 2025

  1. Remove three old methods from Skyfield 0.9.1

    brandon-rhodes committed Feb 22, 2025
    Copy the full SHA
    ee0f4f1 View commit details
  2. Copy the full SHA
    71a2324 View commit details
  3. Advertise .xyz attribute instead of .position

    brandon-rhodes committed Feb 22, 2025
    Copy the full SHA
    171ef02 View commit details
  4. Copy the full SHA
    39e4c16 View commit details
  5. Support ephemeris DE441 with >1 segment per target

    Fixes #622. Fixes #691. Fixes #803.
    brandon-rhodes committed Feb 22, 2025
    Copy the full SHA
    ef0342f View commit details
  6. Fix: comets were always giving 'str' as their name

    brandon-rhodes committed Feb 22, 2025
    Copy the full SHA
    869958c View commit details
  7. Fix #959: Kepler orbits with time arrays of len==1

    brandon-rhodes committed Feb 22, 2025
    Copy the full SHA
    fb12074 View commit details
  8. Turn deprecated parse_tle() into a small wrapper

    brandon-rhodes committed Feb 22, 2025
    Copy the full SHA
    4465fc7 View commit details

Commits on Feb 23, 2025

  1. Switch build_arrays to dict version of IERS data

    brandon-rhodes committed Feb 23, 2025
    Copy the full SHA
    3413954 View commit details
  2. Streamline the satellite find_events() tests

    This cleanup sets us up to start tackling recent find_events() issues.
    brandon-rhodes committed Feb 23, 2025
    Copy the full SHA
    24745f4 View commit details
  3. Fix: find_events() rising without any culmination

    It shouldn’t refuse to return the moment of satellite rising just
    because the culmination itself is out of the time range.
    brandon-rhodes committed Feb 23, 2025
    Copy the full SHA
    01499b6 View commit details
  4. Add find_rising() test to keep #996 fixed

    This test is based on the example script in the issue.  Fixes #996.
    brandon-rhodes committed Feb 23, 2025
    Copy the full SHA
    39f1626 View commit details
  5. Replace calls to .take() with plain NumPy indexing

    brandon-rhodes committed Feb 23, 2025
    Copy the full SHA
    3ca3426 View commit details
  6. Prevent satellite find_events() from exiting early

    The discrete finder, as it narrowed in on N events, was watching only
    how tightly it was bracketing the first event, on the assumption that
    they were all bracketed by roughly equal intervals.  But for satellite
    find_events(), this is not the case: their brackets are not the same
    size, so all intervals need watching.  Fixes #1000.
    brandon-rhodes committed Feb 23, 2025
    Copy the full SHA
    9ab9633 View commit details
  7. Add two more recent issue links to the CHANGELOG

    brandon-rhodes committed Feb 23, 2025
    Copy the full SHA
    88f2cec View commit details
  8. Avoid scrambling close satellite rise + culminate

    This required a bit of archaeology.  Back in February 2020, Skyfield had
    a severe issue: the curves it produced for the Moon’s ecliptic longitude
    were non-monotonic over very short intervals.  So a search for its phase
    might encounter jitter right at the boundary between, say, first quarter
    and second quarter, and report two spurious transitions that were
    fractions of a second apart.
    
    So I added a filter to detect and remove ‘stutter’ — if a cluster of
    events was found within a fraction of a second, I only kept the last.
    This worked well with all of the long lazy almanac routines, which look
    for things like Moon phases and seasons that are spaced weeks apart.
    See issue #333 and commit 3e93e2b.
    
    But over the next five years, two things changed.
    
    1. The discrete-events finder found a second use: Earth satellite
       events!  These are not weeks apart, but minutes, and sometimes
       seconds for a satellite that only momentarily crests the horizon.
       The filter, alas, was tossing out closely spaced events.
    
    2. I’m tentative about this, but it looks like the non-monotonic
       problems, the jitter at very short timescales, disappeared when
       Skyfield switched to two-float times!  I performed a bisect and found
       the filter became unnecessary on 2020 May 12, just a few months after
       it was added, at 1f8e66a.
    
    So, with some wariness, I am removing the filter.  No tests break!
    
    Users will have to let me know if spurious events reappear.  If they do,
    and I can’t fix the root problem by removing the jitter, then I’ll have
    each routine do its own filtering on the result of _find_discrete(), so
    Moon phases and Earth satellite risings don’t have to share the same
    filter.  (Fixes #559.)
    brandon-rhodes committed Feb 23, 2025
    Copy the full SHA
    8881e22 View commit details
  9. Fix TLE string that was breaking Python 2 in CI

    brandon-rhodes committed Feb 23, 2025
    Copy the full SHA
    b11305b View commit details
  10. Copy the full SHA
    5b0529b View commit details
  11. Switch bin/build from setuptools to uv build

    Since `uv build` can build a pure-Python wheel without setuptools, we
    can remove setuptools support from `setup.py`.  Also, let’s move file
    permission checks to `bin/build` to further simplify `setup.py`.
    brandon-rhodes committed Feb 23, 2025
    Copy the full SHA
    278c751 View commit details
  12. Copy the full SHA
    bfca90b View commit details
  13. Copy the full SHA
    95fc012 View commit details
  14. Declare version 1.51

    brandon-rhodes committed Feb 23, 2025
    Copy the full SHA
    3556c1b View commit details
Showing with 661 additions and 536 deletions.
  1. +56 −0 CHANGELOG.rst
  2. +9 −3 TODO.rst
  3. +18 −7 bin/build
  4. +0 −6 bin/test-python-2.6
  5. +27 −0 bin/test-pythons
  6. +2 −3 builders/build_arrays.py
  7. +2 −2 builders/build_novas_tests.py
  8. +0 −8 containers/python-2.6/Dockerfile
  9. +0 −18 containers/python-2.6/run
  10. +17 −17 design/broadcasting.py
  11. +1 −1 design/satellite_is_sunlit.py
  12. +5 −5 design/sgp4_parallel.py
  13. +1 −1 design/subpoint_accuracy.py
  14. +27 −0 design/time_diagram.dot
  15. +5 −2 documentation/api.rst
  16. +1 −1 documentation/astropy.rst
  17. +4 −4 documentation/earth-satellites.rst
  18. +2 −2 documentation/examples.rst
  19. +4 −6 documentation/index.rst
  20. +1 −1 documentation/positions.rst
  21. +2 −2 documentation/time.rst
  22. +8 −25 setup.py
  23. +3 −3 skyfield/__init__.py
  24. BIN skyfield/data/iers.npz
  25. +2 −2 skyfield/elementslib.py
  26. +9 −40 skyfield/iokit.py
  27. +59 −16 skyfield/jpllib.py
  28. +32 −38 skyfield/keplerlib.py
  29. +29 −31 skyfield/positionlib.py
  30. +2 −2 skyfield/projections.py
  31. +3 −3 skyfield/relativity.py
  32. +15 −23 skyfield/searchlib.py
  33. +0 −2 skyfield/sgp4lib.py
  34. +5 −5 skyfield/starlib.py
  35. BIN skyfield/tests/data/de441-1969.bsp
  36. +3 −3 skyfield/tests/test_against_horizons.py
  37. +51 −51 skyfield/tests/test_against_novas.py
  38. +1 −1 skyfield/tests/test_almanac_searches.py
  39. +6 −6 skyfield/tests/test_api.py
  40. +2 −2 skyfield/tests/test_earth_satellites.py
  41. +2 −2 skyfield/tests/test_frames.py
  42. +0 −3 skyfield/tests/test_io_parsing.py
  43. +17 −0 skyfield/tests/test_jpllib.py
  44. +33 −5 skyfield/tests/test_keplerlib.py
  45. +2 −2 skyfield/tests/test_planetarylib.py
  46. +16 −11 skyfield/tests/test_positions.py
  47. +173 −110 skyfield/tests/test_satellite_events.py
  48. +2 −2 skyfield/tests/test_topos.py
  49. +1 −1 skyfield/tests/test_vectors.py
  50. +1 −58 skyfield/vectorlib.py
56 changes: 56 additions & 0 deletions CHANGELOG.rst
Original file line number Diff line number Diff line change
@@ -9,6 +9,62 @@ Changelog
Released versions
-----------------

v1.51 — 2024 February 23
------------------------

* Skyfield now supports ephemeris files like ``de441.bsp`` that have two
or more segments per target. In earlier versions of Skyfield, only
one segment per target would get used, which for ``de441.bsp`` was
cutting the range of supported dates in half.
`#691 <https://github.com/skyfielders/python-skyfield/issues/691>`_

* The documentation now uses the newer name ``.xyz``, which has been
quietly supported for around three years, for the
:class:`~skyfield.positionlib.ICRF` attribute that was originally
named ``.position`` and holds its position vector. The old name
(which will continue to be supported) could lead to code that looked a
little redundant, asking for the ``position`` of a ``position``::

position = mars.at(t)
x, y, z = position.position

So Skyfield now encourages code to ask for ``position.xyz`` instead.
An advantage is that the new name is self-documenting: the name
reminds the user that it is a 3-vector of Cartesian components.

* Skyfield’s internal table for the ∆T Earth orientation parameter has
been updated, so that its predictions now extend to 2026-02-28.

* Fix: the :meth:`~skyfield.sgp4lib.EarthSatellite.find_events()` Earth
satellite method was returning an empty list of events if the only
event in the time period was a lone rising or setting. It should now
detect and return them.
`#856 <https://github.com/skyfielders/python-skyfield/issues/856>`_
`#996 <https://github.com/skyfielders/python-skyfield/issues/996>`_
`#1017 <https://github.com/skyfielders/python-skyfield/issues/1017>`_

* Fix: the :meth:`~skyfield.sgp4lib.EarthSatellite.find_events()` Earth
satellite method, faced with a single pass that was very close to the
start time, was returning an inaccurate setting time. It should now
return an accurate setting time.
`#1000 <https://github.com/skyfielders/python-skyfield/issues/1000>`_

* Fix: the :meth:`~skyfield.sgp4lib.EarthSatellite.find_events()` Earth
satellite method would miss a rising that came a fraction of a second
before the corresponding culmination. It should now find both.
`#559 <https://github.com/skyfielders/python-skyfield/issues/559>`_

* Fix: bodies with Kepler orbits (like comets and asteroids) were
incorrectly returning positions with only a single dimension if given
a :class:`~skyfield.timelib.Time` that was an array but had only one
element. This could cause the rising and setting almanac routines to
raise a ``ValueError`` if they found only a single rising or setting.
`#959 <https://github.com/skyfielders/python-skyfield/issues/959>`_

* Fix: the position vectors for Kepler orbit bodies, like comets and
asteroids, now have a useful ``.target_name`` like ``'Ceres'`` or
``'1P/Halley'`` instead of the less informative value ``'str'``.

v1.50 — 2024 February 15
------------------------

12 changes: 9 additions & 3 deletions TODO.rst
Original file line number Diff line number Diff line change
@@ -97,7 +97,6 @@ forget.

* Several interesting API questions arise because of
`this Stack Overflow question <https://stackoverflow.com/questions/62654081/path-between-two-topos-locations-determine-latitude-and-longitude-where-a-giv>`_.
Should I finally go through with renaming ``.position`` to ``.xyz``?
Should ``.from_altaz()`` retain observer data
so that a follow-up ``.altaz()`` returns the same coordinates?
Should positions fully support math,
@@ -142,9 +141,9 @@ forget.

* Solar eclipses.

* Some users need to add offsets to vectors like .position and .velocity
* Some users need to add offsets to vectors like .xyz and .velocity
so should there be some official way to do so? We should either
document the maneuver ``.position = Distance(au=old_position.au +
document the maneuver ``.xyz = Distance(au=old_position.au +
xyz)`` or (gulp!) make ``Distance`` objects susceptible of being
modified in-place without their various units going out of sync.

@@ -191,3 +190,10 @@ Adding more smarts to ephemeris handling
need to be vectorized and updated to use a Skyfield approach to vector
and matrix operations. When complete and documented, make a comment
at: https://github.com/skyfielders/python-skyfield/issues/350

* Maybe someday keep notes on ‘the story of Skyfield’ somewhere in the
repository, for details I later forget — like the fact I just
rediscovered, that an ephemeris object like ``earth`` used to have
methods ``.geometry_of(another_body)``, ``.topos(...)``, and
``.satellite(...)``, all of which went away once the concept of vector
addition and subtraction were added to Skyfield.
25 changes: 18 additions & 7 deletions bin/build
Original file line number Diff line number Diff line change
@@ -1,14 +1,25 @@
#!/bin/bash

set -ex
set -e

cd "$(readlink -f $(dirname "${BASH_SOURCE[0]}"))"
cd ..

# Alas! Unless we 'rm' the build/ directory first, the wheel will
# include old files that are no longer part of Skyfield but that are
# left over from previous builds.
rm -rf build/
echo
echo 'Have you remembered to rebuild the delta-T tables?'
echo

python setup.py sdist $@
SKYFIELD_USE_SETUPTOOLS=yes python setup.py bdist_wheel $@
echo 'Looking for bad file permissions...'
bad=$(
find skyfield -type f ! -perm -644
find skyfield -type d ! -perm -755
)
if [ -n "$bad" ]
then
ls -ld $bad
exit 1
fi
echo 'Everything looks good!'
echo

PYTHONDONTWRITEBYTECODE= uv build
6 changes: 0 additions & 6 deletions bin/test-python-2.6

This file was deleted.

27 changes: 27 additions & 0 deletions bin/test-pythons
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
#!/bin/bash

set -e

cd "$(readlink -f $(dirname "${BASH_SOURCE[0]}"))"
cd ..

./bin/build

for version in 3.7 3.13
do
echo
echo $version
echo
venv=.tox/$version
if [ ! -d $venv ]
then
uv venv --python $version $venv
fi
source $venv/bin/activate
pushd ci
uv pip install pytz pandas
uv pip install ../dist/skyfield-1.51-py3-none-any.whl
uv pip install ~/assay
assay --batch skyfield.tests
popd
done
5 changes: 2 additions & 3 deletions builders/build_arrays.py
Original file line number Diff line number Diff line change
@@ -44,11 +44,10 @@ def main(argv):
url = 'https://maia.usno.navy.mil/ser7/finals2000A.all'
f = load.open(url)
with f:
utc_mjd, dut1 = iers.parse_dut1_from_finals_all(f)

finals2000A = iers.parse_x_y_dut1_from_finals_all(f)

daily_tt, daily_delta_t, leap_dates, leap_offsets = (
iers.build_timescale_arrays(utc_mjd, dut1)
iers.build_timescale_arrays(finals2000A['utc_mjd'], finals2000A['dut1'])
)

# Removing the 1.0 increment between terms offers vastly improved
4 changes: 2 additions & 2 deletions builders/build_novas_tests.py
Original file line number Diff line number Diff line change
@@ -291,7 +291,7 @@ def output_ephemeris_tests():
def test_position_and_velocity(de405, ts):
t = ts.tt_jd({jd!r})
e = de405['earth'].at(t)
compare(e.position.au, {pos!r}, 10 * meter)
compare(e.xyz.au, {pos!r}, 10 * meter)
compare(e.velocity.au_per_d, {vel!r}, 1e-5 * meter / one_second)
""")
@@ -316,7 +316,7 @@ def test_{slug}_geocentric_date{i}(de405, ts):
e = de405['earth'].at(t)
p = de405[{planet!r}]
distance = length_of((e - p.at(t)).position.au)
distance = length_of((e - p.at(t)).xyz.au)
compare(distance * OLD_AU, {distance1!r}, 0.014 * meter)
astrometric = e.observe(p)
8 changes: 0 additions & 8 deletions containers/python-2.6/Dockerfile

This file was deleted.

18 changes: 0 additions & 18 deletions containers/python-2.6/run

This file was deleted.

34 changes: 17 additions & 17 deletions design/broadcasting.py
Original file line number Diff line number Diff line change
@@ -17,7 +17,7 @@ def main():
observer = planets['earth'].at(t)
target = planets['mars']
r, v, t2, light_time = cfltt(observer, target)
print(t.shape, observer.position.km.shape, r.shape)
print(t.shape, observer.xyz.km.shape, r.shape)

print('==== N times, one observer, one target ====')

@@ -26,7 +26,7 @@ def main():
observer = planets['earth'].at(t)
target = planets['mars']
r, v, t2, light_time = cfltt(observer, target)
print(t.shape, observer.position.km.shape, '->', r.shape)
print(t.shape, observer.xyz.km.shape, '->', r.shape)
print('Here is where the planet wound up:')
print(r)

@@ -45,17 +45,17 @@ def main():
earth._at = build_multi_at(earth._at)

observer = earth.at(t)
print('observer', observer.position.au.shape)
print('observer', observer.xyz.au.shape)

target = planets['mars']
target._at = build_multi_at(target._at) # Turn Mars into 2 planets.
print('target', target.at(t).position.au.shape)
print('target', target.at(t).xyz.au.shape)

# t = ts.tt(t.tt[:,None]) # What if we add a dimension to t?
# print('t', t.shape)

r, v, t2, light_time = _correct_for_light_travel_time2(observer, target)
print(t.shape, observer.position.km.shape, '->', r.shape)
print(t.shape, observer.xyz.km.shape, '->', r.shape)

print('Does it look like a second planet 1 AU away at the same 4 times?')
print('First planet:')
@@ -74,17 +74,17 @@ def main():
earth = planets['earth']
earth._at = build_multi_at(earth._at)
observer = earth.at(t)
print('observer', observer.position.au.shape)
print('observer', observer.xyz.au.shape)

target = planets['mars']
target._at = build_multi_at(target._at) # Turn Mars into 2 planets.
print('target', target.at(t).position.au.shape)
print('target', target.at(t).xyz.au.shape)

# t = ts.tt(t.tt[:,None]) # What if we add a dimension to t?
# print('t', t.shape)

r, v, t2, light_time = _correct_for_light_travel_time3(observer, target)
print(t.shape, observer.position.km.shape, '->', r.shape)
print(t.shape, observer.xyz.km.shape, '->', r.shape)

print('Does it look like a second planet 1 AU away at the same 4 times?')
print('First planet:')
@@ -109,15 +109,15 @@ def main():

earth = planets['earth']
observer = earth.at(t)
print('observer', observer.position.au.shape)
print('observer', observer.xyz.au.shape)
print('Supplementing dimensions')
observer.position.au = observer.position.au[:,:,None]
observer.xyz.au = observer.xyz.au[:,:,None]
observer.velocity.au_per_d = observer.velocity.au_per_d[:,:,None]
print('observer', observer.position.au.shape)
print('observer', observer.xyz.au.shape)

target = planets['mars']
target._at = build_multi_at(target._at) # Turn Mars into 2 planets.
print('target', target.at(t).position.au.shape)
print('target', target.at(t).xyz.au.shape)

# TODO: if we work out how we can expand the time's dimensions, as
# in the following line, before earth.at(t) without raising an
@@ -130,7 +130,7 @@ def main():
# print('t !!!!!!!!!!!!!!', t.tdb_fraction.shape)

r, v, t2, light_time = _correct_for_light_travel_time4(observer, target)
print(t.shape, observer.position.au.shape, '->', r.shape)
print(t.shape, observer.xyz.au.shape, '->', r.shape)

print('Does it look like a second planet 1 AU away at the same 4 times?')
print('First planet:')
@@ -180,7 +180,7 @@ def _correct_for_light_travel_time(observer, target):
whole = t.whole
tdb_fraction = t.tdb_fraction

cposition = observer.position.au
cposition = observer.xyz.au
cvelocity = observer.velocity.au_per_d

tposition, tvelocity, gcrs_position, message = target._at(t)
@@ -222,7 +222,7 @@ def _correct_for_light_travel_time2(observer, target):
whole = t.whole
tdb_fraction = t.tdb_fraction

cposition = observer.position.au
cposition = observer.xyz.au
cvelocity = observer.velocity.au_per_d

tposition, tvelocity, gcrs_position, message = target._at(t)
@@ -276,7 +276,7 @@ def _correct_for_light_travel_time3(observer, target):
whole = t.whole
tdb_fraction = t.tdb_fraction

cposition = observer.position.au
cposition = observer.xyz.au
cvelocity = observer.velocity.au_per_d

cposition = cposition.T
@@ -327,7 +327,7 @@ def _correct_for_light_travel_time4(observer, target):
whole = t.whole
tdb_fraction = t.tdb_fraction

cposition = observer.position.au
cposition = observer.xyz.au
cvelocity = observer.velocity.au_per_d

print('======', cposition.shape)
2 changes: 1 addition & 1 deletion design/satellite_is_sunlit.py
Original file line number Diff line number Diff line change
@@ -45,7 +45,7 @@
sun_km_per_s = distance_sun_travels_in_one_orbit / 10 / 365.25 / 24 / 60 / 60
print('Sun km/s:', sun_km_per_s)

light_delay_seconds = s2[0].position.length().light_seconds()
light_delay_seconds = s2[0].xyz.length().light_seconds()
print('Sample light delay from Sun to Earth (seconds):', light_delay_seconds)

print('How far does the Sun move in that time?')
10 changes: 5 additions & 5 deletions design/sgp4_parallel.py
Original file line number Diff line number Diff line change
@@ -108,15 +108,15 @@ def test_iss():
satellites = [iss]
city = Topos('44.0247 N', '88.5426 W')
for geo1 in positions_for(satellites, city, times):
print(geo1.position.km)
print(geo1.xyz.km)
#
print("Compare to EarthSatellite at():")
difference = iss - city
geo2 = difference.at(times)
#geo2 = iss.at(times)
print(geo2.position.km)
print(geo2.xyz.km)
#
assert np.allclose(geo1.position.km, geo2.position.km)
assert np.allclose(geo1.xyz.km, geo2.xyz.km)
one_m_per_hour = 1.0 * 24.0 / AU_M
assert abs(geo1.velocity.au_per_d - geo2.velocity.au_per_d).max() < one_m_per_hour

@@ -136,8 +136,8 @@ def test_two_sats():
kestrel1, iss1 = positions
iss2 = (iss - city).at(times)
kestrel2 = (kestrel - city).at(times)
assert np.allclose(iss1.position.km, iss2.position.km)
assert np.allclose(kestrel1.position.km, kestrel2.position.km)
assert np.allclose(iss1.xyz.km, iss2.xyz.km)
assert np.allclose(kestrel1.xyz.km, kestrel2.xyz.km)
assert np.allclose(iss1.velocity.au_per_d, iss2.velocity.au_per_d)
assert np.allclose(kestrel1.velocity.au_per_d, kestrel2.velocity.au_per_d)

2 changes: 1 addition & 1 deletion design/subpoint_accuracy.py
Original file line number Diff line number Diff line change
@@ -23,7 +23,7 @@ def main():
for degrees in trial_angles:
top = Topos(latitude_degrees=degrees, longitude_degrees=123,
elevation_m=elevation_m)
xyz_au = top.at(t).position.au
xyz_au = top.at(t).xyz.au
xyz_au = einsum('ij...,j...->i...', t.M, xyz_au)
lat, lon, elev = reverse_terra(xyz_au, t.gast, n)
lat = lat / DEG2RAD
Loading