1
0
mirror of https://github.com/AG7GN/nexus-utilities.git synced 2025-05-18 07:30:07 -07:00
Steve Magnuson 4c79afbce0 Added README
2019-09-23 13:14:25 -07:00

621 lines
19 KiB
Bash
Executable File

#!/bin/bash
#
# Script Name: tnc.sh
# Author: Steve Magnuson AG7GN
# Date Created: 20180601
#
# Description: This script will start direwolf in one of 3 APRS modes: igate, digipeater,
# or igate + digipeater, OR in AX.25 mode as a TNC for Winlink or other apps.
# Run tnc.sh with no arguments for instructions.
#
# Usage: tnc.sh start digi|igate|digiigate|ax25
# tnc.sh stop
#
# Use the companion script watchdog-tnc.sh in crontab to launch this script
# to keep it running.
#
#===========================================================================================
VERSION="3.1.3"
TNC_CONFIG_FILE="$HOME/tnc.conf"
function Usage() {
echo
echo "Version $VERSION"
echo
echo "$(basename $0) usage:"
echo
echo "$(basename $0) start ax25|ax25+pat [1200|9600 [2]]"
echo " Starts the ax25 TNC or the ax25 TNC and pat email client."
echo " Note that pat requires configuration in"
echo " $HOME/.wl2k/config.json."
echo
echo " Direwolf baud set to 1200 bps (for V/UHF) on a single"
echo " audio channel by default."
echo " You can optionally specify baud (1200 or 9600) and number"
echo " of audio channels. 9600 might work on V/UHF"
echo " with another 9600 station depending on conditions and"
echo " the capabilities of your soundcard. 9600 will likely"
echo " not work with a Signalink."
echo " If you specify the baud, you can optionally also specify"
echo " 2 to tell Direwolf to use both channels. '2' assumes"
echo " you have a stereo audio card and direwolf uses both the left"
echo " and right channels. Winlink clients can access Direwolf's"
echo " second channel by selecting Packet TNC Type 'KISS Port 2'"
echo " in Winlink. Default is a single channel."
echo " 1200 baud uses Direwolf's AFSK 1200 & 2200 Hz modem."
echo " 9600 baud uses Direwolf's K9NG/G3RUH modem."
echo
echo "$(basename $0) start pat"
echo " Starts pat email client in telnet mode only (niether ax25"
echo " not ARDOP TNC is started)."
echo " Note that pat requires configuration in"
echo " $HOME/.wl2k/config.json."
echo
echo "$(basename $0) start ardop|ardop+pat"
echo " Starts the ARDOP TNC (piardop2) or the ARDFOP TNC and pat."
echo " Note that pat requires configuration in"
echo " $HOME/.wl2k/config.json."
echo
echo "$(basename $0) start digiigate [both]"
echo " Starts the Direwolf APRS digipeater and iGate."
echo " If you specify 'both', Direwolf will decode audio on"
echo " channel 1 (stereo left) and channel 2 (stereo right)"
echo " on stereo sound cards only."
echo
echo "$(basename $0) start digi [both]"
echo " Starts the Direwolf APRS digipeater (only)."
echo " If you specify 'both', Direwolf will decode audio on"
echo " channel 1 (stereo left) and channel 2 (stereo right)"
echo " on stereo sound cards only."
echo
echo "$(basename $0) start igate [both]"
echo " Starts the Direwolf APRS iGate (only)."
echo " If you specify 'both', Direwolf will decode audio on"
echo " channel 1 (stereo left) and channel 2 (stereo right)"
echo " on stereo sound cards only."
echo
echo "$(basename $0) stop"
echo " Stops all the apps."
echo
exit 1
}
# BEGINNING OF USER CONFIGURATION SECTION ########################################################
if [ -s "$TNC_CONFIG_FILE" ]
then
source $HOME/tnc.conf
if [[ $MYCALL =~ N0CALL || $MYCALL =~ N0ONE ]]
then
echo >&2 "Error: You must set the MYCALL variable in $TNC_CONFIG_FILE."
exit 1
fi
else
echo >&2 "Error: Configuration file $TNC_CONFIG_FILE is missing or empty."
echo Usage
fi
# END OF USER CONFIGURATION SECTION ########################################################
# Initializations ##########################################################################
# trap ctrl-c and call ctrl_c()
trap ctrl_c INT
LOGFILE="/tmp/tnc.log"
SCREENCONFIG="/tmp/tnc.sh.screenrc"
ACTION="${1,,}" # start|stop
DMODE="${2,,}" # direwolf mode: digi,igate,digi+igate,ax25
SPEED="${3,,}" # speed. No value implies 1200. Otherwise, allowed values are 300 or 9600.
AUDIO_CHANNELS="$4"
[[ $SPEED == "" ]] && SPEED="1200"
[[ $AUDIO_CHANNELS == "" ]] && AUDIO_CHANNELS="1"
declare -a ORDERS
declare -A CMDS
CMDS[direwolf]="$(which direwolf) -a $AUDIOSTATS -t $COLORS -r $ARATE"
cat > $SCREENCONFIG << EOF
logfile $LOGFILE
logfile flush 1
logtstamp on
logtstamp after 60
log on
logtstamp string "[ %n:%t ] ---- TIMESTAMP ---- %Y-%m-%d %c:%s ---- Press Ctrl-C to Quit\012"
EOF
# Functions ################################################################################
function ctrl_c () {
# Do cleanup if Ctrl-C is pressed. Stop all the screens.
$0 stop
exit 0
}
function checkApp () {
APP="$(which $1)"
if [[ $APP == "" ]]
then
echo >&2 "Error: $1 is required but not installed."
exit 1
fi
echo "$APP"
}
function aprsPasscode () {
# Generates the APRS website passcode from the supplied callsign
CALL="$(echo ${1^^} | cut -d'-' -f1)"
H="0x73e2"
declare -i LEN=${#CALL}
declare -i I=0
while [ $I -lt $LEN ]
do
H=$(( $H ^ $(($(printf '%d' "'${CALL:$I:2}") << 8)) ))
H=$(( $H ^ $(printf '%d' "'${CALL:$(( I+1 )):2}") ))
(( I+=2 ))
done
echo -n $(( $H & 0x7fff ))
}
function checkSoundCard () {
# Checks for the presence of the requested sound card
if [[ $AUDIO_DEV != "" ]]
then
CAP_DEV="$AUDIO_DEV"
elif [[ $AUDIO_DEV_SEARCH_STRING == "" ]]
then
echo >&2 "Error: You must set either the AUDIO_DEV or AUDIO_DEV_SEARCH_STRING variables in this script to select the sound card."
exit 1
else
CAP_DEV="$($ARECORD -l | grep -i "$AUDIO_DEV_SEARCH_STRING" | grep "card [0-9]\|device [0-9]" | sed 's/:.*,/,/;s/:.*$//;s/, /,/;s/ /=/g;s/ice//' | tr -s [:lower:] [:upper:])"
if [[ $CAP_DEV == "" ]]
then
echo >&2 "Error: Unable to find audio interface using string $AUDIO_DEV_SEARCH_STRING."
sleep 5
exit 1
fi
CAP_DEV="plughw:$CAP_DEV"
fi
}
function makeConfig() {
# direwolf.conf parameters
ADEVICE="$CAP_DEV"
CONFFILE="$(mktemp)"
case "$1" in
digi*|igate)
PASSCODE="$(aprsPasscode $MYCALL)"
case "$SPEED" in
both) # Decode stereo right (channel 1) and stereo left (channel 0)
FROM_CHANNEL="1"
TO_CHANNEL="0"
cat >> $CONFFILE << EOF
ADEVICE $ADEVICE
ACHANNELS 2
CHANNEL 0
$PTT0
MYCALL $MYCALL
MODEM 1200
CHANNEL 1
$PTT1
MYCALL $MYCALL
MODEM 1200
EOF
;;
*) # Decode stereo left only
FROM_CHANNEL="0"
TO_CHANNEL="0"
cat >> $CONFFILE << EOF
ADEVICE $ADEVICE
ACHANNELS 1
CHANNEL 0
$PTT0
MYCALL $MYCALL
MODEM 1200
EOF
;;
esac
case "$1" in
digi*) # digipeater+igate or digipeater
if [[ $1 == "digi" ]] # digipeat ONLY
then
IGLOGIN=""
PBEACONIG=""
IGTXVIA=""
COMMENT="$COMMENTCALL Digipeater | $LOC"
else # Digipeater + iGate
# IGTXVIA is set at the top of the script
COMMENT="$COMMENTCALL Digipeater+iGate | $LOC"
PBEACONIG="PBEACON sendto=IG delay=$IGDELAY every=$IGEVERY symbol=\"igate\" overlay=T lat=$LAT long=$LONG COMMENT=\"$COMMENT\""
IGLOGIN="IGLOGIN $MYCALL $PASSCODE"
fi
DIGIPEAT="DIGIPEAT $FROM_CHANNEL $TO_CHANNEL ^WIDE[3-7]-[1-7]$|^TEST$ ^WIDE[12]-[12]$ TRACE"
PBEACON="PBEACON delay=$DIGIPEATDELAY every=$DIGIPEATEVERY symbol=\"digi\" overlay=S lat=$LAT long=$LONG POWER=$POWER HEIGHT=$HEIGHT GAIN=$GAIN COMMENT=\"$COMMENT\" via=$HOPS"
;;
*) # iGate
PBEACON=""
DIGIPEAT=""
IGTXVIA=""
COMMENT="$COMMENTCALL iGate | $LOC"
PBEACONIG="PBEACON sendto=IG delay=$IGDELAY every=$IGEVERY symbol=\"igate\" overlay=R lat=$LAT long=$LONG COMMENT=\"$COMMENT\""
IGLOGIN="IGLOGIN $MYCALL $PASSCODE"
;;
esac
cat >> $CONFFILE << EOF
$AGWPORT
$KISSPORT
$PBEACONIG
$PBEACON
$DIGIPEAT
$IGTXVIA
$IGLOGIN
$FILTER
$IGSERVER
$IGTXLIMIT
$IGFILTER
EOF
;;
ax25)
case "$SPEED" in
1200|9600)
case "$AUDIO_CHANNELS" in
2) # Assumes stereo input - use both channels
if [[ $PTT0 =~ "GPIO" ]]
then # Allow only one radio at a time to transmit
TXINH0="TXINH $(echo $PTT1 | sed 's/PTT //')"
TXINH1="TXINH $(echo $PTT0 | sed 's/PTT //')"
else
TXINH0=""
TXINH1=""
fi
cat > $CONFFILE << EOF
ADEVICE $ADEVICE
ACHANNELS 2
CHANNEL 0
MODEM $SPEED
$PTT0
$TXINH0
MYCALL $MYCALL
CHANNEL 1
MODEM $SPEED
$PTT1
$TXINH1
MYCALL $MYCALL
$AGWPORT
$KISSPORT
EOF
;;
*) # Use only left channel
cat > $CONFFILE << EOF
ADEVICE $ADEVICE
ACHANNELS 1
CHANNEL 0
MODEM $SPEED
$PTT0
MYCALL $MYCALL
$AGWPORT
$KISSPORT
EOF
;;
esac
;;
*)
echo >&2 "Error: Valid baud settings are 1200 or 9600."
exit 1
;;
esac
;;
ardop)
CONFFILE=""
;;
*)
;;
esac
echo "$CONFFILE"
}
function checkSerial () {
if [[ $DEVSTRING == "" ]]
then # No rig defined. Don't use rigctld or ARDOP CAT commands to key radio
CMDS[rigctld]=""
else
SERIAL_PORT="$(find -P /dev/serial/by-id -maxdepth 1 -type l -exec echo -n "{} -> " \; -exec readlink {} \; | \
grep "$DEVSTRING" | cut -d' ' -f3 | tr -d './')"
if [[ $SERIAL_PORT == "" ]]
then # rigctl or ardop CAT control requested, but could not find serial port
echo >&2 "Error: Could not locate serial device with name containing \"$DEVSTRING\"."
exit 1
fi
DEVICE="/dev/$SERIAL_PORT"
if [[ $RIGCTL_RADIO != "" ]]
then
CMDS[rigctld]="$(which rigctld) -m $RIGCTL_RADIO -r $DEVICE -s $RIGCTL_SPEED"
else
CMDS[rigctld]=""
fi
fi
}
# Main #############################################################################################
SCREEN="$(checkApp screen)"
ARECORD="$(checkApp arecord)"
WGET="$(checkApp wget)"
case "$ACTION" in
start)
checkSoundCard
echo "" > $LOGFILE
echo
echo "Version $VERSION"
echo "Running $0 $ACTION $DMODE $SPEED $AUDIO_CHANNELS"
echo "Mode: $DMODE Speed: $SPEED Audio Device: $CAP_DEV Audio Channels: $AUDIO_CHANNELS"
echo
case "$DMODE" in
pat)
checkSerial
if [[ ${CMDS[rigctld]} == "" ]]
then
echo "rigctld will not be used."
ORDERS=( pat )
else
echo "rigctld will use radio found on $DEVICE."
ORDERS=( rigctld pat )
fi
echo "NOTE: If you haven't already done so, you must run 'pat configure' or manually"
echo " configure $HOME/.wl2k/config.json to use pat."
echo
CMDS[pat]="$(which pat) -l telnet http"
for i in ${!ORDERS[@]}
do
$SCREEN -c $SCREENCONFIG -L -d -m -S ${ORDERS[$i]} ${CMDS[${ORDERS[$i]}]}
echo "============================"
done
screen -list
echo
sleep 2
rm -f $CONFFILE
if [[ $GRID != "" ]]
then
if $WGET -q --tries=2 --timeout=5 --spider http://google.com
then # There is an internet connection, so get local RMS list
GRID="${GRID:0:4}"
GRID="${GRID^^}"
echo
echo "RMS Stations in grid square $GRID:"
$(which pat) rmslist | grep "${GRID}\|callsign" | sort -k 3,3 -n
fi
fi
echo
echo "Tailing $LOGFILE. All apps log to this file. Press Ctrl-C to quit all apps."
echo
tail -n 150 -F $LOGFILE
;;
*ax25*)
checkSerial
if [[ ${CMDS[rigctld]} == "" ]]
then
echo "rigctld will not be used."
case "$DMODE" in
*pat*)
ORDERS=( direwolf pat )
;;
*)
ORDERS=( direwolf )
;;
esac
else
case "$DMODE" in
*pat*)
ORDERS=( rigctld direwolf pat )
;;
*)
ORDERS=( rigctld direwolf )
;;
esac
echo "rigctld will use radio found on $DEVICE."
fi
if [[ $DMODE =~ "pat" ]]
then
echo "NOTE: If you haven't already done so, you must run 'pat configure' or manually"
echo " configure $HOME/.wl2k/config.json to use pat."
echo
CMDS[pat]="$(which pat) -l ax25,telnet http"
fi
# Check that the app is installed.
for i in ${!ORDERS[@]}
do
which ${ORDERS[$i]} >/dev/null
[ $? -eq 0 ] && echo "${ORDERS[$i]} found." || { echo >&2 "${ORDERS[$i]} required but not found. Aborting."; exit 1; }
# Kill existing session if it exists
SCR="$($SCREEN -list | grep ${ORDERS[$i]} | tr -d ' \t' | cut -d'(' -f1 | tr -d '\n')"
[[ "$SCR" != "" ]] && { pkill piardop2; $SCREEN -S $SCR -X quit; }
done
## Kill existing session if it exists
sudo killall kissattach 2>/dev/null
## Are the apps installed?
for i in kissattach kissparms
do
which $i >/dev/null
[ $? -eq 0 ] && echo "$i found." || { echo >&2 "Error: $i required but not found. Aborting."; exit 1; }
done
CONFFILE="$(makeConfig ax25)"
CMDS[direwolf]+=" -p -d u -c $CONFFILE"
for i in ${!ORDERS[@]}
do
echo
echo "Starting ${CMDS[${ORDERS[$i]}]}"
case "${ORDERS[$i]}" in
direwolf)
if ! grep -q "^$AX25PORT[[:space:]]" $AX25PORTFILE 2>/dev/null
then
echo -n "File $AX25PORTFILE empty or does not contain $AX25PORT. Adding..."
echo "$AX25PORT $MYCALL 0 255 7 Winlink" | sudo tee --append $AX25PORTFILE
echo "done."
fi
rm -f /tmp/kisstnc
$SCREEN -c $SCREENCONFIG -L -d -m -S ${ORDERS[$i]} ${CMDS[${ORDERS[$i]}]}
COUNTER=0
MAXWAIT=8
while [ $COUNTER -lt $MAXWAIT ]
do
# Allocate a PTY to ax25
[ -L /tmp/kisstnc ] && break
sleep 1
let COUNTER=COUNTER+1
done
if [ $COUNTER -ge $MAXWAIT ]
then
echo >&2 "Direwolf failed to allocate a PTY! Aborting."
echo >&2 "Is ADEVICE set to your sound card?"
ctrl_c
exit 1
fi
echo "Direwolf started."
sudo $(which kissattach) $(readlink -f /tmp/kisstnc) $AX25PORT
[ $? -eq 0 ] || { echo "kissattach failed. Aborting."; ctrl_c; exit 1; }
KISSPARMS="-c 1 -p $AX25PORT -t $TXDelay -l $TXTail -s $Slottime -r $Persist -f n"
echo "Setting $(which kissparms) $KISSPARMS"
sleep 2
sudo $(which kissparms) $KISSPARMS
[ $? -eq 0 ] || { echo "kissparms settings failed. Aborting."; ctrl_c; exit 1; }
;;
*)
$SCREEN -c $SCREENCONFIG -L -d -m -S ${ORDERS[$i]} ${CMDS[${ORDERS[$i]}]}
;;
esac
echo "============================"
done
screen -list
echo
echo "------ ax25 Direwolf configuration file ------"
cat $CONFFILE | grep -v "^$"
echo "----------------------------------------------"
sleep 2
rm -f $CONFFILE
if [[ $DMODE == "pat" && $GRID != "" ]]
then
if $WGET -q --tries=2 --timeout=5 --spider http://google.com
then # There is an internet connection, so get local RMS list
GRID="${GRID:0:4}"
GRID="${GRID^^}"
echo
echo "RMS Stations in grid square $GRID:"
$(which pat) rmslist | grep "${GRID}\|callsign" | sort -k 3,3 -n
fi
fi
echo
echo "Tailing $LOGFILE. All apps log to this file. Press Ctrl-C to quit all apps."
echo
tail -n 150 -F $LOGFILE
;;
digi*|igate)
[[ $PTT0 == "" || $PTT0 =~ "GPIO" ]] && ORDERS=( direwolf ) || { checkSerial; ORDERS=( rigctld direwolf ); }
# Check that the app is installed.
for i in ${!ORDERS[@]}
do
which ${ORDERS[$i]} >/dev/null
[ $? -eq 0 ] && echo "${ORDERS[$i]} found." || { echo >&2 "${ORDERS[$i]} required but not found. Aborting."; exit 1; }
# Kill existing session if it exists
SCR="$($SCREEN -list | grep ${ORDERS[$i]} | tr -d ' \t' | cut -d'(' -f1 | tr -d '\n')"
[[ "$SCR" != "" ]] && { pkill piardop2; $SCREEN -S $SCR -X quit; }
#[[ "$SCR" != "" ]] && $SCREEN -S $SCR -X quit
done
CONFFILE="$(makeConfig $DMODE)"
CMDS[direwolf]+=" -d t -c $CONFFILE"
for i in ${!ORDERS[@]}
do
echo
echo "Starting ${CMDS[${ORDERS[$i]}]}"
$SCREEN -c $SCREENCONFIG -L -d -m -S ${ORDERS[$i]} ${CMDS[${ORDERS[$i]}]}
done
screen -list
echo
echo "------ APRS $DMODE Direwolf configuration file ------"
cat $CONFFILE | grep -v "^$"
echo "-----------------------------------------------------"
echo
sleep 2
rm -f $CONFFILE
echo "Tailing $LOGFILE. All apps log to this file. Press Ctrl-C to quit all apps."
echo
tail -n 150 -F $LOGFILE
;;
*ardop*)
case "$DMODE" in
*pat*)
ORDERS=( piardop2 pat )
echo "NOTE: If you haven't already done so, you must run 'pat configure' or manually"
echo " configure $HOME/.wl2k/config.json to use pat."
echo
CMDS[pat]="$(which pat) -l ardop,telnet http"
;;
*)
ORDERS=( piardop2 )
;;
esac
# Check that the app is installed, and kill it if it is already running.
for i in ${!ORDERS[@]}
do
which ${ORDERS[$i]} >/dev/null
[ $? -eq 0 ] && echo "${ORDERS[$i]} found." || { echo >&2 "${ORDERS[$i]} required but not found. Aborting."; exit 1; }
# Kill existing session if it exists
SCR="$($SCREEN -list | grep ${ORDERS[$i]} | tr -d ' \t' | cut -d'(' -f1 | tr -d '\n')"
[[ "$SCR" != "" ]] && $SCREEN -S $SCR -X quit
done
CMDS[piardop2]="$(which piardop2) $ARDOP_PORT $ARDOP_DEV"
if [[ $ARDOP_PTT == "" ]]
then
echo >&2 "Error: Please set PTT type (variable ARDOP_PTT) for ARDOP in this script."
exit 1
else
CMDS[piardop2]+=" $ARDOP_PTT"
fi
for i in ${!ORDERS[@]}
do
echo
echo "Starting ${CMDS[${ORDERS[$i]}]}"
$SCREEN -c $SCREENCONFIG -L -d -m -S ${ORDERS[$i]} ${CMDS[${ORDERS[$i]}]}
done
echo "Tailing $LOGFILE. All apps log to this file. Press Ctrl-C to quit all apps."
echo
tail -n 150 -F $LOGFILE
;;
*)
Usage
;;
esac
;;
stop)
ORDERS=( rigctld piardop2 direwolf pat )
#ORDERS=( rigctld direwolf )
echo
for i in ${!ORDERS[@]}
do
SCR="$($SCREEN -list | grep ${ORDERS[$i]} | tr -d ' \t' | cut -d'(' -f1 | tr -d '\n')"
if [[ "$SCR" != "" ]]
then
echo -n "Stopping $SCR..."
$SCREEN -S $SCR -X quit
echo "done."
else
echo "Stopping ${ORDERS[$i]}: ${ORDERS[$i]} not running"
fi
done
pgrep piardop2 >/dev/null && kill -9 $(pgrep piardop2)
KISSATTACHPID="$(pgrep kissattach)"
if [[ $KISSATTACHPID != "" ]]
then
echo -n "Stopping kissattach..."
sudo killall kissattach
rm -f /tmp/kisstnc
echo "done."
fi
rm -f /tmp/tnc*
;;
*)
Usage
;;
esac