Перейти к содержанию

Расчёт натальной карты

Цель и границы

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. Нормализация времени

  1. Проверить календарную дату, время и диапазон координат.
  2. По timezoneId и исторической базе IANA преобразовать local civil time в UTC.
  3. Если локальное время неоднозначно при осеннем переводе часов, потребовать выбрать один из двух offset; если оно не существовало при весеннем переводе — вернуть validation error. Не угадывать.
  4. Сохранить utcInstant, использованный offset, версию timezone data и snapshot места.
  5. Вызвать utc_to_jd(..., SE_GREG_CAL) и получить jdEt и jdUt. Для calc_ut и домов использовать jdUt.

Swiss Ephemeris конвертирует UT/ET через ΔT; собственную формулу Julian Day писать не нужно.

2. Инициализация эфемерид

  1. Один раз на процесс вызвать set_ephe_path(SWISS_EPHEMERIS_PATH).
  2. Проверить наличие ожидаемых файлов и их SHA-256 из deployment manifest.
  3. Сохранить sweph.version(), версию binding, dataset manifest и flags в результате.
  4. При 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. Знаки

Для долготы λ:

signIndex = floor(normalize360(λ) / 30)
degreeInSign = normalize360(λ) - signIndex * 30

Индексы 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.