SHARE
TWEET

Simplex repeater bash script

howtophil Nov 13th, 2017 (edited) 551 Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
  1. #!/bin/bash
  2.  
  3. #-------------------------------------------
  4. # GENERAL NOTES
  5. #
  6. #-------------------------------------------
  7. #
  8. # Also on Github:
  9. # https://github.com/howtophil/simplex-repeater-bash
  10. #
  11. # Fairly major improvements added as of
  12. # 2020-02-24 though this is still very much
  13. # a very crude and simple simplex repeater
  14. # script written in bash. Requires sox,
  15. # espeak, sed, tr, grep, morse (or cw),
  16. # and multimon-ng.
  17. #
  18. # This version allows for remote DTMF
  19. # controls. (2020/02/22)
  20. #
  21. # Now has automagic conversion of
  22. # espeak spoken call sign into NATO
  23. # Phonetics. (2020/02/23)
  24. #
  25. # Added several more variables to control
  26. # settings for various parts. (2020/02/23)
  27. #
  28. # Made the terminal output a bit more bold
  29. # so you can see what the parrot script
  30. # is doing at a glance. (2020/02/24)
  31. #-------------------------------------------
  32. # Save in its own directory as simplex.sh
  33. # and run:
  34. #
  35. # ./simplex.sh
  36. #-------------------------------------------
  37. # Use pavucontrol to select input and output
  38. # or however is easiest/best for your
  39. # working environment.
  40. #
  41. # Raspberry Pi may use alsa instead of
  42. # pulse. You'll have to configure things
  43. # with alsamixer or similar.
  44. #-------------------------------------------
  45. # Code by Phillip J Rhoades
  46. #-------------------------------------------
  47.  
  48. #-------------------------------------------
  49. # HARDWARE NOTES
  50. #
  51. #-------------------------------------------
  52. # I use an HT with an APRS cable and a
  53. # mic/headphones splitter on one end
  54. # to patch the radio into a Linux computer
  55. # or a Raspberry Pi. The Pi will need a
  56. # cheap USB soundcard. A USB soundcard
  57. # could also help with ground issues
  58. # (buzzing) on computers/laptops.
  59. #
  60. # You'll have to adjust input and output
  61. # volumes on the computer and the output
  62. # volume on the radio. I also set the
  63. # radio to use VOX in this setup.
  64. #
  65. # You could improve on this basic setup
  66. # in a number of ways but that's outside
  67. # the scope of this "simple" project.
  68. #
  69. # If you're reading this script in monospace
  70. # then this rough ASCII diagram of the setup
  71. # might help you get a clearer idea:
  72. #
  73. #                    -->[PC Mic In]
  74. # [HT]<-APRS->SPLIT-||
  75. #                    <--[PC Sound Card Out]
  76. #
  77. # In some cases, a ground loop noise
  78. # isolator or two might help with buzzing
  79. # sounds between the computer and the HT.
  80. #-------------------------------------------
  81.  
  82. #-------------------------------------------
  83. # VARIABLES TO CONTROL THINGS
  84. #
  85. #-------------------------------------------
  86. # Set SECONDS to greater than 600 to trigger
  87. # repeater ID at startup.
  88. #-------------------------------------------
  89. # Set DTMFCONTROLS to 1 in order to be able
  90. # to control remotely via DTMF commands or
  91. # set DTMFCONTROLS to 0 in order to disable
  92. # that functionality.
  93. #-------------------------------------------
  94. # Leave IDLOOPING set to 1 in most cases.
  95. # Since identifying a station every 10
  96. # minutes is legally required.
  97. #
  98. # You might want to turn off the ID Loop
  99. # while testing on your sound card only.
  100. # To do that, set IDLOOPING to 0.
  101. #-------------------------------------------
  102. # Set CALLSIGN to your call sign.
  103. #-------------------------------------------
  104. # Set CALLSIGN_SPEAK to 1 to have espeak
  105. # say your call sign in NATO Phonetics
  106. # during each 10 minute ID announcement.
  107. #-------------------------------------------
  108. # Set CALLSIGN_MORSE to 1 to have morse
  109. # (usually from the bsdgames package)
  110. # or cw beep out your call sign during each
  111. # 10 minute ID announcement.
  112. #-------------------------------------------
  113. # Set MORSEPROG to whichever text to morse
  114. # program you prefer to use. You can
  115. # uncomment for EITHER morse from bsdgames
  116. # or cw from the cw package.
  117. #-------------------------------------------
  118. # Set MULTIMONPROG to either multimon or
  119. # multimon-ng, depending on what you have
  120. # installed or prefer. Both seem to work
  121. # just fine for DTMF decoding.
  122. # (2020/02/23)
  123. #-------------------------------------------
  124. # Setting PARROT to 0 will disable the
  125. # repeat of audio received by the script.
  126. # Setting PARROT to 1 will enable the
  127. # repeat of audio received by the script.
  128. #
  129. # This way, a menu item in dtmfactions
  130. # can be used to turn on and off just the
  131. # audio repeat part of this script while
  132. # keeping the DTMF processing in place.
  133. # (2020/02/24)
  134. #-------------------------------------------
  135. # Some radios and radio/computer combos
  136. # seem to have a slow vox. In those cases
  137. # activating a sound just loud enough to
  138. # trip the vox on can help the radio be
  139. # ready to transmit the first second of
  140. # the recorded audio.
  141. #
  142. # Set PREVOX to 1 in order to activate this
  143. # feature or set PREVOX to 0 in order to
  144. # deactivate it. (2020/02/25)
  145. #-------------------------------------------
  146. # It might be useful to save/log all audio
  147. # traffic that passes through your simplex
  148. # repeater. In order to keep the size down
  149. # I convert the wav file to ogg in the
  150. # background. You could easily set that to
  151. # mp3 instead or you could skip conversion
  152. # and just keep the original wavs, if you
  153. # have plenty of disk space.
  154. #
  155. # Setting SAVERECS to 1 activates this
  156. # feature and setting SAVERECS to 0
  157. # disables it. (2020/02/26)
  158. #-------------------------------------------
  159. # Just a few refinements, more comments,
  160. # and some more DTMF command examples.
  161. # (2020/02/29)
  162. #-------------------------------------------
  163. # Considering a "Dead Man's Switch" option
  164. # that would require a DTMF sequence every
  165. # N amount of time in order to keep the
  166. # repeater running. That way, no spurious
  167. # parrots continue operation if they
  168. # can't be reached within a certain time.
  169. # (2020/02/29)
  170. #-------------------------------------------
  171.  
  172. SECONDS=1000
  173. DTMFCONTROLS=1
  174. PARROT=1
  175. PREVOX=1
  176. SAVERECS=0
  177.  
  178. CALLSIGN="YOURCALLSIGN"
  179. IDLOOPING=1
  180. CALLSIGN_SPEAK=0
  181. CALLSIGN_MORSE=1
  182.  
  183. #MORSEPROG="cw"
  184. MORSEPROG="morse"
  185.  
  186. MULTIMONPROG="multimon-ng"
  187. #MULTIMONPROG="multimon"
  188.  
  189. #-------------------------------------------
  190. # MORE "ELEGANT" EXITING
  191. # (but still pretty much a hammer)
  192. #
  193. # Trap ctrl-c and call ctrl_c()
  194. # to exit somewhat gracefully...
  195. #-------------------------------------------
  196.  
  197. trap ctrl_c INT
  198.  
  199. function ctrl_c() {
  200.           voxy
  201.           if test -f "./recording.wav"; then
  202.                rm recording.wav
  203.           fi
  204.           echo
  205.           echo "#-------------------------------"
  206.           echo "# Terminating the simplex Parrot"
  207.           echo "#-------------------------------"
  208.           espeak "Terminating the simplex parrot"
  209.  
  210.           #-------------------------------------------
  211.           # Kill the parrot and its parent process
  212.           # since exiting any other way via DTMF
  213.           # commands seems to leave the script
  214.           # running in the background right now.
  215.           # Will try for more elegance later.
  216.           # (2020/02/23)
  217.           #
  218.           # Could issue shutdown -h instead in order
  219.           # to shut down Linux/Pi computer completely.
  220.           #-------------------------------------------
  221.  
  222.           kill -9 $PPID
  223.           #shutdown -h now
  224. }
  225.  
  226. #-------------------------------------------
  227. # VOX ACTIVATOR
  228. #
  229. # See notes in the settings area about
  230. # the PREVOX variable which turns this
  231. # on and off.
  232. #
  233. # Adjust this sound to something that
  234. # is not annoying/bothersome/crude.
  235. #
  236. # Sine sounds a tone and "brownnoise" has
  237. # the "benefit" of sounding like radio
  238. # static... So... A bit more natural?
  239. #
  240. # Less lossy. More bossy. (2020/02/25)
  241. #
  242. #-------------------------------------------
  243.  
  244. voxy () {
  245.      if [ $PREVOX -eq 1 ]; then
  246.           play -V1 -n -c1 synth .2 sine 70 #time and tone to activate vox are radio dependent
  247.           #play -n -c1 synth .2 brownnoise
  248.      fi
  249. }
  250.  
  251. #-------------------------------------------
  252. # NATO PHONETICS
  253. #
  254. #-------------------------------------------
  255. # Pipe output into this function and it will
  256. # return the string as NATO Phonetics, at
  257. # least for letters and numbers.
  258. #-------------------------------------------
  259. # Used for espeak to speak CALLSIGN
  260. # in NATO Phonetics
  261. #-------------------------------------------
  262.  
  263. natophonetics () {
  264.     echo $* | sed -e 's/\(.\)/\1\n/g' | natophonetics_sub
  265. }
  266.  
  267. natophonetics_sub () {
  268.     while read data; do
  269.         case "$data" in
  270.             A)  echo -n "Alpha " ;;
  271.             B)  echo -n "Bravo " ;;
  272.             C)  echo -n "Charlie " ;;
  273.             D)  echo -n "Delta " ;;
  274.             E)  echo -n "Echo " ;;
  275.             F)  echo -n "Foxtrot " ;;
  276.             G)  echo -n "Golf " ;;
  277.             H)  echo -n "Hotel " ;;
  278.             I)  echo -n "India " ;;
  279.             J)  echo -n "Juliet " ;;
  280.             K)  echo -n "Kilo " ;;
  281.             L)  echo -n "Lima " ;;
  282.             M)  echo -n "Mike " ;;
  283.             N)  echo -n "November " ;;
  284.             O)  echo -n "Oscar " ;;
  285.             P)  echo -n "Papa " ;;
  286.             Q)  echo -n "Quebec " ;;
  287.             R)  echo -n "Romeo " ;;
  288.             S)  echo -n "Sierra " ;;
  289.             T)  echo -n "Tango " ;;
  290.             U)  echo -n "Uniform " ;;
  291.             V)  echo -n "Victor " ;;
  292.             W)  echo -n "Whiskey " ;;
  293.             X)  echo -n "X-ray " ;;
  294.             Y)  echo -n "Yankee " ;;
  295.             Z)  echo -n "Zulu " ;;
  296.             0)  echo -n "Zero " ;;
  297.             1)  echo -n "One " ;;
  298.             2)  echo -n "Two " ;;
  299.             3)  echo -n "Three " ;;
  300.             4)  echo -n "Four " ;;
  301.             5)  echo -n "Five " ;;
  302.             6)  echo -n "Six " ;;
  303.             7)  echo -n "Seven " ;;
  304.             8)  echo -n "Eight " ;;
  305.             9)  echo -n "Nine " ;;
  306.             " ")  echo -n ". " ;;
  307.         esac
  308.     done
  309. }
  310.  
  311. #-------------------------------------------
  312. # DTMF CONTROLS (using multimon-ng)
  313. #
  314. #-------------------------------------------
  315. # scandtmf runs after every recording to
  316. # check for dtmf codes and passes those to
  317. # dtmfactions to be processed.
  318. #-------------------------------------------
  319. # Complex DTMF Actions could have their own
  320. # functions to call.
  321. #-------------------------------------------
  322.  
  323. scandtmf () {
  324.      dtmfactions $($MULTIMONPROG -q -t wav -a DTMF ./recording.wav 2>/dev/null |grep "DTMF" |grep -v "Enabled" |sed 's/^.\{6\}//'|tr -d '\n')
  325. }
  326.  
  327. dtmfactions () {
  328.      if [ $# -eq 1 ]; then
  329.           rm recording.wav
  330.           voxy
  331.           echo
  332.           echo "#-------------------------------"
  333.           echo "# Received DTMF Command: $1"
  334.           echo "#-------------------------------"
  335.           espeak "Received D T M F Command: $(echo $1| sed -e 's/\(.\)/\1 /g')"
  336.           if [ $1 = "#0" ]; then
  337.                espeak "The DTMF commands are"
  338.                espeak "#0 for help"
  339.                espeak "#1 for date and time"
  340.                espeak "#2 will toggle saving recordings"
  341.                espeak "#3 Weather report"
  342.                espeak "#4 Repeater controller uptime"
  343.                espeak "#73 to terminate the simplex parrot"
  344.                espeak "#99 to toggle the audio parrot"
  345.                return
  346.           fi
  347.           if [ $1 = "#1" ]; then
  348.                espeak "`date +"%A, %B %d, %Y, %I:%M %p"`"
  349.                return
  350.           fi
  351.           if [ $1 = "#2" ]; then
  352.                if [ $SAVERECS -eq 1 ]; then
  353.                     espeak "Recordings will not be saved."
  354.                     SAVERECS=0
  355.                else
  356.                     espeak "Recordings will be saved."
  357.                     SAVERECS=1
  358.                fi
  359.                return
  360.           fi
  361.           if [ $1 = "#3" ]; then
  362.                theweather #call the weather function
  363.                return
  364.           fi
  365.           if [ $1 = "#4" ]; then
  366.                espeak "This parrot has been `uptime -p | sed "s/^up /squawking for /g" |sed "s/,/ and/"`"
  367.                return
  368.           fi
  369.           if [ $1 = "#73" ]; then
  370.                ctrl_c
  371.                exit
  372.           fi
  373.           if [ $1 = "#99" ]; then
  374.                if [ $PARROT -eq 1 ]; then
  375.                     espeak "Parrot will now be silent."
  376.                     PARROT=0
  377.                else
  378.                     espeak "Parrot will now repeat traffic."
  379.                     PARROT=1
  380.                fi
  381.                return
  382.           fi
  383.           espeak "No such code. Send D T M F Code #0 for help." #should say if no matching DTMF code
  384.      fi
  385. }
  386.  
  387. # Just an example of calling another
  388. # function for a received DTMF command.
  389. # DTMF Command "#3"
  390. theweather () {
  391.      espeak "`ansiweather -p false -a false -s false -l "Kalamazoo, MI" -u imperial |tr "-" "\n" \
  392.     |tr "=>" "\n" |sed -e 's/°F/degrees/g' |sed -e 's/mph/miles per hour/g' |sed -e 's/$/./'`"
  393.      return
  394. }
  395.  
  396. #-------------------------------------------
  397. # REPEATER ID LOOP
  398. #
  399. #-------------------------------------------
  400. # Loop in background to identify repeater
  401. # no matter what else is going on.
  402. #-------------------------------------------
  403.  
  404. while [ $IDLOOPING -eq 1 ]; do
  405.      if [ $SECONDS -gt 600 ]; then
  406.           #-------------------------------------------
  407.           # By checking the value of SECONDS we can
  408.           # identify the repeater every 10 minutes
  409.           # (600 seconds).
  410.           #
  411.           # Why the loop and not just sleep for 600
  412.           # seconds? This way an "emergency" ID could
  413.           # be triggered via menu option etc.
  414.           #-------------------------------------------
  415.           #-------------------------------------------
  416.           # Identify repeater using espeak, morse,
  417.           # or both (or cw instead of morse).
  418.           #-------------------------------------------
  419.           if [ $CALLSIGN_SPEAK -eq 1 ] || [ $CALLSIGN_MORSE -eq 1 ]; then
  420.                voxy
  421.           fi
  422.           if [ $CALLSIGN_SPEAK -eq 1 ]; then
  423.                espeak "This is simplex repeater $(natophonetics $CALLSIGN) / R" &
  424.           fi
  425.           if [ $CALLSIGN_MORSE -eq 1 ]; then
  426.                echo "$CALLSIGN/R" |$MORSEPROG &
  427.           fi
  428.           #-------------------------------------------
  429.           # Always reset SECONDS to 0 here
  430.           # so we can begin watching for 600 seconds
  431.           # again and ID every 10-ish minutes.
  432.           #-------------------------------------------
  433.           SECONDS=0
  434.      fi
  435.      sleep 30 #If we loop this less often, it's easier on the CPU
  436. done &
  437.  
  438.  
  439. #-------------------------------------------
  440. # REPEATER PARROT LOOP
  441. #
  442. #-------------------------------------------
  443. # Loop until ctrl-c on keyboard or the
  444. # DTMF command to terminate the repeater
  445. #-------------------------------------------
  446.  
  447. while [ 1 ]; do
  448.           echo
  449.           echo "#-------------------------------"
  450.           echo "# WAITING FOR AUDIO INPUT:"
  451.           echo "#-------------------------------"
  452.           rec -V1 -c1 recording.wav rate 64k silence 1 0.1 1% 1 3.0 1% trim 0 30
  453.           if [ $DTMFCONTROLS -eq 1 ]; then
  454.                scandtmf
  455.           fi
  456.           if [ $PARROT -eq 1 ]; then
  457.                if test -f "./recording.wav"; then
  458.                     #sleep 2 # wait a second or two (radio dependent) so receive closes first (not always needed)
  459.                     voxy
  460.                     echo
  461.                     echo "#-------------------------------"
  462.                     echo "# PLAYING BACK CAPTURED AUDIO:"
  463.                     echo "#-------------------------------"
  464.                     play -V1 recording.wav #play back what was said, activating vox
  465.                     if [ $SAVERECS = 1 ]; then
  466.                          #If you decided to save the recordings
  467.                          TIMESTAMP=$(date)
  468.                          mv recording.wav "./$TIMESTAMP.wav" #moving a file on the smae filesystem is fast and cheap
  469.                          (ffmpeg -loglevel quiet -i "./$TIMESTAMP.wav" "./$TIMESTAMP.ogg"; rm "./$TIMESTAMP.wav") & #Takes time. Do in background
  470.                     else
  471.                          rm recording.wav #cleanup before restarting loop
  472.                     fi
  473.                fi
  474.           fi
  475. done
RAW Paste Data
We use cookies for various purposes including analytics. By continuing to use Pastebin, you agree to our use of cookies as described in the Cookies Policy. OK, I Understand
Top