ГлавнаяБлогОшибка на 360 градусов: как не перепутать ретроградность с переходом через ноль
Алгоритмы

Ошибка на 360 градусов: как не перепутать ретроградность с переходом через ноль

Узнайте, почему наивная проверка ретроградности планет даёт ложные срабатывания из-за перехода через 0° и как исправить это с помощью angularDelta. Практический пример на Python.

Al
Редакция Algolitalgolit.ru
8 мин чтения9 августа 2026 г.

Почему ваша проверка ретроградности врёт

Если вы когда-нибудь писали код для астрономических расчётов, то наверняка сталкивались с задачей определения ретроградности планет. Наивная реализация — вычислить долготу в два момента времени и проверить знак разности — даёт сбой на переходе через 0° Овна. В этой статье я покажу, как избежать этой ошибки, и объясню, почему она важна не только для астрономии, но и для любого кода, работающего с углами.

Классический баг: переход через 0°

Представьте, что вы вычисляете ретроградность как lon(t+step) - lon(t) < 0. Для Сатурна в апреле 1996 года реальные значения долготы:

λ(1996-04-07) = 359.95541°
λ(1996-04-09) =   0.19549°

Сатурн движется прямо, приращение +0.24008°, но разность даёт −359.75992°. Детектор видит огромную отрицательную скорость и объявляет ретроградность. На самом деле планета просто пересекла 0° Овна — начало системы координат. Это не единичный случай: за 200 лет (1900–2100) для восьми планет таких ложных ретроградных периодов — 584, и каждый попадает на чей-то день рождения.

Решение: нормализация углов

Проблема в том, что долготу нельзя вычитать как обычные числа. Нужно вычислить кратчайшую угловую разность, нормализованную в диапазон (−180, 180]. На Python это выглядит так:

def normalize_degrees(deg: float) -> float:
    """Нормализует угол в диапазон [0, 360)."""
    d = (deg % 360 + 360) % 360
    return 0 if d == 360 else d

def angular_delta(a: float, b: float) -> float:
    """Кратчайшая угловая разность a-b, в (-180, 180]."""
    d = normalize_degrees(a - b)
    return d - 360 if d > 180 else d

# Пример:
print(angular_delta(0.19549, 359.95541))  # +0.24008

Теперь разность имеет правильный знак и величину. Для Сатурна в 1996 году детектор находит два реальных периода: 20 июля и 5 декабря, без ложных срабатываний.

Обобщение: когда ещё нужен wrap-aware код

Эта ошибка характерна для любых вычислений с циклическими величинами: долгота, азимут, фаза, цветовой тон, время суток. Если в вашем коде есть a - b, где a — угол, скорее всего, он где-то неверен. Всегда используйте angular_delta для разности углов.

Практический пример: поиск станций планет

Станция — момент, когда видимое движение планеты меняет знак. Для её поиска нужно найти корень функции скорости. Используем выборку долготы с шагом, зависящим от планеты, и бисекцию для уточнения:

def geocentric_ecliptic_longitude(body, date):
    # Здесь вызов библиотеки astronomy-engine или аналога
    # Возвращает видимую геоцентрическую долготу в градусах
    pass

def velocity(ms, body, half_step_ms):
    lon_plus = geocentric_ecliptic_longitude(body, ms + half_step_ms)
    lon_minus = geocentric_ecliptic_longitude(body, ms - half_step_ms)
    return angular_delta(lon_plus, lon_minus)

def find_station(body, start_ms, end_ms, step_ms, refine_ms=5000):
    """Бисекция для поиска станции между start и end."""
    # Инициализация: ищем интервал, где скорость меняет знак
    lo_ms = start_ms
    hi_ms = end_ms
    v_lo = velocity(lo_ms, body, step_ms / 2)
    while hi_ms - lo_ms > refine_ms:
        mid_ms = (lo_ms + hi_ms) / 2
        v_mid = velocity(mid_ms, body, step_ms / 2)
        if (v_lo < 0) != (v_mid < 0):
            hi_ms = mid_ms
        else:
            lo_ms = mid_ms
            v_lo = v_mid
    return (lo_ms + hi_ms) / 2

Этот код находит станцию с точностью до 5 секунд, что достаточно для практических целей.

Распределение ретроградных планет

После исправления можно проанализировать, сколько планет обычно ретроградны в момент рождения. Сканирование 73 414 дней с 1900 по 2100 год даёт:

  • 0 планет — 6.8% дат
  • 1 планета — 19.8%
  • 2 планеты — 31.0%
  • 3 планеты — 25.7%
  • 4 планеты — 12.4%
  • 5 и более — 4.3%

Среднее значение — 2.3. Удивительно, но дат с нулём ретроградных планет меньше, чем с четырьмя. Это контринтуитивно, но объяснимо: ретроградность — обычное явление.

Почему доля ретроградности растёт с удалением планеты

Доля времени в ретроградности равна длине ретроградной дуги, делённой на синодический период. Для внешних планет синодический период стремится к году, и доля приближается к 50% — как у неподвижной звезды из-за аберрации. Венера и Меркурий — внутренние, их ретроградность связана с нижним соединением, но пропорция сохраняется.

Что делать прямо сейчас

Проверьте свой код на наличие вычитания углов. Замените a - b на angular_delta(a, b). Если вы работаете с астрономическими данными, используйте библиотеку astronomy-engine (MIT) — она корректно вычисляет видимые геоцентрические долготы. Это убережёт вас от ложных ретроградных периодов и других ошибок, связанных с циклическими величинами.

#углы#нормализация#астрономия#ретроградность#Python
Al
Редакция Algolit

Пишем про алгоритмы, подготовку к собеседованиям и карьеру в IT — так, чтобы было понятно и полезно.

Хочешь закрепить знания на практике?

Решай задачи на Algolit — интерактивная платформа для обучения

Начать бесплатно →