Advertisement
howtophil

Simplex repeater bash script

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