Skip to main content

Pure-Rust astronomical & astrology ephemeris — VSOP87 planets, Vedic/Western/KP, house systems, ayanamsa, fixed stars, numerology. A pyswisseph drop-in.

Project description

xalen

Pure-Rust astronomical & astrology ephemeris for Python — VSOP87 planets, Vedic/Western/KP, house systems, ayanamsa, fixed stars, numerology. A drop-in for pyswisseph, with no AGPL and no data files to ship.

PyPI CI License

Part of the XALEN Ephemeris suite — pure-Rust core, zero unsafe in the math, thread-safe, Apache-2.0. The Python wheel is the xalen-python crate (PyO3) wrapping that core.


Install

# FORTHCOMING — these ephemeris bindings are NOT on PyPI yet.
pip install xalen

Pre-publish note: the xalen ephemeris wheel is not on PyPI yet (the badge above is a placeholder). Be aware that an unrelated package also named xalen currently exists on PyPI (a separate "XALEN SDK") — pip install xalen today does not install these bindings. Until the ephemeris wheel ships, build it locally from the repo:

pip install maturin
git clone https://github.com/vedika-io/xalen-ephemeris.git
cd xalen-ephemeris/crates/xalen-python
maturin develop --release        # builds + installs into the active venv
# or build a wheel:  maturin build --release  ->  target/wheels/xalen-*.whl

maturin enables the extension-module Cargo feature automatically (it is set in pyproject.toml), so the wheel resolves Python symbols against the host interpreter — no libpython link needed.

Requires Python ≥ 3.8.

Platform support

Wheels are built with the abi3 stable ABI (abi3-py38), so a single wheel per platform imports unchanged on CPython 3.8 and every later 3.x — there is no per-interpreter-version wheel to pick.

Platform Wheel Built by
Linux x86_64 (manylinux) cp38-abi3 release.yml (PyPI) · build-all-platforms.sh (zig)
Linux aarch64 (manylinux) cp38-abi3 release.yml (PyPI) · build-all-platforms.sh (zig)
macOS x86_64 cp38-abi3 release.yml · build-all-platforms.sh (on a macOS host)
macOS arm64 cp38-abi3 release.yml · build-all-platforms.sh (native)
Windows x86_64 cp38-abi3 release.yml (PyPI)
Any other platform source pip install builds from the sdist (needs a Rust toolchain)

scripts/build-all-platforms.sh produces the wheel matrix locally (macOS targets require a macOS host; Linux/Windows cross-builds use cargo-zigbuild). The durable, per-release builds for all five platforms above run on official runners in .github/workflows/release.yml on every v* tag. An sdist is also published, so any platform with a Rust toolchain can pip install from source.


import xalen.swe as swe — the pyswisseph drop-in

The headline feature: migrating an existing pyswisseph (a.k.a. swisseph) codebase is a one-line search-and-replace.

# before
import swisseph as swe
# after
import xalen.swe as swe

Every function mirrors the shape of the matching pyswisseph entry point — argument order, tuple layout, and the SE_* / SEFLG_* / SIDM_* constants (exposed under both the bare pyswisseph spellings and the SE_-prefixed ones).

import xalen.swe as swe

jd = swe.julday(1990, 6, 15, 10.5)            # -> Julian Day (UT1)

# Sun position with speed. Returns ((lon, lat, dist, lon_speed, lat_speed,
# dist_speed), ret_flag) — exactly like pyswisseph.
xx, retflag = swe.calc_ut(jd, swe.SUN, swe.FLG_SWIEPH | swe.FLG_SPEED)
lon, lat, dist, lon_speed, *_ = xx

# Sidereal (Lahiri) longitude.
swe.set_sid_mode(swe.SIDM_LAHIRI, 0.0, 0.0)
sid, _ = swe.calc_ut(jd, swe.SUN, swe.FLG_SWIEPH | swe.FLG_SIDEREAL)

# Houses: (cusps[12], ascmc[8]). ascmc = (asc, mc, armc, vertex,
# equatorial_ascendant, co_ascendant_koch, co_ascendant_munkasey, polar_ascendant).
cusps, ascmc = swe.houses_ex(jd, 18.52, 73.85, b"P")   # Placidus (hsys as bytes)
asc, mc = ascmc[0], ascmc[1]
# Sidereal houses: pass SEFLG_SIDEREAL (active-mode ayanamsa subtracted).
sid_cusps, sid_ascmc = swe.houses_ex(jd, 18.52, 73.85, b"P", swe.FLG_SIDEREAL)

# Ayanamsa, ΔT, calendar round-trip, fixed stars.
aya  = swe.get_ayanamsa_ut(jd)
dt   = swe.deltat(jd)
y, m, d, h = swe.revjul(jd)
star_xx, star_name, _ = swe.fixstar2_ut("Aldebaran", jd, swe.FLG_SWIEPH)
mag, _ = swe.fixstar2_mag("Aldebaran")

Honest compatibility scope

XALEN is a faithful shape drop-in, not a byte-for-byte clone. The differences are deliberate and documented:

  • Speeds (xx[3..6]) are 0.0 unless SEFLG_SPEED is in the flags — matching Swiss.
  • houses_ex returns ascmc of length 8; all eight slots are populated[0..4] are asc, mc, armc, vertex and [4..8] are the equatorial ascendant, the Koch and Munkasey co-ascendants, and the polar ascendant (validated against pyswisseph's swe.houses_armc auxiliary points). hsys may be passed as bytes (b"P", the pyswisseph form) or str ("P"). Passing SEFLG_SIDEREAL in flags returns the sidereal frame (active-mode ayanamsa subtracted from every cusp and angle; ARMC, a sidereal-time angle, is unchanged).
  • swe.calc(jd_et, ...) takes a TT/ET epoch (like pyswisseph) and converts it to UT1 via ΔT before computing, so swe.calc(jd_tt, ...) equals swe.calc_ut(jd_tt − deltat(jd_tt), ...). (The compat core still works in UT1; calc does the ΔT bridge for you.)
  • Position-altering flags XALEN does not yet implement (SEFLG_HELCTR, SEFLG_TOPOCTR, SEFLG_J2000, SEFLG_EQUATORIAL, SEFLG_BARYCTR, SEFLG_XYZ, SEFLG_RADIANS) raise ValueError rather than silently returning a geocentric ecliptic position mislabeled as something else. A loud error beats a silent-wrong drop-in.
  • set_ephe_path() and close() are no-ops — XALEN embeds all data at compile time, so there are no ephemeris files to point at or resources to release.

See docs/MIGRATING_FROM_SWISS_EPH.md for the full migration guide.


Native xalen API (richer, structured returns)

When you are not porting Swiss code, the native xalen module gives you dicts instead of positional tuples.

planet_position — the full 6-tuple + retrograde

The high-fidelity counterpart to planet_longitude. Equivalent to Swiss calc_ut(..., FLG_SPEED) plus a retrograde flag:

import xalen

jd = xalen.julian_day(2000, 1, 1, 12.0)        # J2000.0
p = xalen.planet_position(jd, 0)               # body 0 = Sun, tropical
# {
#   "longitude": 280.37, "latitude": 0.0, "distance": 0.9833,   # AU
#   "lon_speed": 1.0194, "lat_speed": ..., "dist_speed": ...,   # per day
#   "is_retrograde": False,
# }

# Sidereal (Lahiri = ayanamsa 0): sidereal longitude AND the ayanamsa's own
# precession rate is removed from lon_speed (matches Swiss SIDEREAL|SPEED).
p_sid = xalen.planet_position(jd, 0, sidereal=True, ayanamsa=0)

# Ketu (id 13) = Rahu + 180°, sharing Rahu's speed/retrograde.
ketu = xalen.planet_position(jd, 13)

longitude, latitude, and the speeds are in degrees (speeds per day); distance/dist_speed are in AU. longitude is wrapped to [0, 360); is_retrograde is taken from the tropical longitude rate regardless of the sidereal flag.

Body IDs

0=Sun · 1=Moon · 2=Mercury · 3=Venus · 4=Mars · 5=Jupiter · 6=Saturn · 7=Uranus · 8=Neptune · 9=MeanNode (Rahu) · 10=TrueNode · 11=Pluto · 12=Chiron · 13=Ketu (Rahu+180).

Other native functions

import xalen

jd = xalen.julian_day(1990, 6, 15, 10.5)

xalen.planet_longitude(jd, 1, sidereal=True, ayanamsa=0)  # just the longitude (float)
xalen.all_planets(jd, sidereal=True, ayanamsa=0)          # {"Sun":..,"Moon":.., "Ketu":..}

# Full Vedic chart: 9 grahas (+Ketu) with nakshatra/pada/rashi/lord, plus
# Whole-Sign ascendant, MC, ayanamsa_deg, and the 12 cusps.
chart = xalen.full_chart(jd, 18.52, 73.85, ayanamsa=0)
print(chart["planets"]["Sun"])     # {"longitude":.., "nakshatra":.., "pada":.., "rashi":.., "lord":..}
print(chart["ascendant"], chart["mc"], chart["ayanamsa_deg"])

# Panchang (five limbs) for a JD.
pan = xalen.panchang(jd, ayanamsa=0)
# {"tithi": {"number":..,"name":..,"paksha":"Shukla"|"Krishna"},
#  "nakshatra": "...", "yoga": {"number":..,"name":..}, "karana": "...", "vara": "..."}

# Nakshatra detail from a sidereal Moon longitude.
nak = xalen.nakshatra(123.45)      # {"name":.., "pada":.., "lord":.., "deity":.., "index":..}
xalen.rashi(123.45)                # e.g. "Simha (Leo)"

# Houses across 14 systems (0=WholeSign .. 13=Krusinski).
h = xalen.houses(jd, 18.52, 73.85, system=2)  # Placidus
# {"cusps":[12], "ascendant":.., "mc":.., "ic":.., "descendant":.., "vertex":..}

# Ayanamsa value (17 systems, 0=Lahiri).
xalen.ayanamsa(jd, system=0)
xalen.delta_t(jd)                  # ΔT (TT−UT1) seconds, SMH 2016 model
xalen.fixed_star_conjunctions(123.45, 1.0, 2000.0)

# Numerology.
xalen.life_path(1990, 6, 15)
xalen.expression_number("Ada Lovelace", "pythagorean")  # or "chaldean"

# String-named convenience variants.
xalen.planet_longitude_by_name("moon", jd)
xalen.sidereal_longitude("moon", jd, "lahiri")
xalen.houses_by_name(jd, 18.52, 73.85, "placidus")
xalen.ayanamsa_by_name(jd, "lahiri")

Accepted name strings: bodies sun, moon, mercury, venus, earth, mars, jupiter, saturn, uranus, neptune, pluto, rahu/mean_node, true_node, chiron, lilith/mean_apogee. Ayanamsas lahiri, kp, raman, fagan-bradley, true-chitra, true-revati, surya-siddhanta, yukteswar, jn-bhasin, deluce, ushashashi, pushya-paksha, galactic-center, lahiri-icrc, kp-straight-line, hipparchos, lahiri-vp285. House systems whole-sign, equal, placidus, koch, porphyry, regiomontanus, campanus, morinus, alcabitius, topocentric, meridian, vehlow, sripati, krusinski-pisa (dashes/spaces ignored, case-insensitive).


Accuracy — what is and is not claimed

XALEN is cross-validated against JPL Horizons (DE440), the real DE440 binary kernel, the official VSOP87 check file, Swiss Ephemeris (swetest), and public calculators. The honest framing: XALEN matches these references to the bounds below — DE440 is the reference; we do not "beat" it. The differentiator is pure Rust, zero unsafe core, thread-safe, Apache-2.0, no data files.

Body Theory Measured error vs JPL DE440 (analytical engine)
Sun, Mercury–Saturn VSOP87A + IAU 2000B nutation sub-arcsecond (Sun 0.21″, Mercury–Saturn ≤ 0.76″; 20k-chart bound)
Uranus, Neptune VSOP87A + nutation Uranus 1.78″, Neptune 2.53″ (20k-chart bound)
Moon ELP2000-82 (Meeus, 60+60 terms) ~3″ RMS (max ~12″), AD 1600–2100
Pluto Meeus Goffin 43-term fit ~1 arcminute, 1885–2099
Rahu/Ketu (Mean Node) Analytical mean model exact (mean model)

vs Swiss Ephemeris: 0 of 5,000,000 charts over 0.1° for any planet or node (most < 0.02°), worldwide, 1850–2150. Loading the optional de440s.bsp kernel gives JPL-grade sub-arcsecond on the Sun + planets and full-range Pluto. Full report: docs/ACCURACY.md.

Which Swiss flags are / are not supported

Supported (shape + behavior) Not supported — raises ValueError
SEFLG_SWIEPH (always the active backend) SEFLG_HELCTR (heliocentric)
SEFLG_SPEED (daily motion) SEFLG_TOPOCTR (topocentric)
SEFLG_SIDEREAL (with set_sid_mode) SEFLG_J2000 (J2000 frame)
SEFLG_EQUATORIAL (RA/Dec)
SEFLG_BARYCTR (barycentric)
SEFLG_XYZ (cartesian)
SEFLG_RADIANS

All these constants are still defined on xalen.swe (so existing import lines and flags = ... arithmetic work); only their position-altering effect is unimplemented, and passing one to calc_ut raises rather than silently returning the wrong frame.


Editor autocomplete

Type stubs ship with the wheel (xalen.pyi, xalen/swe.pyi), so pyswisseph and native users both get full autocomplete and type checking in editors and mypy.


License

Apache-2.0. See LICENSE and CREDITS.md.

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

xalen-0.6.0.tar.gz (1.0 MB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

xalen-0.6.0-cp38-abi3-macosx_11_0_arm64.whl (1.3 MB view details)

Uploaded CPython 3.8+macOS 11.0+ ARM64

File details

Details for the file xalen-0.6.0.tar.gz.

File metadata

  • Download URL: xalen-0.6.0.tar.gz
  • Upload date:
  • Size: 1.0 MB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.2.0 CPython/3.14.2

File hashes

Hashes for xalen-0.6.0.tar.gz
Algorithm Hash digest
SHA256 a6c833f68681f7cca8aae6a83f945406744acce713a2c7ad2a1f3fb876008081
MD5 6ebed4749ed76b17b71c3972617f08b1
BLAKE2b-256 35f7043e11be907786d50887876f5d2d8d56726cbc795650d31d517a733cdb37

See more details on using hashes here.

File details

Details for the file xalen-0.6.0-cp38-abi3-macosx_11_0_arm64.whl.

File metadata

File hashes

Hashes for xalen-0.6.0-cp38-abi3-macosx_11_0_arm64.whl
Algorithm Hash digest
SHA256 3f90324a44cb53ec8552bf5eb2c225041fef65369d281f7bd8432a01017d7506
MD5 20bce639adfcf12d03b6bc42fc09b7ea
BLAKE2b-256 ef0635a34b350569cc3eb6c29aab966b195e09f68ae7f6a0f9898c733b7874e9

See more details on using hashes here.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page