Расчёт натальной карты¶
Цель и границы¶
Backend по дате, точному местному времени и месту рождения детерминированно возвращает положения Солнца, Луны, Меркурия, Венеры, Марса, Юпитера, Сатурна, Урана, Нептуна и Плутона, ASC/MC, 12 домов, знаки и пять мажорных аспектов. AI в расчёте не участвует. Интерпретация — отдельный downstream-модуль.
Источник математических данных — локальная Swiss Ephemeris, не внешний сайт. Node binding sweph вызывает нативную
библиотеку; production-образ должен содержать закреплённый набор ephemeris-файлов.
Контракт входа¶
{
"localDate": "1990-05-17",
"localTime": "14:35:00",
"timeAccuracy": "exact",
"place": {
"label": "Москва, Россия",
"latitude": 55.7558,
"longitude": 37.6173,
"timezoneId": "Europe/Moscow"
}
}
Сохраняются и пользовательская строка места, и immutable snapshot координат/таймзоны. Широта — [-90, 90],
долгота — [-180, 180]. IANA ID важнее текущего числового UTC offset: offset должен быть историческим именно для
даты рождения.
timeAccuracy в MVP:
exact— разрешает полный расчёт;unknown— блокирует астрологию и Дизайн Человека, но не нумерологию и Матрицу;- режимы «утро/день/вечер» не вводятся, пока продукт не примет риск неверных ASC/домов.
Строгий режим принят для MVP. Если позже понадобится date-only результат, он оформляется новой продуктовой возможностью: допустимо показывать только устойчивые положения планет с ясным предупреждением, без ASC, MC, домов и потенциально сменившей знак Луны.
Фиксированные настройки версии natal-v1¶
Чтобы результат был воспроизводимым, недостаточно хранить только дату и координаты. В версии алгоритма фиксируются:
| Параметр | natal-v1 |
|---|---|
| Reference frame | геоцентрический |
| Zodiac | тропический |
| Координаты | видимые эклиптические долгота/широта даты |
| Ephemeris flag | SEFLG_SWIEPH \| SEFLG_SPEED |
| Calendar | Gregorian для современных дат |
| House system | Placidus, код P |
| Bodies | Sun, Moon, Mercury, Venus, Mars, Jupiter, Saturn, Uranus, Neptune, Pluto |
| Aspects | 0°, 60°, 90°, 120°, 180° |
| Orbs | conjunction/opposition 8°, square/trine/sextile 6° — принятый стартовый default |
Не добавляем молча узлы, Лилит, Хирон, астероиды, fixed stars, топоцентрические положения или сидерический режим.
Новая методика получает новый algorithmVersion, старые JSON не пересчитываются незаметно.
Алгоритм¶
1. Нормализация времени¶
- Проверить календарную дату, время и диапазон координат.
- По
timezoneIdи исторической базе IANA преобразовать local civil time в UTC. - Если локальное время неоднозначно при осеннем переводе часов, потребовать выбрать один из двух offset; если оно не существовало при весеннем переводе — вернуть validation error. Не угадывать.
- Сохранить
utcInstant, использованный offset, версию timezone data и snapshot места. - Вызвать
utc_to_jd(..., SE_GREG_CAL)и получитьjdEtиjdUt. Дляcalc_utи домов использоватьjdUt.
Swiss Ephemeris конвертирует UT/ET через ΔT; собственную формулу Julian Day писать не нужно.
2. Инициализация эфемерид¶
- Один раз на процесс вызвать
set_ephe_path(SWISS_EPHEMERIS_PATH). - Проверить наличие ожидаемых файлов и их SHA-256 из deployment manifest.
- Сохранить
sweph.version(), версию binding, dataset manifest и flags в результате. - При shutdown вызвать
close().
Swiss Ephemeris умеет fallback на другой движок, если файлов нет. Для production такой fallback запрещён: если
возвращённые flags не подтверждают SEFLG_SWIEPH, расчёт падает с диагностируемой ошибкой, а не меняет точность.
3. Положения небесных тел¶
Для каждого из десяти ID вызвать:
calc_ut(jdUt, bodyId, SEFLG_SWIEPH | SEFLG_SPEED)
Из результата сохранить без преждевременного округления:
- эклиптическую долготу
λв[0, 360); - эклиптическую широту
β; - расстояние;
- суточные скорости долготы/широты/расстояния;
retrograde = longitudeSpeed < 0.
Если статус/flags ошибочны, весь артефакт помечается failed; частичную карту пользователю не выдаём.
4. ASC, MC и дома¶
Вызвать houses_ex2(jdUt, 0, latitude, longitude, "P"). Получить 12 cusps и special points; из special points
сохранить минимум Ascendant, Midheaven и ARMC. Планету относить к дому через Swiss-функцию house_pos, используя
ARMC, географическую широту, истинное наклонение эклиптики и [λ, β], а не через хрупкое сравнение диапазонов.
Placidus может быть неопределён на экстремальных широтах. Нельзя бесшумно переключаться на другую систему:
- вернуть код
HOUSE_SYSTEM_UNAVAILABLEи предложить другой явно выбранный режим; - либо после продуктового решения создать
natal-v2-whole-sign; - всегда сохранить реально применённую систему.
5. Знаки¶
Для долготы λ:
Индексы 0–11 соответствуют Овну…Рыбам. В JSON хранится числовая долгота, стабильный enum знака и градус внутри знака. Локализованное название — presentation data, оно не входит в математический результат.
6. Аспекты¶
Перебрать каждую неупорядоченную пару тел один раз. Минимальный угол:
raw = abs(longitudeA - longitudeB)
separation = min(raw, 360 - raw)
delta = abs(separation - exactAspectAngle)
aspect exists when delta <= configuredOrb
Если подходит несколько аспектов, выбрать минимальный delta, затем более сильный аспект по фиксированному order.
Сохранить type, exactAngle, separation, orb, пары body ID. Applying/separating можно вывести из относительной
скорости позже, но не включать без эталонных тестов.
Орбисы являются продуктовой методикой, а не данными Swiss Ephemeris. Предложенная таблица v1 должна быть согласована с астрологом и точно повторена в настройках Astro-Seek при приёмочной сверке.
7. Канонизация и кэш¶
inputHash = SHA-256(canonicalJson(normalizedInput + algorithmVersion)). Одновременные одинаковые запросы защищены
unique constraint. В числовом JSON сохраняем до 8 знаков после запятой; округление для UI выполняется отдельно.
Поля объектов и массивы тел/аспектов сортируются стабильно, после чего считается resultHash.
Пример метаданных ответа:
{
"schemaVersion": "natal-result-v1",
"algorithmVersion": "natal-v1",
"engine": {
"name": "Swiss Ephemeris",
"version": "2.10.03",
"binding": "sweph@2.10.3-8",
"datasetHash": "sha256:...",
"flags": 258
},
"time": {
"utcInstant": "1990-05-17T10:35:00.000Z",
"timezoneId": "Europe/Moscow",
"utcOffset": "+04:00"
},
"settings": {
"zodiac": "tropical",
"houseSystem": "placidus",
"orbPolicy": "major-aspects-v1"
},
"points": [],
"houses": [],
"aspects": []
}
Проверка корректности¶
Минимальный набор automated tests:
- нормализация
0/360, границы знаков и аспект через 0°; - UTC conversion для обычной даты, исторической смены offset, ambiguous и nonexistent local time;
- snapshot десяти тел, ASC/MC, cusps и аспектов;
- отсутствие silent fallback с
SWIEPH; - идемпотентность и изменение хэша при новой версии алгоритма;
- ошибка/явный alternate house system для высокой широты;
- property tests: долготы
[0,360), дома 1–12, aspect orb неотрицателен, нет дублей пар.
Приёмка из ТЗ: минимум три заранее зафиксированных человека сверяются с Astro-Seek, расхождение каждого положения меньше 0,5°. Для осмысленного сравнения на обоих сторонах должны совпадать zodiac, geocentric/topocentric mode, house system, координаты, исторический offset, набор объектов и орбисы. Expected fixtures проходят ручное ревью и сохраняются в репозитории; онлайн-сайт не вызывается в CI.
Для отладки используем официальный swetest как второй локальный oracle. Сравнение только с тем же binding не
обнаружит ошибку нашей подготовки времени.
Лицензирование Swiss Ephemeris¶
Astrodienst публикует Swiss Ephemeris по двойной модели: AGPL либо Swiss Ephemeris Professional License. Выбор нужно сделать до распространения или включения публичного сервиса; AGPL требует совместимого лицензирования всего проекта. См. официальную страницу лицензирования и Programmer's Documentation.
Лицензия npm-wrapper не отменяет условия встроенного Swiss Ephemeris core. До внешнего пилота зависимость используется только для разработки и внутреннего тестирования. Выбор лицензии, dataset manifest и notices — обязательный пункт pre-pilot gate.