From 16383693dd987d51c62220f845e11079b9ee2b5e Mon Sep 17 00:00:00 2001 From: Craig McDaniel Date: Sat, 28 Feb 2026 14:45:33 -0600 Subject: [PATCH] ansible revamp dir structure --- ansible/inventory/hosts | 2 +- .../{scanner/scanner.yml => argus/argus.yml} | 26 +- ansible/playbooks/{scanner => argus}/roles | 0 ansible/playbooks/scanner/templates | 1 - .../tasks/main.yml | 0 .../templates/aprs_airspy.sh | 0 .../templates/argus-aprs-airspy.service.j2 | 0 .../templates/direwolf_airspy_aprs.conf | 0 ansible/roles/argus_ax25/tasks/main.yml | 1 + ansible/roles/argus_ax25/templates/axports | 2 +- ansible/roles/argus_ft991a/tasks/main.yml | 283 ++++++++++-------- .../templates/99-ft991a-udev.rules.j2 | 9 +- ...ervice.j2 => argus-ax25-ft991a.service.j2} | 11 +- ...ice.j2 => argus-rigctld-ft991a.service.j2} | 6 +- .../templates/ft991a-asound.conf.j2 | 18 -- .../templates/ft991a-direwolf-hf.conf.j2 | 25 +- .../templates/ft991a-direwolf-uhf.conf.j2 | 22 +- .../templates/ft991a-direwolf-vhf.conf.j2 | 22 +- .../argus_ft991a/templates/ft991a-monitor.sh | 44 +-- .../argus_ft991a/templates/ft991a_monitor.py | 198 ------------ 20 files changed, 242 insertions(+), 428 deletions(-) rename ansible/playbooks/{scanner/scanner.yml => argus/argus.yml} (72%) rename ansible/playbooks/{scanner => argus}/roles (100%) delete mode 120000 ansible/playbooks/scanner/templates rename ansible/roles/{argus_airspy_aprs => argus_aprs_airspy}/tasks/main.yml (100%) rename ansible/roles/{argus_airspy_aprs => argus_aprs_airspy}/templates/aprs_airspy.sh (100%) rename ansible/roles/{argus_airspy_aprs => argus_aprs_airspy}/templates/argus-aprs-airspy.service.j2 (100%) rename ansible/roles/{argus_airspy_aprs => argus_aprs_airspy}/templates/direwolf_airspy_aprs.conf (100%) rename ansible/roles/argus_ft991a/templates/{ft991a-direwolf.service.j2 => argus-ax25-ft991a.service.j2} (84%) rename ansible/roles/argus_ft991a/templates/{ft991a-rigctld.service.j2 => argus-rigctld-ft991a.service.j2} (91%) delete mode 100644 ansible/roles/argus_ft991a/templates/ft991a-asound.conf.j2 delete mode 100644 ansible/roles/argus_ft991a/templates/ft991a_monitor.py diff --git a/ansible/inventory/hosts b/ansible/inventory/hosts index 0c9cfea..6baaac8 100644 --- a/ansible/inventory/hosts +++ b/ansible/inventory/hosts @@ -1,2 +1,2 @@ [all] -scanner.busnet +argus.busnet diff --git a/ansible/playbooks/scanner/scanner.yml b/ansible/playbooks/argus/argus.yml similarity index 72% rename from ansible/playbooks/scanner/scanner.yml rename to ansible/playbooks/argus/argus.yml index fd20265..38d51b8 100644 --- a/ansible/playbooks/scanner/scanner.yml +++ b/ansible/playbooks/argus/argus.yml @@ -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 diff --git a/ansible/playbooks/scanner/roles b/ansible/playbooks/argus/roles similarity index 100% rename from ansible/playbooks/scanner/roles rename to ansible/playbooks/argus/roles diff --git a/ansible/playbooks/scanner/templates b/ansible/playbooks/scanner/templates deleted file mode 120000 index 7cb455a..0000000 --- a/ansible/playbooks/scanner/templates +++ /dev/null @@ -1 +0,0 @@ -../../templates/ \ No newline at end of file diff --git a/ansible/roles/argus_airspy_aprs/tasks/main.yml b/ansible/roles/argus_aprs_airspy/tasks/main.yml similarity index 100% rename from ansible/roles/argus_airspy_aprs/tasks/main.yml rename to ansible/roles/argus_aprs_airspy/tasks/main.yml diff --git a/ansible/roles/argus_airspy_aprs/templates/aprs_airspy.sh b/ansible/roles/argus_aprs_airspy/templates/aprs_airspy.sh similarity index 100% rename from ansible/roles/argus_airspy_aprs/templates/aprs_airspy.sh rename to ansible/roles/argus_aprs_airspy/templates/aprs_airspy.sh diff --git a/ansible/roles/argus_airspy_aprs/templates/argus-aprs-airspy.service.j2 b/ansible/roles/argus_aprs_airspy/templates/argus-aprs-airspy.service.j2 similarity index 100% rename from ansible/roles/argus_airspy_aprs/templates/argus-aprs-airspy.service.j2 rename to ansible/roles/argus_aprs_airspy/templates/argus-aprs-airspy.service.j2 diff --git a/ansible/roles/argus_airspy_aprs/templates/direwolf_airspy_aprs.conf b/ansible/roles/argus_aprs_airspy/templates/direwolf_airspy_aprs.conf similarity index 100% rename from ansible/roles/argus_airspy_aprs/templates/direwolf_airspy_aprs.conf rename to ansible/roles/argus_aprs_airspy/templates/direwolf_airspy_aprs.conf diff --git a/ansible/roles/argus_ax25/tasks/main.yml b/ansible/roles/argus_ax25/tasks/main.yml index e09e968..8872180 100644 --- a/ansible/roles/argus_ax25/tasks/main.yml +++ b/ansible/roles/argus_ax25/tasks/main.yml @@ -55,6 +55,7 @@ - ax25-tools - ax25-apps - libax25 + - socat state: present ################################################################################################### diff --git a/ansible/roles/argus_ax25/templates/axports b/ansible/roles/argus_ax25/templates/axports index 62b9161..1ff826c 100644 --- a/ansible/roles/argus_ax25/templates/axports +++ b/ansible/roles/argus_ax25/templates/axports @@ -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 diff --git a/ansible/roles/argus_ft991a/tasks/main.yml b/ansible/roles/argus_ft991a/tasks/main.yml index 40f30d3..c27eb26 100644 --- a/ansible/roles/argus_ft991a/tasks/main.yml +++ b/ansible/roles/argus_ft991a/tasks/main.yml @@ -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 + diff --git a/ansible/roles/argus_ft991a/templates/99-ft991a-udev.rules.j2 b/ansible/roles/argus_ft991a/templates/99-ft991a-udev.rules.j2 index 8e719d1..94c1cdd 100644 --- a/ansible/roles/argus_ft991a/templates/99-ft991a-udev.rules.j2 +++ b/ansible/roles/argus_ft991a/templates/99-ft991a-udev.rules.j2 @@ -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. diff --git a/ansible/roles/argus_ft991a/templates/ft991a-direwolf.service.j2 b/ansible/roles/argus_ft991a/templates/argus-ax25-ft991a.service.j2 similarity index 84% rename from ansible/roles/argus_ft991a/templates/ft991a-direwolf.service.j2 rename to ansible/roles/argus_ft991a/templates/argus-ax25-ft991a.service.j2 index 6911087..e78cb50 100644 --- a/ansible/roles/argus_ft991a/templates/ft991a-direwolf.service.j2 +++ b/ansible/roles/argus_ft991a/templates/argus-ax25-ft991a.service.j2 @@ -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 diff --git a/ansible/roles/argus_ft991a/templates/ft991a-rigctld.service.j2 b/ansible/roles/argus_ft991a/templates/argus-rigctld-ft991a.service.j2 similarity index 91% rename from ansible/roles/argus_ft991a/templates/ft991a-rigctld.service.j2 rename to ansible/roles/argus_ft991a/templates/argus-rigctld-ft991a.service.j2 index 052c00e..b4584ea 100644 --- a/ansible/roles/argus_ft991a/templates/ft991a-rigctld.service.j2 +++ b/ansible/roles/argus_ft991a/templates/argus-rigctld-ft991a.service.j2 @@ -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 \ No newline at end of file diff --git a/ansible/roles/argus_ft991a/templates/ft991a-asound.conf.j2 b/ansible/roles/argus_ft991a/templates/ft991a-asound.conf.j2 deleted file mode 100644 index 0fcb200..0000000 --- a/ansible/roles/argus_ft991a/templates/ft991a-asound.conf.j2 +++ /dev/null @@ -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" -} \ No newline at end of file diff --git a/ansible/roles/argus_ft991a/templates/ft991a-direwolf-hf.conf.j2 b/ansible/roles/argus_ft991a/templates/ft991a-direwolf-hf.conf.j2 index a339bd8..3319f18 100644 --- a/ansible/roles/argus_ft991a/templates/ft991a-direwolf-hf.conf.j2 +++ b/ansible/roles/argus_ft991a/templates/ft991a-direwolf-hf.conf.j2 @@ -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 diff --git a/ansible/roles/argus_ft991a/templates/ft991a-direwolf-uhf.conf.j2 b/ansible/roles/argus_ft991a/templates/ft991a-direwolf-uhf.conf.j2 index c80927a..de6b39c 100644 --- a/ansible/roles/argus_ft991a/templates/ft991a-direwolf-uhf.conf.j2 +++ b/ansible/roles/argus_ft991a/templates/ft991a-direwolf-uhf.conf.j2 @@ -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 diff --git a/ansible/roles/argus_ft991a/templates/ft991a-direwolf-vhf.conf.j2 b/ansible/roles/argus_ft991a/templates/ft991a-direwolf-vhf.conf.j2 index c960bad..433d4cc 100644 --- a/ansible/roles/argus_ft991a/templates/ft991a-direwolf-vhf.conf.j2 +++ b/ansible/roles/argus_ft991a/templates/ft991a-direwolf-vhf.conf.j2 @@ -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 diff --git a/ansible/roles/argus_ft991a/templates/ft991a-monitor.sh b/ansible/roles/argus_ft991a/templates/ft991a-monitor.sh index 739d3a5..b9874f4 100755 --- a/ansible/roles/argus_ft991a/templates/ft991a-monitor.sh +++ b/ansible/roles/argus_ft991a/templates/ft991a-monitor.sh @@ -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 diff --git a/ansible/roles/argus_ft991a/templates/ft991a_monitor.py b/ansible/roles/argus_ft991a/templates/ft991a_monitor.py deleted file mode 100644 index fa283a1..0000000 --- a/ansible/roles/argus_ft991a/templates/ft991a_monitor.py +++ /dev/null @@ -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() \ No newline at end of file