ansible revamp dir structure

This commit is contained in:
Craig McDaniel
2026-02-28 14:45:33 -06:00
parent 8e9f396eff
commit 16383693dd
20 changed files with 242 additions and 428 deletions
+1 -1
View File
@@ -1,2 +1,2 @@
[all]
scanner.busnet
argus.busnet
@@ -1,38 +1,38 @@
# scanner.busnet
# argus.busnet
#
---
- name: Install and Configure Various Things on scanner.busnet
hosts: scanner.busnet
- name: Install and Configure Various Things on argus.busnet
hosts: argus.busnet
become: true
gather_facts: true
tasks:
- name: Set up base AX.25 stack with Digirig and dedicated VHF radio
ansible.builtin.import_role:
name: scanner_digirig
name: argus_ax25
when: run_ax25 is defined and run_ax25 | bool
- name: Set up base AX.25 stack with Digirig and dedicated VHF radio
- name: Set up APRS listener using AirSpy
ansible.builtin.import_role:
name: scanner_airspy_aprs
name: argus_aprs_airspy
when: run_airspy_aprs is defined and run_airspy_aprs | bool
- name: Set up udev rules, rigctld and direwolf for Yaesu FT991-A
ansible.builtin.import_role:
name: scanner_ft991a
name: argus_ft991a
when: run_ft991a is defined and run_ft991a | bool
- name: Pat
ansible.builtin.import_role:
name: scanner_pat
name: argus_pat
when: run_pat is defined and run_pat | bool
- name: Install and configure PulseAudio for network audio
ansible.builtin.import_role:
name: argus_pulseaudio
when: run_pulseaudio is defined and run_pulseaudio | bool
- name: Hamlib
ansible.builtin.import_role:
name: hamlib
when: run_hamlib is defined and run_hamlib | bool
- name: Install and configure PulseAudio for network audio
ansible.builtin.import_role:
name: scanner_pulseaudio
when: run_pulseaudio is defined and run_pulseaudio | bool
-1
View File
@@ -1 +0,0 @@
../../templates/
+1
View File
@@ -55,6 +55,7 @@
- ax25-tools
- ax25-apps
- libax25
- socat
state: present
###################################################################################################
+1 -1
View File
@@ -6,5 +6,5 @@
# name callsign speed paclen window description
#
digirig K0BIT-1 9600 255 5 VHF Port using ICOM IC-V8000 and Compactenna 2M/440+
airspy N0CAL-1 9600 255 5 AirSpy for VHF packet receive only.
airspy APRS-1 9600 255 5 AirSpy for VHF packet receive only.
ft991a K0BIT-2 9600 255 2 Used when the FT-991A radio is connected
+153 -130
View File
@@ -1,33 +1,43 @@
# Role: scanner_direwolf
# This installs and configures direwolf on the scanner.bus computer
# Role: argus_ft991a
#
# This role installs and configures Hamlib, PulseAudio and an AX.25 stack for the FT-991a! This
# radio is very vesatile, so this role does numerous things.
#
# - Set up udev rules to make special device drivers for the Yaesu 991 in /dev/radio so we don't
# have to relay on /dev/ttyUSB0, 1 or whatever number the OS happens to assign. This gives the
# device names consistency.
#
# - Set up systemd service to start Direwolf when the radio is connected via USB, and to stop it
# when the radio is disconnected.
# - These same udev rules also set up a special ALSA name for the sound card detected with the
# FT991a so that when specifing the ALSA device, it can be referenced using a useful name.
#
# - Download, compile and install direwolf from source.
# - Set up rigctld daemon that allows anything on the network to use hamlib for radio control.
#
# - Copy configuration files
# - Set up systemd service to start an AX.25 stack when the radio is connected via USB and powered
# on, and shut down the stack when the radio is powered off. Also restarts Direwolf with a separate
# config for each band: HF, VHF, UHF with the correct modem settings. Auto detect frequency change
# and auto restart Direwolf/AX.25 stack.
#
# - Creates a psuedo ALSA device that hooks into PulseAudio so that devices that only expect ALSA
# to work, can use the psuedo device normally. This is for this computer only. Of course because
# we run PulseAudio, the source and sink of the FT991-A sound card is available for use by other
# computers on the network!
- name: Create various directories
file:
path: "{{item}}"
state: directory
owner: busnet
group: busnet
owner: argus
group: argus
mode: u=rwx,g=rwx,o=rx
with_items:
- /opt/busnet/direwolf
- /opt/busnet/direwolf/config
- /opt/busnet/direwolf/logs
- /opt/argus/scripts
- /opt/argus/direwolf
- /opt/argus/direwolf/config
- /opt/argus/direwolf/logs
- name: Make sure busnet user is in groups for audio and USB access
- name: Make sure argus user is in groups for audio and USB access
ansible.builtin.user:
name: busnet
name: argus
groups: "{{item}}"
append: yes
with_items:
@@ -36,127 +46,29 @@
- audio
- plugdev
###################################################################################################
# Compile and install Direwolf from source
###################################################################################################
#- name: Install correct prerequite packages when running Ubuntu Noble
# ansible.builtin.include_tasks: ubuntu_noble.yml
# when: ansible_distribution == "Ubuntu" and ansible_distribution_release == "noble"
#- name: Git stuff
# ansible.builtin.command:
# cmd: "git config --global --add safe.directory /opt/busnet/direwolf/source"
#- name: Checkout direwolf source
# ansible.builtin.git:
# repo: "https://www.github.com/wb2osz/direwolf"
# dest: /opt/busnet/direwolf/source
# version: 1.7
#- name: Create the build directory
# ansible.builtin.file:
# path: /opt/busnet/direwolf/source/build
# state: directory
# mode: '0755'
#- name: Configure the build with cmake
# ansible.builtin.command:
# cmd: "cmake .."
# chdir: /opt/busnet/direwolf/source/build
#- name: Build direwolf
# ansible.builtin.command:
# cmd: "make -j4"
# chdir: /opt/busnet/direwolf/source/build
#- name: make install
# community.general.make:
# chdir: /opt/busnet/direwolf/source/build
# target: install
#- name: Copy ft991a_monitor.py auto frequency sensing script
# copy:
# src: "templates/ft991a_monitor.py"
# dest: "/opt/busnet/direwolf/ft991a_monitor.py"
# owner: busnet
# group: busnet
# mode: "0755"
- name: Copy ft991a-monitor.sh script
copy:
src: "templates/ft991a-monitor.sh"
dest: "/opt/busnet/direwolf/ft991a-monitor.sh"
owner: busnet
group: busnet
mode: "0755"
- name: Copy Direwolf HF config for Yaesu FT-991a
template:
src: "templates/ft991a-direwolf-hf.conf.j2"
dest: "/opt/busnet/direwolf/config/ft991a-hf.conf"
owner: busnet
group: busnet
- name: Copy Direwolf VHF config for Yaesu FT-991a
template:
src: "templates/ft991a-direwolf-vhf.conf.j2"
dest: "/opt/busnet/direwolf/config/ft991a-vhf.conf"
owner: busnet
group: busnet
- name: Copy Direwolf UHF config for Yaesu FT-991a
template:
src: "templates/ft991a-direwolf-uhf.conf.j2"
dest: "/opt/busnet/direwolf/config/ft991a-uhf.conf"
owner: busnet
group: busnet
###################################################################################################
# SYSTEMD
###################################################################################################
# udev rules start this automatically when the radio USB is connected, and stop when disconnected.
- name: Install systemd unit file for rigctld and Yaesu FT-991a
template:
src: "templates/ft991a-rigctld.service.j2"
dest: "/etc/systemd/system/ft991a-rigctld.service"
owner: root
group: root
mode: "0644"
- name: Enable ft991a-rigctld service
systemd:
name: "ft991a-rigctld"
daemon_reload: true
enabled: true
# udev rules start this automatically when the radio USB is connected, and stop when disconnected.
- name: Install systemd unit file for Direwolf and Yaesu FT-991a
template:
src: "templates/ft991a-direwolf.service.j2"
dest: "/etc/systemd/system/ft991a-direwolf.service"
owner: root
group: root
mode: "0644"
- name: Enable ft991a-direwolf service
systemd:
name: "ft991a-direwolf"
daemon_reload: true
enabled: true
###################################################################################################
# ALSA CONFIGURATION
###################################################################################################
- name: Copy /etc/asound.conf
template:
src: "templates/ft991a-asound.conf.j2"
dest: "/etc/asound.conf"
owner: root
group: root
mode: '0644'
- name: Configure ALSA pseudo-devices for FT991a in argus user's .asoundrc
ansible.builtin.blockinfile:
path: /opt/argus/.asoundrc # This is the home dir of argus user!
block: |
pcm.ft991a_rx {
type pulse
device "alsa_input.usb-Burr-Brown_from_TI_USB_Audio_CODEC-00.analog-stereo"
}
pcm.ft991a_tx {
type pulse
device "alsa_output.usb-Burr-Brown_from_TI_USB_Audio_CODEC-00.analog-stereo"
}
ctl.ft991a_rx { type pulse }
ctl.ft991a_tx { type pulse }
marker: "# {mark} ANSIBLE MANAGED BLOCK - FT991A DO NOT EDIT THIS"
create: yes
owner: argus
group: argus
mode: '0644'
###################################################################################################
# UDEV RULES
@@ -179,3 +91,114 @@
ansible.builtin.command:
cmd: "udevadm trigger"
when: ft991a_udev_result.changed
###################################################################################################
# AX.25 bash script and Direwolf configuration
###################################################################################################
#- name: Install correct prerequite packages when running Ubuntu Noble
# ansible.builtin.include_tasks: ubuntu_noble.yml
# when: ansible_distribution == "Ubuntu" and ansible_distribution_release == "noble"
#- name: Git stuff
# ansible.builtin.command:
# cmd: "git config --global --add safe.directory /opt/argus/direwolf/source"
#- name: Checkout direwolf source
# ansible.builtin.git:
# repo: "https://www.github.com/wb2osz/direwolf"
# dest: /opt/argus/direwolf/source
# version: 1.7
#- name: Create the build directory
# ansible.builtin.file:
# path: /opt/argus/direwolf/source/build
# state: directory
# mode: '0755'
#- name: Configure the build with cmake
# ansible.builtin.command:
# cmd: "cmake .."
# chdir: /opt/argus/direwolf/source/build
#- name: Build direwolf
# ansible.builtin.command:
# cmd: "make -j4"
# chdir: /opt/argus/direwolf/source/build
#- name: make install
# community.general.make:
# chdir: /opt/argus/direwolf/source/build
# target: install
#- name: Copy ft991a_monitor.py auto frequency sensing script
# copy:
# src: "templates/ft991a_monitor.py"
# dest: "/opt/argus/direwolf/ft991a_monitor.py"
# owner: argus
# group: argus
# mode: "0755"
- name: Copy ft991a-monitor.sh script
copy:
src: "templates/ft991a-monitor.sh"
dest: "/opt/argus/scripts/ft991a-monitor.sh"
owner: argus
group: argus
mode: "0755"
- name: Copy Direwolf HF config for Yaesu FT-991a
template:
src: "templates/ft991a-direwolf-hf.conf.j2"
dest: "/opt/argus/direwolf/config/ft991a-hf.conf"
owner: argus
group: argus
- name: Copy Direwolf VHF config for Yaesu FT-991a
template:
src: "templates/ft991a-direwolf-vhf.conf.j2"
dest: "/opt/argus/direwolf/config/ft991a-vhf.conf"
owner: argus
group: argus
- name: Copy Direwolf UHF config for Yaesu FT-991a
template:
src: "templates/ft991a-direwolf-uhf.conf.j2"
dest: "/opt/argus/direwolf/config/ft991a-uhf.conf"
owner: argus
group: argus
###################################################################################################
# SYSTEMD
###################################################################################################
# udev rules start this automatically when the radio USB is connected, and stop when disconnected.
- name: Install systemd unit file for argus-rigctld-ft991a
template:
src: "templates/argus-rigctld-ft991a.service.j2"
dest: "/etc/systemd/system/argus-rigctld-ft991a.service"
owner: root
group: root
mode: "0644"
- name: Enable argus-rigctld-ft991a service
systemd:
name: "argus-rigctld-ft991a"
daemon_reload: true
enabled: true
# udev rules start this automatically when the radio USB is connected, and stop when disconnected.
- name: Install systemd unit file argus-ax25-ft991a
template:
src: "templates/argus-ax25-ft991a.service.j2"
dest: "/etc/systemd/system/argus-ax25-ft991a.service"
owner: root
group: root
mode: "0644"
- name: Enable argus-ax25-ft991a.service
systemd:
name: "argus-ax25-ft991a"
daemon_reload: true
enabled: true
@@ -1,18 +1,23 @@
# udev rule to provide fixed device name for the Yeasu FT-991a and start/stop services automatically!
# This information was retrieved by the following. Assuming the radio USB is hooked up to /dev/ttyUSB0
# and nothing else is plugged into the USB at the moment.
#
# craig@scanner:~$ udevadm info /dev/ttyUSB0 | grep ID_SERIAL
# $ sudo udevadm info /dev/ttyUSB0 | grep ID_SERIAL
# E: ID_SERIAL=Silicon_Labs_CP2105_Dual_USB_to_UART_Bridge_Controller_01A9F7D6
# E: ID_SERIAL_SHORT=01A9F7D6
#
# $ sudo udevadm info -a /sys/class/sound/card1 | grep -E 'idVendor|idProduct'
# ATTRS{idProduct}=="29b3"
# ATTRS{idVendor}=="08bb"
# This creates /dev/radio/ft991a-00 (CAT control) and /dev/radio/ft991a-01 (sound)
# It does not matter what USB port it's plugged into, or how many other USB devices are also plugged in.
# ---------------------------------------------------------------------------------------------------
SUBSYSTEM=="tty", ENV{ID_SERIAL_SHORT}=="01A9F7D6", SYMLINK+="radio/ft991a-$env{ID_USB_INTERFACE_NUM}", MODE="660", GROUP="plugdev"
# This sets a custom sound card name of "ft991a" in the Linux ALSA system.
SUBSYSTEM=="sound", ATTRS{idVendor}=="08bb", ATTRS{idProduct}=="29b3", ATTR{id}="ft991a"
# Automatically start rigctld and direwolf when the radio is connected. Identify the radio by the
# unique setial number.
@@ -1,4 +1,4 @@
# This file is managed by BusNet Ansible.
# This file is managed by Ansible.
#
# This service starts Direwolf when the Yeasu FT-991A radio USB is connected to this computer.
#
@@ -7,7 +7,7 @@
# is disconnected.
[Unit]
Description=Direwolf TNC for Yaesu FT-991a Auto Frequency Sensing
Description=FT-991A AX.25 stack
# 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
@@ -25,13 +25,12 @@ ExecStartPre=/bin/sleep 5
#
# This is automatic. All you have to do is switch the frequency on the 991a and this detects that and
# stops/starts Direwolf with the matching packet configuration.
ExecStart=/opt/busnet/direwolf/ft991a-monitor.sh
#ExecStart=/opt/busnet/direwolf/ft991a_monitor.py
ExecStart=/opt/argus/scripts/ft991a-monitor.sh
Restart=on-failure
RestartSec=5
User=busnet
Group=busnet
User=argus
Group=argus
[Install]
WantedBy=multi-user.target
@@ -1,4 +1,4 @@
# This file is managed by BusNet Ansible.
# This file is managed by Ansible.
#
# This service starts rigctld when the Yeasu FT-991A radio USB is connected to this computer.
# This uses port 4000
@@ -23,8 +23,8 @@ ExecStartPre=/bin/sleep 4
ExecStart=/usr/local/bin/rigctld -m 1035 -r /dev/radio/ft991a-00 -t 4000 -s 38400 --set-conf=data_bits=8, stop_bits=1, serial_parity=None, serial_handshake=None, dtr_state=OFF, rts_state=ON
Restart=on-failure
RestartSec=5
User=busnet
Group=busnet
User=argus
Group=argus
[Install]
WantedBy=multi-user.target
@@ -1,18 +0,0 @@
# This file is managed by BusNet Ansible.
#
# ALSA pseudo-devices for Yaesu FT-991a
# These bridge ALSA applications to PulseAudio devices
#
# This is an ALSO psuedo device that actually connects to PulseAudio! This way things that need
# ALSA (like Direwolf) can use PulseAudio and won't hog up the device.
pcm.ft991a-playback {
type pulse
device "alsa_output.usb-Burr-Brown_from_TI_USB_Audio_CODEC-00.analog-stereo"
}
# Same for this
pcm.ft991a-capture {
type pulse
device "alsa_input.usb-Burr-Brown_from_TI_USB_Audio_CODEC-00.analog-stereo"
}
@@ -1,26 +1,25 @@
# 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.
#
# 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
# This file is managed by Ansible
#
# There us a udev rule which creates "/dev/radio/ft99a1-00" and "/dev/radio/ft991a-01" so that we
# do not have to worry about what USB port or tty number is assigned when this radio is connected
# to the computer.
# IF Direwolf uses the ALSA device directly, this would be the device name. However, we do not
# use ALSA any more because only one device at a time can use the audio device this way, and when
# Direwolf is running, it locks it.
#ADEVICE plughw:CODEC,0
# We create a custom ALSA sound card name of "ft991a" in our udev rules. Direwolf uses ALSA directly,
# so this tells Direwolf to use the "ft991a" sound card, subdevice #0 (there is only 1 subdevice).
#ADEVICE plughw:CARD=ft991a,DEV=0
# This are peusdo ALSA devices created by PulseAudio! PulseAudio controls the ALSA device and then
# create this fake ALSA device that is actually connected directly to PulseAudio. This way Pulse
# locks the ALSA device, but then Pulse allows many devices to use the FT991a sound card, even
# over the network.
# These are defined in /etc/asound.conf
ADEVICE ft991a-capture ft991a-playback
# These are defined in ~/.asoundrc
ADEVICE ft991a_rx ft991a_tx
# PulseAudio detects the hardware rate of 44,100Khz. However, using that rate generates buffer
# underruns and the audio is garbled. I set it at half the rate randomly and no more buffer underruns
# I don't know the most optimal value to put here. I've also randomly used a rate of 32000 and that
# also worked. Not sure what is better.
ARATE 22500
# The custom udev rule makes this device available by name.
PTT /dev/radio/ft991a-01 RTS
@@ -1,26 +1,28 @@
# This file is managed by BusNet Ansible
# This file is managed by 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 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
#
# There us a udev rule which creates "/dev/radio/ft99a1-00" and "/dev/radio/ft991a-01" so that we
# do not have to worry about what USB port or tty number is assigned when this radio is connected
# to the computer.
# IF Direwolf uses the ALSA device directly, this would be the device name. However, we do not
# use ALSA any more because only one device at a time can use the audio device this way, and when
# Direwolf is running, it locks it.
#ADEVICE plughw:CODEC,0
# We create a custom ALSA sound card name of "ft991a" in our udev rules. Direwolf uses ALSA directly,
# so this tells Direwolf to use the "ft991a" sound card, subdevice #0 (there is only 1 subdevice).
#ADEVICE plughw:CARD=ft991a,DEV=0
# This are peusdo ALSA devices created by PulseAudio! PulseAudio controls the ALSA device and then
# create this fake ALSA device that is actually connected directly to PulseAudio. This way Pulse
# locks the ALSA device, but then Pulse allows many devices to use the FT991a sound card, even
# over the network.
# These are defined in /etc/asound.conf
ADEVICE ft991a-capture ft991a-playback
# These are defined in ~/.asoundrc
ADEVICE ft991a_rx ft991a_tx
# PulseAudio detects the hardware rate of 44,100Khz. However, using that rate generates buffer
# underruns and the audio is garbled. I set it at half the rate randomly and no more buffer underruns
# I don't know the most optimal value to put here. I've also randomly used a rate of 32000 and that
# also worked. Not sure what is better.
ARATE 22500
# The custom udev rule makes this device available by name.
PTT /dev/radio/ft991a-01 RTS
@@ -1,26 +1,28 @@
# This file is managed by BusNet Ansible
# This file is managed by 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 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
#
# There us a udev rule which creates "/dev/radio/ft99a1-00" and "/dev/radio/ft991a-01" so that we
# do not have to worry about what USB port or tty number is assigned when this radio is connected
# to the computer.
# IF Direwolf uses the ALSA device directly, this would be the device name. However, we do not
# use ALSA any more because only one device at a time can use the audio device this way, and when
# Direwolf is running, it locks it.
#ADEVICE plughw:CODEC,0
# We create a custom ALSA sound card name of "ft991a" in our udev rules. Direwolf uses ALSA directly,
# so this tells Direwolf to use the "ft991a" sound card, subdevice #0 (there is only 1 subdevice).
#ADEVICE plughw:CARD=ft991a,DEV=0
# This are peusdo ALSA devices created by PulseAudio! PulseAudio controls the ALSA device and then
# create this fake ALSA device that is actually connected directly to PulseAudio. This way Pulse
# locks the ALSA device, but then Pulse allows many devices to use the FT991a sound card, even
# over the network.
# These are defined in /etc/asound.conf
ADEVICE ft991a-capture ft991a-playback
# These are defined in ~/.asoundrc
ADEVICE ft991a_rx ft991a_tx
# PulseAudio detects the hardware rate of 44,100Khz. However, using that rate generates buffer
# underruns and the audio is garbled. I set it at half the rate randomly and no more buffer underruns
# I don't know the most optimal value to put here. I've also randomly used a rate of 32000 and that
# also worked. Not sure what is better.
ARATE 22500
# The custom udev rule makes this device available by name.
PTT /dev/radio/ft991a-01 RTS
@@ -26,7 +26,7 @@
# | Radio | ----- via USB socket on rear of radio.
# +-------+
# |
# | USB
# | USB
# |
# +----|----------------------------------------------------------------------------+
# | | This is your computer. The things in here are virtual components. |
@@ -95,11 +95,11 @@ WAITING_FOR_RADIO=false
poll_rigctld()
{
local RESP=$(echo "+\\get_freq" | nc -w 2 $RIGCTLD_HOST $RIGCTLD_PORT 2>/dev/null)
if [ -n "$RESP" ]; then
# Parse frequency from response
local FREQ=$(echo "$RESP" | grep "Frequency:" | awk '{print $2}')
if [ -n "$FREQ" ] && [ "$FREQ" -eq "$FREQ" ] 2>/dev/null; then
# Determine band
if [ $FREQ -ge $HF_MIN ] && [ $FREQ -le $HF_MAX ]; then
@@ -117,7 +117,7 @@ poll_rigctld()
fi
fi
fi
echo "ERROR"
return 1
}
@@ -155,20 +155,20 @@ cleanup_stack()
# figure out what the associated callsign for the netrom port is.
# Example: digirig-nr K0BIT-8 8BITS 235 NET/ROM Port
local NETROM_CALLSIGN=$(grep "^${NETROM_PORT_NAME}" /etc/ax25/nrports | awk '{print $2}')
# Find all NET/ROM interfaces (like nr0, nr1, nr2, etc...) and check their callsigns.
# Shut down any NET/ROM interface with the $NETROM_CALLSIGN attached to it.
if [ -n "$NETROM_CALLSIGN" ]; then
for IFACE in $(ip link show | grep -oE 'nr[0-9]+'); do
# Get the callsign for this interface
local IFACE_CALL=$(ip addr show $IFACE | grep 'netrom' | awk '{print $2}')
if [ "$IFACE_CALL" = "$NETROM_CALLSIGN" ]; then
echo "Bringing down NET/ROM interface ${IFACE} ..."
sudo ip link set $IFACE down
return
fi
done
done
fi
echo "FT991a AX.25 ${CUR_BAND} stack is now shut down."
@@ -183,7 +183,7 @@ start_stack()
{
local BAND=$1
local CONFIG=""
case $BAND in
HF) CONFIG="$CONFIG_HF" ;;
VHF) CONFIG="$CONFIG_VHF" ;;
@@ -193,9 +193,9 @@ start_stack()
return 1
;;
esac
echo "Starting $BAND stack..."
# Start Direwolf in background
$DIREWOLF_PATH -t 0 -X 1 -c "$CONFIG" &
DIREWOLF_PID=$!
@@ -209,7 +209,7 @@ start_stack()
return 1
fi
echo "Direwolf started (PID: $DIREWOLF_PID)"
# Start socat PTY bridge
sleep 3
sudo socat PTY,raw,echo=0,link=$TNC_DEV TCP4:127.0.0.1:$KISS_TCP_PORT &
@@ -220,21 +220,21 @@ start_stack()
return 1
fi
echo "socat started (PID: $SOCAT_PID)"
# Wait for PTY device to exist
local WAIT_COUNT=0
while [ ! -L $TNC_DEV ] && [ $WAIT_COUNT -lt 10 ]; do
sleep 0.5
((WAIT_COUNT++))
done
if [ ! -L $TNC_DEV ]; then
echo "PTY device $TNC_DEV not created" >&2
cleanup_stack
return 1
fi
echo "socat started (PID: $SOCAT_PID)"
# Kissattach connects the PTY device created by socat to the kernel.
echo "Running kissattach..."
sudo kissattach $TNC_DEV $AX25_PORT_NAME
@@ -244,7 +244,7 @@ start_stack()
return 1
fi
echo "kissattach completed"
# Set kissparams depending on what band we're operating on.
echo "Setting kissparms..."
case $BAND in
@@ -279,7 +279,7 @@ start_stack()
fi
# If netromd is not running, restart it. Otherwise, leave it alone.
if sudo ystemctl is-enabled --quiet netromd.service; then
if sudo systemctl is-enabled --quiet netromd.service; then
if ! sudo systemctl is-active --quiet netromd.service; then
echo "Re-starting netromd..."
sudo systemctl start netromd.service
@@ -332,7 +332,7 @@ fi
while true; do
TARGET_BAND=$(poll_rigctld)
if [ "$WAITING_FOR_RADIO" = true ]; then
# In waiting mode - check if radio is back online
if [ "$TARGET_BAND" != "ERROR" ]; then
@@ -344,12 +344,12 @@ while true; do
sleep $POLL_INTERVAL
continue
fi
# Normal operation mode
if [ "$TARGET_BAND" == "ERROR" ]; then
((CONSECUTIVE_ERRORS++))
echo "Radio unreachable ($CONSECUTIVE_ERRORS/$MAX_CONSECUTIVE_ERRORS)"
if [ $CONSECUTIVE_ERRORS -ge $MAX_CONSECUTIVE_ERRORS ]; then
echo "Radio offline. Waiting for radio to be reachable again."
cleanup_stack
@@ -359,7 +359,7 @@ while true; do
else
# Reset error counter on any successful poll
CONSECUTIVE_ERRORS=0
# Only act on valid digital bands (HF, VHF, UHF)
if [[ "$TARGET_BAND" =~ ^(HF|VHF|UHF)$ ]]; then
# Band switch needed?
@@ -368,7 +368,7 @@ while true; do
echo "Band change detected: $CUR_BAND -> $TARGET_BAND"
cleanup_stack
fi
start_stack "$TARGET_BAND"
if [ $? -ne 0 ]; then
echo "FATAL: Failed to start FT991a AX.25 stack for $TARGET_BAND" >&2
@@ -380,7 +380,7 @@ while true; do
: # No-op
fi
fi
# Poll interval sleep
sleep $POLL_INTERVAL
done
@@ -1,198 +0,0 @@
#!/usr/bin/env python3
# @author Craig McDaniel using Google Gemini LLM
#
#
# *** THIS IS NOT USED ***
# This was an early version of the monitor script. It's been rewritten in bash. I might just
# delete this.
#
#
# 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
import time
import sys
import os
import signal
from typing import Optional, Dict
# --- Configuration (Centralized) ---
DIREWOLF_PATH = '/usr/local/bin/direwolf'
CONFIG = {
'RIGCTLD_HOST': 'localhost',
'RIGCTLD_PORT': 4000,
'RIGCTLD_TIMEOUT': 5.0,
'POLL_INTERVAL': 5.0,
# !!! CONFIGURATION FILES !!!
'CONFIG_FILES': {
'HF': '/opt/busnet/direwolf/config/ft991a-hf.conf',
'VHF': '/opt/busnet/direwolf/config/ft991a-vhf.conf',
'UHF': '/opt/busnet/direwolf/config/ft991a-uhf.conf',
},
# --- 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'],
}
class ModeSwitcherDaemon:
def __init__(self, config: Dict):
self.rigctld_host = config['RIGCTLD_HOST']
self.rigctld_port = config['RIGCTLD_PORT']
self.rigctld_timeout = config['RIGCTLD_TIMEOUT']
self.poll_interval = config['POLL_INTERVAL']
self.config_files = config['CONFIG_FILES']
self.direwolf_path = DIREWOLF_PATH
self.direwolf_args = config['DIREWOLF_ARGS']
self.direwolf_process: Optional[subprocess.Popen] = None
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]:
temp_sock: Optional[socket.socket] = None
try:
temp_sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
temp_sock.settimeout(self.rigctld_timeout)
temp_sock.setsockopt(socket.IPPROTO_TCP, socket.TCP_NODELAY, 1)
temp_sock.connect((self.rigctld_host, self.rigctld_port))
temp_sock.sendall(b'+\\get_freq\n')
temp_sock.shutdown(socket.SHUT_WR) # Send EOF signal
full_response = temp_sock.recv(4096).decode('utf-8')
temp_sock.close()
lines = full_response.strip().split('\n')
frequency_str = None
for line in lines:
if line.startswith('Frequency:'):
frequency_str = line.split(':')[1].strip()
break
if frequency_str is None or not frequency_str.isdigit():
return None
frequency = int(frequency_str)
# Use strict bounded windows to avoid voice scan triggers
if self.hf_min <= frequency <= self.hf_max:
return 'HF'
elif self.vhf_min <= frequency <= self.vhf_max:
return 'VHF'
elif self.uhf_min <= frequency <= self.uhf_max:
return 'UHF'
else:
return 'UNKNOWN' # Not in a managed digital window
except Exception as e:
print(f"ERROR: rigctld communication error: {e}", file=sys.stderr)
return None
def _kill_direwolf_process(self):
if self.direwolf_process:
print(f"INFO: Terminating Dire Wolf on {self.current_band}...")
try:
self.direwolf_process.terminate()
self.direwolf_process.wait(timeout=5)
except subprocess.TimeoutExpired:
self.direwolf_process.kill()
self.direwolf_process.wait()
finally:
self.direwolf_process = None
self.current_band = None
def _run_direwolf(self, target_band: str):
# 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
# If running on the wrong band, kill it first
if self.direwolf_process:
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 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
except Exception as e:
print(f"FATAL: Failed to launch Dire Wolf: {e}", file=sys.stderr)
def handle_signal(self, signum, frame):
self._shutdown_flag = True
def run(self):
signal.signal(signal.SIGINT, self.handle_signal)
signal.signal(signal.SIGTERM, self.handle_signal)
print("INFO: FT-991A Frequency Monitor Started.")
while not self._shutdown_flag:
target_band = self._get_frequency_and_band()
# 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)
# 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: Monitor exited.")
if __name__ == '__main__':
if not os.path.exists(DIREWOLF_PATH):
print(f"FATAL: Dire Wolf executable not found at {DIREWOLF_PATH}.", file=sys.stderr)
sys.exit(1)
daemon = ModeSwitcherDaemon(CONFIG)
daemon.run()