Исходный код soniks_client.antenna.tracking.flip

"""Стратегия слежения с переворотом для проходов через север."""

from core.configs import logger
from soniks_client.antenna.tracking.base import TrackingStrategy


[документация] class FlipStrategy(TrackingStrategy): """ Переворот на 180° по азимуту и зеркалирование элевации. Решение о flip принимается один раз на весь пролёт — на основе того, проходит ли спутник через север (макс. азимут не между AOS и LOS). """ _AZ_OFFSET = 180.0 _FLIP_BORDER_ELEV = 90.0 def __init__(self, min_elevation: float, threshold: float) -> None: super().__init__(min_elevation, threshold) self._is_flip: bool = False
[документация] def prepare_pass(self, aos_az: float, max_az: float, los_az: float) -> None: """Решить, нужен ли переворот на этот пролёт. Решение не меняется до конца.""" self._is_flip = self._is_flip_required(aos_az, max_az, los_az) logger.info("[FlipStrategy] flip на пролёт: %s", self._is_flip)
[документация] def transform_target(self, sat_az, sat_alt, cur_rot_az, cur_rot_alt): """Вернуть целевые координаты, при перевороте — азимут+180° и зеркальный угол места. """ alt = self._clamp_min_elevation(sat_alt) if not self._is_flip: return sat_az, alt az = (sat_az + self._AZ_OFFSET) % self.FULL_CIRCLE # элевация зеркалится: если спутник на 30°, ротатор смотрит на 150° alt = self.HALF_CIRCLE - alt return az, alt
[документация] def calculate_delta(self, target_az, target_alt, cur_az, cur_alt): # В flip-режиме азимут уже в [0, 360), кратчайший путь корректен """Вернуть рассогласование по кратчайшему пути: азимут уже в [0, 360).""" return ( self.shortest_angular_delta(target_az, cur_az), abs(target_alt - cur_alt), )
def _is_flip_required(self, aos: float, max_: float, los: float) -> bool: """True, если пролёт пересекает север (азимут проходит через 0/360). Сырое сравнение ``aos < max_ < los`` работало только пока пролёт не пересекал 0/360 на одном из участков: пролёт 10° -> 20° -> 350° пересекает север между кульминацией и заходом, но выглядел возрастающим и flip не включался. Азимуты разворачиваются в непрерывную последовательность общим хелпером базового класса; север пересечён, если развёрнутый путь вышел за пределы [0, 360). """ unwrapped_max = self.closest_equivalent(max_, aos) unwrapped_los = self.closest_equivalent(los, unwrapped_max) path = (aos, unwrapped_max, unwrapped_los) return min(path) < 0.0 or max(path) >= self.FULL_CIRCLE @property def is_flipping(self) -> bool: """Выбран ли переворот на текущий пролёт.""" return self._is_flip