howtophil icon

Simplex repeater bash script

howtophil | PRO | 03/12/24 12:16:05 AM UTC (Edited) | 0 ⭐ | 3258 👁️ | Never ⏰ | []
Bash |

16.47 KB

|

None

|

0 👍

/

0 👎

#!/bin/bash
 
#-------------------------------------------
# GENERAL NOTES
#
#-------------------------------------------
#
# Fairly major improvements added as of
# 2020-02-24 though this is still very much
# a very crude and simple simplex repeater
# script written in bash. Requires sox,
# espeak, sed, tr, grep, morse (or cw), 
# and multimon-ng.
#
# This version allows for remote DTMF 
# controls. (2020/02/22)
#
# Now has automagic conversion of
# espeak spoken call sign into NATO
# Phonetics. (2020/02/23)
#
# Added several more variables to control
# settings for various parts. (2020/02/23)
#
# Made the terminal output a bit more bold
# so you can see what the parrot script
# is doing at a glance. (2020/02/24)
#-------------------------------------------
# Save in its own directory as simplex.sh
# and run:
#
# ./simplex.sh
#-------------------------------------------
# Use pavucontrol to select input and output
# or however is easiest/best for your
# working environment.
#
# Raspberry Pi may use alsa instead of 
# pulse. You'll have to configure things
# with alsamixer or similar. It does seem
# to use pulseaudio nicely when pulse is
# installed.
#-------------------------------------------
# Code by Phillip J Rhoades
#-------------------------------------------
 
#-------------------------------------------
# HARDWARE NOTES
#
#-------------------------------------------
# I use an HT with an APRS cable and a
# mic/headphones splitter on one end
# to patch the radio into a Linux computer
# or a Raspberry Pi. The Pi will need a
# cheap USB soundcard. A USB soundcard
# could also help with ground issues
# (buzzing) on computers/laptops.
# 
# You'll have to adjust input and output
# volumes on the computer and the output
# volume on the radio. I also set the 
# radio to use VOX in this setup.
#
# You could improve on this basic setup
# in a number of ways but that's outside
# the scope of this "simple" project.
#
# If you're reading this script in monospace
# then this rough ASCII diagram of the setup
# might help you get a clearer idea:
#
#                    -->[PC Mic In]
# [HT]<-APRS->SPLIT-||
#                    <--[PC Sound Card Out]
#
# In some cases, a ground loop noise
# isolator or two might help with buzzing
# sounds between the computer and the HT.
#-------------------------------------------
 
#-------------------------------------------
# VARIABLES TO CONTROL THINGS
#
#-------------------------------------------
# Set SECONDS to greater than 600 to trigger
# repeater ID at startup.
#-------------------------------------------
# Set DTMFCONTROLS to 1 in order to be able
# to control remotely via DTMF commands or
# set DTMFCONTROLS to 0 in order to disable
# that functionality.
#-------------------------------------------
# Leave IDLOOPING set to 1 in most cases.
# Since identifying a station every 10
# minutes is legally required.
#
# You might want to turn off the ID Loop
# while testing on your sound card only.
# To do that, set IDLOOPING to 0.
#-------------------------------------------
# Set CALLSIGN to your call sign.
#-------------------------------------------
# Set CALLSIGN_SPEAK to 1 to have espeak
# say your call sign in NATO Phonetics
# during each 10 minute ID announcement.
#-------------------------------------------
# Set CALLSIGN_MORSE to 1 to have morse
# (usually from the bsdgames package)
# or cw beep out your call sign during each 
# 10 minute ID announcement.
#-------------------------------------------
# Set MORSEPROG to whichever text to morse
# program you prefer to use. You can
# uncomment for EITHER morse from bsdgames
# or cw from the cw package.
#-------------------------------------------
# Set MULTIMONPROG to either multimon or
# multimon-ng, depending on what you have
# installed or prefer. Both seem to work
# just fine for DTMF decoding.
# (2020/02/23)
#-------------------------------------------
# Setting PARROT to 0 will disable the
# repeat of audio received by the script.
# Setting PARROT to 1 will enable the
# repeat of audio received by the script.
#
# This way, a menu item in dtmfactions
# can be used to turn on and off just the
# audio repeat part of this script while
# keeping the DTMF processing in place.
# (2020/02/24)
#-------------------------------------------
# Some radios and radio/computer combos
# seem to have a slow vox. In those cases
# activating a sound just loud enough to
# trip the vox on can help the radio be
# ready to transmit the first second of
# the recorded audio.
#
# Set PREVOX to 1 in order to activate this
# feature or set PREVOX to 0 in order to
# deactivate it. (2020/02/25)
#-------------------------------------------
# It might be useful to save/log all audio
# traffic that passes through your simplex
# repeater. In order to keep the size down
# I convert the wav file to ogg in the 
# background. You could easily set that to
# mp3 instead or you could skip conversion
# and just keep the original wavs, if you
# have plenty of disk space.
# 
# Setting SAVERECS to 1 activates this
# feature and setting SAVERECS to 0
# disables it. (2020/02/26)
#-------------------------------------------
# Just a few refinements, more comments,
# and some more DTMF command examples.
# (2020/02/29)
#-------------------------------------------
# Considering a "Dead Man's Switch" option
# that would require a DTMF sequence every
# N amount of time in order to keep the
# repeater running. That way, no spurious
# parrots continue operation if they
# can't be reached within a certain time.
# (2020/02/29)
#-------------------------------------------
# Considering a rec -t wav | play -t wav
# pass-through option for use of this script
# as a duplex repeater controller...
# (2020/02/29)
#-------------------------------------------
 
SECONDS=1000
DTMFCONTROLS=1
PARROT=1
PREVOX=1
SAVERECS=0
 
CALLSIGN="YOURCALLSIGN"
IDLOOPING=1
CALLSIGN_SPEAK=1
CALLSIGN_MORSE=0
 
#MORSEPROG="cw"
MORSEPROG="morse"
 
MULTIMONPROG="multimon-ng"
#MULTIMONPROG="multimon"
 
TIMEOUT=120
 
#-------------------------------------------
# MORE "ELEGANT" EXITING
# (but still pretty much a hammer)
#
# Trap ctrl-c and call ctrl_c()
# to exit somewhat gracefully...
#-------------------------------------------
 
trap ctrl_c INT
 
function ctrl_c() {
          voxy
          if test -f "./recording.wav"; then
               rm recording.wav
          fi
          echo
          echo "#-------------------------------"
          echo "# Terminating the simplex Parrot"
          echo "#-------------------------------"
          espeak "Terminating the simplex parrot"
 
          #-------------------------------------------
          # Kill the parrot and its parent process
          # since exiting any other way via DTMF 
          # commands seems to leave the script
          # running in the background right now.
          # Will try for more elegance later.
          # (2020/02/23)
          # 
          # Could issue shutdown -h instead in order
          # to shut down Linux/Pi computer completely.
          #-------------------------------------------
 
          kill -9 $PPID
          #shutdown -h now
}
 
#-------------------------------------------
# VOX ACTIVATOR
#
# See notes in the settings area about
# the PREVOX variable which turns this
# on and off.
#
# Adjust this sound to something that
# is not annoying/bothersome/crude.
#
# Sine sounds a tone and "brownnoise" has
# the "benefit" of sounding like radio
# static... So... A bit more natural?
#
# Less lossy. More bossy. (2020/02/25)
#
#-------------------------------------------
 
voxy () {
     if [ $PREVOX -eq 1 ]; then
          play -V1 -n -c1 synth .4 sine 179.9 #time and tone to activate vox are radio dependent
          #play -n -c1 synth .4 brownnoise
     fi
}
 
#-------------------------------------------
# NATO PHONETICS
#
#-------------------------------------------
# Pipe output into this function and it will
# return the string as NATO Phonetics, at
# least for letters and numbers.
#-------------------------------------------
# Used for espeak to speak CALLSIGN
# in NATO Phonetics
#-------------------------------------------
 
natophonetics () {
    echo $* | sed -e 's/\(.\)/\1\n/g' | natophonetics_sub
}
 
natophonetics_sub () {
    while read data; do
        case "$data" in
            A)  echo -n "Alpha " ;;
            B)  echo -n "Bravo " ;;
            C)  echo -n "Charlie " ;;
            D)  echo -n "Delta " ;;
            E)  echo -n "Echo " ;;
            F)  echo -n "Foxtrot " ;;
            G)  echo -n "Golf " ;;
            H)  echo -n "Hotel " ;;
            I)  echo -n "India " ;;
            J)  echo -n "Juliet " ;;
            K)  echo -n "Kilo " ;;
            L)  echo -n "Lima " ;;
            M)  echo -n "Mike " ;;
            N)  echo -n "November " ;;
            O)  echo -n "Oscar " ;;
            P)  echo -n "Papa " ;;
            Q)  echo -n "Quebec " ;;
            R)  echo -n "Romeo " ;;
            S)  echo -n "Sierra " ;;
            T)  echo -n "Tango " ;;
            U)  echo -n "Uniform " ;;
            V)  echo -n "Victor " ;;
            W)  echo -n "Whiskey " ;;
            X)  echo -n "X-ray " ;;
            Y)  echo -n "Yankee " ;;
            Z)  echo -n "Zulu " ;;
            0)  echo -n "Zero " ;;
            1)  echo -n "One " ;;
            2)  echo -n "Two " ;;
            3)  echo -n "Three " ;;
            4)  echo -n "Four " ;;
            5)  echo -n "Five " ;;
            6)  echo -n "Six " ;;
            7)  echo -n "Seven " ;;
            8)  echo -n "Eight " ;;
            9)  echo -n "Nine " ;;
            " ")  echo -n ". " ;;
        esac
    done
}
 
#-------------------------------------------
# DTMF CONTROLS (using multimon-ng)
#
#-------------------------------------------
# scandtmf runs after every recording to 
# check for dtmf codes and passes those to
# dtmfactions to be processed.
#-------------------------------------------
# Complex DTMF Actions could have their own
# functions to call.
#-------------------------------------------
 
scandtmf () {
     dtmfactions $($MULTIMONPROG -q -t wav -a DTMF ./recording.wav 2>/dev/null |grep "DTMF" |grep -v "Enabled" |sed 's/^.\{6\}//'|tr -d '\n')
}
 
dtmfactions () {
     if [ $# -eq 1 ]; then
          rm recording.wav
          voxy
          echo
          echo "#-------------------------------"
          echo "# Received DTMF Command: $1"
          echo "#-------------------------------"
          espeak "Received D T M F Command: $(echo $1| sed -e 's/\(.\)/\1 /g')"
          if [ $1 = "#0" ]; then
               espeak "The DTMF commands are"
               espeak "#0 for help"
               espeak "#1 for date and time"
               espeak "#2 will toggle saving recordings"
               espeak "#3 Weather report"
               espeak "#4 Repeater controller uptime"
               espeak "#73 to terminate the simplex parrot"
               espeak "#99 to toggle the audio parrot"
               return
          fi
          if [ $1 = "#1" ]; then
               espeak "`date +"%A, %B %d, %Y, %I:%M %p"`"
               return
          fi
          if [ $1 = "#2" ]; then
               if [ $SAVERECS -eq 1 ]; then
                    espeak "Recordings will not be saved."
                    SAVERECS=0
               else
                    espeak "Recordings will be saved."
                    SAVERECS=1
               fi 
               return
          fi
          if [ $1 = "#3" ]; then
               theweather #call the weather function
               return
          fi
          if [ $1 = "#4" ]; then
               espeak "This parrot has been `uptime -p | sed "s/^up /squawking for /g"`"
               return
          fi
          if [ $1 = "#73" ]; then
               ctrl_c 
               exit
          fi
          if [ $1 = "#99" ]; then
               if [ $PARROT -eq 1 ]; then
                    espeak "Parrot will now be silent."
                    PARROT=0
               else
                    espeak "Parrot will now repeat traffic."
                    PARROT=1
               fi 
               return
          fi
          espeak "No such code. Send D T M F Code #0 for help." #should say if no matching DTMF code
     fi
}
 
# Just an example of calling another
# function for a received DTMF command.
# DTMF Command "#3"
theweather () {
     espeak "`ansiweather -p false -a false -s false -l "Kalamazoo, MI" -u imperial |tr "-" "\n" \
     |tr "=>" "\n" |sed -e 's/°F/degrees/g' |sed -e 's/mph/miles per hour/g' |sed -e 's/$/./'`"
     return
}
 
#-------------------------------------------
# REPEATER ID LOOP
#
#-------------------------------------------
# Loop in background to identify repeater
# no matter what else is going on.
#-------------------------------------------
 
while [ $IDLOOPING -eq 1 ]; do
     if [ $SECONDS -gt 600 ]; then
          #-------------------------------------------
          # By checking the value of SECONDS we can
          # identify the repeater every 10 minutes
          # (600 seconds). 
          # 
          # Why the loop and not just sleep for 600
          # seconds? This way an "emergency" ID could
          # be triggered via menu option etc.
          #-------------------------------------------
          #-------------------------------------------
          # Identify repeater using espeak, morse,
          # or both (or cw instead of morse).
          #-------------------------------------------
          if [ $CALLSIGN_SPEAK -eq 1 ] || [ $CALLSIGN_MORSE -eq 1 ]; then
               voxy
          fi
          if [ $CALLSIGN_SPEAK -eq 1 ]; then
               espeak "This is simplex repeater $(natophonetics $CALLSIGN) / R" &
          fi
          if [ $CALLSIGN_MORSE -eq 1 ]; then
               echo "$CALLSIGN/R" |$MORSEPROG &
          fi
          #-------------------------------------------
          # Always reset SECONDS to 0 here
          # so we can begin watching for 600 seconds
          # again and ID every 10-ish minutes.
          #-------------------------------------------
          SECONDS=0
     fi
     sleep 30 #If we loop this less often, it's easier on the CPU
done &
 
 
#-------------------------------------------
# REPEATER PARROT LOOP
#
#-------------------------------------------
# Loop until ctrl-c on keyboard or the
# DTMF command to terminate the repeater
#-------------------------------------------
 
while [ 1 ]; do
          echo
          echo "#-------------------------------"
          echo "# WAITING FOR AUDIO INPUT:"
          echo "#-------------------------------"
          rec -V1 -c1 recording.wav rate 64k silence 1 0.1 1% 1 3.0 1% trim 0 $TIMEOUT
 
          # Potential command for pass-through for duplex repeater
          # while still recording a wav for scandtmf to check and
          # still enforcing the transmit timeout.
          # Some latency dependent on computer, but basically live.
          # Making the mic hot in alsa or pulse would have
          # less latency but other issues.
          # 
          #rec -V1 -c1 -t wav - rate 64k silence 1 0.1 1% 1 3.0 1% trim 0 $TIMEOUT |tee recording.wav |play -t wav -
 
          if [ $DTMFCONTROLS -eq 1 ]; then
               scandtmf
          fi
          if [ $PARROT -eq 1 ]; then
               if test -f "./recording.wav"; then
                    #sleep 2 # wait a second or two (radio dependent) so receive closes first (not always needed)
                    voxy
                    echo
                    echo "#-------------------------------"
                    echo "# PLAYING BACK CAPTURED AUDIO:"
                    echo "#-------------------------------"
                    play -V1 recording.wav #play back what was said, activating vox
                    if [ $SAVERECS = 1 ]; then
                         #If you decided to save the recordings
                         TIMESTAMP=$(date)
                         mv recording.wav "./$TIMESTAMP.wav" #moving a file on the same filesystem is fast and cheap
                         (ffmpeg -loglevel quiet -i "./$TIMESTAMP.wav" "./$TIMESTAMP.ogg"; rm "./$TIMESTAMP.wav") & #Takes time. Do in background
                    else 
                         rm recording.wav #cleanup before restarting loop
                    fi
               fi
          fi
done
 

Comments

  • mizike2 icon
    mizike2 PRO
    09/07/23 03:06:06 AM UTC
    text |

    0 B

    |

    1 👍

    /

    0 👎

    Any chance this could be updated to allow Raspberry PI GPIO for COS and PTT to get away from VOX?