# Точність розрахунків

## TL;DR

AstroWay API використовує **Swiss Ephemeris** (офіційний C-код від Astrodienst,
творців astro.com) — той самий движок, на якому професійні астрологи 30+ років
працюють у **Solar Fire ($495), Kepler ($995), Astro Gold ($29.99/міс) і Janus**.
Ми зібрали його у WebAssembly і виставили REST API-шаром, без посередницької
націнки. Кожен із базових розрахункових ендпоінтів покритий regression
snapshot-тестами; точність двигуна верифікована через **triangulation** з трьома
незалежними джерелами + проти **NASA 5-Millennium Eclipse Catalog**:

- **Планетні позиції**: `< 0.1 arcsec` vs офіційного swetest CGI Astrodienst
- **Куспиди домів Placidus**: `0.000"` exact match
- **Затемнення**: `< 1 хвилина` vs NASA Eclipse Catalog
- **ACG лінії**: `< 1.5 км` на екваторі vs swetest
- **Sunrise/sunset**: `< 10 секунд` vs timeanddate.com
- **Moon VOC, ingresses, planet conjunctions**: точність до частки секунди

## Двигун

| Компонент | Значення |
|-----------|----------|
| Бібліотека | Swiss Ephemeris C code (aloistr/swisseph) |
| Версія | upstream Astrodienst C code |
| Bindings | swisseph-wasm (WebAssembly у Node.js) |
| Ephemeris | DE431 JPL ephemeris (через .se1 файли) |
| Fallback | Moshier analytical (для дат поза 1800-2399) |
| Code sharing | Shared `@/core` із app.astroway.info — один двигун, два транспорти |

## Методика

Точність перевіряється через **триангуляцію** — порівняння з трьома
незалежними джерелами:

### 1. swetest CGI (еталон)

Офіційний reference-implementation Swiss Ephemeris від самих Astrodienst —
команди, що створили **Astro.com** і обслуговує мільйони астрологів по всьому
світу. Найавторитетніше публічне джерело. Наш двигун **ідентичний**
(0.00–0.07 кутової секунди дрифту).

### 2. Kerykeion (Python)

Незалежна Python-бібліотека використовує `pyswisseph`-bindings замість нашого
WASM. Підтверджує, що наш WASM-шар не спотворює дані.

### 3. Prokerala API

Віддалений Swiss Ephemeris API (sidereal Lahiri). Systemic drift 8–17 кутових
секунд пов'язаний з різними версіями формули Lahiri ayanamsa, а **не** з
точністю двигуна.

## Результати триангуляції

| Карта | проти swetest (Astrodienst) | проти Kerykeion (Python) | проти Prokerala API |
|-------|------------------------------|---------------------------|---------------------|
| Monroe 1926   | **0.00"** | 0.19" | 16.95" (systemic) |
| Diana 1961    | **0.00"** | 0.69" | 8.18" (systemic)  |
| Einstein 1879 | 0.07"     | LMT-артефакт Kerykeion | 14.96" (systemic) |

## Regression-набір на рівні ендпоінтів

Кожен із базових обчислювальних ендпоінтів покритий замороженими snapshot-тестами
на 3 референсних картах (Monroe / Diana / Einstein) = **{siteMeta.snapshotCount} snapshot-ів**.

Snapshot-suite ловить:

- **Баги мапінгу вхідних даних** — неправильний id планети, UT, система будинків
- **Пост-обробку** — округлення, конвертація одиниць, втрата знаку
- **Розбіжність дефолтів** — mean vs true node, geocentric vs topocentric
- **Schema drift** — валідація пропустила неправильну форму
- **Застарілий деплой** — prod dist не відповідає коду

Tolerance: `5e-5°` (≈0.18 кутової секунди) за замовчуванням для всіх числових полів.

## Детальні бенчмарки

### Позиції планет (тропік, проти swetest)

| Карта | Дата | Максимум drift |
|-------|------|-----------------|
| Marilyn Monroe  | 1926-06-01 | **0.00"** |
| Princess Diana  | 1961-07-01 | **0.00"** |
| Albert Einstein | 1879-03-14 | 0.07"     |

### Куспіди домів Placidus (проти swetest)

| Карта | drift ASC | drift MC | Максимум drift куспіда |
|-------|-----------|----------|-------------------------|
| Monroe | 0.000" | 0.000" | 0.000" |
| Diana  | 0.000" | 0.000" | 0.000" |

### Затемнення (проти NASA 5-Millennium Catalog)

| Подія | Максимум NASA | Наш максимум | Drift |
|-------|----------------|--------------|-------|
| 2025-03-14 Lunar Total  | 06:58 UT | 06:58 UT | 0.8 хв |
| 2025-03-29 Solar Partial | 10:47 UT | 10:47 UT | 0.5 хв |
| 2025-09-07 Lunar Total  | 18:11 UT | 18:11 UT | 0.8 хв |
| 2025-09-21 Solar Partial | 19:41 UT | 19:42 UT | 1.0 хв |

### Астрокартографія (проти формули RA з swetest)

Усі лінії MC/IC/ASC/DSC використовують правильну формулу `longitude = RA − GMST`
(стандарт Kenneth Bowser). Drift від еталона: **< 1.5 км** на екваторі для
всіх планет.

### Схід / Захід Сонця (проти timeanddate.com)

| Локація | Дата | Параметр | Drift |
|---------|------|----------|-------|
| London | 2026-04-15 | Sunrise | 0.6 с |
| London | 2026-04-15 | Sunset  | 9 с   |

**Полярні локації** (|lat| > 66.5°) автоматично повертають `polarState` +
warning, що звичайні planetary hours не визначені.

### Орбіси аспектів

AstroWay використовує **змінні орбіси** per-planet (правило MIN двох планет),
як у ZET9 та astro.com. Орбіси за замовчуванням (для натальної карти):

| Аспект | Sun | Moon | Inner | Jupiter | Outer |
|--------|-----|------|-------|---------|-------|
| Conjunction | 12°  | 10° | 5° | 8° | 5° |
| Sextile     | 6.5° | 6°  | 5° | 5° | 5° |
| Square      | 10°  | 8°  | 5° | 7° | 5° |
| Trine       | 12°  | 8°  | 5° | 5° | 5° |
| Opposition  | 12°  | 10° | 5° | 8° | 5° |

Мінорні аспекти (36°, 40°, 45°, 72°, 108°, 135°, 144°) **за замовчуванням
вимкнені**. Вмикаються явно через `ALL_ASPECTS`.

## Полярні широти

Для |lat| > 66.5° системи Placidus / Koch / Regiomontanus математично не
визначені. У таких випадках Swiss Ephemeris автоматично повертає Porphyry,
і наш API додає warning:

```json
{
  "system": "P",
  "warning": "Система Placidus не визначена для lat=68.96° (> 66.5°). Swiss Ephemeris підставив Porphyry..."
}
```

## Безперервна перевірка

Regression перевіряється **на кожен PR** через CI:

- `api-calc/tests/endpoints/` — {siteMeta.snapshotCount} snapshot-ів проти референсних карт (Monroe / Diana / Einstein) + synastry / composite / davison
- `.github/workflows/api-accuracy.yml` — автозапуск на PR
- Триангуляція проти swetest CGI + Kerykeion — щотижня
- Моніторинг upstream Swiss Ephemeris через Dependabot

## Надійність MCP — типізований вивід

Точність двигуна — половина довіри. Друга половина — **контракт виводу**, який
агент (Claude, ChatGPT, Cursor) може валідувати. Наш MCP-сервер не повертає
«сирий текст, розберіться самі»:

- **Типізований `structuredContent`.** Понад 600 MCP-інструментів, і переважна
  більшість із них публікують строгий `outputSchema` — MCP-клієнт отримує
  валідовану структуру, а не рядок, який треба парсити навмання.
- **Захист від schema drift.** CI-тест `openapi-example-drift` валідує кожен
  regression-snapshot проти схеми, виведеної з його ж прикладу: якщо реальний
  вивід ендпоінта розійдеться з опублікованою схемою, збірка падає. Саме цей клас
  розбіжностей спричиняє помилку MCP `-32602 Output validation error`.
- **Помилки — це помилки.** Збій інструмента завжди повертається з прапором
  `isError` і типізованим кодом (`UPPER_SNAKE`). Ми **ніколи** не віддаємо моделі
  сирий stack trace або внутрішні шляхи сервера як «дані».

Тобто інтеграція через MCP так само передбачувана, як і прямий REST-виклик: те,
що описано в `/openapi.json`, — це те, що повертає інструмент.

## Відомі обмеження

- **Дати поза діапазоном** (до 1800 і після 2399): використовується Moshier
  analytical, точність ~0.1" (проти < 0.01" для SWIEPH з файлами DE431)
- **True Lilith (id=13) vs Mean Lilith (id=12)**: різниця до 12° — за дефолтом
  Mean Lilith (стабільна поведінка), True Lilith доступний через
  `planetIds: [13]`
- **Topocentric vs geocentric**: за замовчуванням geocentric

## Посилання

- Swiss Ephemeris: https://www.astro.com/swisseph/
- swetest CGI: https://www.astro.com/swisseph/swetest.htm
- Каталог затемнень NASA: https://eclipse.gsfc.nasa.gov/
- Kerykeion: https://github.com/g-battaglia/kerykeion
- Astrodienst: https://www.astro.com/

## Контакти

Про питання точності: пиши на support@astroway.info з даними карти
й очікуваним еталоном (astro.com або інше авторитетне джерело).
