wis
Where Is Satellite/Observatory – A Python package for calculating positions of observatories using NASA/JPL SPICE kernels.
Why "wis"?
A traditional astronomical backronym:
- Where Is Satellite
- Where Is Satellite‑spice‑kernel
- Where is Station‑code/Observatory‑code
- (also) very light & wispy
Overview
wis provides access to SPICE kernels' high‑precision observatory positions for both ground‑based and satellite observatories. It automatically downloads and manages the necessary SPICE kernels (NASA/JPL ephemeris data) and queries the MPC Observatory Codes API to obtain geocentric coordinates of ground stations.
Key Features
- Ground‑based observatory positions – Supports all MPC observatory codes (e.g.,
F51,W84,500geocenter) via the MPC REST API. - Satellite observatory positions – Currently supports TESS (
C57), Kepler/K2 (C55), Gaia (258), Hubble Space Telescope (250), and James Webb Space Telescope (274) with extensible kernel specifications. - Multiple ephemeris files – Choose between DE430 and DE440 ephemeris models via explicit kernel selection.
- Automatic SPICE kernel management – Kernels are downloaded to
~/.wispykernels/and kept up‑to‑date (time‑critical files refreshed daily). - Explicit kernel loading – Users specify exactly which kernels to load, improving startup performance.
- Public methods:
get_obs_helio_equ_AU- Returns heliocentric equatorial positions (AU) and light‑travel times (days) for a given MPC observatory code, falling back to obscode 500 (geocenter) if
fallback_to_geois set. ReturnsNoneif the code is not recognized andfallback_to_geo=False.
- Returns heliocentric equatorial positions (AU) and light‑travel times (days) for a given MPC observatory code, falling back to obscode 500 (geocenter) if
get_bary_wrt_helio- Returns Solar System barycenter position (AU), velocity (AU/day), and light‑travel time (days) relative to the heliocenter.
- see below for examples
Installation
From source (recommended for development)
git clone https://github.com/Smithsonian/mpc-public.git
cd mpc-public/wis
pip install .
Usage
Basic Example (Ground Observatories)
from wis import Wis
from wis.kernels import DE430
from astropy.time import Time
# Create a Wis context manager with DE430 kernel
with Wis(kernels=DE430) as w:
# Get heliocentric equatorial positions for Pan‑STARRS 1 (F51)
# at two Julian dates (TDB scale)
obscode = "F51"
times = Time([2451545.000742869, 2451546.000742869], format="jd", scale="tdb")
pos, ltt = w.get_obs_helio_equ_AU(obscode, times)
# pos: shape (N,3) array of positions [AU]
# ltt: shape (N,) array of light‑travel times [days]
print(f"Positions [AU]:\n{pos}")
print(f"Light‑travel times [days]:\n{ltt}")
# Position of barycenter wrt heliocenter
bary_pos, bary_vel, bary_ltt = w.get_bary_wrt_helio(times)
# bary_pos: shape (N,3) position vectors [AU]
# bary_vel: shape (N,3) velocity vectors [AU/day]
# bary_ltt: shape (N,) light‑travel times [days]
Loading Multiple Kernels (Ground + Satellite)
from wis import Wis
from wis.kernels import DE430, TESS
from astropy.time import Time
# Load both ground and satellite kernels
with Wis(kernels=[DE430, TESS]) as w:
# Calculate TESS position
times = Time([2458337.829157830], format="jd", scale="tdb")
pos, ltt = w.get_obs_helio_equ_AU("C57", times)
print(f"TESS position [AU]:\n{pos}")
# Can also calculate ground observatory positions
pos_f51, ltt_f51 = w.get_obs_helio_equ_AU("F51", times)
print(f"Pan-STARRS position [AU]:\n{pos_f51}")
Using DE440 Ephemeris
from wis import Wis
from wis.kernels import DE440
from astropy.time import Time
times = Time([2451545.000742869, 2451546.000742869], format="jd", scale="tdb")
with Wis(kernels=DE440) as w:
# Use DE440 instead of DE430
pos, ltt = w.get_obs_helio_equ_AU("500", times)
Creating Custom Kernels
You can create custom KernelSpecifier objects for other satellites or observatories:
from wis import Wis
from wis.kernelspecifier import KernelSpecifier
from wis.kernels import DE430 # Still need ground kernel for Earth position
from astropy.time import Time
# Define a custom kernel for your satellite
MySatelliteKernel = KernelSpecifier(
obscodeMPC="C99",
obscodeJPL="-999",
name="MySatellite",
files=["https://example.com/mysatellite.bsp"],
wildcards={},
timecritical=[]
)
times = Time([2458337.829157830], format="jd", scale="tdb")
# Use it alongside built-in kernels
with Wis(kernels=[DE430, MySatelliteKernel]) as w:
pos, ltt = w.get_obs_helio_equ_AU("C99", times)
Supported Observatories
Ground‑Based Observatories
All MPC observatory codes are supported. The geocentric coordinates are retrieved from the MPC REST API (https://data.minorplanetcenter.net/api/obscodes) and cached locally for one day. Codes that use two‑line observations are flagged and excluded from position calculations.
Satellite Observatories
| MPC Code | Name | NAIF ID | Kernel Source |
|---|---|---|---|
C57 |
TESS | -95 |
STScI archive |
C55 |
Kepler | -227 |
STScI archive |
258 |
Gaia | -123 |
ESA SPICE archive |
250 |
HST | -48 |
NAIF HST archive |
274 |
JWST | -170 |
NAIF JWST archive |
Additional satellites can be added by creating custom KernelSpecifier objects or by extending the satellite definitions in your own module (see CLI section below).
Kernel Management
Explicit Kernel Loading
Unlike previous versions, wis now requires you to explicitly specify which kernels to load. This improves startup performance and gives you full control over which SPICE data is downloaded and loaded into memory.
from wis import Wis
from wis.kernels import DE430, DE440, KEPLER, TESS, GAIA, HST, JWST
# Load only what you need
with Wis(kernels=DE430) as w: # Ground only
pass
with Wis(kernels=[DE430, TESS]) as w: # Ground + TESS
pass
with Wis(kernels=[DE440, KEPLER, TESS]) as w: # Ground (DE440) + KEPLER + TESS
pass
Note: A ground kernel (
DE430orDE440) is always required. It supplies the leapsecond file for time conversion and the planetary ephemeris needed to compute heliocentric positions.Wis(kernels=TESS)alone will raise aValueErrorat startup.
Automatic Download & Loading
When a Wis instance is entered (the __enter__ method), it:
- Downloads any missing or out-of-date kernel files for the specified kernels.
- Loads all necessary kernels into SPICE memory.
Kernels are stored in ~/.wispykernels/. Each kernel has its own subdirectory. Time‑critical files (e.g., earth_latest_high_prec.bpc) are refreshed every 24 hours; other files are considered static.
Command‑Line Download Utility
A script download-wis-kernels is installed with the package. It downloads specified kernels without loading them.
# List available built-in kernels
download-wis-kernels --list
# Download specific kernels
download-wis-kernels DE430
download-wis-kernels DE430 TESS
download-wis-kernels DE430 DE440 KEPLER TESS GAIA HST JWST
# Download with custom kernels from a Python module
download-wis-kernels --module mypackage.kernels DE430 MyCustomKernel
This is useful for pre‑populating the kernel cache in Docker images or CI environments.
Kernel Metadata
Each KernelSpecifier maintains a JSON metadata file (kernel_filepath_map.json) that records the remote URL, download date, file size, and MD5 hash of each kernel. This metadata is used to decide whether a file needs to be re‑downloaded.
Ground‑Based Kernels (DE430 & DE440)
| Path / Wildcard | Ephemeris | Refresh | Purpose |
|---|---|---|---|
https://naif.jpl.nasa.gov/pub/naif/generic_kernels/lsk/latest_leapseconds.tls |
Both | Static | Leap‑seconds |
https://naif.jpl.nasa.gov/pub/naif/generic_kernels/pck/pck00011.tpc |
Both | Static | Planetary constants |
https://naif.jpl.nasa.gov/pub/naif/generic_kernels/pck/earth_latest_high_prec.bpc |
Both | Daily | High‑precision Earth orientation (time‑critical) |
https://naif.jpl.nasa.gov/pub/naif/generic_kernels/pck/earth_*_combined.bpc (wildcard) |
Both | Static | Combined Earth orientation predictions+reconstructions |
https://naif.jpl.nasa.gov/pub/naif/generic_kernels/spk/planets/de430.bsp |
DE430 only | Static | DE430 planetary ephemeris |
https://naif.jpl.nasa.gov/pub/naif/generic_kernels/pck/gm_de431.tpc |
DE430 only | Static | DE431 GM constants |
https://naif.jpl.nasa.gov/pub/naif/generic_kernels/spk/planets/de440.bsp |
DE440 only | Static | DE440 planetary ephemeris |
https://naif.jpl.nasa.gov/pub/naif/generic_kernels/pck/gm_de440.tpc |
DE440 only | Static | DE440 GM constants |
Static kernels are downloaded once and never automatically refreshed (unless manually deleted). Daily kernels are re‑downloaded if older than 24h.
Satellite Kernels
| Satellite | MPC Code | Path / Wildcard | Refresh | Notes |
|---|---|---|---|---|
| TESS | C57 | https://archive.stsci.edu/missions/tess/models/TESS_EPH_DEF* (wildcard) |
Static | Kernel list from 2019; may be outdated |
| Kepler | C55 | https://archive.stsci.edu/pub/k2/spice/kplr2018134232543.tsc |
Static | Kepler spacecraft clock |
| Kepler | C55 | https://archive.stsci.edu/pub/k2/spice/spk_2018290000000_2018306220633_kplr.bsp |
Static | Kepler ephemeris (2018‑2019) |
| Gaia | 258 | https://spiftp.esac.esa.int/data/SPICE/GAIA/kernels/spk/gaia_flp_20131219_21250328_v01.bsp |
Static | Reconstructed mission trajectory, then long prediction |
| HST | 250 | https://naif.jpl.nasa.gov/pub/naif/HST/kernels/spk/hst.bsp |
Daily | Current HST trajectory |
| JWST | 274 | https://naif.jpl.nasa.gov/pub/naif/JWST/kernels/spk/jwst_pred.bsp |
Daily | Predicted trajectory; loaded before reconstructed data |
| JWST | 274 | https://naif.jpl.nasa.gov/pub/naif/JWST/kernels/spk/jwst_rec.bsp |
Daily | Reconstructed trajectory; loaded after predicted data so it wins in overlap |
Static satellite kernels are downloaded once and never automatically refreshed (note that the TESS mission adds new kernels periodically, and these will be discovered/downloaded if not present locally). Daily satellite kernels are re‑downloaded if older than 24h.
Migration Guide (Breaking Changes)
Version 2.0.0 introduces breaking changes from earlier versions:
Old API (no longer supported)
# OLD - Automatically loaded all kernels
with Wis() as w:
pass
# OLD - Used ephemeris parameter
with Wis(ephemeris="DE440") as w:
pass
# OLD - Environment variable controlled ephemeris
import os
os.environ['EPHEMERIS'] = 'DE440'
with Wis() as w:
pass
New API
from wis import Wis
from wis.kernels import DE430, DE440, KEPLER, TESS, GAIA, HST, JWST
# NEW - Must explicitly specify kernels
with Wis(kernels=DE430) as w:
pass
# NEW - Use DE440 ephemeris
with Wis(kernels=DE440) as w:
pass
# NEW - Multiple kernels
with Wis(kernels=[DE430, TESS]) as w:
pass
2.1.0: Cached Methods Return Read-Only Arrays
get_obs_helio_equ_AU and get_bary_wrt_helio cache their results, so every caller
with the same times receives the same arrays. To stop one caller's in-place write
from corrupting the cache for everyone, those arrays are now read-only: modifying
one raises ValueError.
posns, _ = w.get_obs_helio_equ_AU("F51", times)
posns[0, 0] = 0.0 # ValueError: assignment destination is read-only
Take a copy if you need to modify the result:
posns, _ = w.get_obs_helio_equ_AU("F51", times)
posns = posns.copy() # writeable; the cached original is left untouched
posns[0, 0] = 0.0
CLI Changes
# OLD - Downloaded all kernels by default
download-wis-kernels
# NEW - Must specify which kernels to download
download-wis-kernels DE430 TESS
# NEW - List available kernels
download-wis-kernels --list
Development
Running Tests
pytest tests/
Tests include unit tests for individual modules and integration tests that verify kernel downloads and position calculations.
Future work
- Add more satellite kernels
- Better caching (currently a local cache for entire call signatures; could be remote-friendly, as well as individual times)
- Add more unit tests
- Provide a Docker image with pre‑downloaded kernels
Releasing to PyPI
Publication is automated via GitHub Actions and PyPI trusted
publishing (no tokens), mirroring
the digest2 release flow in this repository.
To release a new version:
- Update
versioninwis/pyproject.tomland merge that change tomain. - On the repository's Releases page,
create a new release:
- Tag:
wis-v<version>(must start withwis-vand match thepyproject.tomlversion — the workflow verifies this and fails on mismatch) - Target:
main - Title/notes: describe what changed.
- Tag:
- Publishing the release triggers
wis-python-release.yml, which builds the sdist + wheel, runstwine check, and uploads to PyPI.
Metadata
Release files for wis 2.2.0
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | Provenance | |
|---|---|---|---|---|
| wis-2.2.0.tar.gz | 60.1 kB | With provenance | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| wis-2.2.0-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 89.1 kB
Release files / wis-2.2.0.tar.gz
| Download URL | wis-2.2.0.tar.gz |
|---|---|
| Size | 60.1 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
4025652905b752671189f9fed7e21a11a2270ab533ed0a2412b6419899746baf
|
|
BLAKE2b-256 checksum How to use checksums |
8718ff14286c5b451cb3db9488b7f26f535011938eefec94955a4706e1a8f818
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.13.14
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Oct 1, 2026.
Transparency logRelease files / wis-2.2.0-py3-none-any.whl
| Download URL | wis-2.2.0-py3-none-any.whl |
|---|---|
| Size | 29.1 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
d8e03094d055fdb23d80b7399c3ef392e32f876bc41b0bde4e08c71b210f9485
|
|
BLAKE2b-256 checksum How to use checksums |
1f80ba6f1b17410c31660254e41e55b35c087a19f3af56305f048b758c669b8e
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.13.14
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Oct 1, 2026.
Transparency logSecurity for wis 2.2.0
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
Full provenance
All 2 files in this release have been uploaded with provenance metadata.
Attestation for wis-2.2.0.tar.gz
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Oct 1, 2026.
Transparency logA note on package security
PyPI is a repository for distributing packages, not a service for auditing them. While third-party security researchers and our staff do their best to detect and remove malicious packages, PyPI cannot guarantee the quality, security or suitability of any given package.