Full Range Coverage #
Plan for extending minor body coverage across the full DE441 ephemeris range (-13200 to +17191 CE).
Table of Contents #
- Problem Statement
- Current Situation
- Target Coverage
- Bugs Found During Investigation
- Implementation Steps
- Verification
- JPL Horizons SPK Limits
Problem Statement #
The extended precision tier uses DE441 which covers -13200 to +17191 for major planets, but minor bodies (asteroids, TNOs, centaurs) fall back to low-precision Keplerian propagation (~10-30 arcsec error) outside a narrow SPK window.
Current Situation #
| Source | Range | Precision | Status |
|---|---|---|---|
SPK (on disk, *_190001_210001) |
1900-2100 | Sub-arcsecond | Registered (by chance) |
SPK (on disk, *_185001_215001) |
1850-2150 | Sub-arcsecond | Present but ignored |
| SPK (max from JPL Horizons) | 1600-2500 | Sub-arcsecond | Not downloaded |
| ASSIST (n-body, with DE441) | -13000 to +17000 | Sub-arcsecond | Not configured |
| Keplerian fallback | Unlimited | 10-30 arcsec | Active everywhere else |
Target Coverage #
DE441 range: -13200 ==============================================> +17191
SPK (Horizons): |1600 ============ 2500|
ASSIST (DE441): -13000 ========================================> +17000
Keplerian: only outside -13000/+17000 (very rare use case)
Bugs Found During Investigation #
1. discover_local_spks registers random SPK file (first-wins) #
File: libephemeris/spk_auto.py, discover_local_spks() (~line 1634)
os.listdir() returns files in filesystem-dependent order. When multiple SPK
files exist for the same body (e.g. 2060_190001_210001.bsp and
2060_185001_215001.bsp), whichever file is listed first gets registered.
The wider-range file may be silently ignored.
Status: Pending fix
2. spk_date_range for extended tier is wrong #
File: libephemeris/state.py, line 64
Currently set to ("1550-01-01", "2650-01-01"), but JPL Horizons only accepts
SPK requests in the range 1600-01-01 to 2500-01-01 (verified empirically).
This causes _try_auto_spk_download() to request impossible ranges, which fail
silently and fall through to Keplerian.
Status: Pending fix
3. Diagnostic _get_source() was lying about data source #
File: scripts/_tier_diagnostic.py
Was reporting “SPK” for all minor bodies regardless of whether the date was
actually covered by the SPK file. Now checks actual SPK file coverage via
get_spk_coverage().
Status: Fixed
Implementation Steps #
Step 1: Fix discover_local_spks to prefer widest SPK #
Status: Pending
File: libephemeris/spk_auto.py
When a body is already registered and a new SPK file containing the same body is found, compare coverage ranges. Re-register if the new file has wider coverage.
# Pseudocode for the fix:
if ipl in state._SPK_BODY_MAP:
existing_file, _ = state._SPK_BODY_MAP[ipl]
existing_coverage = get_spk_coverage(existing_file)
new_coverage = get_spk_coverage(filepath)
if new_coverage and existing_coverage:
new_span = new_coverage[1] - new_coverage[0]
existing_span = existing_coverage[1] - existing_coverage[0]
if new_span > existing_span:
# Re-register with wider file
spk.register_spk_body(ipl, filepath, target_naif)
Step 2: Update spk_date_range for tiers #
Status: Pending
File: libephemeris/state.py
Update the extended tier to reflect the actual JPL Horizons maximum:
"extended": PrecisionTier(
name="extended",
ephemeris_file="de441.bsp",
spk_date_range=("1600-01-01", "2500-01-01"), # Was 1550-2650
description="Extended range (-13200 to +17191), ~3.1 GB",
),
Also update base and medium tiers if their ranges exceed what Horizons accepts:
- base:
("1850-01-01", "2150-01-01")— OK, within 1600-2500 - medium:
("1900-01-01", "2100-01-01")— OK, within 1600-2500
Step 3: Create max-range SPK download script #
Status: Pending
File: new scripts/download_max_range_spk.py
Downloads SPK files with the maximum range (1600-2500) for all 21 bodies in
SPK_BODY_NAME_MAP. Replaces existing narrower-range files.
Bodies (from libephemeris/constants.py):
- Chiron (2060), Pholus (5145), Ceres (Ceres;), Pallas (Pallas;), Juno (Juno;), Vesta (Vesta;), Eris (136199), Sedna (90377), Haumea (136108), Makemake (136472), Ixion (28978), Orcus (90482), Quaoar (50000), Varuna (20000), Nessus (7066), Asbolus (8405), Chariklo (10199), Gonggong (225088), Apophis (99942), Hygiea (Hygiea;), Eros (433)
Step 4: Add poe task for max-range download #
Status: Pending
File: pyproject.toml
"spk:download:maxrange" = "python scripts/download_max_range_spk.py"
Step 5: Document ASSIST configuration for full DE441 range #
Status: Pending
ASSIST with DE441 data covers -13000 to +17000. The fallback chain in
planets.py:1612-1644 already works — it just needs ASSIST installed and
configured.
Requirements:
pip install rebound assist
# Download ASSIST data files (~1 GB total):
# Option A: Use existing de441.bsp (already ~3.1 GB)
# Option B: Download Linux-format DE441:
curl https://ssd.jpl.nasa.gov/ftp/eph/planets/Linux/de441/linux_m13000p17000.441 \
-o data/linux_m13000p17000.441
# Required: asteroid perturbers for n-body integration:
curl https://ssd.jpl.nasa.gov/ftp/eph/small_bodies/asteroids_de441/sb441-n16.bsp \
-o data/sb441-n16.bsp
# Set environment variable:
export ASSIST_DIR=/path/to/data/
The AssistEphemConfig in rebound_integration.py searches for ephemeris files
in this order: de441.bsp, de440.bsp, linux_m13000p17000.441,
linux_p1550p2650.440.
Verification #
After implementation, run the tier diagnostics to verify:
poe diag:extended
Expected results:
- Dates 1600-2500: all minor bodies show SPK
- Dates outside 1600-2500 but within -13000/+17000: show ASSIST (if installed)
- Dates outside -13000/+17000: show Keplerian (only extreme edges of DE441)
JPL Horizons SPK Limits #
Tested empirically against the Horizons API on 2026-02-20:
| Parameter | Value |
|---|---|
| Earliest START_TIME | 1600-01-01 |
| Latest STOP_TIME | 2500-01-01 |
| Max span | 900 years |
| Applies to | All 21 bodies in SPK_BODY_NAME_MAP |
| Bodies using name syntax | Ceres;, Pallas;, Juno;, Vesta;, Hygiea; |
Requesting dates outside this range returns:
"START time outside set SPK limits" or "STOP time outside set SPK limits".