From bb5b8b95b415487955023c99420bac9549695c92 Mon Sep 17 00:00:00 2001 From: Craig McDaniel Date: Fri, 26 Dec 2025 12:41:56 -0600 Subject: [PATCH] many modifications to digirg and yaesu 991a packet. --- ansible/roles/scanner_digirig/tasks/main.yml | 62 ++++----- .../templates/98-digirig-udev.rules.j2 | 7 +- .../templates/digirig-direwolf.conf.j2 | 3 +- .../templates/digirig-direwolf.service.j2 | 21 ++- .../templates/digirig-kissattach.service.j2 | 26 ---- .../templates/digirig-socat.service.j2 | 22 --- .../templates/99-ft991a-udev.rules.j2 | 7 +- .../templates/ft991a-direwolf-hf.conf.j2 | 4 +- .../templates/ft991a-direwolf-uhf.conf.j2 | 6 +- .../templates/ft991a-direwolf-vhf.conf.j2 | 6 +- .../templates/ft991a-direwolf.service.j2 | 6 + .../templates/ft991a-rigctld.service.j2 | 6 + .../templates/ft991a_monitor.py | 130 ++++++++---------- .../scanner_pat/templates/config.json.j2 | 18 +-- 14 files changed, 132 insertions(+), 192 deletions(-) delete mode 100644 ansible/roles/scanner_digirig/templates/digirig-kissattach.service.j2 delete mode 100644 ansible/roles/scanner_digirig/templates/digirig-socat.service.j2 diff --git a/ansible/roles/scanner_digirig/tasks/main.yml b/ansible/roles/scanner_digirig/tasks/main.yml index 4ce3409..a20c047 100644 --- a/ansible/roles/scanner_digirig/tasks/main.yml +++ b/ansible/roles/scanner_digirig/tasks/main.yml @@ -34,10 +34,27 @@ - audio - plugdev +- name: Grant full passwordless sudo access to busnet user + ansible.builtin.copy: + content: "busnet ALL=(ALL) NOPASSWD:ALL" + dest: /etc/sudoers.d/busnet + mode: '0440' + owner: root + group: root + validate: '/usr/sbin/visudo -cf %s' + ################################################################################################### -# DIREWOLF CONF +# AX.25 BASH / DIREWOLF ################################################################################################### +- name: Copy digirig-direwolf.sh AX.25 stack script + copy: + src: "templates/digirig-direwolf.sh" + dest: "/opt/busnet/direwolf/digirig-direwolf.sh" + owner: busnet + group: busnet + mode: "0755" + - name: Copy Direwolf config for Digirig template: src: "templates/digirig-direwolf.conf.j2" @@ -50,8 +67,15 @@ # SYSTEMD ################################################################################################### +- name: Copy AX.25 bash script to start direwolf and utils + template: + src: "templates/digirig-direwolf.conf.j2" + dest: "/opt/busnet/direwolf/config/digirig.conf" + owner: busnet + group: busnet + # udev rules start this automatically when the radio USB is connected, and stop when disconnected. -- name: Install systemd unit file for Direwolf and Digirig USB sound card +- name: Install systemd unit file for Direwolf/AX.25 stack template: src: "templates/digirig-direwolf.service.j2" dest: "/etc/systemd/system/digirig-direwolf.service" @@ -65,40 +89,6 @@ daemon_reload: true enabled: true -# socat creates a full duplex/bidirectional pipe between a serial port /dev/radio/digirig-tnc and -# Direwolf's KISS TCP interface, which is running on localhost:8001. The kissattach program needs -# a serial port to work, and this is how we accomplish connecting kissattach to direwolf. -- name: Install systemd unit file for digirig-socat - template: - src: "templates/digirig-socat.service.j2" - dest: "/etc/systemd/system/digirig-socat.service" - owner: root - group: root - mode: "0644" - -- name: Enable digirig-socat service - systemd: - name: "digirig-socat" - daemon_reload: true - enabled: true - -# This starts after digirig-socat and makes the attachment to the new serial device we just created -# with socat! This effectively connects the Linux AX.25 system to direwolf's KISS TCP port. -- name: Install systemd unit file for digirig-kissattach - template: - src: "templates/digirig-kissattach.service.j2" - dest: "/etc/systemd/system/digirig-kissattach.service" - owner: root - group: root - mode: "0644" - -- name: Enable digirig-kissattach service - systemd: - name: "digirig-kissattach" - daemon_reload: true - enabled: true - - ################################################################################################### # UDEV RULES ################################################################################################### diff --git a/ansible/roles/scanner_digirig/templates/98-digirig-udev.rules.j2 b/ansible/roles/scanner_digirig/templates/98-digirig-udev.rules.j2 index 5035e98..6d28e6f 100644 --- a/ansible/roles/scanner_digirig/templates/98-digirig-udev.rules.j2 +++ b/ansible/roles/scanner_digirig/templates/98-digirig-udev.rules.j2 @@ -15,13 +15,10 @@ SUBSYSTEM=="tty", ENV{ID_SERIAL_SHORT}=="809986b5195bed119acbed165820eef3", SYMLINK+="radio/digirig-sound", MODE="660", GROUP="plugdev" -# Automatically start direwolf when the radio is connected. Identify the radio by the +# Automatically start direwolf bash script when the radio is connected. Identify the radio by the # unique setial number. # - This direwolf instance is only for this radio. # --------------------------------------------------------------------------------------------------- ACTION=="add", SUBSYSTEM=="tty", ENV{ID_SERIAL_SHORT}=="809986b5195bed119acbed165820eef3", TAG+="systemd" ENV{SYSTEMD_WANTS}+="digirig-direwolf.service" - -# Automatically stop direwolf when the radio is connected. -# --------------------------------------------------------------------------------------------------- -ACTION=="remove", SUBSYSTEM=="tty", ENV{ID_SERIAL_SHORT}=="809986b5195bed119acbed165820eef3",TAG+="systemd" ENV{SYSTEMD_WANTS}-="digirig-direwolf.service" +# Note: We cannot auto-stop direwolf bash script here. We use systemd for that. See the unit file. \ No newline at end of file diff --git a/ansible/roles/scanner_digirig/templates/digirig-direwolf.conf.j2 b/ansible/roles/scanner_digirig/templates/digirig-direwolf.conf.j2 index 4a2e2fb..a17dd0f 100644 --- a/ansible/roles/scanner_digirig/templates/digirig-direwolf.conf.j2 +++ b/ansible/roles/scanner_digirig/templates/digirig-direwolf.conf.j2 @@ -1,6 +1,7 @@ # This file is managed by BusNet Ansible # -# This is the Direwolf configuration for connecting Direwolf to the Digirig USB sound card. +# This is the Direwolf configuration for connecting Direwolf to the Digirig USB sound card. This assumes +# that we will operate using 1200 baud mode in 2 meters VHF with a dedicated radio for this purpose. # # There us a udev rule which creates "/dev/radio/digirig-sound" so that we do not have to worry about # what USB port or tty number is assigned when this device is connected to the computer. diff --git a/ansible/roles/scanner_digirig/templates/digirig-direwolf.service.j2 b/ansible/roles/scanner_digirig/templates/digirig-direwolf.service.j2 index 6bf0b1e..c878620 100644 --- a/ansible/roles/scanner_digirig/templates/digirig-direwolf.service.j2 +++ b/ansible/roles/scanner_digirig/templates/digirig-direwolf.service.j2 @@ -1,24 +1,31 @@ # This file is managed by BusNet Ansible. # -# This service starts Direwolf when the Digirig device is connected to this computer. +# This service starts the direwolf/AX.25 stack bash script when the Digirig device is connected to +# this computer. The bash script then initializes the Linux AX.25 stack. # # There is a corresponding udev rule that triggers this service automatically when it detects that # the radio's USB devices are connected. There is also a udev rule that shuts it down when the USB # is disconnected. [Unit] -Description=Direwolf TNC for Digirig +Description=Direwolf TNC and AX.25 Stack +#After=network.target -# Automatically start these other services to complete the Linux AX25 stack. -Requires=digirig-socat.service -Requires=digirig-kissattach.service +# Link the service life cycle to the hardware device /dev/radio/digirig-sound. I don't know why, but +# you can't specify the file location of the device here. You have to do this syntax. Note that the +# udev rule creates this device for the digirig. +BindsTo=dev-radio-digirig\x2dsound.device +After=dev-radio-digirig\x2dsound.device [Service] -ExecStart=/usr/local/bin/direwolf -q hdx -t 0 -X 1 -c /opt/busnet/direwolf/config/digirig.conf +Type=simple +ExecStart=/bin/bash /opt/busnet/direwolf/digirig-direwolf.sh Restart=on-failure RestartSec=5 User=busnet Group=busnet +# This tells systemd to kill all child processe started by the bash script. +KillMode=control-group [Install] -WantedBy=multi-user.target +WantedBy=multi-user.target \ No newline at end of file diff --git a/ansible/roles/scanner_digirig/templates/digirig-kissattach.service.j2 b/ansible/roles/scanner_digirig/templates/digirig-kissattach.service.j2 deleted file mode 100644 index 92f300e..0000000 --- a/ansible/roles/scanner_digirig/templates/digirig-kissattach.service.j2 +++ /dev/null @@ -1,26 +0,0 @@ -[Unit] -Description=AX.25 Interface Setup via kissattach for Digirig - -# We must start AFTER the socat service has created the virtual TNC link. -#Requires=digirig-socat.service -After=digirig-socat.service - -[Service] -Type=Forking # kissattach will fork itself and run as a daemon in the background -User=root -Restart=always -RestartSec=10s - -# Wait 6 seconds to give both direwolf and socat time to start. -ExecStartPre=/bin/sleep 6 - -# This PTY is created by the digirg-socat service, which is a pipe between this device and -# Direwolf's KISS port for the digirig. Kissattach cannot use KISS over TCP directly to Direwolf -# so it needs the extra step of using socat. -ExecStart=/usr/sbin/kissattach /dev/radio/digirig-tnc digirig - -# We must do this because it forked itself. -ExecStop=/usr/bin/pkill -f 'kissattach /dev/radio/digirig-tnc digirig' - -[Install] -WantedBy=multi-user.target \ No newline at end of file diff --git a/ansible/roles/scanner_digirig/templates/digirig-socat.service.j2 b/ansible/roles/scanner_digirig/templates/digirig-socat.service.j2 deleted file mode 100644 index bd014a9..0000000 --- a/ansible/roles/scanner_digirig/templates/digirig-socat.service.j2 +++ /dev/null @@ -1,22 +0,0 @@ -[Unit] -Description=SOCAT Virtual Serial Port Bridge to Digirig Direwolf - -# This ensures socat only starts AFTER the main Dire Wolf service is active. -#Requires=digirig-direwolf.service -After=digirig-direwolf.service - -[Service] -User=root -Restart=always -RestartSec=5s - -# Wait 3 seconds to give Direwolf time to start. -ExecStartPre=/bin/sleep 3 - -# socat creates a full duplex/bidirectional pipe between a serial port /dev/radio/digirig-tnc and -# Direwolf's KISS TCP interface, which is running on localhost:8001. The kissattach program needs -# a serial port to work, and this is how we accomplish connecting kissattach to direwolf. -ExecStart=/usr/bin/socat PTY,raw,echo=0,link=/dev/radio/digirig-tnc TCP4:127.0.0.1:8001 - -[Install] -WantedBy=multi-user.target \ No newline at end of file diff --git a/ansible/roles/scanner_ft991a/templates/99-ft991a-udev.rules.j2 b/ansible/roles/scanner_ft991a/templates/99-ft991a-udev.rules.j2 index 5acce0c..8e719d1 100644 --- a/ansible/roles/scanner_ft991a/templates/99-ft991a-udev.rules.j2 +++ b/ansible/roles/scanner_ft991a/templates/99-ft991a-udev.rules.j2 @@ -22,8 +22,5 @@ SUBSYSTEM=="tty", ENV{ID_SERIAL_SHORT}=="01A9F7D6", SYMLINK+="radio/ft991a-$env{ ACTION=="add", SUBSYSTEM=="tty", ENV{ID_SERIAL_SHORT}=="01A9F7D6", TAG+="systemd" ENV{SYSTEMD_WANTS}+="ft991a-rigctld.service" ACTION=="add", SUBSYSTEM=="tty", ENV{ID_SERIAL_SHORT}=="01A9F7D6", TAG+="systemd" ENV{SYSTEMD_WANTS}+="ft991a-direwolf.service" - -# Automatically stop rigctld and direwolf when the radio is connected. -# --------------------------------------------------------------------------------------------------- -ACTION=="remove", SUBSYSTEM=="tty", ENV{ID_SERIAL_SHORT}=="01A9F7D6",TAG+="systemd" ENV{SYSTEMD_WANTS}-="ft991a-rigctld.service" -ACTION=="remove", SUBSYSTEM=="tty", ENV{ID_SERIAL_SHORT}=="01A9F7D6",TAG+="systemd" ENV{SYSTEMD_WANTS}-="ft991a-direwolf.service" \ No newline at end of file +# Note: We cannot auto-stop rigctld and direwolf here. We use systemd for that. See each service +# systemd unit file. diff --git a/ansible/roles/scanner_ft991a/templates/ft991a-direwolf-hf.conf.j2 b/ansible/roles/scanner_ft991a/templates/ft991a-direwolf-hf.conf.j2 index 804fe5e..4be6960 100644 --- a/ansible/roles/scanner_ft991a/templates/ft991a-direwolf-hf.conf.j2 +++ b/ansible/roles/scanner_ft991a/templates/ft991a-direwolf-hf.conf.j2 @@ -24,5 +24,5 @@ MODEM 300 1600:1800 D # Enable FX.25 with fallback to regular AX.25 FX25TX 1 -KISSPORT 8005 -AGWPORT 8006 \ No newline at end of file +AGWPORT 8005 +KISSPORT 8006 diff --git a/ansible/roles/scanner_ft991a/templates/ft991a-direwolf-uhf.conf.j2 b/ansible/roles/scanner_ft991a/templates/ft991a-direwolf-uhf.conf.j2 index 1d0c66e..fd412bc 100644 --- a/ansible/roles/scanner_ft991a/templates/ft991a-direwolf-uhf.conf.j2 +++ b/ansible/roles/scanner_ft991a/templates/ft991a-direwolf-uhf.conf.j2 @@ -1,7 +1,7 @@ # This file is managed by BusNet Ansible # # This is the Direwolf configuration for connecting Direwolf to the Yaesu FT-991a radio when it is -# plugged in via USB and tuned to an HF frequency. +# plugged in via USB and tuned to an UHF frequency. # # Note that only one direwolf will ever be running at the same time for this 991a radio, depending # on what frequency it is tuned to. See /opt/busnet/direwolf/ft991a_monitor.py @@ -23,5 +23,5 @@ MODEM 9600 # Enable FX.25 with fallback to regular AX.25 FX25TX 1 -KISSPORT 8005 -AGWPORT 8006 \ No newline at end of file +AGWPORT 8005 +KISSPORT 8006 diff --git a/ansible/roles/scanner_ft991a/templates/ft991a-direwolf-vhf.conf.j2 b/ansible/roles/scanner_ft991a/templates/ft991a-direwolf-vhf.conf.j2 index 93504bb..f26fbec 100644 --- a/ansible/roles/scanner_ft991a/templates/ft991a-direwolf-vhf.conf.j2 +++ b/ansible/roles/scanner_ft991a/templates/ft991a-direwolf-vhf.conf.j2 @@ -1,7 +1,7 @@ # This file is managed by BusNet Ansible # # This is the Direwolf configuration for connecting Direwolf to the Yaesu FT-991a radio when it is -# plugged in via USB and tuned to an HF frequency. +# plugged in via USB and tuned to an VHF frequency. # # Note that only one direwolf will ever be running at the same time for this 991a radio, depending # on what frequency it is tuned to. See /opt/busnet/direwolf/ft991a_monitor.py @@ -23,5 +23,5 @@ MODEM 1200 # Enable FX.25 with fallback to regular AX.25 FX25TX 1 -KISSPORT 8005 -AGWPORT 8006 \ No newline at end of file +AGWPORT 8005 +KISSPORT 8006 diff --git a/ansible/roles/scanner_ft991a/templates/ft991a-direwolf.service.j2 b/ansible/roles/scanner_ft991a/templates/ft991a-direwolf.service.j2 index 174d77c..0726528 100644 --- a/ansible/roles/scanner_ft991a/templates/ft991a-direwolf.service.j2 +++ b/ansible/roles/scanner_ft991a/templates/ft991a-direwolf.service.j2 @@ -9,6 +9,12 @@ [Unit] Description=Direwolf TNC for Yaesu FT-991a Auto Frequency Sensing +# Link the service life cycle to the hardware device /dev/radio/ft99a1-00. I don't know why, but +# you can't specify the file location of the device here. You have to do this syntax. Note that +# the udev rule creates this device. +BindsTo=dev-radio-ft991a\x2d01.device +After=dev-radio-ft991a\x2d01.device + [Service] ExecStartPre=/bin/sleep 5 diff --git a/ansible/roles/scanner_ft991a/templates/ft991a-rigctld.service.j2 b/ansible/roles/scanner_ft991a/templates/ft991a-rigctld.service.j2 index 6c957e5..052c00e 100644 --- a/ansible/roles/scanner_ft991a/templates/ft991a-rigctld.service.j2 +++ b/ansible/roles/scanner_ft991a/templates/ft991a-rigctld.service.j2 @@ -11,6 +11,12 @@ [Unit] Description=rigctld for Yaesu FT-991a +# Link the service life cycle to the hardware device /dev/radio/ft99a1-01. I don't know why, but +# you can't specify the file location of the device here. You have to do this syntax. Note that +# the udev rule creates this device. +BindsTo=dev-radio-ft991a\x2d01.device +After=dev-radio-ft991a\x2d01.device + [Service] ExecStartPre=/bin/sleep 4 # It uses device symlink /dev/radio/ft991a-00 which is the radio's CAT USB interface diff --git a/ansible/roles/scanner_ft991a/templates/ft991a_monitor.py b/ansible/roles/scanner_ft991a/templates/ft991a_monitor.py index a6f6c7f..29096af 100644 --- a/ansible/roles/scanner_ft991a/templates/ft991a_monitor.py +++ b/ansible/roles/scanner_ft991a/templates/ft991a_monitor.py @@ -1,4 +1,25 @@ #!/usr/bin/env python3 +# @author Craig McDaniel using Google Gemini LLM +# +# This script polls the Yeasu FT-991a by connecting to a running rigctld daemon and asking that +# daemon what the current radio's frequency is. If the radio is in any HF frequency, it makes +# sure Direwolf is running with a HF AX.25 configuration at 300 baud. If a VHF or UHF frequency +# is detected, it restarts Direwolf with a configuration suitable for that band +# +# * Note that ANY HF frequency will trigger a Direwolf restart with HF config. +# +# * For VHF anf UHF, specific ranges are specified as to only run Direwolf when the radio is +# detected to be in the accepted packet frequencies of each band. The reason is to stop the +# start and restart of Direwolf when the radio is in scan mode, and it constantly scans both +# VHF and UHF frequencies. This will randomly trigger a reload of Direwolf over and over +# when the 5 second poll randomly returns a VHF or UHF frequency. I think scanning data freqs +# are not useful, and a operator would not normally scan those. But they will scan simplex and +# repeater output frequencies, but those aren't for data, so no need to restart Direwolf. +# +# * If this script detects that the radio is on an out of range frequency, it does nothing. If +# Direwolf is already running, it leaves it alone. As soon as it detects that the radio is within +# range of a HF-UHF packet frequency, it makes sure Direwolf is running with the correct config. +# import socket import subprocess @@ -13,9 +34,9 @@ DIREWOLF_PATH = '/usr/local/bin/direwolf' CONFIG = { 'RIGCTLD_HOST': 'localhost', - 'RIGCTLD_PORT': 4000, # Port 4000 - 'RIGCTLD_TIMEOUT': 5.0, # Timeout - 'POLL_INTERVAL': 5.0, # Poll interval + 'RIGCTLD_PORT': 4000, + 'RIGCTLD_TIMEOUT': 5.0, + 'POLL_INTERVAL': 5.0, # !!! CONFIGURATION FILES !!! 'CONFIG_FILES': { @@ -24,11 +45,17 @@ CONFIG = { 'UHF': '/opt/busnet/direwolf/config/ft991a-uhf.conf', }, - # Frequency Band Definitions (in Hz) - 'HF_MIN_FREQ': 1_800_000, - 'HF_MAX_FREQ': 30_000_000, - 'VHF_MAX_FREQ': 148_000_000, - 'UHF_MAX_FREQ': 450_000_000, + # --- Narrowed Frequency Windows (in Hz) --- + # Edit these to the exact ranges you use for Digital modes. + # Frequencies outside these ranges will cause Dire Wolf to stop. + 'HF_MIN_FREQ': 1_800_000, + 'HF_MAX_FREQ': 30_000_000, + + 'VHF_MIN_FREQ': 144_390_000, # This captures APRS at 143390 + 'VHF_MAX_FREQ': 145_100_000, # and various commonly used packet frequencies between 145000 - 145100 + + 'UHF_MIN_FREQ': 432_000_000, + 'UHF_MAX_FREQ': 433_000_000, 'DIREWOLF_ARGS': ['-t', '0', '-X', '1'], } @@ -48,41 +75,28 @@ class ModeSwitcherDaemon: self.current_band: Optional[str] = None self._shutdown_flag = False + # Ranges self.hf_min = config['HF_MIN_FREQ'] self.hf_max = config['HF_MAX_FREQ'] + self.vhf_min = config['VHF_MIN_FREQ'] self.vhf_max = config['VHF_MAX_FREQ'] + self.uhf_min = config['UHF_MIN_FREQ'] self.uhf_max = config['UHF_MAX_FREQ'] def _get_frequency_and_band(self) -> Optional[str]: - """ - Connects, queries, SHUTS DOWN WRITE (EOF), and reads response. - """ temp_sock: Optional[socket.socket] = None - try: - # 1. Connect temp_sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) temp_sock.settimeout(self.rigctld_timeout) - # Disable Nagle's algorithm for speed temp_sock.setsockopt(socket.IPPROTO_TCP, socket.TCP_NODELAY, 1) temp_sock.connect((self.rigctld_host, self.rigctld_port)) - # 2. Send Command (Matches your echo "+\get_freq") - # We explicitly send the backslash + newline. temp_sock.sendall(b'+\\get_freq\n') + temp_sock.shutdown(socket.SHUT_WR) # Send EOF signal - # CRITICAL FIX: Send EOF (End of File) signal. - # This tells rigctld "I am done writing", forcing it to process and reply. - # This mimics the behavior of piping echo into netcat. - temp_sock.shutdown(socket.SHUT_WR) - - # 3. Read Response full_response = temp_sock.recv(4096).decode('utf-8') - - # 4. Clean up temp_sock.close() - # 5. Parse lines = full_response.strip().split('\n') frequency_str = None @@ -92,48 +106,31 @@ class ModeSwitcherDaemon: break if frequency_str is None or not frequency_str.isdigit(): - if full_response: - print(f"WARN: Invalid response format: {full_response.replace('\n', ' ')}", file=sys.stderr) return None frequency = int(frequency_str) - if frequency < self.hf_min: - return 'UNKNOWN' - elif frequency <= self.hf_max: + # Use strict bounded windows to avoid voice scan triggers + if self.hf_min <= frequency <= self.hf_max: return 'HF' - elif frequency <= self.vhf_max: + elif self.vhf_min <= frequency <= self.vhf_max: return 'VHF' - elif frequency <= self.uhf_max: + elif self.uhf_min <= frequency <= self.uhf_max: return 'UHF' else: - return 'UNKNOWN' + return 'UNKNOWN' # Not in a managed digital window - except socket.timeout: - print(f"ERROR: rigctld response timed out ({self.rigctld_timeout}s).", file=sys.stderr) - return None - except socket.error as e: - print(f"ERROR: Socket error: {e}", file=sys.stderr) - return None except Exception as e: - print(f"ERROR: Unexpected error: {e}", file=sys.stderr) + print(f"ERROR: rigctld communication error: {e}", file=sys.stderr) return None - finally: - if temp_sock: - try: - temp_sock.close() - except: - pass def _kill_direwolf_process(self): if self.direwolf_process: - print("INFO: Shutting down Dire Wolf child process...") + print(f"INFO: Terminating Dire Wolf on {self.current_band}...") try: self.direwolf_process.terminate() self.direwolf_process.wait(timeout=5) - print("INFO: Dire Wolf terminated successfully.") except subprocess.TimeoutExpired: - print("WARN: Dire Wolf unresponsive, killing process.") self.direwolf_process.kill() self.direwolf_process.wait() finally: @@ -141,65 +138,50 @@ class ModeSwitcherDaemon: self.current_band = None def _run_direwolf(self, target_band: str): - # Idempotency check + # Idempotency check: Already running on the right band? if self.direwolf_process and self.direwolf_process.poll() is None and self.current_band == target_band: return - # Cleanup dead process - if self.direwolf_process and self.direwolf_process.poll() is not None: - print(f"WARN: Dire Wolf process died unexpectedly (Exit Code: {self.direwolf_process.returncode}). Restarting for {target_band}.") - self.direwolf_process = None - - # Switch bands + # If running on the wrong band, kill it first if self.direwolf_process: - print(f"INFO: Switching Dire Wolf from {self.current_band} to {target_band}.") + print(f"INFO: Band changed. Switching from {self.current_band} to {target_band}.") self._kill_direwolf_process() try: config_file = self.config_files[target_band] - print(f"INFO: Launching Dire Wolf with config: {config_file}") + print(f"INFO: Launching Dire Wolf for {target_band}...") command_args = [self.direwolf_path] + self.direwolf_args + ['-c', config_file] - self.direwolf_process = subprocess.Popen(command_args) self.current_band = target_band - print(f"INFO: Dire Wolf started (PID: {self.direwolf_process.pid}).") - except KeyError: - print(f"FATAL: Configuration file not defined for band '{target_band}'.", file=sys.stderr) - self._shutdown_flag = True except Exception as e: print(f"FATAL: Failed to launch Dire Wolf: {e}", file=sys.stderr) - self._shutdown_flag = True def handle_signal(self, signum, frame): - print(f"\nINFO: Signal {signum} received. Initiating graceful shutdown...") self._shutdown_flag = True def run(self): signal.signal(signal.SIGINT, self.handle_signal) signal.signal(signal.SIGTERM, self.handle_signal) - print(f"INFO: Starting Mode Switcher Daemon.") + print("INFO: FT-991A Frequency Monitor Started.") while not self._shutdown_flag: target_band = self._get_frequency_and_band() - if target_band is None: - pass - elif target_band == 'UNKNOWN': - print("WARN: Frequency is outside defined ranges. Dire Wolf is not managed.") - else: + # Change: Only act if we find a valid digital band. + # If target_band is 'UNKNOWN' or None (comm error), we do absolutely nothing. + if target_band in ['HF', 'VHF', 'UHF']: self._run_direwolf(target_band) - # Simple sleep loop to allow signal interrupt + # Poll interval sleep for _ in range(int(self.poll_interval * 10)): if self._shutdown_flag: break time.sleep(0.1) self._kill_direwolf_process() - print("INFO: Script exited gracefully.") - + print("INFO: Monitor exited.") if __name__ == '__main__': if not os.path.exists(DIREWOLF_PATH): diff --git a/ansible/roles/scanner_pat/templates/config.json.j2 b/ansible/roles/scanner_pat/templates/config.json.j2 index deaea02..e00f39e 100644 --- a/ansible/roles/scanner_pat/templates/config.json.j2 +++ b/ansible/roles/scanner_pat/templates/config.json.j2 @@ -14,7 +14,9 @@ "connect_aliases": { "telnet": "telnet://{mycall}:CMSTelnet@cms.winlink.org:8772/wl2k" }, - "listen": [], + "listen": [ + "ax25" + ], "hamlib_rigs": { "ft-991a": {"address": "localhost:4000", "network": "tcp"} }, @@ -22,17 +24,16 @@ "engine": "linux", "rig": "", "beacon": { - "every": 0, - "message": "", - "destination": "" + "every": 750, + "message": "This is my Pat Winlink client for P2P Winlink Email. Connect directly and send me a message.", + "destination": "IDENT" } }, "ax25_linux": { - "port": "wl2k" + "port": "digirig" }, "agwpe": { - "name": "hehehe", - "addr": "scanner.busnet:8011", + "addr": "scanner.busnet:8005", "radio_port": 0 }, "serial-tnc": { @@ -79,7 +80,8 @@ "enable_http": false, "allow_forms": false, "use_server_time": false, - "addr": "localhost:2947" + "update_locator": true, + "addr": "192.168.7.1:2947" }, "schedule": {}, "version_reporting_disabled": false