emq-TE1/service/dv-list-tg.sh

1377 lines
47 KiB
Bash

#!/bin/bash
sudo cat > /bin/menu-update-tg <<- "EOF"
#!/bin/bash
while : ; do
choix=$(whiptail --title "Raspbian Proyect HP3ICC Menu TG List DVSwitch" --menu "Suba o Baje con las flechas del teclado y seleccione presionando intro, al finalizar debe actualizar la lista de tg en su DVSwitch Mobile. " 18 55 5 \
1 " TG list FreeDMR " \
2 " TG list Brandmeister " \
3 " Menu Principal " 3>&1 1>&2 2>&3)
exitstatus=$?
#on recupere ce choix
#exitstatus=$?
if [ $exitstatus = 0 ]; then
echo "Your chosen option:" $choix
else
echo "You chose cancel."; break;
fi
# case : action en fonction du choix
case $choix in
1)
sudo sed -i '538s/curl --fail -o "$NODE_DIR\/$1" -s.*/curl --fail -o "$NODE_DIR\/$1" -s http:\/\/datafiles.ddns.net:8888\/downloads\/$1/' /opt/MMDVM_Bridge/dvswitch.sh &&
menu-update-tg2 ;;
2)
sudo sed -i '538s/curl --fail -o "$NODE_DIR\/$1" -s.*/curl --fail -o "$NODE_DIR\/$1" -s http:\/\/www.pistar.uk\/downloads\/$1/' /opt/MMDVM_Bridge/dvswitch.sh &&
menu-update-tg2 ;;
3)
/usr/local/dvs/dvs;
esac
done
exit 0
EOF
####
sudo cat > /bin/menu-update-tg2 <<- "EOF"
#!/bin/bash
while : ; do
choix=$(whiptail --title "Raspbian Proyect HP3ICC Menu TG List DVSwitch" --menu "Proceda a actualizar su DVSwitch Mobile." 9 55 1 \
1 "Press: Update database files, in you DVSMobile APP" 3>&1 1>&2 2>&3)
exitstatus=$?
#on recupere ce choix
#exitstatus=$?
if [ $exitstatus = 0 ]; then
echo "Your chosen option:" $choix
else
echo "You chose cancel."; break;
fi
# case : action en fonction du choix
case $choix in
1)
break;
esac
done
exit 0
EOF
####
sudo cat > /opt/MMDVM_Bridge/dvswitch.sh <<- "EOF"
#!/bin/bash
#################################################################
# /*
# * Copyright (C) 2019, 2020, 2021 N4IRR
# *
# * Permission to use, copy, modify, and/or distribute this software for any
# * purpose with or without fee is hereby granted, provided that the above
# * copyright notice and this permission notice appear in all copies.
# *
# * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
# * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
# * AND FITNESS. IN NO EVENT SHALL N4IRR BE LIABLE FOR ANY SPECIAL, DIRECT,
# * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
# * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
# * OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
# * PERFORMANCE OF THIS SOFTWARE.
# */
#################################################################
#DEBUG=echo
#set -xv # this line will enable debug
SCRIPT_VERSION="1.6.1"
AB_DIR=${AB_DIR:-"/var/lib/dvswitch"}
MMDVM_DIR=${MMDVM_DIR:-"/var/lib/mmdvm"}
DVSWITCH_INI=${DVSWITCH_INI:-"/opt/MMDVM_Bridge/DVSwitch.ini"}
MMDVM_INI=${MMDVM_INI:-"/opt/MMDVM_Bridge/MMDVM_Bridge.ini"}
NODE_DIR=${NODE_DIR:-"/tmp"}
# Default server and port assignment, but overridden by value in ABInfo
TLV_PORT=36000
SERVER=127.0.0.1
# HTTP_PORT is used for the simple server that supports data file uploads
HTTP_PORT=9042
# Error codes defined below
SUCCESSS=0
ERROR_FILE_NOT_FOUND=-1
ERROR_INVALID_ARGUMENT=-2
ERROR_EMPTY_FILE=-3
ERROR_DIR_NOT_FOUND=-4
ERROR_INVALID_FILE=-5
ERROR_LOOKUP_FAILED=-6
ERROR_INI_FAILURE=-7
_ERRORCODE=$SUCCESSS
#################################################################
# Return value from ABInfo_xxxx.json
# The value may be an value, object/value or object/object/value
#################################################################
function getABInfoValue() {
declare _json_file=`getABInfoFileName`
python3 - <<END
#!/usr/bin/env python
try:
import json, os, sys
json = json.loads(open("$_json_file").read())
if "$2" == "": # Not all values are enclosed in an object
value = json["$1"]
else:
if "$3" == "":
value = json["$1"]["$2"]
else:
value = json["$1"]["$2"]["$3"]
print(value)
except:
sys.stderr.write("getABInfoValue: error getting value(s) $1 $2 $3\n")
print("ERROR")
exit(1)
END
}
#################################################################
# get file name of the current ABInfo json file
#################################################################
function getABInfoFileName() {
if [ -z "${ABINFO}" ]; then # if no enviornment variable, use the latest file in /tmp
declare _json_file=`ls -t /tmp/ABInfo_*.json 2>/dev/null | head -1`
else
declare _json_file=$ABINFO # Use the environment variable (probably set by AB)
fi
echo $_json_file
}
#################################################################
# Parse and print out an ini file value
# parseIniFile fileName stanza tag
#################################################################
function parseIniFile() {
python3 - <<END
#!/usr/bin/env python
try:
import sys, configparser
with open("$1") as f:
file_content = '[dummy_section]\n' + f.read()
config = configparser.RawConfigParser(inline_comment_prefixes=(';',))
config.read_string(file_content)
print( config.get('$2', '$3') )
except:
sys.stderr.write("parseIniFile: Config parse error for file: $1. Error: " + str(sys.exc_info()[1]) + "\n")
print("ERROR")
exit(1)
END
}
#################################################################
# Return TLV_PORT from ABInfo_xxxx.json
# This is the port that AB is listening to for commands and MB
# packets.
#################################################################
function getTLVPort() {
getABInfoValue tlv rx_port
}
#################################################################
# Tune to a specific TG/Reflector/Server, etc
# Argument 1 is the TG to tune to. The argument is mode specific.
#################################################################
function tune() {
if [ $# -eq 0 ]; then
getABInfoValue last_tune
else
remoteControlCommand "txTg=$1"
fi
}
#################################################################
# Set the number of bits that AB will use to encode a PCM sample
# The bits in argument 1 (48, 49, 72 or 88) are mode specific
#################################################################
function setAmbeSize() {
if [ $# -eq 0 ]; then
getABInfoValue tlv ambe_size
else
remoteControlCommand "ambeSize=$1"
fi
}
#################################################################
# Set the slot to transmit on. Slot may be 1 or 2
#################################################################
function setSlot() {
if [ $# -eq 0 ]; then
getABInfoValue digital ts
else
remoteControlCommand "txTs=$1"
fi
}
#################################################################
# Set the AMBE mode of Analog_Bridge to DMR|DSTAR|NXDN|YSFN|YSFW|P25
#################################################################
function setAmbeMode() {
if [ $# -eq 0 ]; then
getABInfoValue tlv ambe_mode
else
remoteControlCommand "ambeMode=$1"
fi
}
#################################################################
# Send graceful exit command to Analog_Bridge
#################################################################
function exitAnalogBridge() {
remoteControlCommand "exit=$1 $2"
}
#################################################################
# Set the analog audio shaping type
# argument may be AUDIO_UNITY, AUDIO_USE_AGC, AUDIO_USE_GAIN
#################################################################
function setUSRPAudioType() {
if [ $# -eq 0 ]; then
getABInfoValue usrp to_pcm shape
else
remoteControlCommand "usrpAudio=$1"
fi
}
#################################################################
# Set the digital audio shaping type
# argument may be AUDIO_UNITY, AUDIO_USE_GAIN, AUDIO_USE_BPF
#################################################################
function setTLVAudioType() {
if [ $# -eq 0 ]; then
getABInfoValue usrp to_ambe shape
else
remoteControlCommand "tlvAudio=$1"
fi
}
#################################################################
# Set the analog (PCM) audio gain
# Argument may be between 0 - x, where
# < 1 will decrease audio level from unity
# 1 = UNITY gain
# > 1 will increase audio level above unity
#################################################################
function setUSRPGain() {
if [ $# -eq 0 ]; then
getABInfoValue usrp to_pcm gain
else
remoteControlCommand "usrpGain=$1"
fi
}
#################################################################
# Set the digital audio gain
#################################################################
function setTLVGain() {
if [ $# -eq 0 ]; then
getABInfoValue usrp to_ambe gain
else
remoteControlCommand "tlvGain=$1"
fi
}
#################################################################
# Set the USRP agc params to threshold, slope and decay
#################################################################
function setUSRPAgc() {
if [ $# -eq 0 ]; then
echo "Argument required: AGC parameters (threshold, slope and decay)"
_ERRORCODE=$ERROR_INVALID_ARGUMENT
else
remoteControlCommand "agcUSRP=$1,$2,$3"
fi
}
#################################################################
# Set the TLV agc params to threshold, slope and decay
#################################################################
function setTLVAgc() {
if [ $# -eq 0 ]; then
echo "Argument required: AGC parameters (threshold, slope and decay)"
_ERRORCODE=$ERROR_INVALID_ARGUMENT
else
remoteControlCommand "agcTLV=$1,$2,$3"
fi
}
#################################################################
# Set the USRP audio codec to {SLIN|ULAW|ADPCM}
#################################################################
function setUSRPCodec() {
if [ $# -eq 0 ]; then
echo "Argument required: codec"
_ERRORCODE=$ERROR_INVALID_ARGUMENT
else
string='|SLIN|ULAW|ADPCM|slin|ulaw|adpcm|'
if [[ $string == *"|$1|"* ]]; then
remoteControlCommand "codec=$1"
else
echo "Invalid argument: {slin|ulaw|adpcm}"
_ERRORCODE=$ERROR_INVALID_ARGUMENT
fi
fi
}
#################################################################
# set the AB listener port
#################################################################
function setTLVRxPort() {
if [ $# -eq 0 ]; then
getABInfoValue tlv rx_port
else
remoteControlCommand "rxport=$1"
sleep 1
TLV_PORT=`getTLVPort` # We have changed the listener on AB, so we must adjust our sending port
fi
}
#################################################################
# Set the AB -> MB transmit port
#################################################################
function setTLVTxPort() {
if [ $# -eq 0 ]; then
getABInfoValue tlv tx_port
else
remoteControlCommand "txport=$1"
fi
}
#################################################################
# Send the info packet to a USRP client (DVSM/UC)
#################################################################
function getInfo() {
if [ $# -eq 0 ]; then
remoteControlCommand "info"
else
getABInfoValue $1 $2
fi
}
#################################################################
# mute AB ("OFF", "USRP", "TLV", "BOTH")
#################################################################
function setMute() {
if [ $# -eq 0 ]; then
getABInfoValue mute
else
remoteControlCommand "mute=$1"
fi
}
#################################################################
# Send "text" message to Mobile
#################################################################
function sendMessage() {
if [ -z "$1" ]; then
echo "Argument required: text"
_ERRORCODE=$ERROR_INVALID_ARGUMENT
else
remoteControlCommand "message=$1"
fi
}
#################################################################
# Send a macro definition or file to Mobile
#################################################################
function sendMacro() {
if [ -z "$2" ]; then
echo "Argument required: file or text"
_ERRORCODE=$ERROR_INVALID_ARGUMENT
else
remoteControlCommand "$1=$2"
fi
}
#################################################################
# Set the ping timer (keep alive)
#################################################################
function setPingTimer() {
if [ -z "$1" ]; then
getABInfoValue usrp ping
else
remoteControlCommand "ping=$1"
fi
}
#################################################################
# Tell AB to reload database files from disk into memory
#################################################################
function reloadDatabase() {
remoteControlCommand "reloadDatabase"
}
#################################################################
# Send the remote control TLV command to Analog_Bridge
#################################################################
function remoteControlCommand() {
if [ ! -z "${DEBUG}" ]; then
echo "remoteControlCommand $1"
else
PYTHON_ARG="$1" python3 - <<END
#!/usr/bin/env python
try:
import sys, socket, struct, os
cmd = os.environ['PYTHON_ARG'].replace("\\\" + "n", "\n").encode("utf-8")
_sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
cmd = struct.pack("BB", 0x05, len(cmd))[0:2] + cmd
_sock.sendto(cmd, ('$SERVER', $TLV_PORT))
_sock.close()
except:
print('$SERVER', '$TLV_PORT')
sys.stderr.write("remoteControlCommand: error sending command " + str(sys.exc_info()[1]) + "\n")
exit(1)
END
fi
}
#################################################################
# Compose a USRP packet and send it to AB (WIP: address and port)
#################################################################
function USRPCommand() {
python3 - <<END
#!/usr/bin/env python
import traceback, struct, socket, sys
try:
usrpSeq = 1
packetType = $1
cmd = "$2"
usrp = 'USRP'.encode('ASCII') + (struct.pack('>iiiiiii',usrpSeq, 0, 0, 0, packetType << 24, 0, 0)) + cmd
usrpSeq = (usrpSeq + 1) & 0xffff
udp = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
udp.sendto(usrp, ("127.0.0.1", 12345))
udp.close()
except:
sys.stderr.write("USRPCommand: error sending command\n")
traceback.print_exc()
END
}
#################################################################
#
#################################################################
function setCallAndID() {
if [ ! -z "${DEBUG}" ]; then
echo "setCallAndID $1"
else
python3 - <<END
#!/usr/bin/env python
try:
import sys, socket, struct
call = "$1"
dmr_id = $2
tlvLen = 3 + 4 + 3 + 1 + 1 + len(call) + 1 # dmrID, repeaterID, tg, ts, cc, call, 0
_sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
cmd = struct.pack("BBBBBBBBBBBBBB", 0x08, tlvLen, ((dmr_id >> 16) & 0xff),((dmr_id >> 8) & 0xff),(dmr_id & 0xff),0,0,0,0,0,0,0,0,0)[0:14] + call + chr(0)
_sock.sendto(cmd, ('$SERVER', $TLV_PORT))
_sock.close()
except:
sys.stderr.write("setCallAndID: error sending command\n")
exit(1)
END
fi
}
#################################################################
# Tell AB to upload a file to the Mobile client
#################################################################
function pushFileToClient() {
if [ ! -z "${DEBUG}" ]; then
echo "remoteControlCommand pushFileToClient $1"
else
if [ ! -f $1 ]; then
echo "File $1 does not exist, abort transfer"
return
fi
size=`wc -c $1 | awk '{print $1}'`
if (($size == 0)); then
echo "file is empty, abort transfer"
return
fi
python3 - <<END
#!/usr/bin/env python
try:
import sys, socket, struct
TLV_TAG_FILE_XFER = 11
FILE_SUBCOMMAND_READ = 3
name = "$1".encode("utf-8")+b'\x00'
_sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
cmd = struct.pack("BBB", TLV_TAG_FILE_XFER, len(name)+1, FILE_SUBCOMMAND_READ)[0:3] + name
_sock.sendto(cmd, ('$SERVER', $TLV_PORT))
_sock.close()
except:
sys.stderr.write("pushFileToClient: error pushing file $1\n")
exit(1)
END
fi
}
#################################################################
# Push a local file as a URL to DVSM. The file is checked for
# whether it exists and has a size > 0 bytes. Arguments are
# Directory, Server IP and file name.
#################################################################
function pushLocalFileAsURLToClient() {
if [ ! -f "$1/$3" ]; then
echo "File $1/$3 does not exist, abort transfer"
_ERRORCODE=$ERROR_FILE_NOT_FOUND
return
fi
declare size=`wc -c "$1/$3" | awk '{print $1}'`
if (($size == 0)); then
echo "file is empty, abort transfer"
_ERRORCODE=$ERROR_EMPTY_FILE
return
fi
pushURLToClient "$2/$3"
}
#################################################################
# Send the URL of a file to download to DVSM. DVSM knows that if
# the name begins with http it is a URL.
#################################################################
function pushURLToClient() {
python3 - <<END
#!/usr/bin/env python
try:
import sys, socket, struct
TLV_TAG_FILE_XFER = 11
FILE_SUBCOMMAND_READ = 3
name = "$1".encode("utf-8")+b'\x00'
_sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
cmd = struct.pack("BBB", TLV_TAG_FILE_XFER, len(name)+1, FILE_SUBCOMMAND_READ)[0:3] + name
_sock.sendto(cmd, ('$SERVER', $TLV_PORT))
_sock.close()
except:
sys.stderr.write("pushURLToClient: error sending URL $1\n")
exit(1)
END
}
#################################################################
# Parse Pi-Star YSF reflector file
#################################################################
function ParseYSFile() {
curl --fail -o "$NODE_DIR/$1" -s http://www.pistar.uk/downloads/$1
python3 - <<END
try:
import sys
print("disconnect|||Unlink") # Make sure unlink is first in list
f=open("$NODE_DIR/$1", "r")
if f.mode == 'r':
lines = f.readlines()
for line in lines:
line = line.replace('\r', '')
line = line.replace('\n', '')
if line[0:1] == '#':
pass
else:
fields = line.split(';')
print(fields[3] + ":" + fields[4] + "|||" + fields[1])
f.close()
except:
sys.stderr.write("parseYSFile: error parsing file $1\n")
print("ERROR|||ERROR")
exit(1)
END
}
#################################################################
# Parse Pi-Star talk group files
#################################################################
function ParseTGFile() {
curl --fail -o "$NODE_DIR/$1" -s http://www.pistar.uk/downloads/$1
python3 - <<END
try:
import sys
print("4000|||Unlink") # Make sure unlink is first in list
f=open("$NODE_DIR/$1", "r")
if f.mode == 'r':
lines = f.readlines()
for line in lines:
line = line.replace('\r', '')
line = line.replace('\n', '')
if line[0:1] == '#':
pass
else:
fields = line.split(';')
print(fields[0] + "|||" + fields[2].split('_TG')[0].replace('_',' '))
f.close()
except:
sys.stderr.write("parseTGFile: error parsing $1\n")
print("ERROR|||ERROR")
exit(1)
END
}
#################################################################
# Create a default DSTAR database. I know this is probably not
# what you want, but I feel that a full list of all DSTAR nodes
# with all modules included would not be very useful. So, just
# add your own favorites here (like I did).
#################################################################
function ParseDStarFile() {
echo " U|||Unlink"
echo "REF001EL|||Echo"
echo " I|||Info"
echo "REF001CL|||REF001 C"
echo "REF004CL|||REF004 C"
echo "REF012AL|||REF012 A"
echo "XRF012AL|||XRF012 A"
echo "REF014CL|||REF014 C"
echo "REF030BL|||REF030 B"
echo "REF030CL|||REF030 C"
echo "REF038CL|||REF038 C"
echo "REF050CL|||REF050 C"
echo "REF058BL|||REF058 B"
echo "REF078BL|||REF078 B"
echo "REF078CL|||REF078 C"
echo "DCS006FL|||DCS006 F"
echo "DCS059AL|||DCS059 A"
}
#################################################################
# A general function to parse MMDVM host files
#################################################################
function ParseNodeFile() {
if [ -z "$2" ]; then
curl --fail -o "$NODE_DIR/$1" -s http://www.pistar.uk/downloads/$1
else
curl --fail -o "$NODE_DIR/$1" -s "$2"
fi
python3 - <<END
try:
import sys
print("9999|||Unlink") # Make sure unlink is first in list
f=open("$NODE_DIR/$1", "r")
if f.mode == 'r':
lines = f.readlines()
state = 0
for line in lines:
line = line.replace('\r', '')
line = line.replace('\n', '')
if state == 0:
if len(line) == 0:
state = 1
elif state == 1:
comment = line[2:]
state = 2
elif state == 2:
node = line.split()[0]
print(node + "|||" + comment)
state = 0
f.close()
except:
sys.stderr.write("parseNodeFile: error parsing $1\n")
print("ERROR|||ERROR")
exit(1)
END
}
#################################################################
# Get the current ASL node list (used by allmon) and do a simple
# validation (look for my node number)
#################################################################
function DownloadAndValidateASLNodeList() {
declare _OS=$(uname -s)
curl --fail -s https://allmondb.allstarlink.org/allmondb.php | sed -e :a -e '$d;N;2,7ba' -e 'P;D' > "$NODE_DIR/$1"
if [ ${_OS} == Darwin ]; then
sed -i '' 's/||/|<None>|/g' "$NODE_DIR/$1"
else
sed -i 's/||/|<None>|/g' "$NODE_DIR/$1"
fi
declare isValid=`grep -i N4IRS "$NODE_DIR/$1"`
if [ -z "${isValid}" ]; then
rm "$NODE_DIR/$1"
echo "ASL node list is not valid, ignoring"
fi
}
#################################################################
#
#################################################################
function collectProcessDataFiles() {
declare gitURL="https://raw.githubusercontent.com/g4klx"
echo "Processing NXDN"
ParseNodeFile NXDN_Hosts.txt "${gitURL}/NXDNClients/master/NXDNGateway/NXDNHosts.txt" > $NODE_DIR/NXDN_node_list.txt 2>/dev/null
echo "Processing P25"
ParseNodeFile P25_Hosts.txt "${gitURL}/P25Clients/master/P25Gateway/P25Hosts.txt" > $NODE_DIR/P25_node_list.txt 2>/dev/null
echo "Processing DMR"
ParseTGFile TGList_BM.txt > $NODE_DIR/DMR_node_list.txt 2>/dev/null
echo "Processing YSF"
ParseYSFile YSF_Hosts.txt > $NODE_DIR/YSF_node_list.txt 2>/dev/null
echo "Processing DStar"
ParseDStarFile DSTAR_Hosts.txt > $NODE_DIR/DSTAR_node_list.txt 2>/dev/null
echo "Processing ASL"
DownloadAndValidateASLNodeList node_list.txt 2>/dev/null
}
#################################################################
# Get all mobile data files, proces them into proper format and
# push them to the device
#################################################################
function collectProcessPushDataFiles() {
collectProcessDataFiles
echo "Pushing NXDN"
pushFileToClient "$NODE_DIR/NXDN_node_list.txt"
echo "Pushing P25"
pushFileToClient "$NODE_DIR/P25_node_list.txt"
echo "Pushing DMR"
pushFileToClient "$NODE_DIR/DMR_node_list.txt"
echo "Pushing YSF"
pushFileToClient "$NODE_DIR/YSF_node_list.txt"
echo "Pushing DStar"
pushFileToClient "$NODE_DIR/DSTAR_node_list.txt"
echo "Pushing ASL"
pushFileToClient "$NODE_DIR/node_list.txt"
}
#################################################################
# Utility function to get the primary IP address
#################################################################
function getMyIP() {
declare _ip _line
while IFS=$': \t' read -a _line ;do
[ -z "${_line%inet}" ] &&
_ip=${_line[${#_line[1]}>4?1:2]} &&
[ "${_ip#127.0.0.1}" ] && echo $_ip && return 0
done< <(LANG=C /sbin/ifconfig)
}
#################################################################
# Get all mobile data files, proces them into proper format and
# push the URL to the device. Starts a simple web server on port
# $HTTP_PORT (9042).
#################################################################
function collectProcessPushDataFilesHTTP() {
declare processID=`ps aux | grep "python -m SimpleHTTPServer $HTTP_PORT" | grep -v grep | awk '{print $2}'`
kill $processID 2>/dev/null
pushd "$NODE_DIR"
python -m SimpleHTTPServer $HTTP_PORT &
popd
declare _MYIP=`getMyIP`
PSERVER="http://${_MYIP}:$HTTP_PORT"
collectProcessDataFiles
echo "Pushing NXDN"
pushLocalFileAsURLToClient "$NODE_DIR" "$PSERVER" "NXDN_node_list.txt"
sleep 5
echo "Pushing P25"
pushLocalFileAsURLToClient "$NODE_DIR" "$PSERVER" "P25_node_list.txt"
sleep 5
echo "Pushing DMR"
pushLocalFileAsURLToClient "$NODE_DIR" "$PSERVER" "DMR_node_list.txt"
sleep 5
echo "Pushing YSF"
pushLocalFileAsURLToClient "$NODE_DIR" "$PSERVER" "YSF_node_list.txt"
sleep 5
echo "Pushing DStar"
pushLocalFileAsURLToClient "$NODE_DIR" "$PSERVER" "DSTAR_node_list.txt"
sleep 5
echo "Pushing ASL"
pushLocalFileAsURLToClient "$NODE_DIR" "$PSERVER" "node_list.txt"
sleep 10
processID=`ps aux | grep "python -m SimpleHTTPServer $HTTP_PORT" | grep -v grep | awk '{print $2}'`
kill $processID 2>/dev/null
sendMessage "Database update complete"
}
#################################################################
# Download and validate a file. This function will use curl to download
# a file from a server and test for valid data. The tests include
# a warning on download failure, and errors for file size and valid contents.
#################################################################
function downloadAndValidate() {
${DEBUG} curl --fail -o "$MMDVM_DIR/$1" -s "http://www.pistar.uk/downloads/$2"
if (( $? != 0 )); then
echo "Warning, download failure"
_ERRORCODE=$ERROR_FILE_NOT_FOUND
fi
if [ ! -f $MMDVM_DIR/$1 ]; then
echo "Error, $1 file does not seem to exist"
_ERRORCODE=$ERROR_INVALID_FILE
else
declare _fileSize=`wc -c $MMDVM_DIR/$1 | awk '{print $1}'`
if (( ${_fileSize} < 10 )); then
echo "Error, $1 file has no contents"
_ERRORCODE=$ERROR_INVALID_FILE
else
declare isValid=`grep $3 "$MMDVM_DIR/$1"`
if [ -z "$isValid" ]; then
echo "Error, $1 file does not seem to be valid"
_ERRORCODE=$ERROR_INVALID_FILE
fi
fi
fi
}
#################################################################
# Download all user databases
#################################################################
function downloadDatabases() {
if [ -d "${MMDVM_DIR}" ] && [ -d "${AB_DIR}" ]; then
${DEBUG} curl -s -N "https://database.radioid.net/static/user.csv" | awk -F, 'NR>1 {if ($1 > "") print $1,$2,$3}' > "${MMDVM_DIR}/DMRIds.dat"
${DEBUG} curl -s -N "https://database.radioid.net/static/user.csv" | awk -F, 'BEGIN{OFS=",";} NR>1 {if ($1 > "") print $1,$2,$3}' > "${AB_DIR}/subscriber_ids.csv"
${DEBUG} curl -s -N "https://database.radioid.net/static/nxdn.csv" > "${MMDVM_DIR}/NXDN.csv"
${DEBUG} curl -s -N "http://www.pistar.uk/downloads/DMR_Hosts.txt" > "${MMDVM_DIR}/DMR_Hosts.txt"
downloadAndValidate "NXDNHosts.txt" "NXDN_Hosts.txt" "dvswitch.org"
downloadAndValidate "P25Hosts.txt" "P25_Hosts.txt" "dvswitch.org"
downloadAndValidate "TGList_BM.txt" "TGList_BM.txt" "DVSWITCH"
downloadAndValidate "YSFHosts.txt" "YSF_Hosts.txt" "dvswitch.org"
downloadAndValidate "FCSRooms.txt" "FCS_Hosts.txt" "FCS00106"
downloadAndValidate "DCS_Hosts.txt" "DCS_Hosts.txt" "DCS006"
downloadAndValidate "DPlus_Hosts.txt" "DPlus_Hosts.txt" "REF030"
downloadAndValidate "DExtra_Hosts.txt" "DExtra_Hosts.txt" "XRF012"
downloadAndValidate "XLXHosts.txt" "XLXHosts.txt" "001"
downloadAndValidate "APRS_Hosts.txt" "APRS_Hosts.txt" "noam.aprs2.net"
declare isValid=`grep 3113043 "${MMDVM_DIR}/DMRIds.dat"`
if [ -z "$isValid" ]; then
${DEBUG} curl -s -N "http://registry.dstar.su/dmr/DMRIds.php" > "${MMDVM_DIR}/DMRIds.dat"
${DEBUG} curl -s -N "http://registry.dstar.su/dmr/DMRIds.php" | awk -F, 'BEGIN{FS=" ";OFS=",";} NR>1 {if ($1 > "") print $1,$2,$3}' > "${AB_DIR}/subscriber_ids.csv"
isValid=`grep 3113043 "${MMDVM_DIR}/DMRIds.dat"`
if [ -z "$isValid" ]; then
echo "Error, DMR ID file does not seem to be valid"
_ERRORCODE=$ERROR_INVALID_FILE
fi
fi
else
echo "Destination directory does not exist, aborting"
_ERRORCODE=$ERROR_DIR_NOT_FOUND
fi
}
#################################################################
# Set digital mode of AB/MB getting the proper ports from DVSwitch.ini
#################################################################
function setMode() {
if [ $# -eq 0 ]; then # No argument passed, just return the current value
echo `getABInfoValue tlv ambe_mode`
else
declare _MODE=`echo $1 | tr '[:lower:]' '[:upper:]'`
if [[ "|DMR|YSF|P25|NXDN|DSTAR|ASL|STFU|" == *"$_MODE"* ]]; then
${DEBUG} setTLVRxPort 30000 # cause AB to stop listening
_MBTX=`parseIniFile "$DVSWITCH_INI" "$_MODE" "TXPort"`
_MBRX=`parseIniFile "$DVSWITCH_INI" "$_MODE" "RXPort"`
if [ ! -z $_MBTX ]; then
sendMessage "Setting mode to $_MODE"
${DEBUG} setAmbeMode $_MODE
${DEBUG} setTLVTxPort ${_MBRX}
${DEBUG} setTLVRxPort ${_MBTX}
if [ $# -ge 2 ]; then ${DEBUG} setTLVGain $2; setTLVAudioType AUDIO_USE_GAIN; fi
if [ $# -ge 3 ]; then ${DEBUG} setUSRPGain $3; setUSRPAudioType AUDIO_USE_GAIN; fi
${DEBUG} getInfo
else
echo "Error, DVSwitch.ini file not found"
_ERRORCODE=$ERROR_FILE_NOT_FOUND
fi
else
echo "Error, Mode must be DMR or YSF or P25 or DSTAR, NXDN, ASL or STFU"
_ERRORCODE=$ERROR_INVALID_ARGUMENT
fi
fi
}
#################################################################
# Show pretty ABInfo json file
#################################################################
function prettyPrintInfo() {
declare _abname=`getABInfoFileName`
if [ -f ${_abname} ]; then
python -mjson.tool ${_abname}
else
echo ABInfo file not found
fi
}
#################################################################
# Lookup info in database file
#################################################################
function lookup() {
declare databaseName="${MMDVM_DIR}/DMRIds.dat"
if [ -f "${databaseName}" ]; then
found=`grep -i $1 "${databaseName}"`
if [ -z "$found" ]; then
_ERRORCODE=$ERROR_LOOKUP_FAILED
else
echo $found
fi
else
echo DMR ID database file not found at ${databaseName}
_ERRORCODE=$ERROR_LOOKUP_FAILED
fi
}
#################################################################
# Get version information from AB and MB
#################################################################
function appVersion() {
if [ $# -eq 0 ]; then
echo "dvswitch.sh version $SCRIPT_VERSION"
else
case $1 in
ab|AB|Analog_Bridge)
if [ -f "/opt/Analog_Bridge/Analog_Bridge" ]; then
"/opt/Analog_Bridge/Analog_Bridge" -v
else
declare _ver=`getABInfoValue ab version`
echo "Analog_Bridge version ${_ver}"
fi
;;
mb|MB|MMDVM_Bridge)
if [ -f "/opt/MMDVM_Bridge/MMDVM_Bridge" ]; then
"/opt/MMDVM_Bridge/MMDVM_Bridge" -v
else
echo "MMDVM_Bridge version UNKNOWN"
fi
;;
gw|GW)
for gw in P25Gateway NXDNGateway YSFGateway; do
if [ -f "/opt/$gw/$gw" ]; then
"/opt/$gw/$gw" -v
fi
done
;;
path)
if [ -f "$2" ]; then
"$2" -v
fi
;;
all|ALL)
appVersion
for app in ab mb gw; do
appVersion $app
done
for app in Analog_Reflector STFU; do
appVersion path "/opt/${app}/${app}"
done
;;
esac
fi
}
#################################################################
# Echo the list of "enabled" modes in MB.ini
#################################################################
function getEnabledModes() {
# For each mode, disable the main section and the network
declare _MODE=""
declare _NET=""
declare enabledModes=""
for mode in DMR "System Fusion" P25 D-Star NXDN; do
_MODE=`parseIniFile "$MMDVM_INI" "${mode}" "Enable"`
_NET=`parseIniFile "$MMDVM_INI" "${mode} Network" "Enable"`
#echo "${mode} mode = ${_MODE} and Network = ${_NET}"
if [ ${_MODE} == "1" ] && [ ${_NET} == "1" ]; then
enabledModes=`echo ${enabledModes}${mode}" " `
fi
done
echo "$1${enabledModes}"
}
#################################################################
# Print out the owner for a specified UDP port
#################################################################
function getUDPPortOwner() {
if [ -z "$1" ]; then
echo "Argument required: port number"
_ERRORCODE=$ERROR_INVALID_ARGUMENT
else
declare port=":$1"
declare _OS=$(uname -s)
if [ ${_OS} == Darwin ]; then
declare pid=$(lsof -i udp$port -P +c 0 | awk 'NR>1 {print $2}')
if [ -z "$pid" ]; then
echo "No processes listening on port $port"
else
ps -f $pid | awk 'NR>1 {print $8 " " $9 " " $10}'
fi
else
declare pid=$(sudo netstat -unap | grep "$port" | awk '{print $6}' | cut -d'/' -f1)
if [ -z "$pid" ]; then
echo "No processes listening on port $port"
else
ps -f $pid | awk 'NR>1 {print $9 " " $10 " " $11}'
fi
fi
fi
}
#################################################################
# Print out the ports owned by a specified process
#################################################################
function getUDPPortsForProcess() {
if [ -z "$1" ]; then
echo "Argument required: process name"
_ERRORCODE=$ERROR_INVALID_ARGUMENT
else
declare process="$1"
declare _OS=$(uname -s)
set -f;
if [ ${_OS} == Darwin ]; then
declare ports=($(lsof -i udp -P +c 0 | grep -i "${process:0:14}" | awk '{if ($9 != "*:*") print $9}' | cut -d':' -f2))
if [ ${#ports[@]} -gt 0 ]; then
echo "$process owns UDP ports: ${ports[@]}"
fi
else
declare ports=($(sudo netstat -unap | grep -i "${process:0:14}" | awk '{split($4, a, ":"); print a[2]}'))
if [ ${#ports[@]} -gt 0 ]; then
echo "$process owns UDP ports: ${ports[@]}"
fi
fi
set +f;
fi
}
#################################################################
# Print out the ports for all DVSwitch processes
#################################################################
function getUDPPortsForDVSwitch() {
for i in Analog_Bridge MMDVM_Bridge Quantar_Bridge Analog_Reflector STFU P25Gateway NXDNGateway DMRGateway YSFGateway ircddbgateway YSFParrot NXDNParrot md380-emu; do
getUDPPortsForProcess "$i"
done
}
#################################################################
#
#################################################################
function updateINIFileValue() {
declare _file="$1"
declare _section="$2"
declare _tag="$3"
declare _value="${@:4}"
if [ $# -ge 2 ]; then # Do we have the correct number of arguments?
if [ -f ${_file} ]; then # Check if the file exists (better error message then parseIniFile)
declare _secFound=$(grep -i "^\\[${_section}\\]" "${_file}")
if [ ! -z "${_secFound}" ]; then # See if the section exists
if [ ! -z ${_tag} ]; then
declare _tagLine=$(sed -n "/^\[${_section}\]/,/^\[/ p" "${_file}" | sed -n "/${_tag}/p")
if [ ! -z "${_tagLine}" ]; then
if [ ! -z "${_value}" ]; then
declare _oldValue=`parseIniFile "${_file}" "${_section}" "${_tag}"`
declare _oldLine="^${_tag}.*=.*${_oldValue}"
declare _equal=`[[ "${_tagLine}" == *" = "* ]] && echo " = " || echo "="`
declare _newLine="${_tag}${_equal}${_value}"
sed -i -e "/^\[${_section}\]/,/^\[/ s/${_oldLine}/${_newLine}/i" "${_file}"
else
echo "${_tagLine}"
fi
else
echo "Error Tag \"${_tag}\" was not found in section \"${_section}\" of file \"${_file}\""
_ERRORCODE=$ERROR_INI_FAILURE
fi
else
declare _fullSection=$(sed -n "/^\[${_section}\]/,/^\[/ p" "${_file}")
echo "${_fullSection}"
fi
else
echo "Error, section \"${_section}\" was not found in file \"${_file}\""
_ERRORCODE=$ERROR_INI_FAILURE
fi
else
echo "INI File \"${_file}\" not found"
_ERRORCODE=$ERROR_INI_FAILURE
fi
else
echo "Error, argument number: file section {tag} {value}"
_ERRORCODE=$ERROR_INI_FAILURE
fi
}
#################################################################
#
#################################################################
function setGpsToIP() {
declare ip=$(curl -s ifconfig.me)
declare json=$(curl -s -L ipvigilante.com/$ip)
latlon=(`python3 - <<END
#!/usr/bin/env python
try:
import json, os, sys
json = json.loads('$json')
print(json['data']['latitude'])
print(json['data']['longitude'])
except:
pass
END
`)
remoteControlCommand "gps=${latlon[0]},${latlon[1]}"
}
function parseAnyIniFile() {
if [ $# -ge 2 ]; then
case $1 in
AB|ab)
parseIniFile "/opt/Analog_Bridge/Analog_Bridge.ini" $2 $3
;;
MB|mb)
parseIniFile "${MMDVM_INI}" $3 $3
;;
DV|dv)
parseIniFile "${DVSWITCH_INI}" $2 $3
;;
*)
if [ -f "$1" ]; then
parseIniFile "$1" $2 $3
else
echo "INI file $1 was not found"
fi
;;
esac
else
echo "Wrong number of arguments: [path | AB | MB | DV] [section] [tag]"
fi
}
#################################################################
# Show usage string to someone who wants to know the available options
#################################################################
function usage() {
echo -e "Usage:"
echo -e "$0 \n\t { version | mode | tune | ambesize | ambemode | slot | update | tlvAudio | usrpAudio | usrpCodec | tlvPorts | "
echo -e "\t info | show | lookup | mute | message | macro |"
echo -e "\t pushfile | collectProcessDataFiles | collectProcessPushDataFiles | pushurl | collectProcessPushDataFilesHTTP }"
echo -e "\t version {AB|MB|GW|ALL}\t\t\t\t Show version of dvswitch.sh, Analog_Bridge or MMDVM_Bridge"
echo -e "\t mode {DMR|NXDN|P25|YSF|DSTAR} \t\t\t Set Analog_Bridge digital mode"
echo -e "\t tune tg \t\t\t\t\t Tune to specific TG/Reflector"
echo -e "\t ambesize {72|88|49}\t\t\t\t Set number of bits for ambe data"
echo -e "\t ambemode {DMR|NXDN|P25|YSFN|YSFW|DSTAR} \t Set AMBE mode"
echo -e "\t slot {1|2} \t\t\t\t\t Set DMR slot to transmit on"
echo -e "\t update \t\t\t\t\t Update callsign and host databases"
echo -e "\t tlvAudio mode gain\t\t\t\t Set AMBE audio mode and gain"
echo -e "\t usrpAudio mode gain\t\t\t\t Set PCM audio mode and gain"
echo -e "\t usrpAgc threshold slope decay\t\t\t Set PCM audio agc threshold slope and decay"
echo -e "\t usrpCodec {SLIN|ULAW|ADPCM}\t\t\t Set AB -> DVSM/UC audio codec"
echo -e "\t tlvPorts rxport txport\t\t\t\t Set Analog_Bridge receive and transmit ports"
echo -e "\t info \t\t\t\t\t\t Update ABInfo and send to DVSM/UC"
echo -e "\t show \t\t\t\t\t\t Pretty print the ABInfo json file"
echo -e "\t lookup \t\t\t\t\t Lookup a DMR ID/call in the local database"
echo -e "\t mute {OFF|USRP|TLV|BOTH}\t\t\t Cause Aanlog_Bridge to mute a stream"
echo -e "\t message msg\t\t\t\t\t Send a text message to DVSM/UC"
echo -e "\t macro {file|text}\t\t\t\t Send a macro collection to DVSM"
echo -e "\t pushfile file\t\t\t\t\t Push file to DVSM"
echo -e "\t pushurl url\t\t\t\t\t Push URL to DVSM"
echo -e "\t collectProcessDataFiles \t\t\t Collect and prepare DVSM data files"
echo -e "\t collectProcessPushDataFiles \t\t\t Collect, prepare and upload DVSM data files"
echo -e "\t collectProcessPushDataFilesHTTP \t\t Collect, prepare and upload DVSM data files over http"
echo -e "\t reloadDatabase \t\t\t\t Tell AB to reload database files into memory"
echo -e "\t getEnabledModes \t\t\t\t Return the list of "enabled" modes in MB.ini"
echo -e "\t getUDPPortOwner {UDP port}\t\t\t Print out the process owner for the specified port"
echo -e "\t getUDPPortsForProcess {process name|ALL}\t Print out the ports owned by the specified process (or all DVSwitch processes)"
echo -e "\t updateINIFileValue file section {tag} {value}\t Display or edit a tag in an INI file"
echo -e "\t gps lat long \t\t\t\t\t Set GPS coordinates for YSF to lat and long"
echo -e "\t setGpsToIP \t\t\t\t\t Set GPS coordinates for YSF to the lat and long of your public IP address"
exit 1
}
#################################################################
# The main application
#################################################################
if [ $# -eq 0 ]; then
usage # No arguments, so just report usage information
else
case $1 in
-h|--help|"-?"|help)
usage
;;
update)
downloadDatabases
;;
lookup)
lookup $2
;;
collectProcessDataFiles|collectprocessdatafiles|cpdf)
collectProcessDataFiles
;;
version|-v)
appVersion $2
;;
getEnabledModes|getenabledmodes|gem)
if [ $# -eq 1 ]; then # No argument passed, just return the current value
getEnabledModes "Enabled Modes: "
else
getEnabledModes "$2"
fi
;;
getUDPPortOwner|getudpportowner|gupo)
getUDPPortOwner "$2"
;;
getUDPPortsForProcess|getudpportsforprocess|gupfp)
if [ -z "$2" ] || [ $2 == "all" ] || [ $2 == "ALL" ]; then
getUDPPortsForDVSwitch
else
for i in "${@:1}"; do
getUDPPortsForProcess "$i"
done
fi
;;
updateINIFileValue|updateinifilevalue|uifv)
updateINIFileValue "$2" "$3" $4 $5 ${@:6}
;;
parseIniFile|parseinifile|pif)
parseAnyIniFile "$2" $3 $4
;;
*)
# All the commands below require that a valid ABInfo file exists.
TLV_PORT=`getTLVPort` # Get the communications port to use before we go further
if [ $TLV_PORT == "ERROR" ]; then
echo "Can not find /tmp/ABInfo file (have you run Analog_Bridge?), aborting"
exit 1
fi
case $1 in
mode)
setMode $2 $3 $4
;;
tune)
${DEBUG} tune $2
${DEBUG} getInfo
;;
ambeSize|ambesize)
${DEBUG} setAmbeSize $2
;;
ambeMode|ambemode)
${DEBUG} setAmbeMode $2
;;
slot)
${DEBUG} setSlot $2
;;
setCallAndId|setcallandid)
setCallAndID $2 $3
getInfo
;;
tlvAudio|tlvaudio)
setTLVAudioType $2
setTLVGain $3
;;
usrpAudio|usrpaudio)
setUSRPAudioType $2
setUSRPGain $3
;;
USRPAgc|usrpagc)
setUSRPAgc $2 $3 $4
;;
TLVAgc|tlvagc)
setTLVAgc $2 $3 $4
;;
usrpCodec|usrpcodec)
setUSRPCodec $2
;;
tlvPorts|tlvports)
setTLVRxPort $2
setTLVTxPort $3
;;
info)
# no arguments fill just tell AB to update the json file
# two arguments returns the value of "object" and "name" object{name:value}
getInfo $2 $3
;;
show)
prettyPrintInfo
;;
mute)
setMute $2
;;
pushFile|pushfile|pf)
pushFileToClient "$2"
;;
collectProcessPushDataFiles|collectprocesspushdatafiles|cppdf)
collectProcessPushDataFiles
;;
pushUrl|pushurl)
pushURLToClient "$2"
;;
collectProcessPushDataFilesHTTP|collectprocesspushdatafileshttp|cppdfh)
collectProcessPushDataFilesHTTP
;;
reloadDatabase|reloaddatabase)
reloadDatabase
;;
message)
sendMessage "$2"
;;
macro)
sendMacro macro "$2"
;;
menu)
sendMacro menu "$2"
;;
ping)
setPingTimer "$2"
;;
gps)
remoteControlCommand "gps=$2,$3"
;;
exit)
remoteControlCommand "exit=0 0"
;;
setGpsToIP)
setGpsToIP
;;
exitAB|exitab)
exitAnalogBridge $2 $3
;;
usrpCommand|usrp) # undocumented ATM/WIP
USRPCommand "$2" "$3"
;;
*)
# unknown option, update branch info (no option is specified, just ordered by placement)
echo "Unknown command line option:" $1
usage
;;
esac
;;
esac
fi
exit $_ERRORCODE
EOF
sudo chmod +x /opt/MMDVM_Bridge/dvswitch.sh
#sudo sed -i "s/https:\/\/database.radioid.net\/static\/user.csv/http:\/\/datafiles.ddns.net:8888\/user.csv/g" /opt/MMDVM_Bridge/dvswitch.sh
#############
chmod +x /bin/menu*
/bin/menu-update-tg