← На главную

Управление поворотным устройством при помощи GPredict

На прошлой неделе мы рассмотрели самодельное поворотное устройство для СВЧ-антенн. Данная поворотка может быть использована для приема сигналов с LEO-спутников. Только сначала ее нужно подружить с программой GPredict.

Первым делом добавим поворотку в GPredict. Открываем Edit → Preferences → Interfaces → Rotators и нажимаем Add New. Имя устройства вводим произвольное. В качестве хоста и порта пусть будут localhost и 4533. В выпадающем списке Az Type выбираем 0-180-360. Ниже в диапазонах наведения указываем 0°..360° для азимута и 0°..180° для элевации. Нажимаем OK. Поворотка добавлена.

GPredict будет ходить на указанные хост и порт по TCP и давать команды поворотке. Насколько я смог выяснить, протокол не документирован. А значит, изучать его предстоит по исходникам. К слову, реализация содержит ошибки. С сервером, запущенным на удаленной машине, GPredict работать не будет. Звучит, как отличная возможность законтрибьютить в open-source.

Был написан скрипт, который переводит протокол GPredict в команды, понятные ротатору:

#!/usr/bin/env python3 # antenna-rotator-control.py ~ (c) Alex R2AUK 2026 # https://eax.me/2026/2026-08-26-antenna-rotator-control.html import argparse import serial import socket import sys import time BUFFER_SIZE = 100 def uart_get_current(ser): ser.write(b"get current\n") response = ser.readline().decode('utf-8', errors='replace').strip() print(f"uart_get_current(), response: {response}") # expected: "AZ=45.0 EL=55.8" parts = response.split() az = float(parts[0].split('=')[1]) el = float(parts[1].split('=')[1]) return az, el def uart_set_target(ser, az, el): cmd = f"set target {az:.1f} {el:.1f}\n" ser.write(cmd.encode('utf-8')) response = ser.readline().decode('utf-8', errors='replace').strip() print(f"uart_set_target(), response: {response}") return response == "OK" def uart_set_shortpath(ser, enabled): cmd = b"set shortpath on\n" if enabled else b"set shortpath off\n" ser.write(cmd) response = ser.readline().decode('utf-8', errors='replace').strip() print(f"uart_set_shortpath(), response: {response}") return response == "OK" parser = argparse.ArgumentParser(description='Antenna rotator control bridge') parser.add_argument('-d', default='/dev/ttyUSB0', metavar='DEVICE', help='Serial device (default: /dev/ttyUSB0)') parser.add_argument('-b', default=9600, type=int, metavar='BAUDRATE', help='Baud rate (default: 9600)') parser.add_argument('-l', default='127.0.0.1', metavar='HOST', help='TCP listen address (default: 127.0.0.1)') parser.add_argument('-p', default=4533, type=int, metavar='PORT', help='TCP listen port (default: 4533)') parser.add_argument('-s', action='store_true', default=False, help='Enable shortest path algorithm (default: off)') args = parser.parse_args() print(f"Opening serial port {args.d} at {args.b} baud...") try: ser = serial.Serial(args.d, args.b, timeout=2) except serial.SerialException as e: print(f"Error: {e}") sys.exit(1) # Discard any garbage data that may arrive right after connecting time.sleep(2.0) ser.reset_input_buffer() print("Serial port opened successfully") shortpath_str = 'on' if args.s else 'off' print(f"Setting shortpath: {shortpath_str}") if not uart_set_shortpath(ser, args.s): print("Error: failed to set shortpath") sys.exit(1) s = socket.socket(socket.AF_INET, socket.SOCK_STREAM) s.bind((args.l, args.p)) s.listen(1) while True: print("Waiting for connection...") conn, addr = s.accept() print(f"Connection from: {addr}") while True: raw = conn.recv(BUFFER_SIZE) if not raw: break data = raw.decode('utf-8', errors='replace').strip() if data == "p": az, el = uart_get_current(ser) print(f"pos query: az={az} el={el}") conn.send(f"{az}\n{el}\n".encode()) elif data.startswith("P "): values = data.split(" ") az = float(values[1]) el = float(values[2]) print(f"set target: az={az} el={el}") uart_set_target(ser, az, el) conn.send(b"RPRT 0\n") elif data == "S": print("stop command, doing nothing") conn.send(b"RPRT 0\n") elif data == "q": print("close command") break else: print(f"unknown command: {data}") break conn.close() print("Connection closed")

Подключаем поворотку к компьютеру. Запускаем скрипт. В интерфейсе GPredict справа нажимаем на «сэндвич». В появившимся меню выбираем Configure. Для теста добавляем десяток-другой спутников Iridium. Так в GPredict постоянно будет хотя бы один пролетающий над нами спутник.

Далее в том же «сэндвиче» выбираем Antenna Control. В списке Target выбираем один из пролетающих спутников. Нажимаем Track. В списке Device выбираем ранее созданную поворотку. Нажимаем Engage:

Управление поворотным устройством при помощи GPredict

В выводе скрипта видим, как он принимает команды TCP-протокола и переводит их в команды UART-протокола. Поворотное устройство наводится на спутник.

Обратите внимание на разницу между положением спутника (az 47.13°, el 74.37°) и поворотки (az 228.21°, el 105.52°). Это не ошибка, а крутая фича. GPredict управляет повороткой так, чтобы она не пересекала нулевой азимут в рамках прохода спутника. Таким образом, использовать shortpath в сочетании с GPredict не нужно и даже вредно. Он приведет лишь к наматыванию проводов.

Осталось сделать антенну, и можно принимать сигналы со спутников. Этим мы займемся в следующий раз.