Hi, thanks for shipping this library — the Wasm-first packaging fits Cloudflare Workers perfectly so we adopted it across our sidereal-astrology site. We're now in a parity audit against VedicRishi (json.astrologyapi.com) and a second open-source Node.js Swiss-Ephemeris binding, and we'd love help interpreting the results.
What we ran
@fusionstrings/panchangam v0.2.1 (same numbers on v0.2.0)
- 200 random cases, dates 1925–2025, 30 cities (lat −40° to +60°, lon −150° to +150°), 5 ayanamsa modes
- Same case set sent to:
@fusionstrings/panchangam via p_julday(year, month, day, utc_hour, 1) → calculate_planets(jd, mode)
- VedicRishi
/v1/planets/ — industry baseline used by most KP/Vedic apps
- A second Node.js Swiss-Ephemeris stack —
mivion/swisseph (canonical Node binding to the upstream Swiss Ephemeris C library, v0.5.17) wrapped via rish-0-0/astroreha v1.1.5. Different binding to the same Swiss Ephemeris algorithm.
Results — Lahiri ayanamsa (mode 1), 200 cases, p95 |Δ| in arcsec
| Planet |
panchangam vs VR |
mivion/swisseph vs VR |
panchangam vs mivion/swisseph |
| Sun |
18.5″ |
1.9″ |
19.0″ |
| Moon |
41.5″ |
28.9″ |
33.1″ |
| Mars |
18.1″ |
3.2″ |
19.8″ |
| Mercury |
18.8″ |
6.4″ |
22.9″ |
| Jupiter |
17.8″ |
0.9″ |
18.0″ |
| Venus |
18.5″ |
4.2″ |
19.7″ |
| Saturn |
18.1″ |
0.5″ |
17.9″ |
| Rahu |
17.9″ |
0.1″ |
17.8″ |
| Ketu |
17.9″ |
0.1″ |
17.8″ |
Means hover near zero for both implementations — so this isn't a systematic ayanamsa offset, it's spread. Pattern is identical for Raman (mode 3) and Krishnamurti (mode 5). True Citra (mode 27) shows a separate +55″ systematic offset which is a different issue (likely different True-Citra formula).
The pattern that surprises us: the other Swiss Ephemeris implementation reproduces VedicRishi to 0.1″ on Rahu/Ketu (mean node) and 0.5–1″ on Saturn/Jupiter, while @fusionstrings/panchangam shows ~18″ p95 on every planet. Whatever is causing the spread looks specific to your library, not Swiss Ephemeris in general.
Minimal reproducer
import { p_julday, calculate_planets } from '@fusionstrings/panchangam';
// 2026-05-11 00:00:00 IST → UT 2026-05-10 18:30
const jd = p_julday(2026, 5, 10, 18.5, 1);
const sun = calculate_planets(jd, 1).find(p => p.name === 'Sun');
console.log(sun.longitude);
// panchangam → 25.97303° (Aries 25°58'23")
// VedicRishi → 25.97139° (Aries 25°58'17") Δ = +5.9″
// `mivion/swisseph` stack → 25.97142° Δ = +0.1″ from VR
Our current workaround (Moon)
While debugging, I added a manual correction in our wrapper: after calling calculate_planets, I shift the Moon longitude by +(ΔT_seconds × 0.549″/sec) / 3600° using a local IERS ΔT table (1900–2100). With this patch:
- Without it: Moon mean Δ vs VR ≈ −11″, p95 ≈ 39″, max ≈ 60″
- With it: Moon mean Δ vs VR ≈ +15″, p95 ≈ 41″, max ≈ 51″
So the correction moves us in the right direction (closes a ~26″ gap) but overshoots by ~14″ — implying the real internal ΔT use inside calculate_planets is between "treats jd as TT" and "treats jd as UT", or the correction needs a different scaling. The mivion/swisseph stack matches VR without any wrapper-level patch — its swe_calc_ut applies ΔT internally and the output is correct out of the box.
For Sun / Mercury / Venus / Mars / Jupiter / Saturn / Rahu we apply no manual correction — the values come straight from calculate_planets. That's the column shown as "panchangam vs VR" in the table above (still ~18″ p95).
Our approach so far
- Time conversion: parse local birth time + timezone offset → UTC decimal hour →
p_julday(year, month, day, utc_hour, 1). Handles day wraparound when UTC crosses midnight.
- Planet positions:
calculate_planets(jd, mode) straight out, no transformation, used as-is for Sun/Mars/Mercury/Venus/Jupiter/Saturn/Rahu.
- Moon: manual arc-second correction (described above) before consuming the longitude.
- Ketu: derived as
Rahu + 180° (mod 360).
- KP custom variants (
kp_new, kp_straight_line, kp_kullar): compute the panchangam Lahiri sidereal output, then add a custom adjustment (Lahiri_ayanamsa − our_KP_ayanamsa). Works for non-Rahu planets; for Rahu it produces a mean-node value (matches the mivion/swisseph stack) while VedicRishi returns true-node for KP_NEW — that's a known third-party divergence we accept.
- Outer planets (Uranus / Neptune / Pluto): use
calc_ut(jd, SE_URANUS, …) directly, subtracting the ayanamsa value. This path works correctly — agrees with VR to within ~1″ on those.
What we'd love help with
We need our charts to reproduce VedicRishi-style output to within ≤1–2″ across these five ayanamsas. We're stuck because:
- We can't tell if we're calling
calculate_planets wrong (TT vs UT semantics on the JD argument), or if the spread is inherent to your wasm build.
- The Moon workaround works directionally but overshoots — we'd like the underlying mechanism documented so we can apply the right correction (or have the library apply it).
- Outer planets via
calc_ut agree with VR to ~1″, but calculate_planets for the main 7 shows ~18″ — strongly suggests calculate_planets uses a different internal path.
Specific questions:
- What does
calculate_planets(jd, mode) treat jd as — TT or UT? (i.e., does it call swe_calc or swe_calc_ut internally?)
- Which Swiss Ephemeris data set is the wasm compiled against — DE441 / Moshier / built-in? This affects last-arcsecond agreement.
- Is the ~18″ spread expected, or does it indicate a config issue on our side?
- Could you expose a
calc vs calc_ut distinction so downstream apps can pick the convention that matches their reference software?
- For mode 27 (TrueCitra) — which Spica/Citra formula is implemented?
I can share the full CSV (200 cases × 5 ayanamsas × all planets, signed Δ vs each reference) if it helps reproduce. Thanks!
Hi, thanks for shipping this library — the Wasm-first packaging fits Cloudflare Workers perfectly so we adopted it across our sidereal-astrology site. We're now in a parity audit against VedicRishi (
json.astrologyapi.com) and a second open-source Node.js Swiss-Ephemeris binding, and we'd love help interpreting the results.What we ran
@fusionstrings/panchangamv0.2.1 (same numbers on v0.2.0)@fusionstrings/panchangamviap_julday(year, month, day, utc_hour, 1)→calculate_planets(jd, mode)/v1/planets/— industry baseline used by most KP/Vedic appsmivion/swisseph(canonical Node binding to the upstream Swiss Ephemeris C library, v0.5.17) wrapped viarish-0-0/astrorehav1.1.5. Different binding to the same Swiss Ephemeris algorithm.Results — Lahiri ayanamsa (mode 1), 200 cases, p95 |Δ| in arcsec
Means hover near zero for both implementations — so this isn't a systematic ayanamsa offset, it's spread. Pattern is identical for Raman (mode 3) and Krishnamurti (mode 5). True Citra (mode 27) shows a separate +55″ systematic offset which is a different issue (likely different True-Citra formula).
The pattern that surprises us: the other Swiss Ephemeris implementation reproduces VedicRishi to 0.1″ on Rahu/Ketu (mean node) and 0.5–1″ on Saturn/Jupiter, while
@fusionstrings/panchangamshows ~18″ p95 on every planet. Whatever is causing the spread looks specific to your library, not Swiss Ephemeris in general.Minimal reproducer
Our current workaround (Moon)
While debugging, I added a manual correction in our wrapper: after calling
calculate_planets, I shift the Moon longitude by+(ΔT_seconds × 0.549″/sec) / 3600°using a local IERS ΔT table (1900–2100). With this patch:So the correction moves us in the right direction (closes a ~26″ gap) but overshoots by ~14″ — implying the real internal ΔT use inside
calculate_planetsis between "treats jd as TT" and "treats jd as UT", or the correction needs a different scaling. Themivion/swissephstack matches VR without any wrapper-level patch — itsswe_calc_utapplies ΔT internally and the output is correct out of the box.For Sun / Mercury / Venus / Mars / Jupiter / Saturn / Rahu we apply no manual correction — the values come straight from
calculate_planets. That's the column shown as "panchangam vs VR" in the table above (still ~18″ p95).Our approach so far
p_julday(year, month, day, utc_hour, 1). Handles day wraparound when UTC crosses midnight.calculate_planets(jd, mode)straight out, no transformation, used as-is for Sun/Mars/Mercury/Venus/Jupiter/Saturn/Rahu.Rahu + 180°(mod 360).kp_new,kp_straight_line,kp_kullar): compute the panchangam Lahiri sidereal output, then add a custom adjustment (Lahiri_ayanamsa − our_KP_ayanamsa). Works for non-Rahu planets; for Rahu it produces a mean-node value (matches themivion/swissephstack) while VedicRishi returns true-node for KP_NEW — that's a known third-party divergence we accept.calc_ut(jd, SE_URANUS, …)directly, subtracting the ayanamsa value. This path works correctly — agrees with VR to within ~1″ on those.What we'd love help with
We need our charts to reproduce VedicRishi-style output to within ≤1–2″ across these five ayanamsas. We're stuck because:
calculate_planetswrong (TT vs UT semantics on the JD argument), or if the spread is inherent to your wasm build.calc_utagree with VR to ~1″, butcalculate_planetsfor the main 7 shows ~18″ — strongly suggestscalculate_planetsuses a different internal path.Specific questions:
calculate_planets(jd, mode)treatjdas — TT or UT? (i.e., does it callswe_calcorswe_calc_utinternally?)calcvscalc_utdistinction so downstream apps can pick the convention that matches their reference software?I can share the full CSV (200 cases × 5 ayanamsas × all planets, signed Δ vs each reference) if it helps reproduce. Thanks!