French Ephemeris library
FrenchEphemeris is the open source ephemeris engine behind FreeAstroAPI: a Rust library with Python bindings that computes planetary positions from NASA/JPL DE441 and DE442 data, plus houses, ayanamsas, nodes, fixed stars, rise and set times, eclipses and heliacal events. Use it to run calculations inside your own application; use the Ephemeris API when you would rather call HTTP endpoints.
Install
Prebuilt wheels are published for Linux (x86_64, aarch64), macOS (Intel, Apple Silicon) and Windows (x64). Each wheel includes a compact data set for years 0 to 2400, so it works at once. JPL's exact numbers come as FEPK data packs: download them once with the bundled command. The npm package is the same engine compiled to WebAssembly, with the mobile data set for years 1800 to 2400 included.
pip install frenchephemeris # works at once: years 0 to 2400 are built in
# optional: JPL's exact numbers (SHA-256 checked)
python -m frenchephemeris.packs download # 1800 to 2401, ~69 MB
python -m frenchephemeris.packs download --set full # −2 to 2581, ~256 MB
python -m frenchephemeris.packs list # every pack and set
# JavaScript (Node.js, Deno, Bun, browsers): WebAssembly, data for 1800 to 2400 included
npm install frenchephemeris
python -m frenchephemeris.packs path # where packs are storedData packs
Packs hold JPL coefficients stored in the FEPK format and published on the packs-v1 release. Choose a set with --set; files already present are verified and skipped. The modern, full, dev and extended sets keep JPL's numbers exactly as published (bit identical); the built in data, compact and mobile are lossy.
| Set | Size | Years | Contents |
|---|---|---|---|
| built in | 7.7 MB | 0 to 2400 | Inside the package, no download: lossy (planets ≤ 0.007″, Moon ≤ 0.4″ from JPL); used for any date the exact packs do not cover |
| modern | ~69 MB | 1800 to 2401 | The default download: JPL's exact numbers for modern charts, and the minor bodies |
| full | ~256 MB | −2 to 2581 | DE441/DE442 planets, Sun and Moon; Chiron, Pholus, Ceres, Pallas, Juno, Vesta, Eris, Eros; 2401 to 2581 refit |
| dev | ~27 MB | 1900 to 2100 | DE442 development pack and the minor bodies |
| mobile | ~2 MB | 1800 to 2400 | Lossy, for apps: planets, Moon and minor bodies (planets ≤ 0.007″, Moon ≤ 0.4″ from JPL), smaller than the Swiss Ephemeris files for the same years |
| compact | ~13 MB | 1800 to 2400 | Lossy refit for small installs (planets ≤ 0.001″, Moon ≤ 0.04″ from JPL) and the minor bodies |
| extended | ~305 MB | −13198 to 17190 | DE441 refits before year −2 and after 2401 |
| asteroids | ~108 MB | varies | 326 asteroids commonly used in astrology |
| all | ~770 MB | −13198 to 17190 | Every pack above |
Environment variables: FRENCHEPHEMERIS_DATA sets the pack directory, FRENCHEPHEMERIS_PACK names the main pack file directly.
Quick start with swe_compat
frenchephemeris.swe_compat follows pyswisseph's function names, flags and return values, so most astrology code runs unchanged after the import.
import frenchephemeris.swe_compat as swe
jd = swe.julday(1990, 5, 17, 14.5) # UT
# planets: ((lon, lat, dist, lon_speed, lat_speed, dist_speed), flags)
(lon, lat, dist, speed, *_), _ = swe.calc_ut(jd, swe.MOON, swe.FLG_SPEED)
# houses: (cusps[12], ascmc[8]); ascmc[0] = Ascendant, ascmc[1] = MC
cusps, ascmc = swe.houses(jd, 48.8566, 2.3522, b"P")
# sidereal positions
swe.set_sid_mode(swe.SIDM_LAHIRI)
(sid_lon, *_), _ = swe.calc_ut(jd, swe.SUN, swe.FLG_SIDEREAL | swe.FLG_SPEED)
ayanamsa = swe.get_ayanamsa_ut(jd)
# topocentric Moon
swe.set_topo(2.3522, 48.8566, 35)
(topo_lon, *_), _ = swe.calc_ut(jd, swe.MOON, swe.FLG_TOPOCTR)Native engine and NumPy batches
frenchephemeris.open_default() returns an immutable engine. calc_batch_ut computes many bodies at many times in one call, returns a NumPy array and runs without the GIL, at about 4.5 million positions per second on an Apple M4.
import numpy as np
import frenchephemeris as fe
eng = fe.open_default() # main pack + every optional pack found
times = 2448029.1 + np.arange(10_000.0) # JD UT
bodies = list(range(10)) # Sun .. Pluto
pos = eng.calc_batch_ut(times, bodies) # FLG_SPEED by default
pos.shape # (10000, 10, 6): lon, lat, dist and their speeds
# sidereal and topocentric batches
sid = eng.calc_batch_ut(times, bodies, sidereal="lahiri", topo=(2.3522, 48.8566, 35.0))Threads and async code
The engine has no global mutable state. In swe_compat, the sidereal mode, topocentric position and Delta T settings are stored per thread and per asyncio task, so concurrent requests in a web server never see each other's settings. Throughput stays the same from 1 to 8 threads.
from concurrent.futures import ThreadPoolExecutor
import frenchephemeris.swe_compat as swe
def chart(jd, mode):
swe.set_sid_mode(mode) # applies to this thread only
return swe.calc_ut(jd, swe.SUN, swe.FLG_SIDEREAL)[0][0]
with ThreadPoolExecutor(8) as pool:
lahiri = pool.submit(chart, 2451545.0, swe.SIDM_LAHIRI)
raman = pool.submit(chart, 2451545.0, swe.SIDM_RAMAN)
print(lahiri.result(), raman.result()) # each thread keeps its own modeMigrating from pyswisseph
| Import | Replace import swisseph as swe with import frenchephemeris.swe_compat as swe. Function names, constants and return shapes follow pyswisseph. |
|---|---|
| Ephemeris path | set_ephe_path() is accepted and ignored. Packs are found in $FRENCHEPHEMERIS_DATA, the data directory of the current user, or a path in $FRENCHEPHEMERIS_PACK. |
| Settings scope | set_sid_mode, set_topo and Delta T settings apply to the current thread or asyncio task, not the whole process. Set them where you compute. |
| Out of range | Years 0 to 2400 always work: the built in data answers any date the exact packs do not cover. Outside that range, and outside any extended packs you add, calls raise swe_compat.Error instead of falling back to another model. |
| Ayanamsas | 46 modes are available, plus user defined values. J.N. Bhasin and Vedic/Sheoran follow their authors' own publications, so they can differ slightly from Swiss Ephemeris; Vettius Valens Moon raises an error until its source is published. |
| Data source | Positions come from JPL DE441 and DE442, so they can differ from Swiss Ephemeris by fractions of an arcsecond; signs and retrograde status do not change in practice. |
Using it from Rust
The core crate fe-core lives in the same repository. Add it as a Git dependency and point it at a downloaded pack.
# Cargo.toml
[dependencies]
fe-core = { git = "https://github.com/FreeAstroApi/frenchephemeris", tag = "v1.5.0" }
# charts away from Earth: add features = ["space"]use fe_core::context::{Body, CalcOptions, Engine};
fn main() -> fe_core::Result<()> {
let eng = Engine::open("packs/fepk-de441-de442-v1.bin".as_ref())?;
let jd_ut = 2448029.104166667; // 1990-05-17 14:30 UT
let moon = eng.calc_ut(Body::Moon, jd_ut, &CalcOptions::default())?;
println!("Moon {:.6}°, {:.4}°/day", moon.lon, moon.lon_speed);
Ok(())
}Fixed stars
The catalogue holds 8,969 objects: every star visible to the naked eye (Hipparcos V ≤ 6.5), plus named clusters, galaxies and reference points such as the Galactic Center. Positions and motions come from the Hipparcos New Reduction (van Leeuwen 2007), names from the IAU Working Group on Star Names where they exist, and every star can also be found by its Bayer or Flamsteed designation, HR, HD or HIP number.
import frenchephemeris.swe_compat as swe
jd = swe.julday(1990, 5, 17, 14.5)
(lon, lat, dist, *_), name, _ = swe.fixstar2_ut("Spica", jd, 0) # by name
swe.fixstar2_ut("alf Vir", jd, 0) # Bayer designation
swe.fixstar2_ut("HR 5056", jd, 0) # HR, HD or HIP number
mag, _ = swe.fixstar2_mag("Spica") # V magnitudeCharts away from Earth
The optional frenchephemeris.ext.space extension, new in 1.4.0, casts a chart as if someone were born on Mars, on the Moon, on any planet out to Pluto, at the centre of a world or anywhere in space. In Rust, enable the space feature of fe-core.
import frenchephemeris
from frenchephemeris.ext import space
eng = frenchephemeris.open_default()
jezero = space.Site("mars", lon=77.45, lat=18.44) # east longitude, latitude on Mars
chart = space.chart(eng, 2460000.5, jezero, house_system="P") # JD UT, Earth's zodiac
chart["bodies"]["earth"]["lon"] # Earth is a planet in the chart
chart["houses"]["asc"], chart["houses"]["cusps"]
space.chart(eng, 2460000.5, jezero, zodiac="native") # Mars's own zodiac
space.horizon(eng, 2460000.5, "sun", jezero) # azimuth, elevation
space.position(eng, 2460000.5, "earth", space.Point("sun", z=1e9)) # anywhere in space
space.mars_time(2460000.5, lon=77.45) # sol date, Mars time, local solar time- Positions with speeds and retrograde status, azimuth and elevation, and houses with Ascendant and MC on the world's own rotation. Earth appears in the chart; the observer's own world is left out.
- Two zodiacs: Earth's tropical zodiac (default), or the world's native zodiac, its orbital plane with 0° at its own spring equinox.
- Venus, Uranus and Pluto spin backwards; the Ascendant is still the point that rises there. Placidus and Koch are undefined inside a polar circle, as on Earth, which on Uranus is most of the planet: use Whole Sign, Porphyry or Regiomontanus.
- Rotation models from the IAU (2015 report); positions and horizons agree with NASA JPL Horizons within 0.4″ on every world.
Method and validation: space extension report on GitHub.
Accuracy and validation
- Physical positions match JPL Horizons to 0.0005″ or better for years 0 to 1550, and within 0.13″ (inner planets, Sun, Moon) and 1.76″ (Uranus to Pluto) after 1550.
- On charts from 1800 to 2100, agreement with Swiss Ephemeris 2.10 is within 0.07″ for the Sun, 0.83″ for the Moon and 0.04″ for the nodes, with no sign or retrograde changes in 8,400 samples.
- Eclipse search against the NASA Five Millennium Canons, years 0 to 2400: no missed or spurious eclipses and no type mismatches.
Method, sample sizes and the commands to reproduce each figure: engine report on GitHub.
Licence
FrenchEphemeris is free under the GNU AGPL-3.0. For closed source products, the commercial licence costs USD 200 once per company and covers every future version and unlimited end users. See the licence terms, buy the licence online, or write to [email protected].
FAQ
Do I need to change my pyswisseph code to use FrenchEphemeris?
Usually only the import. frenchephemeris.swe_compat exposes calc_ut, calc, houses, houses_ex, set_sid_mode, get_ayanamsa_ut, fixstar2, rise_trans, eclipse and occultation functions with pyswisseph style arguments and return values. Settings are per thread, so set the sidereal mode in the thread that computes.
Where are the FrenchEphemeris data packs stored?
In $FRENCHEPHEMERIS_DATA if it is set, otherwise in the data directory of the current user (~/.local/share/frenchephemeris/packs on Linux, ~/Library/Application Support/frenchephemeris/packs on macOS, %LOCALAPPDATA%\frenchephemeris\packs on Windows). Run python -m frenchephemeris.packs path to print it.
How do I use FrenchEphemeris in Docker or on a server?
Install the wheel; it works at once for years 0 to 2400. For JPL's exact numbers, run python -m frenchephemeris.packs download --dest /app/packs during the image build, then set FRENCHEPHEMERIS_DATA=/app/packs at runtime. The packs are read with memory mapping, so several worker processes share the same pages.
Which date range does FrenchEphemeris cover?
Years 0 to 2400 out of the box. The full set covers years −2 to 2581 with JPL's exact numbers, and the extended packs add −13198 to 17190. Outside the loaded data every call raises an error rather than returning an approximation.
Can FrenchEphemeris cast a birth chart on Mars or another planet?
Yes, since version 1.4.0, with the optional frenchephemeris.ext.space extension: a site on Mercury, Venus, the Moon, Mars, Jupiter, Saturn, Uranus, Neptune or Pluto, the centre of a world, or any point in space. It returns positions, horizon coordinates and houses on that world's rotation, in Earth's tropical zodiac or the world's own zodiac, and converts Mars Sol Dates and local Mars time.
Can I use FrenchEphemeris in a closed source product?
Yes, with the commercial licence: USD 200 once per company, covering all versions and unlimited end users. Without it, the AGPL-3.0 applies, which requires publishing the source of modified versions you distribute or offer over a network.
Related
- French Ephemeris overview: features, comparison and licensing.
- Ephemeris API: the same engine over HTTP.
- Ephemeris table: browse planetary positions online.