Dart bindings for Taiyin Ephemeris, the C++ astronomy, ephemeris, traditional-calendar, and astrology core.
This repository is a monorepo containing three separately published packages:
-
ephemeris— astronomy, time, positions, events, eclipses, astrology, the Chinese calendar, and Ganzhi; -
ephemeris_bazi— optional BaZi (八字) extension; -
ephemeris_ziwei— optional Ziwei Doushu (紫微斗数) extension and its default TOML rule profile. -
GitHub repository:
dart-ephemeris -
pub.dev packages:
ephemeris,ephemeris_bazi,ephemeris_ziwei -
Dart imports:
package:ephemeris/ephemeris.dartand the matching extension libraries
dart pub add ephemeris
dart pub add ephemeris_bazi # optional
dart pub add ephemeris_ziwei # optionalThis is a prerelease. Public APIs and native packaging may still change before
the stable 1.0.0 line.
The packages load their bundled Taiyin native modules automatically on macOS arm64, Linux x64, and Windows x64. Ordinary users do not locate DLLs or shared libraries manually. The core package also bundles and automatically loads a 2,057-star lite catalog covering all HIP members of the supported Chinese and western-zodiac line figures. Other Dart Native targets can supply compatible Taiyin modules explicitly. Dart Web and Flutter Web are not currently supported.
import 'package:ephemeris/ephemeris.dart' as eph;
final runtime = eph.Ephemeris.open();
final context = runtime.createContext();
final instantUt1 =
eph.Ut1JulianDate.fromDouble(2460409.25);
final mars = context.position.atUt1(eph.Body.mars, instantUt1);
final state = context.position.stateAtUt1(eph.Body.mars, instantUt1);
print('Mars longitude/latitude/distance: ${mars.value.coordinates}');
print('Mars Cartesian position (AU): ${state.value.positionAu}');
print('Execution flags: ${mars.flags.values}');The position service also accepts TT, TDB, UTC, explicit Delta-T, batch, and
Cartesian-state requests. Successful native calls return Dart records with a
value and flags; fatal statuses throw typed EphemerisError subclasses.
Close contexts explicitly (normally with try/finally) in production code;
the short examples below keep one context alive for readability.
final searchStart =
eph.Ut1JulianDate.fromDouble(2460310.5);
final solar = context.eclipses.nextSolarAtUt1(searchStart);
final lunar = context.eclipses.nextLunarAtUt1(searchStart);
print('Next solar eclipse: ${solar.value.kinds}');
print('Next lunar eclipse: ${lunar.value.kinds}');
final solarMaximum = solar.value.maximum;
if (solarMaximum != null) {
print('Maximum UTC: ${context.time.utcCalendarFromUt1(solarMaximum).value}');
}
print('Execution flags: ${(solar.flags | lunar.flags).values}');The eclipse service also supports contact times, local circumstances, global routes and map products, and observer-specific visibility.
final instantUtc =
DateTime.parse('2003-03-13T14:15:00+08:00').toUtcJulianDate();
final localTime = eph.AstroDateTime(2003, 3, 13, 14, 15); // UTC+08:00
final lunarDate = context.chineseCalendar.fromLocal(localTime);
final pillars = context.chineseCalendar.fourPillarsInstant(instantUtc);
print('Lunar date: ${lunarDate.value}');
print('Four pillars: ${pillars.value}');
print('Day NaYin: ${context.ganzhi.nayinElement(pillars.value.day)}');The Chinese calendar and Ganzhi APIs are part of the core ephemeris package;
they do not require an extension package. The calendar context derives the UTC
instant from its configured fixed UTC offset when given a local wall clock.
A separately selected mean-solar meridian changes only the calendar day to
which new moons and solar terms belong. Alternatively, Dart's built-in DateTime can be converted
once with toUtcJulianDate(); its timezone identifies the physical instant,
while the calendar context still controls the fixed clock and day boundary.
Callers should not subtract the offset a second time.
Sidereal positions, ayanamsha models, house systems, precession, nutation, and lunar points are included in the core package:
context.configuration.setObserverLocation(
const eph.ObserverLocation(
longitudeDegrees: 118.582,
latitudeDegrees: 37.449,
),
);
final sun = context.astrology.siderealPositionAtUt1(
eph.Body.sun,
instantUt1,
ayanamsha: eph.Ayanamsha.lahiri,
);
final houses = context.astrology.housesAtUt1(
instantUt1,
system: eph.HouseSystem.porphyry,
);
print('Sidereal Sun: ${sun.value.siderealLongitudeRadians} rad');
print('Ascendant: ${houses.value.ascendantRadians} rad');
print('House cusps: ${houses.value.cuspLongitudesRadians}');Install and import the optional package:
dart pub add ephemeris_baziimport 'package:ephemeris_bazi/ephemeris_bazi.dart';
final bazi = context.bazi;
final result = bazi.calculateLocal(
eph.AstroDateTime(2003, 3, 13, 14, 15),
gender: BaziGender.male,
);
final fromDay = bazi.calculateSolarDay(
const eph.SolarDate(year: 2003, month: 3, day: 13),
hour: 14,
minute: 15,
gender: BaziGender.male,
);
print('Four pillars: ${result.value.pillars}');
print('Qi-Yun start: ${result.value.qiyun.startCivilTime}');
print('Visible Ten Gods: ${result.value.chart.visibleTenGods}');The cached BaZi context uses the calculation context's default Chinese-calendar
policy. createBazi(calendar: ...) can bind another calendar created by the
same EphemerisContext.
Use BaziClock(mode: BaziClockMode.meanSolar/apparentSolar, ...) when the
chart should use local mean or apparent ("true") solar time. Results keep the
physical instantUtc, original civil clockTime, and effective chartTime
separate.
dart pub add ephemeris_ziweiimport 'package:ephemeris_ziwei/ephemeris_ziwei.dart';
final ziwei = context.ziwei;
final chartResult = ziwei.calculateLocal(
eph.AstroDateTime(2003, 3, 13, 14, 15),
gender: ZiweiGender.male,
);
final chart = chartResult.value;
try {
final life = chart.palace(ZiweiPalace.life);
print('Bureau: ${chart.anchors.bureau}');
print('Life-palace stars: ${life.stars.map((star) => star.key)}');
} finally {
chart.close();
}The extension includes natal charts, independent TOML rule selections,
brightness and transformation overlays, decade-through-hour flow layers,
early/late Rat-hour navigation, and finite birth-time reverse lookup.
calculateSolarDay() and calculateLunarDay() accept a day plus explicit
clock fields; all high-level factories accept ZiweiClock for fixed-offset,
mean-solar, or apparent-solar chart time.
Taiyin supports its OPM2 compressed ephemeris format, NASA/JPL BSP/SPK kernels,
TKC1/Kepler fallback files, and a built-in semi-analytical route. Applications
can register files or directories with Ephemeris.addSourcePath(); the AUTO
router selects a compatible route from the loaded catalog.
For a typical major-body OPM2 product, the compression/reconstruction difference from its source DE441 or DE442 ephemeris is on the order of 0.001 arcsec. This is a state-compression metric, not a blanket guarantee for final apparent, topocentric, eclipse-contact, or historical-calendar results. Those also depend on the selected source, coverage, time scales, observer model, and requested corrections.
The current Dart packages bundle the native runtime, not a full planetary OPM2/SPK archive. Without external data, the runtime can use its built-in semi-analytical fallback over its declared interval. For precision work, load an appropriate OPM2 product or an original NASA/JPL BSP/SPK kernel.
The separately downloadable
full-range DE441 OPM2 data release
covers approximately 13,200 BCE through 17,191 CE. It is intentionally not
bundled in the Dart packages; applications can register its extracted
directory with Ephemeris.addSourcePath().
See the core package's accuracy and data notes for scope, limitations, and route-selection guidance.
Call Ephemeris.open() once in the main isolate to initialize the process-wide
runtime. Worker isolates use Ephemeris.attach() and create their own
EphemerisContext. Do not send native-backed Dart objects between isolates.
The base package owns libtaiyin; BaZi and Ziwei ship their own matching
libtaiyin_bazi and libtaiyin_ziwei modules. Environment-variable and
explicit-path overrides remain available for development and unsupported
platforms.
- Documentation index
- Getting started
- Time scales and Chinese-calendar policies
- Ephemeris data and route selection
- Custom targets, houses, ayanamsha, and calculation models
- Contexts, isolates, and ownership
- Positions, events, eclipses, and astrology
- Accuracy and ephemeris data
- Custom callback lifecycle
- BaZi guide
- Ziwei Doushu guide
The public /// comments generate the package API reference on pub.dev.
dart pub get
dart analyze packages
dart test packages/ephemeris/test
dart test packages/ephemeris_bazi/test
dart test packages/ephemeris_ziwei/testPull requests run formatting, analysis, and dartdoc generation. The complete
macOS/Linux/Windows native integration matrix runs only through a manual
workflow_dispatch or a v* release tag.
Optional stress matrices stay out of the ordinary test run:
TAIYIN_BAZI_STRESS_CASES=10000 \
dart test packages/ephemeris_bazi/test/bazi_stress_test.dart
TAIYIN_ZIWEI_STRESS_CASES=10000 \
dart test packages/ephemeris_ziwei/test/ziwei_oracle_test.dartDart Web and Flutter Web are not currently supported because these bindings
use dart:ffi and native shared libraries.