Skip to content
Open
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
236 changes: 140 additions & 96 deletions carryout_rotor.py
Original file line number Diff line number Diff line change
@@ -1,113 +1,157 @@
#Python program to control Winegard Carryout as an AZ/EL Rotor from Gpredict
#Version 1.0
#Gabe Emerson / Saveitforparts 2024, Email: gabe@saveitforparts.com
# Python program to control Winegard Carryout as an AZ/EL Rotor from Gpredict
# Version 1.0
# Gabe Emerson / Saveitforparts 2024, Email: gabe@saveitforparts.com

import argparse
import socket
import sys

import serial
import socket
import regex as re
import serial


#initialize some variables
current_az = 0.0
def parse_arguments():
"""Parse command-line arguments for configuring the Winegard Carryout controller.

This function defines and parses the command-line arguments required to configure
the serial communication and TCP/IP settings for interfacing with the Winegard Carryout
antenna and Gpredict.

Returns:
argparse.Namespace: Parsed command-line arguments containing:
- port (str): Serial port to communicate with the Winegard Carryout.
- baudrate (int): Baudrate for the serial communication.
- listen_ip (str): IP address to listen for Gpredict commands.
- listen_port (int): Port to listen for Gpredict commands.
"""
parser = argparse.ArgumentParser(
description="Control Winegard Carryout as an AZ/EL Rotor from Gpredict.",
)
parser.add_argument(
"--port",
type=str,
default="/dev/ttyUSB0",
help="Serial port to communicate with the Winegard Carryout.",
)
parser.add_argument(
"--baudrate",
type=int,
default=57600,
help="Baudrate for the serial communication.",
)
parser.add_argument(
"--listen_ip",
type=str,
default="127.0.0.1",
help="IP address to listen for Gpredict commands.",
)
parser.add_argument(
"--listen_port",
type=int,
default=4533,
help="Port to listen for Gpredict commands.",
)
return parser.parse_args()


# Parse command-line arguments
args = parse_arguments()

# initialize some variables
current_az = 0.0
current_el = 0.0
index = 0

#define "carryout" as the serial port device to interface with
# define "carryout" as the serial port device to interface with
carryout = serial.Serial(
port='/dev/ttyUSB0', #pass this from command line in future?
baudrate = 57600,
parity=serial.PARITY_NONE,
stopbits=serial.STOPBITS_ONE,
bytesize=serial.EIGHTBITS,
timeout=1)

print ('Carryout antenna connected on ', carryout.port)

carryout.write(bytes(b'q\r')) #go back to root menu in case firmware was left in a submenu
carryout.write(bytes(b'\r')) #clear firmware prompt to avoid unknown command errors


#listen to local port for rotctld commands
listen_ip = '127.0.0.1' #listen on localhost
listen_port = 4533 #pass this from command line in future?
port=args.port,
baudrate=args.baudrate,
parity=serial.PARITY_NONE,
stopbits=serial.STOPBITS_ONE,
bytesize=serial.EIGHTBITS,
timeout=1,
)

print("Carryout antenna connected on ", carryout.port)

carryout.write(b"q\r") # go back to root menu in case firmware was left in a submenu
carryout.write(b"\r") # clear firmware prompt to avoid unknown command errors


# listen to local port for rotctld commands
listen_ip = args.listen_ip # Use the IP address provided via command-line arguments
listen_port = args.listen_port # Use the port provided via command-line arguments
client_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
client_socket.bind((listen_ip, listen_port))
client_socket.listen(1)

print ('Listening for rotor commands on', listen_ip, ':', listen_port)
print(f"Listening for rotor commands on {listen_ip} : {listen_port}")
conn, addr = client_socket.accept()
print ('Connection from ',addr)
print(f"Connection from {addr}")


#Would be nice to get initial / resting position from Carryout firmware
#I have not found a way to do this, just live position while motors are running
# Would be nice to get initial / resting position from Carryout firmware
# I have not found a way to do this, just live position while motors are running


#pass rotor commands to Carryout
# pass rotor commands to Carryout
while 1:
data = conn.recv(100) #get Gpredict's message
if not data:
break

cmd = data.decode("utf-8").strip().split(" ") #grab the incoming command

#print("Received: ",cmd) #debugging, what did Gpredict send?

if cmd[0] == "p": #Gpredict is requesting current position
response = "{}\n{}\n".format(current_az, current_el)
conn.send(response.encode('utf-8'))

elif cmd[0] == "P": #Gpredict is sending desired position
target_az = float(cmd[1])
target_el = float(cmd[2])
print(' Move antenna to:', target_az, ' ', target_el, end="\r")


#tell Carryout to move to target position
carryout.write(bytes(b'target\r'))
command = ('g ' + str(target_az) + ' ' + str(target_el) + '\r').encode('ascii')
carryout.write(command)


#read live position updates from Carryout
reply = carryout.read(100).decode().strip() #read dish response
header, *readings = reply.split(" ") #Split into list
[re.sub('[^a-z0-9]+', '', _) for _ in readings] #clean out garbage chars
#print('Carryout replied:', readings) #debugging


#massage messy output into az/el
while index < len(readings):
if readings[index] == "el" and (index+2) < len(readings):
current_az = readings[index-3]
current_el = readings[index+2]
current_el = current_el[:4] #strip off excess garbage
current_az = int(current_az)/100 #convert to format Gpredict expects
current_el = int(current_el)/100 #(Add the decimal)
index +=1 #maybe unnecessary
break
else:
index +=1
continue

#Tell Gpredict things went correctly
response="RPRT 0\n " #Everything's under control, situation normal
conn.send(response.encode('utf-8'))

carryout.write(bytes(b'q\r')) #go back to Carryout's root menu


elif cmd[0] == "S": #Gpredict says to stop
print('Gpredict disconnected, exiting') #Do we want to do something else with this?
conn.close()
carryout.close()
exit()
else:
print('Exiting')
conn.close()
carryout.close()
exit()





data = conn.recv(100) # get Gpredict's message
if not data:
break

cmd = data.decode("utf-8").strip().split(" ") # grab the incoming command

# print(f"Received: {cmd}") #debugging, what did Gpredict send?

if cmd[0] == "p": # Gpredict is requesting current position
response = f"{current_az}\n{current_el}\n"
conn.send(response.encode("utf-8"))

elif cmd[0] == "P": # Gpredict is sending desired position
target_az = float(cmd[1])
target_el = float(cmd[2])
print(f"Move antenna to: {target_az} {target_el}", end="\r")

# tell Carryout to move to target position
carryout.write(b"target\r")
command = (f"g {target_az} {target_el}\r").encode("ascii")
carryout.write(command)

# read live position updates from Carryout
reply = carryout.read(100).decode().strip() # read dish response
header, *readings = reply.split(" ") # Split into list
[re.sub("[^a-z0-9]+", "", _) for _ in readings] # clean out garbage chars

# print(f"Carryout replied: {readings}") # debugging

# massage messy output into az/el
while index < len(readings):
if readings[index] == "el" and (index + 2) < len(readings):
current_az = readings[index - 3]
current_el = readings[index + 2]
current_el = current_el[:4] # strip off excess garbage
current_az = int(current_az) / 100 # convert to format Gpredict expects
current_el = int(current_el) / 100 # (Add the decimal)
index += 1 # maybe unnecessary
break
else:
index += 1
continue

# Tell Gpredict things went correctly
response = "RPRT 0\n " # Everything's under control, situation normal
conn.send(response.encode("utf-8"))

carryout.write(b"q\r") # go back to Carryout's root menu

elif cmd[0] == "S": # Gpredict says to stop
print("Gpredict disconnected, exiting") # Do we want to do something else with this?
conn.close()
carryout.close()
sys.exit()
else:
print("Exiting")
conn.close()
carryout.close()
sys.exit()