Metadata-Version: 2.4
Name: libephemeris
Version: 0.15.0
Summary: A high-precision, open-source astronomical ephemeris library for Python, powered by Skyfield.
Author-email: Giacomo Battaglia <giacomo@libephemeris.dev>
Maintainer-email: Giacomo Battaglia <giacomo@libephemeris.dev>
License-Expression: LGPL-3.0-only
Project-URL: Homepage, https://github.com/g-battaglia/libephemeris
Project-URL: Documentation, https://github.com/g-battaglia/libephemeris#readme
Project-URL: Repository, https://github.com/g-battaglia/libephemeris
Project-URL: Issues, https://github.com/g-battaglia/libephemeris/issues
Project-URL: Changelog, https://github.com/g-battaglia/libephemeris/blob/main/CHANGELOG.md
Keywords: astronomy,ephemeris,astrology,planetary,skyfield,swiss-ephemeris,astrological,celestial,horoscope,zodiac
Classifier: Development Status :: 2 - Pre-Alpha
Classifier: Intended Audience :: Science/Research
Classifier: Intended Audience :: Developers
Classifier: Intended Audience :: Education
Classifier: Operating System :: OS Independent
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3.9
Classifier: Programming Language :: Python :: 3.10
Classifier: Programming Language :: Python :: 3.11
Classifier: Programming Language :: Python :: 3.12
Classifier: Programming Language :: Python :: 3.13
Classifier: Topic :: Scientific/Engineering :: Astronomy
Classifier: Topic :: Scientific/Engineering :: Physics
Classifier: Typing :: Typed
Requires-Python: >=3.9
Description-Content-Type: text/markdown
License-File: LICENSE
Requires-Dist: skyfield>=1.54
Requires-Dist: skyfield-data>=7.0.0
Requires-Dist: astroquery>=0.4.7
Requires-Dist: certifi
Requires-Dist: spktype21>=0.1.0
Requires-Dist: pyerfa>=2.0.0
Provides-Extra: stars
Requires-Dist: astropy>=5.0.0; extra == "stars"
Provides-Extra: nbody
Requires-Dist: rebound>=4.0.0; extra == "nbody"
Requires-Dist: assist>=1.1.0; extra == "nbody"
Provides-Extra: all
Requires-Dist: rebound>=4.0.0; extra == "all"
Requires-Dist: assist>=1.1.0; extra == "all"
Provides-Extra: dev
Requires-Dist: black>=25.1.0; extra == "dev"
Requires-Dist: mypy>=1.0.0; extra == "dev"
Requires-Dist: poethepoet<0.38.0,>=0.37.0; extra == "dev"
Requires-Dist: pyswisseph>=2.10.3.2; extra == "dev"
Requires-Dist: pytest<9.0.0,>=8.0.0; extra == "dev"
Requires-Dist: pytest-xdist>=3.5.0; extra == "dev"
Requires-Dist: ruff>=0.14.6; extra == "dev"
Requires-Dist: pytest-cov>=6.0.0; extra == "dev"
Requires-Dist: build>=1.0.0; extra == "dev"
Requires-Dist: twine>=5.0.0; extra == "dev"
Requires-Dist: pytest-sugar>=1.1.1; extra == "dev"
Requires-Dist: spiceypy>=6.0.0; extra == "dev"
Provides-Extra: docs
Requires-Dist: sphinx>=7.0.0; extra == "docs"
Requires-Dist: sphinx-rtd-theme>=2.0.0; extra == "docs"
Requires-Dist: myst-parser>=2.0.0; extra == "docs"
Dynamic: license-file

# LibEphemeris

<div align="left">
    <img src="https://static.pepy.tech/badge/libephemeris/month" alt="PyPI Downloads">
    <img src="https://static.pepy.tech/badge/libephemeris/week" alt="PyPI Downloads">
    <img src="https://static.pepy.tech/personalized-badge/libephemeris?period=total&units=INTERNATIONAL_SYSTEM&left_color=GREY&right_color=BLUE&left_text=downloads/total" alt="PyPI Downloads">
    <img src="https://img.shields.io/pypi/v/libephemeris.svg" alt="PyPI Version">
    <img src="https://img.shields.io/pypi/pyversions/libephemeris.svg" alt="Python Versions">
    <img src="https://img.shields.io/github/license/g-battaglia/libephemeris.svg" alt="License">
</div>

A pure Python astronomical ephemeris library based on NASA JPL data. Designed as a 1:1 API-compatible drop-in replacement for PySwisseph, strictly focused on scientific precision.

- Drop-in replacement for `pyswisseph`
- Uses NASA JPL ephemerides (DE440/DE441) via Skyfield
- Uses modern IAU standards (ERFA/pyerfa)

> [!WARNING]
> **Pre-Alpha** -- The public API may change without notice.

---

## Why

Swiss Ephemeris is fast and widely used. LibEphemeris makes a different trade-off:

- **Accuracy-first:** JPL numerical integrations (DE440/DE441) + IAU-standard precession/nutation
- **Transparent:** pure Python, fully testable, and documented with references
- **Slower than SwissEph:** Swiss is C; LibEphemeris is Python (see Performance)

Precision details (models, term counts, measured comparisons, references): `docs/PRECISION.md`.

---

## Installation

```bash
pip install libephemeris
```

DE440 (~128 MB) downloads automatically on first use.

Download all data files for your use case (ephemeris + SPK kernels):

```bash
libephemeris download:medium      # recommended for most users
```

See [CLI commands](#cli-commands) for all options and tiers.

### Optional extras

```bash
pip install libephemeris[spk]    # Automatic SPK downloads from JPL Horizons
pip install libephemeris[nbody]  # REBOUND/ASSIST n-body integration
pip install libephemeris[all]    # Everything
```

**Requirements:** Python 3.9+ &bull; skyfield >= 1.54 &bull; pyerfa >= 2.0

---

## Quick start

```python
import libephemeris as swe
from libephemeris.constants import SE_SUN, SE_MOON, SEFLG_SPEED

jd = swe.julday(2000, 1, 1, 12.0)  # J2000.0

sun, _ = swe.calc_ut(jd, SE_SUN, SEFLG_SPEED)
moon, _ = swe.calc_ut(jd, SE_MOON, SEFLG_SPEED)

print("Sun lon:", sun[0], "deg")
print("Moon lon:", moon[0], "deg")
```

Houses:

```python
jd = swe.julday(2024, 11, 5, 18.0)
cusps, ascmc = swe.houses(jd, 41.9028, 12.4964, b"P")  # Placidus
print("ASC:", ascmc[0], "deg")
print("MC:", ascmc[1], "deg")
```

---

## Calculation flags

Flags are bitmasks that control what is calculated and how the result is returned. `calc_ut()` returns a 6-element tuple `(longitude, latitude, distance, speed_lon, speed_lat, speed_dist)` and a return flag. Combine multiple flags with bitwise OR (`|`).

### Velocity

- `SEFLG_SPEED` — Populates the speed fields (`pos[3]`–`pos[5]`) with daily motion in longitude, latitude, and distance. Without this flag those values are zero. Almost every call should include it.

### Observer

By default the observer is at Earth's center (geocentric).

- `SEFLG_HELCTR` — Heliocentric: moves the observer to the Sun. Distances become heliocentric AU.
- `SEFLG_TOPOCTR` — Topocentric: places the observer on Earth's surface at the position set with `swe_set_topo()`. This matters most for the Moon (up to ~1° parallax).

### Coordinates

By default the output is ecliptic longitude/latitude of date.

- `SEFLG_EQUATORIAL` — Switches to equatorial coordinates: `pos[0]` becomes Right Ascension (0–360°) and `pos[1]` becomes Declination (±90°). Speeds change accordingly.

### Reference frame

By default positions are precessed to the equinox of date.

- `SEFLG_J2000` — Keeps coordinates in the J2000.0 reference frame instead of precessing to the equinox of date.
- `SEFLG_NONUT` — Excludes nutation, giving positions on the mean ecliptic/equator.

### Position corrections

By default positions are apparent (light-time and aberration corrected).

- `SEFLG_TRUEPOS` — Geometric position: no light-time correction. Returns where the body actually is at the instant of calculation.
- `SEFLG_NOABERR` — Astrometric position: light-time corrected but no aberration. Comparable to star catalog positions.
- `SEFLG_ASTROMETRIC` — Convenience shorthand for `SEFLG_NOABERR | SEFLG_NOGDEFL`.

### Sidereal zodiac

- `SEFLG_SIDEREAL` — Subtracts the ayanamsha from ecliptic longitude, returning sidereal rather than tropical positions. Requires a prior `swe_set_sid_mode()` call to select the ayanamsha (Lahiri, Fagan-Bradley, etc.).

### Combining flags

```python
# Heliocentric position with velocity
pos, _ = swe.calc_ut(jd, SE_MARS, SEFLG_SPEED | SEFLG_HELCTR)

# Sidereal equatorial coordinates
pos, _ = swe.calc_ut(jd, SE_SUN, SEFLG_SPEED | SEFLG_EQUATORIAL | SEFLG_SIDEREAL)
```

> [!NOTE]
> `SEFLG_MOSEPH` and `SEFLG_SWIEPH` are accepted for API compatibility but
> silently ignored — all calculations always use JPL DE440/DE441 via Skyfield.
> `SEFLG_BARYCTR` is mapped to heliocentric. `SEFLG_XYZ`, `SEFLG_RADIANS`,
> `SEFLG_SPEED3`, and `SEFLG_ICRS` are defined but not yet implemented.

---

## Choose the ephemeris (DE440 vs DE441)

Default ephemeris: **DE440** (1550--2650 CE).

To use **DE441** (extended range -13200 to +17191 CE):

```python
import libephemeris as swe
swe.set_ephemeris_file("de441.bsp")
```

Or via environment variable:

```bash
export LIBEPHEMERIS_EPHEMERIS=de441.bsp
```

Dates outside the loaded kernel raise `EphemerisRangeError`.

---

## Outer-planet centers (barycenter vs center)

JPL DE ephemerides provide system barycenters for Jupiter/Saturn/Uranus/Neptune/Pluto.
LibEphemeris corrects these to planet body centers automatically:

1. Uses a compact SPK file (`planet_centers.bsp`) downloaded by `libephemeris download-data`
2. Falls back to analytical satellite models when SPK coverage is not available

Full technical details are in `docs/PRECISION.md`.

---

## Minor bodies (SPK)

For many asteroids and TNOs, high precision requires JPL SPK kernels.

```python
import libephemeris as swe
from libephemeris.constants import SE_CHIRON

swe.set_auto_spk_download(True)
swe.set_spk_cache_dir("./spk_cache")

pos, _ = swe.calc_ut(2460000.0, SE_CHIRON, 0)
print(pos[0])
```

### Optional Dependencies

LibEphemeris has several optional dependencies for enhanced functionality:

| Extra | Description | Dependencies |
|-------|-------------|--------------|
| `[spk]` | Automatic SPK downloads from JPL Horizons | `astroquery` |
| `[stars]` | Star catalog access | `astropy` |
| `[nbody]` | N-body integration | `rebound`, `reboundx` |
| `[all]` | All optional features | All above |

```bash
pip install libephemeris[spk]    # For auto-downloading SPK kernels from Horizons
pip install libephemeris[stars]  # For accessing star catalogs via astropy
pip install libephemeris[all]    # Install all optional dependencies
```

**Note:** `pyerfa` is a required dependency and provides IAU 2006/2000A precession-nutation models for high-precision calculations.

---

## Thread safety

The global API is pyswisseph-compatible and uses global mutable state.
For concurrency, use `EphemerisContext`:

```python
from libephemeris import EphemerisContext, SE_SUN

ctx = EphemerisContext()
ctx.set_topo(12.5, 41.9, 0)
pos, _ = ctx.calc_ut(2451545.0, SE_SUN, 0)
```

---

## Docs

- `docs/PRECISION.md` (scientific models, measured precision, references)
- `docs/migration-guide.md` (pyswisseph -> libephemeris)
- `docs/HOUSE_SYSTEMS.md`
- `docs/AYANAMSHA.md`
- `docs/testing.md`

---

## Performance

LibEphemeris is pure Python; Swiss Ephemeris is C. Expect LibEphemeris to be slower.
For batch workloads, use `EphemerisContext`, parallelism, and caching.

---

## Development

```bash
git clone https://github.com/g-battaglia/libephemeris.git
cd libephemeris
uv pip install -e ".[dev]"
```

All development tasks use [poethepoet](https://poethepoet.naberhaus.dev/) (`poe`).

### Code quality

| Command | Description |
|---------|-------------|
| `poe format` | Format code with Ruff |
| `poe lint` | Lint and auto-fix with Ruff |
| `poe typecheck` | Type-check with mypy |

### Tests

| Command | Description |
|---------|-------------|
| `poe test` | Fast tests (excludes `@pytest.mark.slow`) |
| `poe test:full` | All tests including slow ones |
| `poe test:fast` | Fast tests, parallel (`-n auto`) |
| `poe test:fast:essential` | Parallel essential subset (~670 tests, 1 file per module) |
| `poe test:unit` | Unit tests only (`tests/`) |
| `poe test:unit:fast` | Unit tests, parallel |
| `poe test:compare` | Comparison tests vs pyswisseph (`compare_scripts/tests/`) |
| `poe test:compare:fast` | Comparison tests, parallel |
| `poe coverage` | Fast tests with coverage report |
| `poe coverage:full` | All tests with coverage report |

### Tier diagnostics

Run diagnostic tables showing all celestial bodies with coordinates, velocities, and data source for each precision tier:

| Command | Description |
|---------|-------------|
| `poe diag:base` | Diagnostic for base tier (1850-2150) |
| `poe diag:medium` | Diagnostic for medium tier (1550-2650) |
| `poe diag:extended` | Diagnostic for extended tier (-13200 to +17191) |

### SPK downloads (dev)

Download SPK kernels for minor bodies directly (without the full CLI tier setup):

| Command | Description |
|---------|-------------|
| `poe spk:download:base` | SPK for base tier range (1850-2150) |
| `poe spk:download:medium` | SPK for medium tier range (1900-2100) |
| `poe spk:download:extended` | Max-range SPK files (1600-2500, single file per body) |

### Data generation

| Command | Description |
|---------|-------------|
| `poe generate-planet-centers-spk` | Generate `planet_centers.bsp` (requires `spiceypy`) |
| `poe generate-lunar-corrections` | Regenerate lunar correction tables (requires `de441.bsp`) |

---

## License

LGPL-3.0. See `LICENSE`.
