# stem-branch (Rust)
[](https://crates.io/crates/stem-branch)
[](https://docs.rs/stem-branch)
[](https://github.com/h4x0r/stem-branch/blob/main/rust/LICENSE)
[](https://github.com/h4x0r/stem-branch/actions/workflows/rust.yml)
Native Rust port of [stem-branch](https://github.com/h4x0r/stem-branch)'s
astronomical core. Computes the Sun's geocentric ecliptic state (full VSOP87D
Earth series + JPL DE441-fitted correction + IAU2000B nutation), the Moon's
apparent position (full ELP/MPP02 theory), and the complete Chinese lunisolar
calendar (new moons, solar terms, and 閏月 leap months) — no runtime
dependencies, no JavaScript.
```rust
use stem_branch::solar_ecliptic_state;
// Julian Ephemeris Day in Terrestrial Time (J2000.0).
let s = solar_ecliptic_state(2451545.0);
assert!((s.apparent_longitude_degrees - 280.368152).abs() < 1.0 / 3600.0);
// s.true_longitude_degrees — mean equinox of date
// s.apparent_longitude_degrees — + IAU2000B nutation + aberration
// s.radius_au — Sun–Earth distance
```
The Chinese lunisolar calendar — Lunar New Year, leap months (閏月), conversion:
```rust
use stem_branch::{gregorian_to_lunisolar, lunar_new_year, CivilDate};
// 2023-03-22 is 閏二月初一 — day 1 of the leap 2nd month of 2023.
let d = gregorian_to_lunisolar(CivilDate { year: 2023, month: 3, day: 22 });
assert_eq!((d.year, d.month, d.day, d.is_leap_month), (2023, 2, 1, true));
// Lunar New Year (正月初一) 2024 falls on 2024-02-10 (Beijing).
let lny = lunar_new_year(2024);
assert_eq!((lny.year, lny.month, lny.day), (2024, 2, 10));
```
The Moon's apparent geocentric position (ELP/MPP02):
```rust
use stem_branch::moon_position;
// Julian Ephemeris Day (TT). At a new moon the Moon's longitude meets the Sun's.
let m = moon_position(2451545.0);
// m.longitude_degrees / m.latitude_degrees — apparent ecliptic, of date
// m.distance_km — geocentric distance
// m.ra_degrees / m.dec_degrees — apparent equatorial
assert!((356_000.0..=407_000.0).contains(&m.distance_km));
```
Input is a Julian Ephemeris Day in **Terrestrial Time**; the UTC↔TT / ΔT
boundary is the caller's responsibility.
## Validation
The implementation is validated against **JPL Horizons (DE441 ephemeris)** —
an independent third-party oracle, the same reference the upstream TypeScript
[`stem-branch`](https://h4x0r.github.io/stem-branch/accuracy) is validated
against. The test asserts
`solar_ecliptic_state().apparent_longitude_degrees` against JPL's apparent
geocentric ecliptic longitude of the Sun. The expected values are authored by
JPL, not by us, so the test is an independent check rather than a circular one.
**Result — apparent ecliptic longitude vs JPL DE441 (1900–2100 CE):**
| 1900-01-01 00:00 | 2415020.5 | 280.1533022 | 280.1533171 | 0.054″ |
| 1940-06-15 06:00 | 2429795.75 | 83.9726193 | 83.9726417 | 0.081″ |
| 1980-09-23 00:00 | 2444505.5 | 180.1158230 | 180.1158349 | 0.043″ |
| 2000-01-01 12:00 (J2000) | 2451545.0 | 280.3681559 | 280.3681519 | 0.014″ |
| 2024-03-20 03:00 | 2460389.625 | 359.9947954 | 359.9947742 | 0.076″ |
| 2060-12-21 00:00 | 2473814.5 | 269.8705294 | 269.8705076 | 0.079″ |
| 2100-07-04 12:00 | 2488254.0 | 102.6537711 | 102.6537510 | 0.073″ |
**Maximum residual: 0.081″** — identical to the upstream TypeScript engine
(the Rust port reproduces the same VSOP87D + DE441 model bit-for-bit). For
reference, the upstream engine's headline figure is mean **1.05 s** solar-term
timing vs JPL DE441 over 209–2493 CE; see the
[full accuracy report](https://h4x0r.github.io/stem-branch/accuracy).
Reference-value provenance (source, identity, license) is documented in
[`tests/data/README.md`](tests/data/README.md). JPL Horizons output is a U.S.
Government work in the public domain.
### Reproduce
```sh
cargo test # runs the JPL DE441 oracle tests
cargo clippy --all-targets -- -D warnings
cargo fmt --check
```
## How it's built
The 2,077 VSOP87D Earth terms and 77 IAU2000B nutation rows are **generated**
from the canonical TypeScript source by
[`scripts/gen-rust-solar-data.mjs`](../scripts/gen-rust-solar-data.mjs), so the
Rust crate and the TypeScript engine share a single source of truth and cannot
silently drift. Only the math (≈130 lines in `src/lib.rs`) is hand-written; the
coefficient tables are mechanical translations.
The port matches the TypeScript implementation faithfully, including JavaScript's
truncated-remainder (`%`) semantics for argument reduction and the literal
`206264.806` arcsec-per-radian factor used by the DE441 correction polynomial.
## Scope
Covers the solar ephemeris (`solar_ecliptic_state`), the Moon ephemeris
(`moon_position`, ELP/MPP02), the full Chinese lunisolar calendar
(`gregorian_to_lunisolar`, `lunar_months_for_year`, `lunar_new_year`, plus
`new_moon_jde` and `find_solar_term_moment`), and the Traditional-Chinese name
labels (`SOLAR_TERM_NAMES` + `solar_term_for_longitude`, `HEAVENLY_STEMS`,
`EARTHLY_BRANCHES`) so consumers need not hard-code them. The planets share the
same VSOP series machinery upstream and can be ported the same way when needed.