Исходный код soniks_client.antenna.tracking.range_250
"""Стратегия слежения для ротатора с расширенным диапазоном азимута ±250°."""
from core.configs import logger
from soniks_client.antenna.tracking.base import TrackingStrategy
[документация]
class Range250Strategy(TrackingStrategy):
"""
Ротатор с жёсткими границами по азимуту [-250, 250].
Сопровождение идёт «через 0»: если спутник движется 3° -> 2° -> 1° -> 0° -> 359°,
ротатор продолжает движение -1° -> -2° -> -3° (а не разворачивается на 360°).
Решение о направлении (в + или в -) принимается на каждой итерации по
текущему положению ротатора: берётся значение sat_az, эквивалентное
(mod 360) и ближайшее к текущему rot_az.
"""
LOWER_LIMIT = -250.0
UPPER_LIMIT = 250.0
def __init__(self, min_elevation: float, threshold: float) -> None:
super().__init__(min_elevation, threshold)
# Азимут упора, команда на который уже отправлена. Пока спутник за
# пределами диапазона, ротатор всё равно стоит: повторять команду
# каждые ROTATOR_UPDATE_INTERVAL до конца прохода бессмысленно.
self._limit_commanded: float | None = None
[документация]
def prepare_pass(self, aos_az: float, max_az: float, los_az: float) -> None:
# Будущее улучшение: по aos/los можно заранее предсказать, пройдёт ли
# спутник через 0 или через 180. Если через 180 — режим 250 невозможен,
# можно залогировать предупреждение.
"""Предупредить, если пролёт идёт около юга и может оказаться недостижимым."""
self._limit_commanded = None
if max_az is not None and 170 < max_az < 190:
logger.warning(
"[Range250] Спутник проходит около юга (max_az=%.1f°). "
"Ротатор с границами ±250° может не успеть сопровождать.",
max_az,
)
[документация]
def calculate_delta(self, target_az, target_alt, cur_az, cur_alt):
# Здесь НЕТ взятия по модулю 360 — обе координаты уже в одной
# непрерывной системе отсчёта (например, могут быть отрицательными).
"""Вернуть рассогласование без приведения по модулю 360.
Обе координаты уже в одной непрерывной системе отсчёта и могут быть
отрицательными.
На упоре рассогласование по азимуту обнуляется после первой отправки:
ротатор в предел уже упёрся, и дальше дельта остаётся большой до конца
прохода, заставляя переотправлять одну и ту же команду.
"""
alt_delta = abs(target_alt - cur_alt)
if target_az in (self.LOWER_LIMIT, self.UPPER_LIMIT):
if self._limit_commanded == target_az:
return 0.0, alt_delta
self._limit_commanded = target_az
else:
self._limit_commanded = None
return abs(target_az - cur_az), alt_delta