A high-precision Western astrology computation library in pure Swift, covering 1800–2100.
Tiered local-validation accuracy, zero runtime dependencies, thread-safe.
This README describes the v3 package split:
AstroCorefor low-level astronomy,AstroAstrologyfor astrology models, andAstroCoreLocationsfor optional city lookup data.
简体中文 · Report Issue · Changelog · Releases
Table of Contents
- ✨ Features
- 📦 Installation
- 🚀 Usage
- 🎯 Precision
- ⚡ Performance
- 🧪 Testing
- 🗂️ API Reference
- 📋 Supported Range
- 🔬 Algorithms
- 📝 License
Important
Star Us — you will receive all release notifications from GitHub without any delay ~ ⭐️
| Feature | Description | |
|---|---|---|
| ♈ | Ascendant (ASC) | Sidereal time + nutation + true obliquity, global coordinates |
| 🏠 | House Systems | 16 twelve-house systems plus an independent 36-sector Gauquelin model, with angles and polar-latitude handling |
| ☀️ | Sun Sign | VSOP87D + FK5 correction + aberration + nutation |
| 🌙 | Moon Sign | ELP-2000/82 (120 terms) + residual correction + nutation |
| 🪐 | Planet Signs | Mercury through Saturn — light-time + FK5 + gravitational deflection + residual correction |
| 🔭 | Outer Bodies | Uranus, Neptune, Pluto, lunar nodes, and Black Moon Lilith (tiered precision) |
| 🔗 | Aspects & Patterns | Single-pair, full grids, applying/separating, exact moments, cross-chart synastry, and pattern detection |
| 🌅 | Rise · Set · Transit | Civil-day rise/set events, twilight overlays, and meridian transits |
| ↩️ | Stations & Retrograde | Direct/retrograde stations and retrograde intervals |
| 🏃 | Motion States | Per-body longitude speed and retrograde flags |
| 📊 | Batch Natal Chart | Compute planets, ASC, houses, and angles in one call |
| 🌐 | City Database | 33,000+ global cities with coordinates & timezones (optional module) |
| 🧵 | Thread-Safe | Full Sendable conformance |
| 🚫 | Zero Dependencies | No third-party packages — pure Swift on Apple's Foundation + Accelerate |
| ✅ | Tiered Precision | Primary real bodies are locally validated; definition points are documented separately |
Add to your Package.swift:
dependencies: [
.package(name: "AstroCore", url: "https://github.com/wbx1-Ltd/AstroCore-Swift.git", from: "3.0.0"),
]Then add as a target dependency:
.target(
name: "YourTarget",
dependencies: [
.product(name: "AstroCore", package: "AstroCore"), // core astronomy
.product(name: "AstroAstrology", package: "AstroCore"), // signs, houses, charts
.product(name: "AstroCoreLocations", package: "AstroCore"), // optional city lookup
]
),If your app already has city/coordinate data, skip the optional locations product:
.target(
name: "YourTarget",
dependencies: [
.product(name: "AstroCore", package: "AstroCore"),
.product(name: "AstroAstrology", package: "AstroCore"),
]
),Or in Xcode: File → Add Package Dependencies… → paste the URL above.
AstroCore provides the low-level astronomy primitives. AstroAstrology
builds signs, houses, Gauquelin sectors, natal charts, and aspects on top of
those primitives. Neither requires city data when you already have coordinates
(GeoCoordinate) and a timezone (timeZoneIdentifier).
If a local wall-clock time falls inside a DST fall-back repeat hour, pass
repeatedTimeResolution: .firstOccurrence or .lastOccurrence to select the
exact instant explicitly.
import AstroCore
import AstroAstrologylet moment = try CivilMoment(
year: 2000, month: 6, day: 21, hour: 12, minute: 0,
timeZoneIdentifier: "UTC"
)
let sun = AstroCalculator.sunPosition(for: moment)
let sign = ZodiacSign(longitude: sun.longitude)
print(sign.name) // "Cancer"
print(sign.emoji) // "♋"
print(sun.longitude) // 90.406° (summer solstice)
print(sun.longitude - sign.startLongitude) // 0.406° into the signlet moment = try CivilMoment(
year: 2000, month: 1, day: 1, hour: 12, minute: 0,
timeZoneIdentifier: "UTC"
)
let moon = AstroCalculator.moonPosition(for: moment)
let sign = ZodiacSign(longitude: moon.longitude)
print(sign.name) // "Scorpio"
print(sign.emoji) // "♏"
print(moon.latitude) // 5.17° (ecliptic latitude)let moment = try CivilMoment(
year: 2000, month: 1, day: 1, hour: 12, minute: 0,
timeZoneIdentifier: "UTC"
)
// Single planet
let venus = AstroCalculator.planetPosition(.venus, for: moment)
let venusSign = ZodiacSign(longitude: venus.longitude)
print("\(venusSign.emoji) Venus in \(venusSign.name)") // "♐ Venus in Sagittarius"
// Bodies: .sun, .moon, .mercury, .venus, .mars, .jupiter, .saturn,
// .uranus, .neptune, .pluto, lunar nodes, and Lilith variantslet moment = try CivilMoment(
year: 1990, month: 8, day: 15, hour: 14, minute: 30,
timeZoneIdentifier: "America/New_York"
)
let coord = try GeoCoordinate(latitude: 40.7128, longitude: -74.0060)
let asc = try AstrologyCalculator.ascendant(for: moment, coordinate: coord)
print(asc.sign.name) // "Sagittarius"
print(asc.eclipticLongitude) // 240.93°
print(asc.degreeInSign) // 0.93°let houses = try AstrologyCalculator.houses(
for: moment,
coordinate: coord,
system: .placidus,
polarFallback: .porphyry
)
print(houses.requestedSystem.displayName) // "Placidus"
print(houses.resolvedSystem.displayName) // "Placidus"
print(houses.cusps[0].sign.name) // House 1 cusp sign
print(houses.angles.ascendant) // ASC longitude
print(houses.angles.midheaven) // MC longitudeSupported systems:
.equalASC, .equalMC, .wholeSign, .vehlow, .porphyry, .sripati,
.placidus, .koch, .alcabitius, .campanus, .regiomontanus,
.morinus, .topocentric, .horizontal, .meridian, .carter
HouseSystem.meridian is the published API for Meridian / Axial Rotation /
Zariel houses.
Polar fallback strategies:
.porphyry (default), .equalASC, .wholeSign, .error
Independent Gauquelin sectors (36-sector model):
let sectors = try AstrologyCalculator.gauquelinSectors(
for: moment,
coordinate: coord
)
print(sectors.sectors[0].number) // 1
print(sectors.sectors[0].eclipticLongitude) // sector 1 = ASC
print(sectors.sectors[9].eclipticLongitude) // sector 10 = MC
print(sectors.sectors[18].eclipticLongitude) // sector 19 = DSC
print(sectors.sectors[27].eclipticLongitude) // sector 28 = ICNot part of the v3 public API yet:
Krusinski-Pisa-Goelzer, APC, Sunshine (Treindl),
Sunshine (Makransky), Pullen SD, Pullen SR
let moment = try CivilMoment(
year: 1990, month: 8, day: 15, hour: 14, minute: 30,
timeZoneIdentifier: "America/New_York"
)
let coord = try GeoCoordinate(latitude: 40.7128, longitude: -74.0060)
let natal = try AstrologyCalculator.natalPositions(
for: moment,
coordinate: coord,
bodies: [.sun, .moon, .mercury, .venus, .mars, .jupiter, .saturn],
includeAscendant: true
)
// Ascendant
print("ASC: \(natal.ascendant!.sign.emoji) \(natal.ascendant!.sign.name)")
// All body positions
for (body, pos) in natal.bodies {
let sign = ZodiacSign(longitude: pos.longitude)
print("\(sign.emoji) \(body) in \(sign.name) \(pos.longitude - sign.startLongitude)°")
}If you also want houses and angles in the same response:
let chart = try AstrologyCalculator.natalChart(
for: moment,
coordinate: coord,
system: .placidus
)
print(chart.houses.angles.vertex ?? .nan)
print(chart.houses.cusps[9].eclipticLongitude) // House 10 cusp / MC sectorimport AstroCoreLocations
let cities = CityIndex.shared
// Search cities
let results = cities.search("Tokyo", limit: 5)
for city in results {
print("\(city.name), \(city.countryCode)") // "Tokyo, JP"
print(" \(city.latitude), \(city.longitude)")
print(" \(city.timeZoneIdentifier)") // "Asia/Tokyo"
}
// Use GeoCoordinate directly for calculations
let tokyo = results.first!
let asc = try AstrologyCalculator.ascendant(for: moment, coordinate: tokyo.coordinate)// Julian Day
let jd = AstroCalculator.julianDayUT(for: moment)
// Local Apparent Sidereal Time (degrees)
let lst = AstroCalculator.localSiderealTimeDegrees(for: moment, longitude: 139.65)
// Zodiac signs
let sign = ZodiacSign.leo
print(sign.name) // "Leo"
print(sign.emoji) // "♌"
print(sign.startLongitude) // 120.0
print(sign.contains(longitude: 135.0)) // trueValidated at 2000-01-01 12:00 UTC, apparent ecliptic longitude:
| Body | Baseline | AstroCore | Error | |
|---|---|---|---|---|
| ☀️ | Sun | 280.3689° | 280.3689° | 0.02″ |
| 🌙 | Moon | 223.3238° | 223.3239° | 0.51″ |
| ☿ | Mercury | 271.8893° | 271.8893° | 0.08″ |
| ♀️ | Venus | 241.5658° | 241.5658° | 0.15″ |
| ♂️ | Mars | 327.9633° | 327.9633° | 0.06″ |
| ♃ | Jupiter | 25.2531° | 25.2531° | 0.14″ |
| ♄ | Saturn | 40.3956° | 40.3956° | 0.04″ |
Listed core bodies are < 1 arcsecond in the local validation set.
The committed CI proves the checked release tests plus gated reference baselines
enabled by ASTROCORE_ENABLE_BASELINE_VERIFICATION=1. Broader unpublished
epoch sweeps should be treated as local validation data until their fixtures are
checked into the repository.
Release build, Apple Silicon (M-series). VSOP ephemeris evaluation is vectorized with Apple's Accelerate.
| Computation | Time |
|---|---|
| Ascendant | 0.03 µs |
| House cusps (per system) | 0.3–6 µs |
| Sun position | 4.6 µs |
| Moon position | 1.6 µs |
| Single planet (Mercury–Pluto) | 17–55 µs |
| Aspect grid (10 bodies, 45 pairs) | 15 µs |
| Cross-chart synastry (7×7) | 9 µs |
| Aspect pattern detection | 27 µs |
| Full natal positions (7 bodies + ASC) | 175 µs |
| Motion-rich natal states (7 bodies + ASC) | 485 µs |
Default chart throughput: ~5,700 charts/sec. Numbers reproduce via
swift test -c release --filter Benchmark.
v3 vectorizes VSOP ephemeris evaluation with Accelerate, so computations shared with v2 are markedly faster — at identical accuracy (every regression baseline still passes):
| Computation | v2.0.0 | v3.0.0 | Speedup |
|---|---|---|---|
| Sun position | 9.4 µs | 4.6 µs | 2.0× |
| Mercury position | 164 µs | 55 µs | 3.0× |
| Saturn position | 139 µs | 49 µs | 2.8× |
| Full natal positions (7 bodies + ASC) | 616 µs | 175 µs | 3.5× |
| Natal throughput | ~1,620 charts/sec | ~5,700 charts/sec | 3.5× |
| Metric | Value |
|---|---|
| Test cases | 253 |
| Test suites | 39 |
Validation (swift test -c release):
- ✅ Local validation baselines — multi-epoch verification with tiered accuracy claims, 1800–2100
- ✅ Solstice & equinox references — 2000 summer solstice, 1990 spring equinox, and 2024 winter solstice Sun longitudes checked against fixtures
- ✅ Global cities — ascendant & natal regression across New York, London, Tokyo, Berlin, and Sydney
- ✅ House systems — 16 systems checked for cusp validity, angle alignment, and polar fallback behavior
- ✅ Gauquelin sectors — independent 36-sector model with clockwise numbering and baseline coverage
- ✅ Edge cases — year boundaries (1800/2100), polar latitudes, sign boundaries
- ✅ Regression baselines — gated reference checks in CI and local release verification for houses, Gauquelin sectors, aspects, rise/set/transit, and stations
| Type | Description |
|---|---|
AstroCalculator |
Main entry — Julian day, Sun/Moon/planet positions, motion states, derived coordinates, illumination, equation of time, rise/set/transit, stations, and aspect primitives |
CivilMoment |
Civil time (year/month/day/hour/minute/second + IANA timezone, with explicit repeated-time resolution when needed) |
RepeatedTimeResolution |
DST fall-back ambiguity policy: reject, first occurrence, or last occurrence |
GeoCoordinate |
Geographic coordinate with range-checked latitude and longitude |
CelestialPosition |
Lightweight body position (ecliptic longitude/latitude + optional distance) |
CelestialState |
Motion-rich body state (position + longitude speed/retrograde) |
CelestialBody |
Body enum — Sun through Pluto plus lunar nodes and Lilith variants |
EquatorialCoordinate / HorizontalCoordinate |
Derived equatorial and topocentric horizontal frames |
StationKind |
Direct / retrograde / stationary classification |
AstroError |
Typed core errors (invalid coordinate, unsupported year, missing ephemeris data) |
| Type | Description |
|---|---|
AstrologyCalculator |
Main astrology entry — zodiac mapping, ascendant, houses, Gauquelin sectors, natal charts/states, rise/set events & twilight, stations & retrograde, and aspects (grids, patterns, synastry) |
ZodiacSign |
12 zodiac signs with name, emoji, start longitude, contains(), and init(longitude:) |
AscendantResult |
Ascendant (ecliptic longitude, sign, degree in sign, boundary flag) |
NatalPositions |
Batch result (optional ascendant + body dictionary) |
NatalStates |
Motion-rich batch result (optional ascendant + body state dictionary) |
NatalChart |
Full chart payload (positions + houses + context) |
HouseSystem |
16 supported 12-house systems with display metadata |
HouseResult |
Cusps + angles + requested/resolved system metadata |
HouseCusp |
One cusp entry (1...12) with longitude/sign metadata |
GauquelinResult |
Independent 36-sector result with shared chart angles |
GauquelinSector |
One clockwise Gauquelin sector boundary (1...36) |
Angles |
ASC / MC / DSC / IC and optional vertex |
PolarFallback |
Fallback strategy when a house system is undefined at polar latitudes |
AspectKind / AspectGrid |
Aspect definitions and matched body-body aspect grids |
Aspect / ChartAspect / CrossAspect |
One matched aspect: orb, applying/separating, exactness |
AspectPattern / AspectPatternKind |
Detected multi-body aspect patterns |
ChartAngle / AspectParticipant |
Chart angles and aspect participants (body or angle) |
OrbPolicy |
Configurable per-aspect / per-body orb allowances |
RiseSetEvents / EventInstant |
Rise/set/transit instants with circumpolar and twilight state |
Station |
A direct/retrograde station instant for a body |
AstrologyError |
Typed astrology-layer errors (missing coordinate, polar fallback error, wrapped core errors) |
| Type | Description |
|---|---|
CityIndex |
Singleton city search engine |
CityRecord |
City record (name, country code, coordinate, timezone) |
| Item | Range | |
|---|---|---|
| 📆 | Year range | 1800 — 2100 (301 years) |
| 🪐 | Bodies | Sun, Moon, Mercury, Venus, Mars, Jupiter, Saturn, Uranus, Neptune, Pluto, lunar nodes, Lilith variants |
| 🏠 | House systems | 16 systems: Equal (ASC/MC), Whole Sign, Vehlow, Porphyry, Sripati, Placidus, Koch, Alcabitius, Campanus, Regiomontanus, Morinus, Topocentric, Horizontal, Meridian / Axial Rotation, Carter |
| 📈 | Gauquelin sectors | 36-sector statistical model via dedicated API |
| 🖥️ | Platforms | iOS 15+ · macOS 12+ · tvOS 15+ · watchOS 8+ · visionOS 1+ |
| 🔧 | Swift | 6.0+ |
| Source | Usage |
|---|---|
| Standard astronomical algorithms | Julian Day, ΔT, sidereal time, nutation, ascendant formulas |
| VSOP87D | Heliocentric ecliptic coordinates (full series) |
| ELP-2000/82 | Lunar longitude/latitude (120-term truncated series) |
| Classical house-system geometry | Equal, Whole Sign, Porphyry, Sripati, semi-arc, and great-circle house constructions |
| IAU 1980 Nutation Model | 63-term nutation in longitude/obliquity |
| Mean obliquity polynomial | 10th-degree obliquity series |
| Standard ΔT model (2006) | ΔT piecewise polynomials (1800–2100) |
Copyright © 2026-present Babywbx.
This project is MIT licensed.
AstroCoreLocations bundles derived city data. If you redistribute or surface the
packaged dataset, review the attribution requirements before release.