From 71eebab35cce735e4b8bb1fe8f86b072d69080d7 Mon Sep 17 00:00:00 2001 From: Sylvester Kaczmarek <16242628+sylvesterkaczmarek@users.noreply.github.com> Date: Sun, 20 Sep 2026 03:43:32 +0100 Subject: [PATCH 01/14] Correct right ascension to hour angle conversion --- ssapy/utils.py | 25 ++++++++++----------- tests/test_hour_angle_regressions.py | 33 ++++++++++++++++++++++++++++ tests/test_utils.py | 4 ++-- 3 files changed, 46 insertions(+), 16 deletions(-) create mode 100644 tests/test_hour_angle_regressions.py diff --git a/ssapy/utils.py b/ssapy/utils.py index 1a45d1b8..13a0cb7b 100644 --- a/ssapy/utils.py +++ b/ssapy/utils.py @@ -1866,17 +1866,16 @@ def rightascension_to_hourangle(right_ascension, local_time): rightascension_to_hourangle("10:30:00", "12:45:00") -> "02:15:00" rightascension_to_hourangle(157.5, 191.25) -> "02:15:00" """ - if type(right_ascension) is not str: - right_ascension = dd_to_hms(right_ascension) - if type(local_time) is not str: - local_time = dd_to_dms(local_time) - _ra = float(right_ascension.split(':')[0]) - _lt = float(local_time.split(':')[0]) - if _ra > _lt: - __ltm, __lts = local_time.split(':')[1:] - local_time = f'{24 + _lt}:{__ltm}:{__lts}' - - return dd_to_dms(hms_to_dd(local_time) - hms_to_dd(right_ascension)) + if isinstance(right_ascension, str): + right_ascension = hms_to_dd(right_ascension) + else: + right_ascension = float(right_ascension) + if isinstance(local_time, str): + local_time = hms_to_dd(local_time) + else: + local_time = float(local_time) + hour_angle = (local_time - right_ascension) % 360.0 + return dd_to_hms(hour_angle) def equatorial_to_horizontal(observer_latitude, declination, right_ascension=None, hour_angle=None, local_time=None, hms=False): @@ -1907,9 +1906,7 @@ def equatorial_to_horizontal(observer_latitude, declination, right_ascension=Non if hms: hour_angle = hms_to_dd(hour_angle) elif right_ascension is not None: - hour_angle = rightascension_to_hourangle(right_ascension, local_time) - if hms: - hour_angle = hms_to_dd(hour_angle) + hour_angle = hms_to_dd(rightascension_to_hourangle(right_ascension, local_time)) elif hour_angle is not None: if hms: hour_angle = hms_to_dd(hour_angle) diff --git a/tests/test_hour_angle_regressions.py b/tests/test_hour_angle_regressions.py new file mode 100644 index 00000000..88ccc424 --- /dev/null +++ b/tests/test_hour_angle_regressions.py @@ -0,0 +1,33 @@ +import numpy as np +import pytest + +from ssapy import utils + + +def test_rightascension_to_hourangle_matches_documented_examples(): + for right_ascension, local_time in [ + ("10:30:00", "12:45:00"), + (157.5, 191.25), + ]: + hour_angle = utils.rightascension_to_hourangle(right_ascension, local_time) + assert utils.hms_to_dd(hour_angle) == pytest.approx(33.75) + + +def test_rightascension_to_hourangle_wraps_through_midnight(): + hour_angle = utils.rightascension_to_hourangle("02:00:00", "01:00:00") + assert utils.hms_to_dd(hour_angle) == pytest.approx(345.0) + + +def test_equatorial_to_horizontal_accepts_numeric_ra_and_local_time(): + from_ra = utils.equatorial_to_horizontal( + observer_latitude=35.0, + declination=20.0, + right_ascension=157.5, + local_time=191.25, + ) + from_hour_angle = utils.equatorial_to_horizontal( + observer_latitude=35.0, + declination=20.0, + hour_angle=33.75, + ) + np.testing.assert_allclose(from_ra, from_hour_angle, atol=1e-12) diff --git a/tests/test_utils.py b/tests/test_utils.py index 816d2ae3..6acf9524 100644 --- a/tests/test_utils.py +++ b/tests/test_utils.py @@ -184,8 +184,8 @@ def test_small_coordinate_and_angle_helpers(capsys): assert utils.zenithangle_to_altitude(60.0) == 30.0 assert np.isclose(utils.altitude_to_zenithangle(np.pi / 6, deg=False), np.pi / 3) assert np.isclose(utils.zenithangle_to_altitude(np.pi / 3, deg=False), np.pi / 6) - assert utils.rightascension_to_hourangle(30.0, 2.0) == "0:0:0" - assert utils.rightascension_to_hourangle("02:00:00", "01:00:00").count(":") == 2 + assert utils.hms_to_dd(utils.rightascension_to_hourangle(30.0, 2.0)) == pytest.approx(332.0) + assert utils.hms_to_dd(utils.rightascension_to_hourangle("02:00:00", "01:00:00")) == pytest.approx(345.0) assert utils.dms_to_dd("12:30:00") == 12.5 assert utils.dms_to_dd(["12:30:00", "-12:30:00"]) == [12.5, -12.5] From 7bfe2bcc666e1a70e4fe3a8e811827f5c30bd462 Mon Sep 17 00:00:00 2001 From: Travis Yeager Date: Fri, 9 Oct 2026 11:24:58 -0700 Subject: [PATCH 02/14] Bump SSAPy to 1.1.11 --- pyproject.toml | 3 +-- requirements.txt | 1 - 2 files changed, 1 insertion(+), 3 deletions(-) diff --git a/pyproject.toml b/pyproject.toml index 5add373a..05c4df7a 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [project] name = "llnl-ssapy" -version = "1.1.10" +version = "1.1.11" authors = [ { name = "LLNL SSAPy Software Team", email = "yeagerastro@gmail.com" }, ] @@ -28,7 +28,6 @@ dependencies = [ "ipython_genutils", "jplephem", "tqdm", - "llnl-ssapy-data>=0.2.0", ] [project.optional-dependencies] diff --git a/requirements.txt b/requirements.txt index c9687c4c..cac06051 100644 --- a/requirements.txt +++ b/requirements.txt @@ -2,7 +2,6 @@ graphviz numpy scipy jplephem -llnl-ssapy-data>=0.2.0 astropy pyerfa emcee From 8c6ef8e5bdb1f8e41d409cac44e0aea13a11742d Mon Sep 17 00:00:00 2001 From: Travis Yeager Date: Fri, 9 Oct 2026 11:30:22 -0700 Subject: [PATCH 03/14] Retain SSAPy data compatibility dependency --- pyproject.toml | 1 + requirements.txt | 1 + 2 files changed, 2 insertions(+) diff --git a/pyproject.toml b/pyproject.toml index 05c4df7a..c9c48794 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -28,6 +28,7 @@ dependencies = [ "ipython_genutils", "jplephem", "tqdm", + "llnl-ssapy-data>=0.2.0", ] [project.optional-dependencies] diff --git a/requirements.txt b/requirements.txt index cac06051..05c26786 100644 --- a/requirements.txt +++ b/requirements.txt @@ -10,6 +10,7 @@ sgp4 pytest pytest-timeout tqdm +llnl-ssapy-data>=0.2.0 build cibuildwheel delocate From dcd1d679dd46f654ae8cad56ffe97c7b4f24ec5a Mon Sep 17 00:00:00 2001 From: Travis Yeager Date: Fri, 9 Oct 2026 11:40:02 -0700 Subject: [PATCH 04/14] Use available SSAPy data compatibility release --- pyproject.toml | 2 +- requirements.txt | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/pyproject.toml b/pyproject.toml index c9c48794..61ec127f 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -28,7 +28,7 @@ dependencies = [ "ipython_genutils", "jplephem", "tqdm", - "llnl-ssapy-data>=0.2.0", + "llnl-ssapy-data>=0.1.5", ] [project.optional-dependencies] diff --git a/requirements.txt b/requirements.txt index 05c26786..074d1009 100644 --- a/requirements.txt +++ b/requirements.txt @@ -10,7 +10,7 @@ sgp4 pytest pytest-timeout tqdm -llnl-ssapy-data>=0.2.0 +llnl-ssapy-data>=0.1.5 build cibuildwheel delocate From 5f663cc6a45980f187842b1b6100d99883ce4ec8 Mon Sep 17 00:00:00 2001 From: Travis Yeager Date: Fri, 9 Oct 2026 11:43:53 -0700 Subject: [PATCH 05/14] Load data from split SSATK packages --- pyproject.toml | 1 - requirements.txt | 5 ++++- ssapy/__init__.py | 10 +++++++--- ssapy/utils.py | 29 +++++++++++++++++------------ 4 files changed, 28 insertions(+), 17 deletions(-) diff --git a/pyproject.toml b/pyproject.toml index 61ec127f..05c4df7a 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -28,7 +28,6 @@ dependencies = [ "ipython_genutils", "jplephem", "tqdm", - "llnl-ssapy-data>=0.1.5", ] [project.optional-dependencies] diff --git a/requirements.txt b/requirements.txt index 074d1009..0e1e767a 100644 --- a/requirements.txt +++ b/requirements.txt @@ -10,7 +10,6 @@ sgp4 pytest pytest-timeout tqdm -llnl-ssapy-data>=0.1.5 build cibuildwheel delocate @@ -18,3 +17,7 @@ ipython_genutils pytest-cov pytest-xdist flake8 +ssatk-data-core==0.0.1 +ssatk-data-gravity==0.0.1 +ssatk-data-lunar==0.0.1 +ssatk-data-lunar-gravity==0.0.1 diff --git a/ssapy/__init__.py b/ssapy/__init__.py index d8ee4160..7d3384f0 100644 --- a/ssapy/__init__.py +++ b/ssapy/__init__.py @@ -1,14 +1,18 @@ import os -__version__ = "1.1.10" +__version__ = "1.1.11" def _datadir(): - """Directory holding SSAPy's data files: ssapy/ in llnl-ssapy-data.""" + """Best-effort directory for installed split SSATK data packages.""" try: from importlib.resources import files - return os.fspath(files("ssapy_data") / "data" / "ssapy") + for package in ("ssapy_data_core", "ssapy_data_gravity", "ssapy_data_lunar", "ssapy_data_lunar_gravity"): + try: + return os.fspath(files(package) / "data") + except (ImportError, TypeError): + continue except (ImportError, TypeError): # llnl-ssapy-data is a required dependency; without it find_file # raises a FileNotFoundError that says how to install it. diff --git a/ssapy/utils.py b/ssapy/utils.py index 5c8ce568..e58a4b5f 100644 --- a/ssapy/utils.py +++ b/ssapy/utils.py @@ -46,15 +46,21 @@ def _is_lfs_pointer(path): def _ssapy_data_files(): - """Paths of the files shipped by the optional ``llnl-ssapy-data`` package.""" - try: - import ssapy_data - except ImportError: - return [] - try: - return [os.fspath(path) for path in ssapy_data.iter_data_files()] - except Exception: - return [] + """Return files shipped by installed split SSATK data packages.""" + from importlib.resources import files + result = [] + for package in ("ssapy_data_core", "ssapy_data_gravity", "ssapy_data_lunar", "ssapy_data_lunar_gravity", "ssapy_data_propulsion", "ssapy_data_benchmarks"): + try: + stack = [files(package) / "data"] + while stack: + entry = stack.pop() + if entry.is_dir(): stack.extend(entry.iterdir()) + elif entry.is_file(): + try: result.append(os.fspath(entry)) + except TypeError: pass + except (ImportError, TypeError, FileNotFoundError): + continue + return result def find_file(filename, ext=None): @@ -81,11 +87,10 @@ def find_file(filename, ext=None): if os.path.isfile(candidate): raise FileNotFoundError( f"{candidate} is a git LFS pointer, not the data file. SSAPy's data now " - "comes from the llnl-ssapy-data package: pip install 'llnl-ssapy-data>=0.2.0'." + "comes from the split ssatk-data-* packages." ) raise FileNotFoundError( - f"{filename} was not found in the working directory or in llnl-ssapy-data " - f"({datadir}); install it with pip install 'llnl-ssapy-data>=0.2.0'." + f"{filename} was not found in the working directory or installed ssatk-data-* packages ({datadir})." ) From 0845218be6092a7029c5c85b2e04e84318809017 Mon Sep 17 00:00:00 2001 From: Travis Yeager Date: Fri, 9 Oct 2026 11:44:21 -0700 Subject: [PATCH 06/14] Update data lookup tests for split packages --- tests/test_data_file_lookup.py | 19 +++++++------------ 1 file changed, 7 insertions(+), 12 deletions(-) diff --git a/tests/test_data_file_lookup.py b/tests/test_data_file_lookup.py index 1472528d..1b353e12 100644 --- a/tests/test_data_file_lookup.py +++ b/tests/test_data_file_lookup.py @@ -19,7 +19,7 @@ def test_lfs_pointer_is_not_returned_as_data(tmp_path, monkeypatch): (tmp_path / "de440s.bsp").write_text(POINTER) monkeypatch.setattr(utils, "datadir", str(tmp_path)) monkeypatch.setattr(utils, "_ssapy_data_files", lambda: []) - with pytest.raises(FileNotFoundError, match="git LFS pointer.*llnl-ssapy-data"): + with pytest.raises(FileNotFoundError, match="git LFS pointer.*split"): utils.find_file("de440s.bsp") @@ -38,21 +38,16 @@ def test_ssapy_data_is_searched_when_the_package_data_is_missing(tmp_path, monke assert os.path.samefile(utils.find_file("de440s.bsp"), real) -def test_datadir_is_the_ssapy_tree_of_llnl_ssapy_data(): - # SSAPy no longer ships data: datadir is ssapy/ inside llnl-ssapy-data, and - # every file the body and gravity loaders need resolves there. +def test_split_data_packages_are_searchable(): + # Data is distributed across independently installable SSATK packages. import ssapy - import ssapy_data - from ssapy.body import _planetary_ephemeris_path - - assert os.path.samefile(ssapy.datadir, os.path.join(os.path.dirname(ssapy_data.__file__), "data", "ssapy")) - for name in ("moon_pa_de440_200625.bpc", "egm84.egm", "egm2008.egm.cof", "gggrx_1200a_sha.tab", "earth.png", "moon.png"): - assert os.path.dirname(utils.find_file(name)) == os.path.normpath(ssapy.datadir) - assert os.path.basename(_planetary_ephemeris_path()) == "de440s.bsp" + import ssapy_data_core, ssapy_data_gravity, ssapy_data_lunar, ssapy_data_lunar_gravity + assert ssapy.datadir + assert all(pkg.__file__ for pkg in (ssapy_data_core, ssapy_data_gravity, ssapy_data_lunar, ssapy_data_lunar_gravity)) def test_missing_file_names_the_data_package(monkeypatch, tmp_path): monkeypatch.setattr(utils, "datadir", str(tmp_path)) monkeypatch.setattr(utils, "_ssapy_data_files", lambda: []) - with pytest.raises(FileNotFoundError, match="llnl-ssapy-data"): + with pytest.raises(FileNotFoundError, match="ssatk-data"): utils.find_file("no_such_file.bsp") From 16485388bb9b8597276d2ed47cf7dddeffddb223 Mon Sep 17 00:00:00 2001 From: Travis Yeager Date: Fri, 9 Oct 2026 11:45:57 -0700 Subject: [PATCH 07/14] Update documentation for split data packages --- docs/source/installation.rst | 2 +- skills.md | 12 ++++++------ ssapy/__init__.py | 4 ++-- ssapy/ephemeris.py | 8 ++++---- ssapy/utils.py | 6 +++--- tests/test_accel.py | 4 ++-- tests/test_data_file_lookup.py | 2 +- tests/test_orbit.py | 4 ++-- tests/test_tle_drag.py | 6 +++--- 9 files changed, 24 insertions(+), 24 deletions(-) diff --git a/docs/source/installation.rst b/docs/source/installation.rst index a364e85b..ee6755af 100644 --- a/docs/source/installation.rst +++ b/docs/source/installation.rst @@ -46,7 +46,7 @@ As the package has been published on `PyPI =0.2.0'." + f"{kernel.name} is missing; install the appropriate ssatk-data-* package." ) destination = os.path.join(cache_dir(), kernel.name) manual = (f"Download {kernel.url} ({kernel.nbytes / 1e6:.0f} MB) into {cache_dir()} " diff --git a/ssapy/utils.py b/ssapy/utils.py index e58a4b5f..441272ae 100644 --- a/ssapy/utils.py +++ b/ssapy/utils.py @@ -64,12 +64,12 @@ def _ssapy_data_files(): def find_file(filename, ext=None): - """ Find a file in the current directory or the ssapy datadir (the - ``ssapy/`` tree of the ``llnl-ssapy-data`` package). If ext is not None, + """Find a file in the current directory or installed split data packages. + If ext is not None, also try appending ext to the filename. Git LFS pointer files (left over from SSAPy versions that stored data - with Git LFS) are skipped, and the rest of ``llnl-ssapy-data`` is searched + with Git LFS) are skipped, and installed split data packages are searched by file name. """ names = [filename] if ext is None else [filename, filename + ext] diff --git a/tests/test_accel.py b/tests/test_accel.py index 33b19e22..746c18c3 100644 --- a/tests/test_accel.py +++ b/tests/test_accel.py @@ -19,7 +19,7 @@ def _has_data(name): - # SSAPy's data comes from llnl-ssapy-data; skip only if it is missing. + # SSAPy's data comes from split SSATK data packages; skip only if missing. try: ssapy.utils.find_file(name) return True @@ -151,7 +151,7 @@ def _make_body_dependent_objects(): earth = moon = sun = earth_MG = moon_MG = sun_MG = None -@pytest.mark.skipif(not HAS_EGM84, reason="EGM84 gravity file unavailable (llnl-ssapy-data not installed)") +@pytest.mark.skipif(not HAS_EGM84, reason="EGM84 gravity file unavailable (split data package not installed)") @timer def test_MG_3_1(): """Exercise 3.1 from Montenbruck and Gill diff --git a/tests/test_data_file_lookup.py b/tests/test_data_file_lookup.py index 1b353e12..211c1320 100644 --- a/tests/test_data_file_lookup.py +++ b/tests/test_data_file_lookup.py @@ -25,7 +25,7 @@ def test_lfs_pointer_is_not_returned_as_data(tmp_path, monkeypatch): def test_ssapy_data_is_searched_when_the_package_data_is_missing(tmp_path, monkeypatch): # With the package copy only a pointer, a real file of the same name - # shipped by llnl-ssapy-data is returned instead. + # shipped by a split SSATK data package is returned instead. local = tmp_path / "local" shipped = tmp_path / "shipped" / "ephemerides" local.mkdir() diff --git a/tests/test_orbit.py b/tests/test_orbit.py index c0b7c478..3c913525 100755 --- a/tests/test_orbit.py +++ b/tests/test_orbit.py @@ -20,7 +20,7 @@ def _has_ephemeris(): - # SSAPy's ephemeris comes from llnl-ssapy-data; skip only if it is missing. + # SSAPy's ephemeris comes from split SSATK data packages; skip only if missing. try: ssapy.body._planetary_ephemeris_path() return True @@ -1570,7 +1570,7 @@ def test_light_time_correction(): np.testing.assert_allclose(dircos, dc_linear, rtol=0, atol=1e-7) np.testing.assert_allclose(dircos, dc_exact, rtol=0, atol=1e-14) -@pytest.mark.skipif(not _has_ephemeris(), reason="planetary ephemeris unavailable (llnl-ssapy-data not installed)") +@pytest.mark.skipif(not _has_ephemeris(), reason="planetary ephemeris unavailable (split data package not installed)") @pytest.mark.timeout(90) @timer def test_find_passes(): diff --git a/tests/test_tle_drag.py b/tests/test_tle_drag.py index 18e102ca..6f6f396b 100644 --- a/tests/test_tle_drag.py +++ b/tests/test_tle_drag.py @@ -16,9 +16,9 @@ # The numerical drag propagator needs the gravity and ephemeris data from -# llnl-ssapy-data; skip those tests if it is not installed. +# split SSATK data packages; skip those tests if unavailable. def _has_data(name): - # SSAPy's data comes from llnl-ssapy-data; skip only if it is missing. + # SSAPy's data comes from split SSATK data packages; skip only if missing. try: ssapy.utils.find_file(name) return True @@ -39,7 +39,7 @@ def _has_ephemeris(): HAS_MOON_PA = _has_data("moon_pa_de440_200625.bpc") HAS_BODY_DATA = HAS_EGM84 and HAS_EPHEMERIS and HAS_MOON_PA needs_body_data = pytest.mark.skipif( - not HAS_BODY_DATA, reason="body data unavailable (llnl-ssapy-data not installed)") + not HAS_BODY_DATA, reason="body data unavailable (split data package not installed)") ISS = ("1 25544U 98067A 24015.54791435 .00016717 00000-0 30074-3 0 9993", "2 25544 51.6416 247.4627 0006703 130.5360 325.0288 15.49514637123456") From aca844975bcfad3feb4dfb59bdff285ad62b9e4f Mon Sep 17 00:00:00 2001 From: Travis Yeager Date: Fri, 9 Oct 2026 11:56:45 -0700 Subject: [PATCH 08/14] Cache split data resource discovery --- ssapy/utils.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/ssapy/utils.py b/ssapy/utils.py index 441272ae..6ed23952 100644 --- a/ssapy/utils.py +++ b/ssapy/utils.py @@ -6,6 +6,7 @@ import re import numpy as np import warnings +from functools import lru_cache from astropy.time import Time as _Time import astropy.units as u from typing import Union, Tuple @@ -45,6 +46,7 @@ def _is_lfs_pointer(path): return False +@lru_cache(maxsize=1) def _ssapy_data_files(): """Return files shipped by installed split SSATK data packages.""" from importlib.resources import files From a38786629894e57fa6eabb02f3fc242ae0835fc7 Mon Sep 17 00:00:00 2001 From: Travis Yeager Date: Fri, 9 Oct 2026 12:23:19 -0700 Subject: [PATCH 09/14] Limit CI test parallelism on Linux --- .github/workflows/ci.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index c0d56216..f543c4de 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -123,8 +123,8 @@ jobs: - name: Test with pytest if: matrix.os == 'ubuntu-latest' run: | - # Run pytest with coverage, using all available CPUs and verbose output - pytest -v -n auto --cov-report=xml --cov=ssapy --durations=20 + # Limit xdist workers to avoid memory exhaustion on Linux runners. + pytest -v -n 2 --cov-report=xml --cov=ssapy --durations=20 - name: Upload coverage reports to Codecov if: matrix.os == 'ubuntu-latest' && matrix.python-version == '3.12' uses: codecov/codecov-action@v4 From 2c05354d2f81ba74acfa508bc30b28527fc9e26d Mon Sep 17 00:00:00 2001 From: Travis Yeager Date: Sat, 10 Oct 2026 12:17:30 -0700 Subject: [PATCH 10/14] Update documentation --- README.rst | 46 ++++++------------------------------ docs/source/conf.py | 4 +++- docs/source/examples.rst | 4 +--- docs/source/installation.rst | 4 ++-- skills.md | 22 ++++++++--------- ssapy/utils.py | 4 ++-- 6 files changed, 26 insertions(+), 58 deletions(-) diff --git a/README.rst b/README.rst index f8a614c5..83d260e2 100644 --- a/README.rst +++ b/README.rst @@ -23,14 +23,10 @@ SSAPy - Space Situational Awareness for Python orbital modeling and analysis tool for orbits spanning from low-Earth orbit into the cislunar regime. -SSAPy retains the core coordinate, observer-geometry, and propagation routines. -For higher-level utilities, convenience workflows, plotting tools, workflow -wrappers around coordinate conversions, Lambertian magnitude / brightness -calculations, and related extensions, see the companion project -`SSAPy-Toolkit `__. +SSAPy provides physics-based orbital modeling and analysis for space-domain +applications. -Optional packaged datasets are available with ``llnl-ssapy[data]``. Install -``llnl-ssapy[all-data]`` to install every split ``ssatk-data-*`` distribution. +Optional packaged datasets are available with the ``data`` extra. SSAPy includes: @@ -65,26 +61,6 @@ SSAPy includes: including GCRF, IERS, GCRS Cartesian, TEME Cartesian, RA/Dec, NTW, zenith/azimuth, apparent positions, and orthogonal tangent plane coordinates -SSAPy-Toolkit -------------- - -SSAPy provides the core propagation and modeling engine. Many -higher-level, analysis-ready capabilities built on top of it live in the -companion project -`SSAPy-Toolkit `__ (sometimes abbreviated -*SSATK*), including: - -- Higher-level utilities and convenience workflows that wrap common SSAPy tasks -- Plotting tools for orbit and analysis visualization -- Workflow-level wrappers around SSAPy's coordinate and observer-geometry - routines -- Lambertian magnitude / brightness calculations -- Additional related extensions - -If your work centers on plotting, dashboards, convenience utilities, or -higher-level workflows, SSAPy-Toolkit is often the best place to start — and the -natural home for contributions of that kind. - Installation ------------ @@ -92,10 +68,6 @@ For installation details, see the `Installing SSAPy `_ section of the documentation. -If you are looking for higher-level utilities or plotting-oriented workflows, -you may also want to install or explore -`SSAPy-Toolkit `__. - Strict dependencies ------------------- @@ -114,9 +86,6 @@ The following Python packages are installed automatically when you install SSAPy - `jplephem `_ - `tqdm `_ -Earth/Moon mesh helpers and other visualization tools are maintained in -`SSAPy-Toolkit `__. - Documentation ------------- @@ -143,17 +112,16 @@ Contributing to SSAPy is straightforward. Please open a targeting the ``main`` branch of the `SSAPy repository `_. -For work that primarily concerns plotting, dashboards, convenience utilities, -or higher-level workflows, please also consider whether the contribution belongs -in the companion repository -`SSAPy-Toolkit `__. - Your PR must pass SSAPy's required CI checks. For local testing guidance, documentation builds, and Git workflow tips, see the `Contribution Guide `_. SSAPy's ``main`` branch contains the latest development work. +For higher-fidelity simulation, visualization, and mission-level workflows, +see the +`Space Situational Awareness Toolkit (SSATK) `__. + Releases -------- diff --git a/docs/source/conf.py b/docs/source/conf.py index 62fc49eb..9a3ba330 100644 --- a/docs/source/conf.py +++ b/docs/source/conf.py @@ -43,7 +43,9 @@ def linkcode_resolve(domain, info): return "https://github.com/LLNL/SSAPy/tree/main/%s.py" % filename autosummary_generate = True -autosummary_imported_members = True +# Document objects implemented by SSAPy rather than imported implementation +# helpers (for example ``functools.lru_cache``). +autosummary_imported_members = False numpydoc_show_class_members = False sphinx_tabs_valid_builders = ['linkcheck'] source_suffix = ['.rst', '.md'] diff --git a/docs/source/examples.rst b/docs/source/examples.rst index 023f3d15..0347a3c1 100644 --- a/docs/source/examples.rst +++ b/docs/source/examples.rst @@ -1,9 +1,7 @@ SSAPy by Example ================ -The examples below use base SSAPy APIs that are part of the current package. -Higher-level plotting workflows and apparent-magnitude calculations are -maintained in `SSAPy-Toolkit `_. +The examples below demonstrate the core SSAPy APIs. Define an epoch and a simple geosynchronous orbit: diff --git a/docs/source/installation.rst b/docs/source/installation.rst index ee6755af..10ba25ed 100644 --- a/docs/source/installation.rst +++ b/docs/source/installation.rst @@ -45,8 +45,8 @@ As the package has been published on `PyPI Date: Sat, 10 Oct 2026 20:00:48 +0000 Subject: [PATCH 11/14] Bump version to 1.1.12 v1.1.11 is already tagged from main and published on PyPI, so this release needs a new version for PyPI to accept the upload. --- pyproject.toml | 2 +- ssapy/__init__.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/pyproject.toml b/pyproject.toml index 05c4df7a..02bdf1ab 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [project] name = "llnl-ssapy" -version = "1.1.11" +version = "1.1.12" authors = [ { name = "LLNL SSAPy Software Team", email = "yeagerastro@gmail.com" }, ] diff --git a/ssapy/__init__.py b/ssapy/__init__.py index 43e93a7a..86a357c9 100644 --- a/ssapy/__init__.py +++ b/ssapy/__init__.py @@ -1,6 +1,6 @@ import os -__version__ = "1.1.11" +__version__ = "1.1.12" From 8b87562c08529810d728e2faaf1a2da9ba802b1f Mon Sep 17 00:00:00 2001 From: Travis Yeager Date: Sat, 10 Oct 2026 20:00:48 +0000 Subject: [PATCH 12/14] Restore warnings-based hour-angle assertions in test_utils Commit 0926f58 reintroduced assertions that expect printed output ("Using hour_angle", "cannot be negative"), but utils raises a UserWarning and a ValueError, so two tests failed. The fix itself is already on main; tests/test_hour_angle_regressions.py stays. Co-authored-by: Sylvester Kaczmarek <16242628+sylvesterkaczmarek@users.noreply.github.com> --- tests/test_utils.py | 24 ++++++++++++------------ 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/tests/test_utils.py b/tests/test_utils.py index 6acf9524..9e7c2301 100644 --- a/tests/test_utils.py +++ b/tests/test_utils.py @@ -184,8 +184,9 @@ def test_small_coordinate_and_angle_helpers(capsys): assert utils.zenithangle_to_altitude(60.0) == 30.0 assert np.isclose(utils.altitude_to_zenithangle(np.pi / 6, deg=False), np.pi / 3) assert np.isclose(utils.zenithangle_to_altitude(np.pi / 3, deg=False), np.pi / 6) - assert utils.hms_to_dd(utils.rightascension_to_hourangle(30.0, 2.0)) == pytest.approx(332.0) - assert utils.hms_to_dd(utils.rightascension_to_hourangle("02:00:00", "01:00:00")) == pytest.approx(345.0) + # Numeric arguments are degrees (docstring): LST 2 deg - RA 30 deg = -28 deg = 22h08m. + assert utils.rightascension_to_hourangle(30.0, 2.0) == "22:8:00" + assert utils.rightascension_to_hourangle("02:00:00", "01:00:00") == "23:0:0" assert utils.dms_to_dd("12:30:00") == 12.5 assert utils.dms_to_dd(["12:30:00", "-12:30:00"]) == [12.5, -12.5] @@ -193,7 +194,6 @@ def test_small_coordinate_and_angle_helpers(capsys): assert utils.hms_to_dd("12:00:00") == 180.0 assert utils.hms_to_dd(["12:00:00", "01:30:00"]) == [180.0, 22.5] assert utils.dd_to_hms(-180.0) == "12:0:0" - assert "cannot be negative" in capsys.readouterr().out def test_sun_ra_dec_matches_astropy_solar_position(): @@ -469,17 +469,17 @@ def test_coordinate_angle_edge_cases_and_errors(capsys): assert np.isfinite(az) assert np.isfinite(alt) - az2, alt2 = utils.equatorial_to_horizontal( - observer_latitude=45.0, - declination=0.0, - right_ascension="01:00:00", - hour_angle="00:00:00", - local_time="02:00:00", - hms=True, - ) + with pytest.warns(UserWarning, match="using hour_angle"): + az2, alt2 = utils.equatorial_to_horizontal( + observer_latitude=45.0, + declination=0.0, + right_ascension="01:00:00", + hour_angle="00:00:00", + local_time="02:00:00", + hms=True, + ) assert np.isfinite(az2) assert np.isfinite(alt2) - assert "Using hour_angle" in capsys.readouterr().out az3, alt3 = utils.equatorial_to_horizontal( observer_latitude=45.0, From 9626e0c8d88fdc919c61c730e92c86b7b0990b10 Mon Sep 17 00:00:00 2001 From: Travis Yeager Date: Sat, 10 Oct 2026 21:09:12 +0000 Subject: [PATCH 13/14] Move to the ssa-data packages and require the core set 1.1.10 bundled its data (254.7 MB sdist); 1.1.11 moved the data to the optional data extra, so a plain `pip install llnl-ssapy` installs no ephemeris or gravity files. CI did not catch this because it installs requirements.txt, which already lists the packages. The data packages are also renamed: ssatk-data- (import ssapy_data_) becomes ssa-data- (import ssa_data_), still at 0.0.1. - Require ssa-data-core, -gravity, -lunar and -lunar-gravity ==0.0.1; the data and all-data extras keep working with the new names - Add ssapy/_data_packages.py as the single list of import names; __init__ and utils previously hard-coded 4 and 6 names - _datadir(): return the in-tree fallback path, never None; find_file raised TypeError instead of FileNotFoundError without data installed - Name the install command in find_file and ephemeris errors - Add a regression test for the no-data-package path --- pyproject.toml | 27 +++++++++++++++++---------- requirements.txt | 8 ++++---- ssapy/__init__.py | 23 +++++++++++------------ ssapy/_data_packages.py | 25 +++++++++++++++++++++++++ ssapy/ephemeris.py | 11 +++++++---- ssapy/utils.py | 16 +++++++++------- tests/test_accel.py | 4 ++-- tests/test_data_file_lookup.py | 32 ++++++++++++++++++++++++++------ tests/test_orbit.py | 4 ++-- tests/test_tle_drag.py | 6 +++--- 10 files changed, 106 insertions(+), 50 deletions(-) create mode 100644 ssapy/_data_packages.py diff --git a/pyproject.toml b/pyproject.toml index 02bdf1ab..1423ef49 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -28,23 +28,30 @@ dependencies = [ "ipython_genutils", "jplephem", "tqdm", + # Ephemerides, gravity models, and textures. Required so that a plain + # `pip install llnl-ssapy` can propagate; pins match ssatk's. + "ssa-data-core==0.0.1", + "ssa-data-gravity==0.0.1", + "ssa-data-lunar==0.0.1", + "ssa-data-lunar-gravity==0.0.1", ] [project.optional-dependencies] +# Kept so `llnl-ssapy[data]` still resolves; the same packages are required. data = [ - "ssatk-data-core==0.0.1", - "ssatk-data-gravity==0.0.1", - "ssatk-data-lunar==0.0.1", - "ssatk-data-lunar-gravity==0.0.1", + "ssa-data-core==0.0.1", + "ssa-data-gravity==0.0.1", + "ssa-data-lunar==0.0.1", + "ssa-data-lunar-gravity==0.0.1", ] all-data = [ - "ssatk-data-core==0.0.1", - "ssatk-data-gravity==0.0.1", - "ssatk-data-lunar==0.0.1", - "ssatk-data-lunar-gravity==0.0.1", - "ssatk-data-propulsion==0.0.1", - "ssatk-data-benchmarks==0.0.1", + "ssa-data-core==0.0.1", + "ssa-data-gravity==0.0.1", + "ssa-data-lunar==0.0.1", + "ssa-data-lunar-gravity==0.0.1", + "ssa-data-propulsion==0.0.1", + "ssa-data-benchmarks==0.0.1", ] dev = [ diff --git a/requirements.txt b/requirements.txt index 0e1e767a..2fd02aec 100644 --- a/requirements.txt +++ b/requirements.txt @@ -17,7 +17,7 @@ ipython_genutils pytest-cov pytest-xdist flake8 -ssatk-data-core==0.0.1 -ssatk-data-gravity==0.0.1 -ssatk-data-lunar==0.0.1 -ssatk-data-lunar-gravity==0.0.1 +ssa-data-core==0.0.1 +ssa-data-gravity==0.0.1 +ssa-data-lunar==0.0.1 +ssa-data-lunar-gravity==0.0.1 diff --git a/ssapy/__init__.py b/ssapy/__init__.py index 86a357c9..ba77fa1b 100644 --- a/ssapy/__init__.py +++ b/ssapy/__init__.py @@ -5,18 +5,17 @@ def _datadir(): - """Best-effort directory for installed split SSATK data packages.""" - try: - from importlib.resources import files - for package in ("ssapy_data_core", "ssapy_data_gravity", "ssapy_data_lunar", "ssapy_data_lunar_gravity"): - try: - return os.fspath(files(package) / "data") - except (ImportError, TypeError): - continue - except (ImportError, TypeError): - # Split data packages are optional at import time; find_file reports - # the missing package when a resource is requested. - return os.path.join(os.path.dirname(__file__), "data") + """Data directory of the first installed ``ssa-data-*`` package.""" + from importlib.resources import files + from ._data_packages import REQUIRED + for package in REQUIRED: + try: + return os.fspath(files(package) / "data") + except (ImportError, TypeError): + continue + # No data package is installed. Return a path (never None) so find_file + # raises FileNotFoundError with install instructions instead of TypeError. + return os.path.join(os.path.dirname(__file__), "data") datadir = _datadir() diff --git a/ssapy/_data_packages.py b/ssapy/_data_packages.py new file mode 100644 index 00000000..ad730ce9 --- /dev/null +++ b/ssapy/_data_packages.py @@ -0,0 +1,25 @@ +"""The ``ssa-data-*`` distributions that ship SSAPy's data. + +Each distribution ``ssa-data-`` installs the import package +``ssa_data_`` with its files below ``ssa_data_/data``. This module +is the single list SSAPy searches. +""" + +#: Required dependencies of SSAPy (ephemerides, gravity models, textures). +REQUIRED = ( + "ssa_data_core", + "ssa_data_gravity", + "ssa_data_lunar", + "ssa_data_lunar_gravity", +) + +#: Added by the ``all-data`` extra. +OPTIONAL = ( + "ssa_data_propulsion", + "ssa_data_benchmarks", +) + +ALL = REQUIRED + OPTIONAL + +#: Command that installs every data package. +INSTALL_HINT = "pip install 'llnl-ssapy[all-data]'" diff --git a/ssapy/ephemeris.py b/ssapy/ephemeris.py index cf4a50ce..c92c99ed 100644 --- a/ssapy/ephemeris.py +++ b/ssapy/ephemeris.py @@ -2,7 +2,7 @@ Planetary ephemeris selection and long-span kernels. SSAPy evaluates Sun, Moon and planet positions from JPL development -ephemerides (DE). The split SSATK data packages ship the supported kernels: +ephemerides (DE). The ``ssa-data-*`` packages ship the supported kernels: ========== ======================== ========================= Ephemeris Shipped kernel Coverage @@ -18,7 +18,7 @@ For epochs outside the shipped kernel, SSAPy uses a longer kernel of the same ephemeris (``de440.bsp`` or ``de430.bsp``, 1550-2650) and, for DE440 beyond that, DE441 (-13200 to +17191). These are 120 MB to 1.65 GB, so they are not -shipped: SSAPy looks for them in the working directory and split data packages +shipped: SSAPy looks for them in the working directory, the ssa-data-* packages, and the cache directory, and otherwise downloads them from NAIF, checks their SHA-256, and caches them, warning with :class:`EphemerisDownloadWarning` before each download. Set ``SSAPY_EPHEMERIS_DOWNLOAD=0`` to forbid downloads @@ -42,6 +42,8 @@ import numpy as np +from ._data_packages import INSTALL_HINT as _DATA_INSTALL_HINT + __all__ = [ "EphemerisDownloadWarning", "EphemerisRangeWarning", @@ -72,7 +74,7 @@ class Kernel: name: str start_jd: float # TDB Julian dates covered by every segment SSAPy uses end_jd: float - sha256: Optional[str] = None # None for kernels shipped in split data packages + sha256: Optional[str] = None # None for kernels shipped in ssa-data-* packages nbytes: Optional[int] = None solution: str = "" @@ -194,7 +196,8 @@ def _path(kernel, reason): return path if kernel.sha256 is None: raise EphemerisUnavailableError( - f"{kernel.name} is missing; install the appropriate ssatk-data-* package." + f"{kernel.name} is missing; install the ssa-data-* packages with: " + f"{_DATA_INSTALL_HINT}" ) destination = os.path.join(cache_dir(), kernel.name) manual = (f"Download {kernel.url} ({kernel.nbytes / 1e6:.0f} MB) into {cache_dir()} " diff --git a/ssapy/utils.py b/ssapy/utils.py index 58175f51..d32a62b4 100644 --- a/ssapy/utils.py +++ b/ssapy/utils.py @@ -12,6 +12,7 @@ from typing import Union, Tuple from . import datadir +from ._data_packages import ALL as _DATA_PACKAGES, INSTALL_HINT as _DATA_INSTALL_HINT from .constants import RGEO, EARTH_RADIUS, MOON_RADIUS, WGS84_EARTH_OMEGA @@ -48,10 +49,10 @@ def _is_lfs_pointer(path): @_lru_cache(maxsize=1) def _ssapy_data_files(): - """Return files shipped by installed split SSATK data packages.""" + """Return files shipped by the installed ``ssa-data-*`` packages.""" from importlib.resources import files result = [] - for package in ("ssapy_data_core", "ssapy_data_gravity", "ssapy_data_lunar", "ssapy_data_lunar_gravity", "ssapy_data_propulsion", "ssapy_data_benchmarks"): + for package in _DATA_PACKAGES: try: stack = [files(package) / "data"] while stack: @@ -66,13 +67,13 @@ def _ssapy_data_files(): def find_file(filename, ext=None): - """Find a file in the current directory or installed split data packages. + """Find a file in the current directory or the installed ``ssa-data-*`` packages. If ext is not None, also try appending ext to the filename. Git LFS pointer files (left over from SSAPy versions that stored data - with Git LFS) are skipped, and installed split data packages are searched - by file name. + with Git LFS) are skipped, and the installed ``ssa-data-*`` packages are + searched by file name. """ names = [filename] if ext is None else [filename, filename + ext] candidates = [] @@ -89,10 +90,11 @@ def find_file(filename, ext=None): if os.path.isfile(candidate): raise FileNotFoundError( f"{candidate} is a git LFS pointer, not the data file. SSAPy's data now " - "comes from the split ssatk-data-* packages." + f"comes from the ssa-data-* packages; install them with: {_DATA_INSTALL_HINT}" ) raise FileNotFoundError( - f"{filename} was not found in the working directory or installed ssatk-data-* packages ({datadir})." + f"{filename} was not found in the working directory or installed ssa-data-* packages ({datadir}). " + f"Install them with: {_DATA_INSTALL_HINT}" ) diff --git a/tests/test_accel.py b/tests/test_accel.py index 746c18c3..a5e7b31b 100644 --- a/tests/test_accel.py +++ b/tests/test_accel.py @@ -19,7 +19,7 @@ def _has_data(name): - # SSAPy's data comes from split SSATK data packages; skip only if missing. + # SSAPy's data comes from the ssa-data-* packages; skip only if missing. try: ssapy.utils.find_file(name) return True @@ -151,7 +151,7 @@ def _make_body_dependent_objects(): earth = moon = sun = earth_MG = moon_MG = sun_MG = None -@pytest.mark.skipif(not HAS_EGM84, reason="EGM84 gravity file unavailable (split data package not installed)") +@pytest.mark.skipif(not HAS_EGM84, reason="EGM84 gravity file unavailable (ssa-data-* package not installed)") @timer def test_MG_3_1(): """Exercise 3.1 from Montenbruck and Gill diff --git a/tests/test_data_file_lookup.py b/tests/test_data_file_lookup.py index 211c1320..8cba8678 100644 --- a/tests/test_data_file_lookup.py +++ b/tests/test_data_file_lookup.py @@ -19,13 +19,13 @@ def test_lfs_pointer_is_not_returned_as_data(tmp_path, monkeypatch): (tmp_path / "de440s.bsp").write_text(POINTER) monkeypatch.setattr(utils, "datadir", str(tmp_path)) monkeypatch.setattr(utils, "_ssapy_data_files", lambda: []) - with pytest.raises(FileNotFoundError, match="git LFS pointer.*split"): + with pytest.raises(FileNotFoundError, match="git LFS pointer.*ssa-data"): utils.find_file("de440s.bsp") def test_ssapy_data_is_searched_when_the_package_data_is_missing(tmp_path, monkeypatch): # With the package copy only a pointer, a real file of the same name - # shipped by a split SSATK data package is returned instead. + # shipped by an ssa-data-* package is returned instead. local = tmp_path / "local" shipped = tmp_path / "shipped" / "ephemerides" local.mkdir() @@ -39,15 +39,35 @@ def test_ssapy_data_is_searched_when_the_package_data_is_missing(tmp_path, monke def test_split_data_packages_are_searchable(): - # Data is distributed across independently installable SSATK packages. + # The four required ssa-data-* packages install with SSAPy. import ssapy - import ssapy_data_core, ssapy_data_gravity, ssapy_data_lunar, ssapy_data_lunar_gravity + import ssa_data_core, ssa_data_gravity, ssa_data_lunar, ssa_data_lunar_gravity assert ssapy.datadir - assert all(pkg.__file__ for pkg in (ssapy_data_core, ssapy_data_gravity, ssapy_data_lunar, ssapy_data_lunar_gravity)) + assert all(pkg.__file__ for pkg in (ssa_data_core, ssa_data_gravity, ssa_data_lunar, ssa_data_lunar_gravity)) def test_missing_file_names_the_data_package(monkeypatch, tmp_path): monkeypatch.setattr(utils, "datadir", str(tmp_path)) monkeypatch.setattr(utils, "_ssapy_data_files", lambda: []) - with pytest.raises(FileNotFoundError, match="ssatk-data"): + with pytest.raises(FileNotFoundError, match="ssa-data"): + utils.find_file("no_such_file.bsp") + + +def test_datadir_falls_back_to_a_path_without_data_packages(monkeypatch): + # With no ssa-data-* package installed, datadir must stay a path so + # find_file raises FileNotFoundError with install instructions rather + # than TypeError from os.path.join(None, ...). + import importlib.resources + import ssapy + + def _missing(package): + raise ModuleNotFoundError(package) + + monkeypatch.setattr(importlib.resources, "files", _missing) + fallback = ssapy._datadir() + assert isinstance(fallback, str) + + monkeypatch.setattr(utils, "datadir", fallback) + monkeypatch.setattr(utils, "_ssapy_data_files", lambda: []) + with pytest.raises(FileNotFoundError, match=r"llnl-ssapy\[all-data\]"): utils.find_file("no_such_file.bsp") diff --git a/tests/test_orbit.py b/tests/test_orbit.py index 3c913525..9ae7531a 100755 --- a/tests/test_orbit.py +++ b/tests/test_orbit.py @@ -20,7 +20,7 @@ def _has_ephemeris(): - # SSAPy's ephemeris comes from split SSATK data packages; skip only if missing. + # SSAPy's ephemeris comes from the ssa-data-* packages; skip only if missing. try: ssapy.body._planetary_ephemeris_path() return True @@ -1570,7 +1570,7 @@ def test_light_time_correction(): np.testing.assert_allclose(dircos, dc_linear, rtol=0, atol=1e-7) np.testing.assert_allclose(dircos, dc_exact, rtol=0, atol=1e-14) -@pytest.mark.skipif(not _has_ephemeris(), reason="planetary ephemeris unavailable (split data package not installed)") +@pytest.mark.skipif(not _has_ephemeris(), reason="planetary ephemeris unavailable (ssa-data-* package not installed)") @pytest.mark.timeout(90) @timer def test_find_passes(): diff --git a/tests/test_tle_drag.py b/tests/test_tle_drag.py index 6f6f396b..bbb424dc 100644 --- a/tests/test_tle_drag.py +++ b/tests/test_tle_drag.py @@ -16,9 +16,9 @@ # The numerical drag propagator needs the gravity and ephemeris data from -# split SSATK data packages; skip those tests if unavailable. +# the ssa-data-* packages; skip those tests if unavailable. def _has_data(name): - # SSAPy's data comes from split SSATK data packages; skip only if missing. + # SSAPy's data comes from the ssa-data-* packages; skip only if missing. try: ssapy.utils.find_file(name) return True @@ -39,7 +39,7 @@ def _has_ephemeris(): HAS_MOON_PA = _has_data("moon_pa_de440_200625.bpc") HAS_BODY_DATA = HAS_EGM84 and HAS_EPHEMERIS and HAS_MOON_PA needs_body_data = pytest.mark.skipif( - not HAS_BODY_DATA, reason="body data unavailable (split data package not installed)") + not HAS_BODY_DATA, reason="body data unavailable (ssa-data-* package not installed)") ISS = ("1 25544U 98067A 24015.54791435 .00016717 00000-0 30074-3 0 9993", "2 25544 51.6416 247.4627 0006703 130.5360 325.0288 15.49514637123456") From 4c42c89f36027fd3d7dcad056d41348371b60eb9 Mon Sep 17 00:00:00 2001 From: Travis Yeager Date: Sat, 10 Oct 2026 21:09:26 +0000 Subject: [PATCH 14/14] Correct SSATK pointer and data install docs - README: drop the duplicate intro sentence, move the SSATK pointer from Contributing to the introduction, replace the unquantified "higher-fidelity" with 6-DOF, note what moved to SSATK, and list the ssa-data packages under strict dependencies - installation.rst: data installs with SSAPy; all-data adds the propulsion and benchmark sets - skills.md: restore the rule that data goes in the split data repos, the distribution/import-name mapping, and the ssatk install name --- README.rst | 11 ++++++++--- docs/source/installation.rst | 9 +++++++-- skills.md | 21 +++++++++++++-------- 3 files changed, 28 insertions(+), 13 deletions(-) diff --git a/README.rst b/README.rst index 83d260e2..27bbe3bf 100644 --- a/README.rst +++ b/README.rst @@ -26,7 +26,9 @@ into the cislunar regime. SSAPy provides physics-based orbital modeling and analysis for space-domain applications. -Optional packaged datasets are available with the ``data`` extra. +Ephemerides, gravity models, and textures install automatically from the +``ssa-data-*`` packages; ``pip install "llnl-ssapy[all-data]"`` adds the +propulsion and benchmark data sets. SSAPy includes: @@ -85,6 +87,9 @@ The following Python packages are installed automatically when you install SSAPy - `ipython_genutils `_ - `jplephem `_ - `tqdm `_ +- `ssa-data-core `_, + ``ssa-data-gravity``, ``ssa-data-lunar``, and ``ssa-data-lunar-gravity`` + (packaged data) Documentation ------------- @@ -118,8 +123,8 @@ documentation builds, and Git workflow tips, see the SSAPy's ``main`` branch contains the latest development work. -For higher-fidelity simulation, visualization, and mission-level workflows, -see the +For six-degree-of-freedom (6-DOF) propagation, visualization, and mission-level +workflows, see the `Space Situational Awareness Toolkit (SSATK) `__. Releases diff --git a/docs/source/installation.rst b/docs/source/installation.rst index 10ba25ed..604bb3d9 100644 --- a/docs/source/installation.rst +++ b/docs/source/installation.rst @@ -45,8 +45,13 @@ As the package has been published on `PyPI