2022-01-18 18:38:05 +00:00
|
|
|
#!/usr/bin/env python3
|
2022-11-19 08:51:48 +00:00
|
|
|
# class taken from darksidelemm
|
2022-01-18 18:38:05 +00:00
|
|
|
# rigctl - https://github.com/darksidelemm/rotctld-web-gui/blob/master/rotatorgui.py#L35
|
|
|
|
#
|
|
|
|
# modified and adjusted to FreeDATA needs by DJ2LS
|
|
|
|
|
2022-12-27 17:17:12 +00:00
|
|
|
import contextlib
|
2022-05-11 22:10:59 +00:00
|
|
|
import socket
|
|
|
|
import time
|
|
|
|
import structlog
|
2022-12-12 11:28:52 +00:00
|
|
|
import threading
|
2023-02-01 14:33:21 +00:00
|
|
|
import static
|
2022-05-11 22:10:59 +00:00
|
|
|
|
2022-01-18 18:38:05 +00:00
|
|
|
# set global hamlib version
|
|
|
|
hamlib_version = 0
|
|
|
|
|
2022-05-09 01:27:24 +00:00
|
|
|
|
2022-05-26 01:23:30 +00:00
|
|
|
class radio:
|
2022-05-11 22:10:59 +00:00
|
|
|
"""rigctld (hamlib) communication class"""
|
2022-05-26 01:23:30 +00:00
|
|
|
|
2022-06-01 00:35:35 +00:00
|
|
|
log = structlog.get_logger("radio (rigctld)")
|
2022-05-26 01:23:30 +00:00
|
|
|
|
2022-01-18 18:38:05 +00:00
|
|
|
def __init__(self, hostname="localhost", port=4532, poll_rate=5, timeout=5):
|
2022-05-28 15:52:05 +00:00
|
|
|
"""Open a connection to rigctld, and test it for validity"""
|
2023-01-04 07:33:25 +00:00
|
|
|
self.ptt_sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
|
|
|
|
self.data_sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
|
2022-03-06 16:23:04 +00:00
|
|
|
|
2023-01-04 07:33:25 +00:00
|
|
|
self.ptt_connected = False
|
|
|
|
self.data_connected = False
|
2022-01-18 18:38:05 +00:00
|
|
|
self.hostname = hostname
|
|
|
|
self.port = port
|
2022-01-24 21:01:01 +00:00
|
|
|
self.connection_attempts = 5
|
2022-01-18 18:38:05 +00:00
|
|
|
|
2022-11-19 08:51:48 +00:00
|
|
|
# class wide variable for some parameters
|
2022-11-19 09:05:17 +00:00
|
|
|
self.bandwidth = ''
|
|
|
|
self.frequency = ''
|
2022-11-19 08:51:48 +00:00
|
|
|
self.mode = ''
|
2023-01-30 11:08:45 +00:00
|
|
|
self.alc = ''
|
2023-01-30 11:31:34 +00:00
|
|
|
self.strength = ''
|
2023-01-30 11:08:45 +00:00
|
|
|
self.rf = ''
|
2022-11-19 08:51:48 +00:00
|
|
|
|
2022-05-26 01:23:30 +00:00
|
|
|
def open_rig(
|
2023-01-31 16:44:46 +00:00
|
|
|
self,
|
|
|
|
rigctld_ip,
|
2023-02-03 07:09:57 +00:00
|
|
|
rigctld_port
|
2022-05-26 01:23:30 +00:00
|
|
|
):
|
2022-03-04 15:50:32 +00:00
|
|
|
"""
|
|
|
|
|
|
|
|
Args:
|
2022-05-09 00:41:49 +00:00
|
|
|
rigctld_ip:
|
|
|
|
rigctld_port:
|
2022-03-04 15:50:32 +00:00
|
|
|
|
|
|
|
Returns:
|
|
|
|
|
|
|
|
"""
|
2022-01-24 21:01:01 +00:00
|
|
|
self.hostname = rigctld_ip
|
2022-01-24 22:29:34 +00:00
|
|
|
self.port = int(rigctld_port)
|
2022-05-26 01:23:30 +00:00
|
|
|
|
2023-01-31 16:44:46 +00:00
|
|
|
# _ptt_connect = self.ptt_connect()
|
|
|
|
# _data_connect = self.data_connect()
|
2023-01-04 07:33:25 +00:00
|
|
|
|
|
|
|
ptt_thread = threading.Thread(target=self.ptt_connect, args=[], daemon=True)
|
|
|
|
ptt_thread.start()
|
|
|
|
|
|
|
|
data_thread = threading.Thread(target=self.data_connect, args=[], daemon=True)
|
|
|
|
data_thread.start()
|
|
|
|
|
|
|
|
# wait some time
|
|
|
|
threading.Event().wait(0.5)
|
|
|
|
|
2023-01-04 18:26:11 +00:00
|
|
|
if self.ptt_connected and self.data_connected:
|
2023-01-04 07:33:25 +00:00
|
|
|
self.log.debug("Rigctl DATA/PTT initialized")
|
2022-01-24 21:01:01 +00:00
|
|
|
return True
|
2022-05-11 22:10:59 +00:00
|
|
|
|
2022-05-26 01:23:30 +00:00
|
|
|
self.log.error(
|
2022-12-01 07:56:21 +00:00
|
|
|
"[RIGCTLD] Can't connect!", ip=self.hostname, port=self.port
|
2022-05-26 01:23:30 +00:00
|
|
|
)
|
2022-05-11 22:10:59 +00:00
|
|
|
return False
|
2022-05-09 00:41:49 +00:00
|
|
|
|
2023-01-04 07:33:25 +00:00
|
|
|
def ptt_connect(self):
|
|
|
|
"""Connect to rigctld instance"""
|
|
|
|
while True:
|
|
|
|
|
|
|
|
if not self.ptt_connected:
|
|
|
|
try:
|
|
|
|
self.ptt_connection = socket.create_connection((self.hostname, self.port))
|
|
|
|
self.ptt_connected = True
|
|
|
|
self.log.info(
|
|
|
|
"[RIGCTLD] Connected PTT instance to rigctld!", ip=self.hostname, port=self.port
|
|
|
|
)
|
|
|
|
except Exception as err:
|
|
|
|
# ConnectionRefusedError: [Errno 111] Connection refused
|
|
|
|
self.close_rig()
|
|
|
|
self.log.warning(
|
|
|
|
"[RIGCTLD] PTT Reconnect...",
|
|
|
|
ip=self.hostname,
|
|
|
|
port=self.port,
|
|
|
|
e=err,
|
|
|
|
)
|
|
|
|
|
|
|
|
threading.Event().wait(0.5)
|
|
|
|
|
|
|
|
def data_connect(self):
|
2022-03-06 16:23:04 +00:00
|
|
|
"""Connect to rigctld instance"""
|
2023-01-04 07:33:25 +00:00
|
|
|
while True:
|
|
|
|
if not self.data_connected:
|
|
|
|
try:
|
|
|
|
self.data_connection = socket.create_connection((self.hostname, self.port))
|
|
|
|
self.data_connected = True
|
|
|
|
self.log.info(
|
|
|
|
"[RIGCTLD] Connected DATA instance to rigctld!", ip=self.hostname, port=self.port
|
|
|
|
)
|
|
|
|
except Exception as err:
|
|
|
|
# ConnectionRefusedError: [Errno 111] Connection refused
|
|
|
|
self.close_rig()
|
|
|
|
self.log.warning(
|
|
|
|
"[RIGCTLD] DATA Reconnect...",
|
|
|
|
ip=self.hostname,
|
|
|
|
port=self.port,
|
|
|
|
e=err,
|
|
|
|
)
|
|
|
|
threading.Event().wait(0.5)
|
|
|
|
|
|
|
|
def close_rig(self):
|
|
|
|
""" """
|
|
|
|
self.ptt_sock.close()
|
|
|
|
self.data_sock.close()
|
|
|
|
self.ptt_connected = False
|
|
|
|
self.data_connected = False
|
|
|
|
|
|
|
|
def send_ptt_command(self, command, expect_answer) -> bytes:
|
|
|
|
"""Send a command to the connected rotctld instance,
|
|
|
|
and return the return value.
|
|
|
|
|
|
|
|
Args:
|
|
|
|
command:
|
|
|
|
|
|
|
|
"""
|
|
|
|
if self.ptt_connected:
|
2022-01-24 21:01:01 +00:00
|
|
|
try:
|
2023-01-04 07:33:25 +00:00
|
|
|
self.ptt_connection.sendall(command + b"\n")
|
|
|
|
except Exception:
|
2022-05-26 01:23:30 +00:00
|
|
|
self.log.warning(
|
2023-01-04 07:33:25 +00:00
|
|
|
"[RIGCTLD] Command not executed!",
|
|
|
|
command=command,
|
2022-05-26 01:23:30 +00:00
|
|
|
ip=self.hostname,
|
|
|
|
port=self.port,
|
|
|
|
)
|
2023-01-04 07:33:25 +00:00
|
|
|
self.ptt_connected = False
|
|
|
|
return b""
|
2022-01-18 18:38:05 +00:00
|
|
|
|
2023-01-04 07:33:25 +00:00
|
|
|
def send_data_command(self, command, expect_answer) -> bytes:
|
2022-03-04 15:50:32 +00:00
|
|
|
"""Send a command to the connected rotctld instance,
|
2022-01-18 18:38:05 +00:00
|
|
|
and return the return value.
|
2022-03-04 15:50:32 +00:00
|
|
|
|
|
|
|
Args:
|
2022-05-09 00:41:49 +00:00
|
|
|
command:
|
2022-03-04 15:50:32 +00:00
|
|
|
|
2022-01-18 18:38:05 +00:00
|
|
|
"""
|
2023-01-04 07:33:25 +00:00
|
|
|
if self.data_connected:
|
2023-01-31 16:44:46 +00:00
|
|
|
self.data_connection.setblocking(False)
|
|
|
|
self.data_connection.settimeout(0.05)
|
2022-01-24 21:01:01 +00:00
|
|
|
try:
|
2023-01-04 07:33:25 +00:00
|
|
|
self.data_connection.sendall(command + b"\n")
|
2023-01-31 16:44:46 +00:00
|
|
|
|
|
|
|
|
2022-05-26 01:23:30 +00:00
|
|
|
except Exception:
|
|
|
|
self.log.warning(
|
|
|
|
"[RIGCTLD] Command not executed!",
|
|
|
|
command=command,
|
|
|
|
ip=self.hostname,
|
|
|
|
port=self.port,
|
|
|
|
)
|
2023-01-04 07:33:25 +00:00
|
|
|
self.data_connected = False
|
2022-01-18 18:38:05 +00:00
|
|
|
|
2022-01-24 21:01:01 +00:00
|
|
|
try:
|
2023-01-04 07:33:25 +00:00
|
|
|
# recv seems to be blocking so in case of ptt we don't need the response
|
2022-12-27 16:48:06 +00:00
|
|
|
# maybe this speeds things up and avoids blocking states
|
2023-01-31 16:44:46 +00:00
|
|
|
recv = True
|
|
|
|
data = b''
|
|
|
|
|
|
|
|
while recv:
|
|
|
|
try:
|
|
|
|
|
2023-02-14 21:02:30 +00:00
|
|
|
data = self.data_connection.recv(4800)
|
2023-01-31 16:44:46 +00:00
|
|
|
|
|
|
|
except socket.timeout:
|
|
|
|
recv = False
|
|
|
|
|
|
|
|
return data
|
|
|
|
|
|
|
|
# return self.data_connection.recv(64) if expect_answer else True
|
2022-05-26 01:23:30 +00:00
|
|
|
except Exception:
|
|
|
|
self.log.warning(
|
|
|
|
"[RIGCTLD] No command response!",
|
|
|
|
command=command,
|
|
|
|
ip=self.hostname,
|
|
|
|
port=self.port,
|
|
|
|
)
|
2023-01-04 07:33:25 +00:00
|
|
|
self.data_connected = False
|
2022-05-25 22:27:33 +00:00
|
|
|
return b""
|
|
|
|
|
2022-11-18 12:08:37 +00:00
|
|
|
def get_status(self):
|
|
|
|
""" """
|
2023-01-04 07:33:25 +00:00
|
|
|
return "connected" if self.data_connected and self.ptt_connected else "unknown/disconnected"
|
2022-11-19 09:11:08 +00:00
|
|
|
|
2023-01-30 11:08:45 +00:00
|
|
|
def get_level(self):
|
|
|
|
try:
|
|
|
|
data = self.send_data_command(b"l RF", True)
|
|
|
|
data = data.split(b"\n")
|
|
|
|
rf = data[0].decode("utf-8")
|
|
|
|
if 'RPRT' not in rf:
|
|
|
|
try:
|
2023-01-31 16:44:46 +00:00
|
|
|
self.rf = str(rf)
|
2023-01-30 11:08:45 +00:00
|
|
|
except ValueError:
|
|
|
|
self.rf = str(rf)
|
|
|
|
|
|
|
|
return self.rf
|
|
|
|
except Exception:
|
|
|
|
return self.rf
|
|
|
|
|
2023-01-30 11:31:34 +00:00
|
|
|
def get_strength(self):
|
2023-01-30 11:08:45 +00:00
|
|
|
try:
|
2023-01-30 11:31:34 +00:00
|
|
|
data = self.send_data_command(b"l STRENGTH", True)
|
2023-01-30 11:08:45 +00:00
|
|
|
data = data.split(b"\n")
|
2023-01-30 11:31:34 +00:00
|
|
|
strength = data[0].decode("utf-8")
|
|
|
|
if 'RPRT' not in strength:
|
2023-01-30 11:08:45 +00:00
|
|
|
try:
|
2023-01-30 11:31:34 +00:00
|
|
|
self.strength = str(strength)
|
2023-01-30 11:08:45 +00:00
|
|
|
except ValueError:
|
2023-01-30 11:31:34 +00:00
|
|
|
self.strength = str(strength)
|
2023-01-30 11:08:45 +00:00
|
|
|
|
2023-01-30 11:31:34 +00:00
|
|
|
return self.strength
|
2023-01-30 11:08:45 +00:00
|
|
|
except Exception:
|
2023-01-30 11:31:34 +00:00
|
|
|
return self.strength
|
2023-01-30 11:08:45 +00:00
|
|
|
|
|
|
|
def get_alc(self):
|
|
|
|
try:
|
|
|
|
data = self.send_data_command(b"l ALC", True)
|
|
|
|
data = data.split(b"\n")
|
|
|
|
alc = data[0].decode("utf-8")
|
|
|
|
if 'RPRT' not in alc:
|
|
|
|
try:
|
2023-02-01 12:41:34 +00:00
|
|
|
alc = float(alc)
|
2023-02-03 12:30:42 +00:00
|
|
|
self.alc = alc if alc != 0.0 else static.HAMLIB_ALC
|
2023-01-30 11:08:45 +00:00
|
|
|
except ValueError:
|
2023-02-01 12:47:24 +00:00
|
|
|
self.alc = 0.0
|
2023-01-30 11:08:45 +00:00
|
|
|
|
|
|
|
return self.alc
|
|
|
|
except Exception:
|
|
|
|
return self.alc
|
|
|
|
|
2022-01-18 18:38:05 +00:00
|
|
|
def get_mode(self):
|
2022-03-04 15:50:32 +00:00
|
|
|
""" """
|
2022-01-24 21:01:01 +00:00
|
|
|
try:
|
2023-01-04 07:33:25 +00:00
|
|
|
data = self.send_data_command(b"m", True)
|
2022-05-26 01:23:30 +00:00
|
|
|
data = data.split(b"\n")
|
2022-11-19 09:14:59 +00:00
|
|
|
data = data[0].decode("utf-8")
|
|
|
|
if 'RPRT' not in data:
|
2022-12-27 16:57:04 +00:00
|
|
|
try:
|
|
|
|
data = int(data)
|
|
|
|
except ValueError:
|
|
|
|
self.mode = str(data)
|
2022-11-19 08:51:48 +00:00
|
|
|
|
2022-11-19 09:05:17 +00:00
|
|
|
return self.mode
|
2022-05-26 01:23:30 +00:00
|
|
|
except Exception:
|
2022-11-19 09:14:59 +00:00
|
|
|
return self.mode
|
2022-05-09 01:27:24 +00:00
|
|
|
|
2022-05-28 12:08:33 +00:00
|
|
|
def get_bandwidth(self):
|
2022-03-04 15:50:32 +00:00
|
|
|
""" """
|
2022-01-24 21:01:01 +00:00
|
|
|
try:
|
2023-01-04 07:33:25 +00:00
|
|
|
data = self.send_data_command(b"m", True)
|
2022-05-26 01:23:30 +00:00
|
|
|
data = data.split(b"\n")
|
2022-11-19 09:14:59 +00:00
|
|
|
data = data[1].decode("utf-8")
|
2022-11-19 08:51:48 +00:00
|
|
|
|
2022-12-27 17:17:12 +00:00
|
|
|
if 'RPRT' not in data and data not in ['']:
|
|
|
|
with contextlib.suppress(ValueError):
|
2022-12-27 16:57:04 +00:00
|
|
|
self.bandwidth = int(data)
|
2022-11-19 09:05:17 +00:00
|
|
|
return self.bandwidth
|
2022-05-26 01:23:30 +00:00
|
|
|
except Exception:
|
2022-11-19 09:14:59 +00:00
|
|
|
return self.bandwidth
|
2022-05-09 00:41:49 +00:00
|
|
|
|
2022-01-18 18:38:05 +00:00
|
|
|
def get_frequency(self):
|
2022-03-04 15:50:32 +00:00
|
|
|
""" """
|
2022-01-24 21:01:01 +00:00
|
|
|
try:
|
2023-01-04 07:33:25 +00:00
|
|
|
data = self.send_data_command(b"f", True)
|
2022-11-19 09:05:17 +00:00
|
|
|
data = data.decode("utf-8")
|
2022-12-27 17:04:29 +00:00
|
|
|
if 'RPRT' not in data and data not in [0, '0', '']:
|
2022-12-27 17:17:12 +00:00
|
|
|
with contextlib.suppress(ValueError):
|
|
|
|
data = int(data)
|
|
|
|
# make sure we have a frequency and not bandwidth
|
|
|
|
if data >= 10000:
|
|
|
|
self.frequency = data
|
2022-11-19 09:05:17 +00:00
|
|
|
return self.frequency
|
2022-05-26 01:23:30 +00:00
|
|
|
except Exception:
|
2022-11-19 09:14:59 +00:00
|
|
|
return self.frequency
|
2022-05-09 00:41:49 +00:00
|
|
|
|
2022-01-18 18:38:05 +00:00
|
|
|
def get_ptt(self):
|
2022-03-04 15:50:32 +00:00
|
|
|
""" """
|
2022-01-24 21:01:01 +00:00
|
|
|
try:
|
2023-02-05 13:18:22 +00:00
|
|
|
return self.send_data_command(b"t", True)
|
2022-05-26 01:23:30 +00:00
|
|
|
except Exception:
|
2022-01-24 21:01:01 +00:00
|
|
|
return False
|
2022-05-09 00:41:49 +00:00
|
|
|
|
2022-01-24 21:01:01 +00:00
|
|
|
def set_ptt(self, state):
|
2022-03-04 15:50:32 +00:00
|
|
|
"""
|
|
|
|
|
|
|
|
Args:
|
2022-05-09 00:41:49 +00:00
|
|
|
state:
|
2022-03-04 15:50:32 +00:00
|
|
|
|
|
|
|
Returns:
|
|
|
|
|
|
|
|
"""
|
2022-01-24 21:01:01 +00:00
|
|
|
try:
|
|
|
|
if state:
|
2023-01-04 07:33:25 +00:00
|
|
|
self.send_ptt_command(b"T 1", False)
|
2022-01-24 21:01:01 +00:00
|
|
|
else:
|
2023-01-04 07:33:25 +00:00
|
|
|
self.send_ptt_command(b"T 0", False)
|
2022-05-09 00:41:49 +00:00
|
|
|
return state
|
2022-05-26 01:23:30 +00:00
|
|
|
except Exception:
|
2022-01-24 21:01:01 +00:00
|
|
|
return False
|
2023-01-04 18:26:11 +00:00
|
|
|
|
|
|
|
def set_frequency(self, frequency):
|
|
|
|
"""
|
|
|
|
|
|
|
|
Args:
|
|
|
|
frequency:
|
|
|
|
|
|
|
|
Returns:
|
|
|
|
|
|
|
|
"""
|
|
|
|
try:
|
|
|
|
command = bytes(f"F {frequency}", "utf-8")
|
|
|
|
self.send_data_command(command, False)
|
|
|
|
except Exception:
|
|
|
|
return False
|
2023-01-04 19:12:03 +00:00
|
|
|
|
|
|
|
def set_mode(self, mode):
|
|
|
|
"""
|
|
|
|
|
|
|
|
Args:
|
|
|
|
mode:
|
|
|
|
|
|
|
|
Returns:
|
|
|
|
|
|
|
|
"""
|
|
|
|
try:
|
|
|
|
command = bytes(f"M {mode} {self.bandwidth}", "utf-8")
|
|
|
|
self.send_data_command(command, False)
|
|
|
|
except Exception:
|
|
|
|
return False
|