soniks_client.antenna

Управление железом и расчёт положения спутника.

Примечание

rig и rotator импортируют системный модуль Hamlib, доступный только внутри контейнера. В документации он подменяется заглушкой.

Стратегии слежения намеренно от Hamlib не зависят — поэтому именно они покрыты тестами.

Расчёт параметров пролёта

Расчёт положения спутника и радиальной скорости по TLE (Skyfield).

class soniks_client.antenna.satellite_parameters.SatelliteParametersCalculator(station_location, tle)[исходный код]

Базовые классы: object

Расчёт параметров пролёта по TLE и координатам станции.

Параметры:
  • station_location (dict[str, float | int])

  • tle (dict[str, str])

get_position()[исходный код]

Вернуть текущие азимут и угол места спутника в градусах.

Тип результата:

tuple[float, float]

get_radial_velocity()[исходный код]

Вернуть радиальную скорость спутника, м/с.

Положительное значение — спутник удаляется.

Тип результата:

float

get_azimuths_satellite_pass(start_time, end_time, satellite_name)[исходный код]

Вернуть азимуты восхода, кульминации и захода для пролёта.

Окно поиска расширяется на два часа в обе стороны: сеанс мог начаться с задержкой, и AOS уже прошло, либо LOS ещё далеко за end_time. В расширенном окне помещается несколько пролётов, поэтому берётся целая тройка событий одного пролёта, ближайшая к окну наблюдения.

Результат:

Тройка азимутов либо None, если пролёт не найден.

Параметры:
  • start_time (datetime)

  • end_time (datetime)

  • satellite_name (str)

Тип результата:

tuple[AOS, Maximum, LOS] | None

Сессии связи

Фоновые потоки, гоняющие вычисленные значения в железо во время прохода.

Фоновые сессии связи: слежение ротатора и доплеровская коррекция частоты.

class soniks_client.antenna.communication_session.CommunicationSessionBase(update_interval)[исходный код]

Базовые классы: Generic[T], ABC

Фоновая сессия, гоняющая расчётные значения в железо во время прохода.

Потомок задаёт, что именно считать и куда отправлять; базовый класс ведёт поток и его жизненный цикл.

Параметры:

update_interval (float)

set_session_parameters(station_location, tle)[исходный код]

Задать координаты станции и TLE. Во время активной сессии игнорируется.

Результат:

False, если сессия уже активна и параметры не приняты. Потомок обязан проверить результат: иначе он перестраивает своё состояние на живой сессии в обход этой защиты.

Параметры:
  • station_location (dict[str, float | int])

  • tle (dict[str, str])

Тип результата:

bool

start_session()[исходный код]

Запустить фоновый поток сессии.

Исключение:

NoSessionParametersError – Параметры пролёта не заданы.

Тип результата:

None

stop_session()[исходный код]

Остановить фоновый поток сессии и дождаться его завершения.

Тип результата:

None

class soniks_client.antenna.communication_session.RotatorTrackingSession(update_interval, rotator)[исходный код]

Базовые классы: CommunicationSessionBase[tuple[float, float]]

Сессия слежения поворотного устройства за спутником.

Применяет стратегию из ANTENNA__ROTATOR__MODE и двигает антенну, только если рассогласование превысило ANTENNA__ROTATOR__THRESHOLD. По завершении всегда возвращает антенну в исходное положение.

Параметры:
set_session_parameters(station_location, tle, start_time=None, end_time=None)[исходный код]

Задать координаты станции и TLE. Во время активной сессии игнорируется.

Результат:

False, если сессия уже активна и параметры не приняты. Потомок обязан проверить результат: иначе он перестраивает своё состояние на живой сессии в обход этой защиты.

Параметры:
  • station_location (dict[str, float | int])

  • tle (dict[str, str])

  • start_time (datetime | None)

  • end_time (datetime | None)

Тип результата:

bool

class soniks_client.antenna.communication_session.RigRXDopplerCorrectedSession(update_interval, rig)[исходный код]

Базовые классы: CommunicationSessionBase[int]

Сессия доплеровской коррекции частоты приёма.

Считает радиальную скорость спутника и отдаёт скорректированную частоту демону rigctld.

Параметры:
set_session_parameters(station_location, tle, frequency)[исходный код]

Задать координаты станции и TLE. Во время активной сессии игнорируется.

Результат:

False, если сессия уже активна и параметры не приняты. Потомок обязан проверить результат: иначе он перестраивает своё состояние на живой сессии в обход этой защиты.

Параметры:
  • station_location (dict[str, float | int])

  • tle (dict[str, str])

  • frequency (int)

Тип результата:

bool

Приёмное устройство

Управление приёмным устройством через Hamlib.

Hamlib — системный модуль, доступный только внутри контейнера.

class soniks_client.antenna.rig.RigController(rig_ip, rig_port, model=Hamlib.RIG_MODEL_NETRIGCTL)[исходный код]

Базовые классы: object

Приёмное устройство через сетевой rigctld (Hamlib).

Используется для доплеровской коррекции частоты во время прохода.

Параметры:
  • rig_ip (str)

  • rig_port (int)

connect()[исходный код]

Открыть соединение с rigctld.

Исключение:

EquipmentConnectionError – Hamlib вернул код ошибки.

Тип результата:

None

disconnect()[исходный код]

Закрыть соединение с rigctld.

Тип результата:

None

property frequency: float
property vfo: int

Текущий VFO приёмника, прочитанный при подключении.

soniks_client.antenna.rig.get_rig_controller()[исходный код]

Вернуть единственный на процесс контроллер приёмника.

Раньше контроллер создавался на импорте модуля: ошибка конфигурации Hamlib роняла клиент на старте трейсбеком вместо записи в лог.

Тип результата:

RigController

Поворотное устройство

Управление поворотным устройством через Hamlib.

Hamlib — системный модуль, доступный только внутри контейнера.

class soniks_client.antenna.rotator.RotatorController(model, baud, port)[исходный код]

Базовые классы: object

Поворотное устройство через Hamlib.

Создаётся только при ANTENNA__ROTATOR__ENABLED; иначе get_rotator_controller() возвращает None.

Параметры:
  • model (str)

  • baud (int)

  • port (str)

connect()[исходный код]

Открыть соединение с поворотным устройством.

Исключение:

EquipmentConnectionError – Hamlib вернул код ошибки.

Тип результата:

None

disconnect()[исходный код]

Закрыть соединение с поворотным устройством.

Тип результата:

None

property position: list[float]
soniks_client.antenna.rotator.get_rotator_controller()[исходный код]

Вернуть единственный на процесс контроллер ротатора либо None.

Раньше контроллер создавался на импорте модуля, поэтому неизвестная модель Hamlib роняла клиент на старте трейсбеком вместо записи в лог.

Тип результата:

RotatorController | None

Стратегии слежения

Поведение каждого режима описано в Поворотном устройстве.

Важно

Общая угловая математика живёт в TrackingStrategy. Правки, связанные с переходом через 0/360, делайте там, а не в конкретной стратегии.

Выбор стратегии слежения по значению ANTENNA__ROTATOR__MODE.

soniks_client.antenna.tracking.create_tracking_strategy()[исходный код]

Создать стратегию слежения по ANTENNA__ROTATOR__MODE.

Тип результата:

TrackingStrategy

Контракт стратегии слежения и общая угловая математика.

Исправления, связанные с переходом через 0/360, вносите сюда, а не в конкретную стратегию — иначе они достанутся одному режиму из трёх.

class soniks_client.antenna.tracking.base.TrackingStrategy(min_elevation, threshold)[исходный код]

Базовые классы: ABC

Контракт преобразования координат спутника в команды для ротатора.

Каждая стратегия отвечает за: - подготовку к пролёту (например, решение о flip); - перевод (az_sat, alt_sat) -> (az_rot, alt_rot); - расчёт дельт между целью и текущей позицией; - решение, нужно ли двигаться.

Параметры:
  • min_elevation (float)

  • threshold (float)

FULL_CIRCLE = 360.0
HALF_CIRCLE = 180.0
abstractmethod prepare_pass(aos_az, max_az, los_az)[исходный код]

Подготовиться к пролёту по азимутам восхода, кульминации и захода.

Параметры:
  • aos_az (float)

  • max_az (float)

  • los_az (float)

Тип результата:

None

abstractmethod transform_target(sat_az, sat_alt, cur_rot_az, cur_rot_alt)[исходный код]

Перевести координаты спутника в целевые координаты ротатора.

Параметры:
  • sat_az (float)

  • sat_alt (float)

  • cur_rot_az (float)

  • cur_rot_alt (float)

Тип результата:

tuple[float, float]

abstractmethod calculate_delta(target_az, target_alt, cur_az, cur_alt)[исходный код]

Вернуть рассогласование по азимуту и углу места в градусах.

Параметры:
  • target_az (float)

  • target_alt (float)

  • cur_az (float)

  • cur_alt (float)

Тип результата:

tuple[float, float]

needs_movement(az_delta, alt_delta)[исходный код]

Превысило ли рассогласование порог, при котором стоит двигать антенну.

Параметры:
  • az_delta (float)

  • alt_delta (float)

Тип результата:

bool

static shortest_angular_delta(a, b)[исходный код]

Кратчайшая угловая разница a-b в диапазоне [-180, 180], возвращаем модуль.

Параметры:
  • a (float)

  • b (float)

Тип результата:

float

static closest_equivalent(sat_az, ref_az)[исходный код]

Вернуть значение, эквивалентное sat_az (mod 360), ближайшее к ref_az. Нужно для режима 250: 350° при ref=-10° даст -10°.

Параметры:
  • sat_az (float)

  • ref_az (float)

Тип результата:

float

Стратегия слежения для обычного ротатора с азимутом 0–360°.

class soniks_client.antenna.tracking.normal.NormalStrategy(min_elevation, threshold)[исходный код]

Базовые классы: TrackingStrategy

Обычный режим. Азимут 0..360, угловое расстояние — кратчайшее через 0/360.

Параметры:
  • min_elevation (float)

  • threshold (float)

prepare_pass(aos_az, max_az, los_az)[исходный код]

Подготовка не требуется.

Параметры:
  • aos_az (float)

  • max_az (float)

  • los_az (float)

Тип результата:

None

transform_target(sat_az, sat_alt, cur_rot_az, cur_rot_alt)[исходный код]

Вернуть азимут спутника как есть, ограничив угол места снизу.

calculate_delta(target_az, target_alt, cur_az, cur_alt)[исходный код]

Вернуть рассогласование, считая азимут по кратчайшему пути через 0/360.

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

class soniks_client.antenna.tracking.flip.FlipStrategy(min_elevation, threshold)[исходный код]

Базовые классы: TrackingStrategy

Переворот на 180° по азимуту и зеркалирование элевации. Решение о flip принимается один раз на весь пролёт — на основе того, проходит ли спутник через север (макс. азимут не между AOS и LOS).

Параметры:
  • min_elevation (float)

  • threshold (float)

prepare_pass(aos_az, max_az, los_az)[исходный код]

Решить, нужен ли переворот на этот пролёт. Решение не меняется до конца.

Параметры:
  • aos_az (float)

  • max_az (float)

  • los_az (float)

Тип результата:

None

transform_target(sat_az, sat_alt, cur_rot_az, cur_rot_alt)[исходный код]

Вернуть целевые координаты, при перевороте — азимут+180° и зеркальный угол места.

calculate_delta(target_az, target_alt, cur_az, cur_alt)[исходный код]

Вернуть рассогласование по кратчайшему пути: азимут уже в [0, 360).

property is_flipping: bool

Выбран ли переворот на текущий пролёт.

Стратегия слежения для ротатора с расширенным диапазоном азимута ±250°.

class soniks_client.antenna.tracking.range_250.Range250Strategy(min_elevation, threshold)[исходный код]

Базовые классы: TrackingStrategy

Ротатор с жёсткими границами по азимуту [-250, 250].

Сопровождение идёт «через 0»: если спутник движется 3° -> 2° -> 1° -> 0° -> 359°, ротатор продолжает движение -1° -> -2° -> -3° (а не разворачивается на 360°).

Решение о направлении (в + или в -) принимается на каждой итерации по текущему положению ротатора: берётся значение sat_az, эквивалентное (mod 360) и ближайшее к текущему rot_az.

Параметры:
  • min_elevation (float)

  • threshold (float)

LOWER_LIMIT = -250.0
UPPER_LIMIT = 250.0
prepare_pass(aos_az, max_az, los_az)[исходный код]

Предупредить, если пролёт идёт около юга и может оказаться недостижимым.

Параметры:
  • aos_az (float)

  • max_az (float)

  • los_az (float)

Тип результата:

None

transform_target(sat_az, sat_alt, cur_rot_az, cur_rot_alt)[исходный код]

Вернуть непрерывный азимут рядом с текущим положением, ограничив его аппаратными пределами ±250°.

calculate_delta(target_az, target_alt, cur_az, cur_alt)[исходный код]

Вернуть рассогласование без приведения по модулю 360.

Обе координаты уже в одной непрерывной системе отсчёта и могут быть отрицательными.

На упоре рассогласование по азимуту обнуляется после первой отправки: ротатор в предел уже упёрся, и дальше дельта остаётся большой до конца прохода, заставляя переотправлять одну и ту же команду.