2022-05-11 22:10:59 +00:00
|
|
|
import socket
|
|
|
|
import structlog
|
2024-02-10 20:28:07 +00:00
|
|
|
import helpers
|
2024-01-12 16:00:28 +00:00
|
|
|
import threading
|
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
|
|
|
|
2024-02-10 20:28:07 +00:00
|
|
|
def __init__(self, config, states, hostname="localhost", port=4532, timeout=5):
|
2022-01-18 18:38:05 +00:00
|
|
|
self.hostname = hostname
|
|
|
|
self.port = port
|
2024-01-12 14:51:23 +00:00
|
|
|
self.timeout = timeout
|
|
|
|
self.states = states
|
2024-02-10 20:28:07 +00:00
|
|
|
self.config = config
|
2024-01-12 14:51:23 +00:00
|
|
|
|
|
|
|
self.connection = None
|
|
|
|
self.connected = False
|
2024-01-12 16:00:28 +00:00
|
|
|
self.await_response = threading.Event()
|
|
|
|
self.await_response.set()
|
2024-01-12 14:51:23 +00:00
|
|
|
|
|
|
|
self.parameters = {
|
|
|
|
'frequency': '---',
|
|
|
|
'mode': '---',
|
|
|
|
'alc': '---',
|
|
|
|
'strength': '---',
|
|
|
|
'bandwidth': '---',
|
|
|
|
'rf': '---',
|
|
|
|
'ptt': False # Initial PTT state is set to False
|
|
|
|
}
|
|
|
|
|
2024-02-10 20:28:07 +00:00
|
|
|
# start rigctld...
|
2024-02-11 09:00:57 +00:00
|
|
|
if self.config["RADIO"]["control"] in ["rigctld_bundle"]:
|
2024-02-11 08:17:52 +00:00
|
|
|
self.start_service()
|
2024-02-10 20:28:07 +00:00
|
|
|
|
2024-01-12 14:51:23 +00:00
|
|
|
# connect to radio
|
|
|
|
self.connect()
|
|
|
|
|
|
|
|
def connect(self):
|
|
|
|
try:
|
|
|
|
self.connection = socket.create_connection((self.hostname, self.port), timeout=self.timeout)
|
|
|
|
self.connected = True
|
|
|
|
self.states.set("radio_status", True)
|
|
|
|
self.log.info(f"[RIGCTLD] Connected to rigctld at {self.hostname}:{self.port}")
|
|
|
|
except Exception as err:
|
|
|
|
self.log.warning(f"[RIGCTLD] Failed to connect to rigctld: {err}")
|
|
|
|
self.connected = False
|
|
|
|
self.states.set("radio_status", False)
|
|
|
|
|
|
|
|
def disconnect(self):
|
|
|
|
self.connected = False
|
2024-02-10 20:44:02 +00:00
|
|
|
if self.connection:
|
|
|
|
self.connection.close()
|
2024-01-12 14:51:23 +00:00
|
|
|
del self.connection
|
|
|
|
self.connection = None
|
|
|
|
self.states.set("radio_status", False)
|
|
|
|
self.parameters = {
|
|
|
|
'frequency': '---',
|
|
|
|
'mode': '---',
|
|
|
|
'alc': '---',
|
|
|
|
'strength': '---',
|
|
|
|
'bandwidth': '---',
|
|
|
|
'rf': '---',
|
|
|
|
'ptt': False # Initial PTT state is set to False
|
|
|
|
}
|
|
|
|
|
|
|
|
def send_command(self, command) -> str:
|
|
|
|
if self.connected:
|
2024-01-12 16:00:28 +00:00
|
|
|
# wait if we have another command awaiting its response...
|
2024-02-07 21:23:49 +00:00
|
|
|
# we need to set a timeout for avoiding a blocking state
|
|
|
|
self.await_response.wait(timeout=1)
|
2024-01-12 16:00:28 +00:00
|
|
|
|
2024-01-12 14:51:23 +00:00
|
|
|
try:
|
2024-01-12 16:00:28 +00:00
|
|
|
self.await_response = threading.Event()
|
2024-01-12 14:51:23 +00:00
|
|
|
self.connection.sendall(command.encode('utf-8') + b"\n")
|
|
|
|
response = self.connection.recv(1024)
|
2024-01-12 16:00:28 +00:00
|
|
|
self.await_response.set()
|
2024-02-22 09:26:18 +00:00
|
|
|
stripped_result = response.decode('utf-8').strip()
|
|
|
|
if 'RPRT' in stripped_result:
|
|
|
|
return None
|
|
|
|
return stripped_result
|
|
|
|
|
2024-01-12 14:51:23 +00:00
|
|
|
except Exception as err:
|
2024-01-12 16:00:28 +00:00
|
|
|
self.log.warning(f"[RIGCTLD] Error sending command [{command}] to rigctld: {err}")
|
2024-01-12 14:51:23 +00:00
|
|
|
self.connected = False
|
|
|
|
return ""
|
2022-11-19 08:51:48 +00:00
|
|
|
|
2024-01-12 14:51:23 +00:00
|
|
|
def set_ptt(self, state):
|
|
|
|
"""Set the PTT (Push-to-Talk) state.
|
2022-03-04 15:50:32 +00:00
|
|
|
|
|
|
|
Args:
|
2024-01-12 14:51:23 +00:00
|
|
|
state (bool): True to enable PTT, False to disable.
|
2022-03-04 15:50:32 +00:00
|
|
|
|
|
|
|
Returns:
|
2024-01-12 14:51:23 +00:00
|
|
|
bool: True if the PTT state was set successfully, False otherwise.
|
2022-03-04 15:50:32 +00:00
|
|
|
"""
|
2024-01-12 14:51:23 +00:00
|
|
|
if self.connected:
|
|
|
|
try:
|
|
|
|
if state:
|
|
|
|
self.send_command('T 1') # Enable PTT
|
|
|
|
else:
|
|
|
|
self.send_command('T 0') # Disable PTT
|
|
|
|
self.parameters['ptt'] = state # Update PTT state in parameters
|
|
|
|
return True
|
|
|
|
except Exception as err:
|
|
|
|
self.log.warning(f"[RIGCTLD] Error setting PTT state: {err}")
|
|
|
|
self.connected = False
|
2022-05-11 22:10:59 +00:00
|
|
|
return False
|
2022-05-09 00:41:49 +00:00
|
|
|
|
2024-01-12 14:51:23 +00:00
|
|
|
def set_mode(self, mode):
|
|
|
|
"""Set the mode.
|
2023-01-04 07:33:25 +00:00
|
|
|
|
|
|
|
Args:
|
2024-01-12 14:51:23 +00:00
|
|
|
mode (str): The mode to set.
|
2023-01-04 07:33:25 +00:00
|
|
|
|
2024-01-12 14:51:23 +00:00
|
|
|
Returns:
|
|
|
|
bool: True if the mode was set successfully, False otherwise.
|
2023-01-04 07:33:25 +00:00
|
|
|
"""
|
2024-01-12 14:51:23 +00:00
|
|
|
if self.connected:
|
2022-01-24 21:01:01 +00:00
|
|
|
try:
|
2024-01-12 19:21:22 +00:00
|
|
|
command = f"M {mode} 0"
|
2024-01-12 14:51:23 +00:00
|
|
|
self.send_command(command)
|
|
|
|
self.parameters['mode'] = mode
|
|
|
|
return True
|
|
|
|
except Exception as err:
|
|
|
|
self.log.warning(f"[RIGCTLD] Error setting mode: {err}")
|
|
|
|
self.connected = False
|
|
|
|
return False
|
2022-01-18 18:38:05 +00:00
|
|
|
|
2024-01-12 14:51:23 +00:00
|
|
|
def set_frequency(self, frequency):
|
|
|
|
"""Set the frequency.
|
2022-03-04 15:50:32 +00:00
|
|
|
|
|
|
|
Args:
|
2024-01-12 14:51:23 +00:00
|
|
|
frequency (str): The frequency to set.
|
2022-03-04 15:50:32 +00:00
|
|
|
|
2024-01-12 14:51:23 +00:00
|
|
|
Returns:
|
|
|
|
bool: True if the frequency was set successfully, False otherwise.
|
2022-01-18 18:38:05 +00:00
|
|
|
"""
|
2024-01-12 14:51:23 +00:00
|
|
|
if self.connected:
|
2022-01-24 21:01:01 +00:00
|
|
|
try:
|
2024-01-12 14:51:23 +00:00
|
|
|
command = f"F {frequency}"
|
|
|
|
self.send_command(command)
|
|
|
|
self.parameters['frequency'] = frequency
|
|
|
|
return True
|
|
|
|
except Exception as err:
|
|
|
|
self.log.warning(f"[RIGCTLD] Error setting frequency: {err}")
|
|
|
|
self.connected = False
|
|
|
|
return False
|
2022-05-09 00:41:49 +00:00
|
|
|
|
2024-01-12 14:51:23 +00:00
|
|
|
def set_bandwidth(self, bandwidth):
|
|
|
|
"""Set the bandwidth.
|
2022-03-04 15:50:32 +00:00
|
|
|
|
|
|
|
Args:
|
2024-01-12 14:51:23 +00:00
|
|
|
bandwidth (str): The bandwidth to set.
|
2022-03-04 15:50:32 +00:00
|
|
|
|
|
|
|
Returns:
|
2024-01-12 14:51:23 +00:00
|
|
|
bool: True if the bandwidth was set successfully, False otherwise.
|
2022-03-04 15:50:32 +00:00
|
|
|
"""
|
2024-01-12 14:51:23 +00:00
|
|
|
if self.connected:
|
|
|
|
try:
|
|
|
|
command = f"M {self.parameters['mode']} {bandwidth}"
|
|
|
|
self.send_command(command)
|
|
|
|
self.parameters['bandwidth'] = bandwidth
|
|
|
|
return True
|
|
|
|
except Exception as err:
|
|
|
|
self.log.warning(f"[RIGCTLD] Error setting bandwidth: {err}")
|
|
|
|
self.connected = False
|
|
|
|
return False
|
2023-01-04 18:26:11 +00:00
|
|
|
|
2024-01-12 19:21:22 +00:00
|
|
|
def set_rf_level(self, rf):
|
2024-01-12 14:51:23 +00:00
|
|
|
"""Set the RF.
|
2023-01-04 18:26:11 +00:00
|
|
|
|
|
|
|
Args:
|
2024-01-12 14:51:23 +00:00
|
|
|
rf (str): The RF to set.
|
2023-01-04 18:26:11 +00:00
|
|
|
|
|
|
|
Returns:
|
2024-01-12 14:51:23 +00:00
|
|
|
bool: True if the RF was set successfully, False otherwise.
|
2023-01-04 18:26:11 +00:00
|
|
|
"""
|
2024-01-12 14:51:23 +00:00
|
|
|
if self.connected:
|
|
|
|
try:
|
2024-01-12 19:21:22 +00:00
|
|
|
command = f"L RFPOWER {rf/100}" #RF RFPOWER --> RFPOWER == IC705
|
2024-01-12 14:51:23 +00:00
|
|
|
self.send_command(command)
|
|
|
|
self.parameters['rf'] = rf
|
|
|
|
return True
|
|
|
|
except Exception as err:
|
|
|
|
self.log.warning(f"[RIGCTLD] Error setting RF: {err}")
|
|
|
|
self.connected = False
|
|
|
|
return False
|
2023-01-04 19:12:03 +00:00
|
|
|
|
2024-01-12 14:51:23 +00:00
|
|
|
def get_parameters(self):
|
|
|
|
if not self.connected:
|
|
|
|
self.connect()
|
2023-01-04 19:12:03 +00:00
|
|
|
|
2024-01-12 14:51:23 +00:00
|
|
|
if self.connected:
|
2024-02-22 09:26:18 +00:00
|
|
|
self.get_frequency()
|
|
|
|
self.get_mode_bandwidth()
|
|
|
|
self.get_alc()
|
|
|
|
self.get_strength()
|
|
|
|
self.get_rf()
|
|
|
|
|
|
|
|
return self.parameters
|
|
|
|
|
|
|
|
def get_frequency(self):
|
|
|
|
try:
|
|
|
|
frequency_response = self.send_command('f')
|
|
|
|
self.parameters['frequency'] = frequency_response if frequency_response is not None else 'err'
|
|
|
|
except Exception as e:
|
|
|
|
self.log.warning(f"Error getting frequency: {e}")
|
|
|
|
self.parameters['frequency'] = 'err'
|
|
|
|
|
|
|
|
def get_mode_bandwidth(self):
|
|
|
|
try:
|
|
|
|
response = self.send_command('m')
|
|
|
|
if response is not None:
|
|
|
|
response = response.strip()
|
|
|
|
mode, bandwidth = response.split('\n', 1)
|
|
|
|
else:
|
2024-01-12 16:00:28 +00:00
|
|
|
mode = 'err'
|
|
|
|
bandwidth = 'err'
|
2024-02-22 09:26:18 +00:00
|
|
|
except ValueError:
|
|
|
|
mode = 'err'
|
|
|
|
bandwidth = 'err'
|
|
|
|
except Exception as e:
|
|
|
|
self.log.warning(f"Error getting mode and bandwidth: {e}")
|
|
|
|
mode = 'err'
|
|
|
|
bandwidth = 'err'
|
|
|
|
finally:
|
2024-01-12 14:51:23 +00:00
|
|
|
self.parameters['mode'] = mode
|
|
|
|
self.parameters['bandwidth'] = bandwidth
|
2023-01-04 19:12:03 +00:00
|
|
|
|
2024-02-22 09:26:18 +00:00
|
|
|
def get_alc(self):
|
|
|
|
try:
|
|
|
|
alc_response = self.send_command('l ALC')
|
|
|
|
self.parameters['alc'] = alc_response if alc_response is not None else 'err'
|
|
|
|
except Exception as e:
|
|
|
|
self.log.warning(f"Error getting ALC: {e}")
|
|
|
|
self.parameters['alc'] = 'err'
|
2024-01-12 14:51:23 +00:00
|
|
|
|
2024-02-22 09:26:18 +00:00
|
|
|
def get_strength(self):
|
|
|
|
try:
|
|
|
|
strength_response = self.send_command('l STRENGTH')
|
|
|
|
self.parameters['strength'] = strength_response if strength_response is not None else 'err'
|
|
|
|
except Exception as e:
|
|
|
|
self.log.warning(f"Error getting strength: {e}")
|
|
|
|
self.parameters['strength'] = 'err'
|
|
|
|
|
|
|
|
def get_rf(self):
|
|
|
|
try:
|
|
|
|
rf_response = self.send_command('l RFPOWER')
|
|
|
|
if rf_response is not None:
|
|
|
|
self.parameters['rf'] = int(float(rf_response) * 100)
|
|
|
|
else:
|
|
|
|
self.parameters['rf'] = 'err'
|
|
|
|
except ValueError:
|
|
|
|
self.parameters['rf'] = 'err'
|
|
|
|
except Exception as e:
|
|
|
|
self.log.warning(f"Error getting RF power: {e}")
|
|
|
|
self.parameters['rf'] = 'err'
|
2024-02-10 20:28:07 +00:00
|
|
|
|
|
|
|
def start_service(self):
|
|
|
|
binary_name = "rigctld"
|
2024-02-22 09:49:30 +00:00
|
|
|
binary_paths = helpers.find_binary_paths(binary_name, search_system_wide=True)
|
2024-02-10 20:28:07 +00:00
|
|
|
additional_args = self.format_rigctld_args()
|
2024-02-22 09:49:30 +00:00
|
|
|
|
|
|
|
if binary_paths:
|
|
|
|
for binary_path in binary_paths:
|
|
|
|
try:
|
|
|
|
self.log.info(f"Attempting to start rigctld using binary found at: {binary_path}")
|
|
|
|
helpers.kill_and_execute(binary_path, additional_args)
|
|
|
|
self.log.info("Successfully executed rigctld.")
|
|
|
|
break # Exit the loop after successful execution
|
|
|
|
except Exception as e:
|
|
|
|
pass
|
|
|
|
# let's keep this hidden for the user to avoid confusion
|
|
|
|
# self.log.warning(f"Failed to start rigctld with binary at {binary_path}: {e}")
|
|
|
|
else:
|
|
|
|
self.log.warning("Failed to start rigctld with all found binaries.", binaries=binary_paths)
|
2024-02-10 20:28:07 +00:00
|
|
|
else:
|
|
|
|
self.log.warning("Rigctld binary not found.")
|
|
|
|
|
2024-02-22 09:49:30 +00:00
|
|
|
|
2024-02-10 20:28:07 +00:00
|
|
|
def format_rigctld_args(self):
|
|
|
|
config = self.config['RADIO'] # Accessing the 'RADIO' section of the INI file
|
2024-02-11 18:09:51 +00:00
|
|
|
config_rigctld = self.config['RIGCTLD'] # Accessing the 'RIGCTLD' section of the INI file for custom args
|
2024-02-10 20:28:07 +00:00
|
|
|
args = []
|
|
|
|
|
|
|
|
# Helper function to check if the value should be ignored
|
|
|
|
def should_ignore(value):
|
2024-02-11 07:48:49 +00:00
|
|
|
return value in ['ignore', 0]
|
2024-02-10 20:28:07 +00:00
|
|
|
|
|
|
|
# Model ID, Serial Port, and Speed
|
2024-02-14 15:29:18 +00:00
|
|
|
if not should_ignore(config.get('model_id')):
|
2024-02-10 20:28:07 +00:00
|
|
|
args += ['-m', str(config['model_id'])]
|
2024-02-14 15:29:18 +00:00
|
|
|
if not should_ignore(config.get('serial_port')):
|
2024-02-10 20:28:07 +00:00
|
|
|
args += ['-r', config['serial_port']]
|
2024-02-14 15:29:18 +00:00
|
|
|
if not should_ignore(config.get('serial_speed')):
|
2024-02-10 20:28:07 +00:00
|
|
|
args += ['-s', str(config['serial_speed'])]
|
|
|
|
|
|
|
|
# PTT Port and Type
|
2024-02-14 15:29:18 +00:00
|
|
|
if not should_ignore(config.get('ptt_port')):
|
2024-02-10 20:28:07 +00:00
|
|
|
args += ['--ptt-port', config['ptt_port']]
|
2024-02-14 15:29:18 +00:00
|
|
|
if not should_ignore(config.get('ptt_type')):
|
2024-02-10 20:28:07 +00:00
|
|
|
args += ['--ptt-type', config['ptt_type']]
|
|
|
|
|
|
|
|
# Serial DCD and DTR
|
2024-02-14 15:29:18 +00:00
|
|
|
if not should_ignore(config.get('serial_dcd')):
|
2024-02-17 21:57:54 +00:00
|
|
|
args += ['--dcd-type', config['serial_dcd']]
|
2024-02-17 15:53:04 +00:00
|
|
|
|
2024-02-14 15:29:18 +00:00
|
|
|
if not should_ignore(config.get('serial_dtr')):
|
2024-02-14 15:20:00 +00:00
|
|
|
args += ['--set-conf', f'dtr_state={config["serial_dtr"]}']
|
2024-02-10 20:28:07 +00:00
|
|
|
|
2024-02-14 15:20:00 +00:00
|
|
|
# Handling Data Bits and Stop Bits
|
2024-02-14 15:29:18 +00:00
|
|
|
if not should_ignore(config.get('data_bits')):
|
2024-02-14 15:20:00 +00:00
|
|
|
args += ['--set-conf', f'data_bits={config["data_bits"]}']
|
2024-02-14 15:29:18 +00:00
|
|
|
if not should_ignore(config.get('stop_bits')):
|
2024-02-10 20:28:07 +00:00
|
|
|
args += ['--set-conf', f'stop_bits={config["stop_bits"]}']
|
|
|
|
|
2024-02-14 15:20:00 +00:00
|
|
|
# Fixme #rts_state
|
2024-02-17 16:03:59 +00:00
|
|
|
# if not should_ignore(config.get('rts_state')):
|
|
|
|
# args += ['--set-conf', f'stop_bits={config["rts_state"]}']
|
2024-02-14 15:20:00 +00:00
|
|
|
|
2024-02-11 18:09:51 +00:00
|
|
|
# Handle custom arguments for rigctld
|
|
|
|
# Custom args are split via ' ' so python doesn't add extranaeous quotes on windows
|
|
|
|
args += config_rigctld["arguments"].split(" ")
|
|
|
|
#print("Hamlib args ==>" + str(args))
|
|
|
|
|
2024-02-10 20:28:07 +00:00
|
|
|
return args
|
|
|
|
|