Merge pull request #17 from N4IRS/master
Add Changes From N4IRS and N4IRR to original
This commit is contained in:
commit
7607a5309d
9
ambe_audio.cfg
Normal file
9
ambe_audio.cfg
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
[SETTINGS]
|
||||||
|
_debug = False
|
||||||
|
_outToFile = False
|
||||||
|
_outToUDP = True
|
||||||
|
_gateway = 127.0.0.1
|
||||||
|
_gateway_port = 1234
|
||||||
|
_remote_control_port = 1235
|
||||||
|
_tg_filter = 2,3,13,3174,3777215,3100,9,9998,3112
|
||||||
|
|
163
ambe_audio.py
163
ambe_audio.py
@ -14,9 +14,10 @@ from twisted.internet import reactor
|
|||||||
from binascii import b2a_hex as h
|
from binascii import b2a_hex as h
|
||||||
from bitstring import BitArray
|
from bitstring import BitArray
|
||||||
|
|
||||||
import sys
|
import sys, socket, ConfigParser, thread, traceback
|
||||||
import cPickle as pickle
|
import cPickle as pickle
|
||||||
from dmrlink import IPSC, NETWORK, networks, logger, int_id, hex_str_3
|
from dmrlink import IPSC, NETWORK, networks, logger, int_id, hex_str_3, get_info, talkgroup_ids, subscriber_ids, peer_ids
|
||||||
|
from time import time
|
||||||
|
|
||||||
__author__ = 'Cortney T. Buffington, N0MJS'
|
__author__ = 'Cortney T. Buffington, N0MJS'
|
||||||
__copyright__ = 'Copyright (c) 2015 Cortney T. Buffington, N0MJS and the K0USY Group'
|
__copyright__ = 'Copyright (c) 2015 Cortney T. Buffington, N0MJS and the K0USY Group'
|
||||||
@ -27,18 +28,91 @@ __version__ = '0.1a'
|
|||||||
__email__ = 'n0mjs@me.com'
|
__email__ = 'n0mjs@me.com'
|
||||||
__status__ = 'pre-alpha'
|
__status__ = 'pre-alpha'
|
||||||
|
|
||||||
|
|
||||||
try:
|
try:
|
||||||
from ipsc.ipsc_message_types import *
|
from ipsc.ipsc_message_types import *
|
||||||
except ImportError:
|
except ImportError:
|
||||||
sys.exit('IPSC message types file not found or invalid')
|
sys.exit('IPSC message types file not found or invalid')
|
||||||
|
|
||||||
|
#
|
||||||
|
# ambeIPSC class,
|
||||||
|
#
|
||||||
class ambeIPSC(IPSC):
|
class ambeIPSC(IPSC):
|
||||||
|
|
||||||
|
_configFile='ambe_audio.cfg'
|
||||||
|
_debug = False
|
||||||
|
_outToFile = False
|
||||||
|
_outToUDP = True
|
||||||
|
#_gateway = "192.168.1.184"
|
||||||
|
_gateway = "127.0.0.1"
|
||||||
|
_gateway_port = 1234
|
||||||
|
_remote_control_port = 1235
|
||||||
|
_tg_filter = [2,3,13,3174,3777215,3100,9,9998,3112] #set this to the tg to monitor
|
||||||
|
_no_tg = -99
|
||||||
|
_sock = -1;
|
||||||
|
lastPacketTimeout = 0
|
||||||
|
_transmitStartTime = 0
|
||||||
|
|
||||||
def __init__(self, *args, **kwargs):
|
def __init__(self, *args, **kwargs):
|
||||||
IPSC.__init__(self, *args, **kwargs)
|
IPSC.__init__(self, *args, **kwargs)
|
||||||
self.CALL_DATA = []
|
self.CALL_DATA = []
|
||||||
|
|
||||||
|
|
||||||
|
#
|
||||||
|
# Define default values for operation. These will be overridden by the .cfg file if found
|
||||||
|
#
|
||||||
|
|
||||||
|
self._currentTG = self._no_tg
|
||||||
|
self._sequenceNr = 0
|
||||||
|
self.readConfigFile(self._configFile)
|
||||||
|
|
||||||
|
print('DMRLink ambe server')
|
||||||
|
|
||||||
|
#
|
||||||
|
# Open output sincs
|
||||||
|
#
|
||||||
|
if self._outToFile == True:
|
||||||
|
f = open('ambe.bin', 'wb')
|
||||||
|
print('Opening output file: ambe.bin')
|
||||||
|
if self._outToUDP == True:
|
||||||
|
self._sock = socket.socket(socket.AF_INET,socket.SOCK_DGRAM)
|
||||||
|
print('Send UDP frames to DMR gateway {}:{}'.format(self._gateway, self._gateway_port))
|
||||||
|
|
||||||
|
try:
|
||||||
|
thread.start_new_thread( self.remote_control, (self._remote_control_port, ) )
|
||||||
|
except:
|
||||||
|
traceback.print_exc()
|
||||||
|
print( "Error: unable to start thread" )
|
||||||
|
|
||||||
|
|
||||||
|
# Utility function to convert bytes to string of hex values (for debug)
|
||||||
|
def ByteToHex( self, byteStr ):
|
||||||
|
return ''.join( [ "%02X " % ord(x) for x in byteStr ] ).strip()
|
||||||
|
|
||||||
|
#
|
||||||
|
# Now read the configuration file and parse out the values we need
|
||||||
|
#
|
||||||
|
def readConfigFile(self, configFileName):
|
||||||
|
config = ConfigParser.ConfigParser()
|
||||||
|
try:
|
||||||
|
self._tg_filter=[]
|
||||||
|
config.read(configFileName)
|
||||||
|
for sec in config.sections():
|
||||||
|
for key, val in config.items(sec):
|
||||||
|
if self._debug == True:
|
||||||
|
print( '%s="%s"' % (key, val) )
|
||||||
|
self._debug = (config.get(sec, '_debug') == "True")
|
||||||
|
self._outToFile = (config.get(sec, '_outToFile') == "True")
|
||||||
|
self._outToUDP = (config.get(sec, '_outToUDP') == "True")
|
||||||
|
self._gateway = config.get(sec, '_gateway')
|
||||||
|
self._gateway_port = int(config.get(sec, '_gateway_port'))
|
||||||
|
_tgs = config.get(sec, '_tg_filter')
|
||||||
|
self._tg_filter = map(int, _tgs.split(','))
|
||||||
|
|
||||||
|
except:
|
||||||
|
traceback.print_exc()
|
||||||
|
sys.exit('Configuration file \''+configFileName+'\' is not a valid configuration file! Exiting...')
|
||||||
|
|
||||||
#************************************************
|
#************************************************
|
||||||
# CALLBACK FUNCTIONS FOR USER PACKET TYPES
|
# CALLBACK FUNCTIONS FOR USER PACKET TYPES
|
||||||
#************************************************
|
#************************************************
|
||||||
@ -53,20 +127,77 @@ class ambeIPSC(IPSC):
|
|||||||
_ambe_frame2 = _ambe_frames[50:99]
|
_ambe_frame2 = _ambe_frames[50:99]
|
||||||
_ambe_frame3 = _ambe_frames[100:149]
|
_ambe_frame3 = _ambe_frames[100:149]
|
||||||
|
|
||||||
if _payload_type == BURST_DATA_TYPE['VOICE_HEAD']:
|
_tg_id = int_id(_dst_sub)
|
||||||
print('Voice Transmission Start')
|
if _tg_id in self._tg_filter: #All TGs
|
||||||
if _payload_type == BURST_DATA_TYPE['VOICE_TERM']:
|
_dst_sub = get_info(int_id(_dst_sub), talkgroup_ids)
|
||||||
print('Voice Transmission End')
|
if _payload_type == BURST_DATA_TYPE['VOICE_HEAD']:
|
||||||
if _payload_type == BURST_DATA_TYPE['SLOT1_VOICE']:
|
if self._currentTG == self._no_tg:
|
||||||
|
_src_sub = get_info(int_id(_src_sub), subscriber_ids)
|
||||||
|
print('Voice Transmission Start on TS {} and TG {} ({}) from {}'.format("2" if _ts else "1", _dst_sub, _tg_id, _src_sub))
|
||||||
|
self._currentTG = _tg_id
|
||||||
|
self._transmitStartTime = time()
|
||||||
|
else:
|
||||||
|
if self._currentTG != _tg_id:
|
||||||
|
if time() > self.lastPacketTimeout:
|
||||||
|
self._currentTG = self._no_tg #looks like we never saw an EOT from the last stream
|
||||||
|
print('EOT timeout')
|
||||||
|
else:
|
||||||
|
print('Transmission in progress, will not decode stream on TG {}'.format(_tg_id))
|
||||||
|
if self._currentTG == _tg_id:
|
||||||
|
if _payload_type == BURST_DATA_TYPE['VOICE_TERM']:
|
||||||
|
print('Voice Transmission End %.2f seconds' % (time() - self._transmitStartTime))
|
||||||
|
self._currentTG = self._no_tg
|
||||||
|
if _payload_type == BURST_DATA_TYPE['SLOT1_VOICE']:
|
||||||
|
self.outputFrames(_ambe_frames, _ambe_frame1, _ambe_frame2, _ambe_frame3)
|
||||||
|
if _payload_type == BURST_DATA_TYPE['SLOT2_VOICE']:
|
||||||
|
self.outputFrames(_ambe_frames, _ambe_frame1, _ambe_frame2, _ambe_frame3)
|
||||||
|
self.lastPacketTimeout = time() + 10
|
||||||
|
|
||||||
|
else:
|
||||||
|
if _payload_type == BURST_DATA_TYPE['VOICE_HEAD']:
|
||||||
|
_dst_sub = get_info(int_id(_dst_sub), talkgroup_ids)
|
||||||
|
print('Ignored Voice Transmission Start on TS {} and TG {}'.format("2" if _ts else "1", _dst_sub))
|
||||||
|
|
||||||
|
def outputFrames(self, _ambe_frames, _ambe_frame1, _ambe_frame2, _ambe_frame3):
|
||||||
|
if self._debug == True:
|
||||||
print(_ambe_frames)
|
print(_ambe_frames)
|
||||||
print('Frame 1:', _ambe_frame1.bytes)
|
print('Frame 1:', self.ByteToHex(_ambe_frame1.tobytes()))
|
||||||
print('Frame 2:', _ambe_frame2.bytes)
|
print('Frame 2:', self.ByteToHex(_ambe_frame2.tobytes()))
|
||||||
print('Frame 3:', _ambe_frame3.bytes)
|
print('Frame 3:', self.ByteToHex(_ambe_frame3.tobytes()))
|
||||||
if _payload_type == BURST_DATA_TYPE['SLOT2_VOICE']:
|
|
||||||
print(_ambe_frames)
|
if self._outToFile == True:
|
||||||
print('Frame 1:', _ambe_frame1.bytes)
|
f.write( _ambe_frame1.tobytes() )
|
||||||
print('Frame 2:', _ambe_frame2.bytes)
|
f.write( _ambe_frame2.tobytes() )
|
||||||
print('Frame 3:', _ambe_frame3.bytes)
|
f.write( _ambe_frame3.tobytes() )
|
||||||
|
|
||||||
|
if self._outToUDP == True:
|
||||||
|
self._sock.sendto(_ambe_frame1.tobytes(), (self._gateway, self._gateway_port))
|
||||||
|
self._sock.sendto(_ambe_frame2.tobytes(), (self._gateway, self._gateway_port))
|
||||||
|
self._sock.sendto(_ambe_frame3.tobytes(), (self._gateway, self._gateway_port))
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
#
|
||||||
|
# Define a function for the thread
|
||||||
|
# Use netcat to dynamically change the TGs that are forwarded to Allstar
|
||||||
|
# echo "x,y,z" | nc 127.0.0.1 1235
|
||||||
|
#
|
||||||
|
def remote_control(self, port):
|
||||||
|
s = socket.socket() # Create a socket object
|
||||||
|
host = socket.gethostname() # Get local machine name
|
||||||
|
s.bind((host, port)) # Bind to the port
|
||||||
|
|
||||||
|
s.listen(5) # Now wait for client connection.
|
||||||
|
print('Remote control is listening on:', host, port)
|
||||||
|
while True:
|
||||||
|
c, addr = s.accept() # Establish connection with client.
|
||||||
|
print( 'Got connection from', addr )
|
||||||
|
tgs = c.recv(1024)
|
||||||
|
if tgs:
|
||||||
|
self._tg_filter = map(int, tgs.split(','))
|
||||||
|
print( 'New TGs=', self._tg_filter )
|
||||||
|
c.close() # Close the connection
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
if __name__ == '__main__':
|
if __name__ == '__main__':
|
||||||
|
@ -218,7 +218,7 @@ class bridgeIPSC(IPSC):
|
|||||||
|
|
||||||
# Calculate and append the authentication hash for the target network... if necessary
|
# Calculate and append the authentication hash for the target network... if necessary
|
||||||
if NETWORK[_target]['LOCAL']['AUTH_ENABLED']:
|
if NETWORK[_target]['LOCAL']['AUTH_ENABLED']:
|
||||||
_tmp_data = self.hashed_packet(NETWORK[_target]['LOCAL']['AUTH_KEY'], _tmp_data)
|
_tmp_data = self.auth_hashed_packet(NETWORK[_target]['LOCAL']['AUTH_KEY'], _tmp_data)
|
||||||
# Send the packet to all peers in the target IPSC
|
# Send the packet to all peers in the target IPSC
|
||||||
networks[_target].send_to_ipsc(_tmp_data)
|
networks[_target].send_to_ipsc(_tmp_data)
|
||||||
|
|
||||||
@ -242,7 +242,7 @@ class bridgeIPSC(IPSC):
|
|||||||
|
|
||||||
# Calculate and append the authentication hash for the target network... if necessary
|
# Calculate and append the authentication hash for the target network... if necessary
|
||||||
if NETWORK[target]['LOCAL']['AUTH_ENABLED']:
|
if NETWORK[target]['LOCAL']['AUTH_ENABLED']:
|
||||||
_tmp_data = self.hashed_packet(NETWORK[target]['LOCAL']['AUTH_KEY'], _tmp_data)
|
_tmp_data = self.auth_hashed_packet(NETWORK[target]['LOCAL']['AUTH_KEY'], _tmp_data)
|
||||||
# Send the packet to all peers in the target IPSC
|
# Send the packet to all peers in the target IPSC
|
||||||
networks[target].send_to_ipsc(_tmp_data)
|
networks[target].send_to_ipsc(_tmp_data)
|
||||||
|
|
||||||
@ -258,7 +258,7 @@ class bridgeIPSC(IPSC):
|
|||||||
|
|
||||||
# Calculate and append the authentication hash for the target network... if necessary
|
# Calculate and append the authentication hash for the target network... if necessary
|
||||||
if NETWORK[target]['LOCAL']['AUTH_ENABLED']:
|
if NETWORK[target]['LOCAL']['AUTH_ENABLED']:
|
||||||
_tmp_data = self.hashed_packet(NETWORK[target]['LOCAL']['AUTH_KEY'], _tmp_data)
|
_tmp_data = self.auth_hashed_packet(NETWORK[target]['LOCAL']['AUTH_KEY'], _tmp_data)
|
||||||
# Send the packet to all peers in the target IPSC
|
# Send the packet to all peers in the target IPSC
|
||||||
networks[target].send_to_ipsc(_tmp_data)
|
networks[target].send_to_ipsc(_tmp_data)
|
||||||
|
|
||||||
|
56
dmr_install
Executable file
56
dmr_install
Executable file
@ -0,0 +1,56 @@
|
|||||||
|
#! /bin/sh
|
||||||
|
|
||||||
|
#################################################
|
||||||
|
# #
|
||||||
|
# Create directory structure for DMRlink #
|
||||||
|
# #
|
||||||
|
#################################################
|
||||||
|
|
||||||
|
# Minor updates to DIAL
|
||||||
|
# rm /etc/asterisk/firsttime
|
||||||
|
cd /etc/asterisk/
|
||||||
|
wget https://github.com/N4IRS/AllStar/raw/master/configs/dnsmgr.conf
|
||||||
|
# wget https://github.com/N4IRS/AllStar/raw/master/configs/modules.conf
|
||||||
|
|
||||||
|
# Checkout DMRlink and put it in /opt
|
||||||
|
cd /srv
|
||||||
|
git clone https://github.com/N4IRS/DMRlink
|
||||||
|
cd /srv/DMRlink/
|
||||||
|
./mk_dmrlink
|
||||||
|
|
||||||
|
# setup boot for DV3000
|
||||||
|
cd /srv
|
||||||
|
systemctl stop getty@ttyAMA0.service
|
||||||
|
systemctl disable getty@ttyAMA0.service
|
||||||
|
apt-get install -y sudo
|
||||||
|
|
||||||
|
# Setup WiringPi
|
||||||
|
git clone git://git.drogon.net/wiringPi
|
||||||
|
cd wiringPi/
|
||||||
|
./build
|
||||||
|
cd ..
|
||||||
|
|
||||||
|
# Setup AMBEserverGPIO
|
||||||
|
git clone https://github.com/dl5di/OpenDV.git
|
||||||
|
mv OpenDV/DummyRepeater/DV3000 DV3000
|
||||||
|
rm -rf OpenDV
|
||||||
|
cd DV3000/
|
||||||
|
make clean
|
||||||
|
make
|
||||||
|
make install
|
||||||
|
make init-install
|
||||||
|
python AMBEtest3.py
|
||||||
|
cd /etc/init.d
|
||||||
|
update-rc.d AMBEserverGPIO start 50 2 3 4 5
|
||||||
|
|
||||||
|
# Setup DMRGateway
|
||||||
|
cd /srv
|
||||||
|
git clone https://github.com/N4IRS/DMRGateway.git
|
||||||
|
cd DMRGateway/
|
||||||
|
./install.sh
|
||||||
|
cp config.txt /boot
|
||||||
|
cp cmdline.txt /boot
|
||||||
|
|
||||||
|
# reboot
|
||||||
|
|
||||||
|
|
@ -46,6 +46,8 @@ __maintainer__ = 'Cort Buffington, N0MJS'
|
|||||||
__email__ = 'n0mjs@me.com'
|
__email__ = 'n0mjs@me.com'
|
||||||
__status__ = 'beta'
|
__status__ = 'beta'
|
||||||
|
|
||||||
|
# Change the current directory to the location of the application
|
||||||
|
os.chdir(os.path.dirname(os.path.realpath(sys.argv[0])))
|
||||||
|
|
||||||
parser = argparse.ArgumentParser()
|
parser = argparse.ArgumentParser()
|
||||||
parser.add_argument('-c', '--config', action='store', dest='CFG_FILE', help='/full/path/to/config.file (usually dmrlink.cfg)')
|
parser.add_argument('-c', '--config', action='store', dest='CFG_FILE', help='/full/path/to/config.file (usually dmrlink.cfg)')
|
||||||
@ -290,7 +292,7 @@ try:
|
|||||||
with open(PATH+'subscriber_ids.csv', 'rU') as subscriber_ids_csv:
|
with open(PATH+'subscriber_ids.csv', 'rU') as subscriber_ids_csv:
|
||||||
subscribers = csv.reader(subscriber_ids_csv, dialect='excel', delimiter=',')
|
subscribers = csv.reader(subscriber_ids_csv, dialect='excel', delimiter=',')
|
||||||
for row in subscribers:
|
for row in subscribers:
|
||||||
subscriber_ids[int(row[1])] = (row[0])
|
subscriber_ids[int(row[0])] = (row[1])
|
||||||
except ImportError:
|
except ImportError:
|
||||||
logger.warning('subscriber_ids.csv not found: Subscriber aliases will not be available')
|
logger.warning('subscriber_ids.csv not found: Subscriber aliases will not be available')
|
||||||
|
|
||||||
@ -298,7 +300,7 @@ try:
|
|||||||
with open(PATH+'peer_ids.csv', 'rU') as peer_ids_csv:
|
with open(PATH+'peer_ids.csv', 'rU') as peer_ids_csv:
|
||||||
peers = csv.reader(peer_ids_csv, dialect='excel', delimiter=',')
|
peers = csv.reader(peer_ids_csv, dialect='excel', delimiter=',')
|
||||||
for row in peers:
|
for row in peers:
|
||||||
peer_ids[int(row[1])] = (row[0])
|
peer_ids[int(row[0])] = (row[1])
|
||||||
except ImportError:
|
except ImportError:
|
||||||
logger.warning('peer_ids.csv not found: Peer aliases will not be available')
|
logger.warning('peer_ids.csv not found: Peer aliases will not be available')
|
||||||
|
|
||||||
|
@ -2,13 +2,15 @@
|
|||||||
#
|
#
|
||||||
# Rename to dmrlink.cfg and add your information
|
# Rename to dmrlink.cfg and add your information
|
||||||
#
|
#
|
||||||
|
# minor tweaks to match install for use by DMRGateway
|
||||||
|
# N4IRS
|
||||||
#
|
#
|
||||||
#
|
#
|
||||||
|
|
||||||
# GLOBAL CONFIGURATION ITEMS
|
# GLOBAL CONFIGURATION ITEMS
|
||||||
#
|
#
|
||||||
[GLOBAL]
|
[GLOBAL]
|
||||||
PATH: /absolute/path/to/DMRlink
|
PATH: /opt/dmrlink/
|
||||||
|
|
||||||
|
|
||||||
# NETWORK REPORTING CONFIGURATION
|
# NETWORK REPORTING CONFIGURATION
|
||||||
@ -54,9 +56,9 @@ PRINT_PEERS_INC_FLAGS: 0
|
|||||||
# used.
|
# used.
|
||||||
#
|
#
|
||||||
[LOGGER]
|
[LOGGER]
|
||||||
LOG_FILE: /tmp/dmrlink.log
|
LOG_FILE: /var/log/dmrlink/dmrlink.log
|
||||||
LOG_HANDLERS: console
|
LOG_HANDLERS: file
|
||||||
LOG_LEVEL: CRITICAL
|
LOG_LEVEL: INFO
|
||||||
LOG_NAME: DMRlink
|
LOG_NAME: DMRlink
|
||||||
|
|
||||||
|
|
||||||
@ -67,7 +69,7 @@ LOG_NAME: DMRlink
|
|||||||
# [NAME] The name you want to use to identify the IPSC instance (use
|
# [NAME] The name you want to use to identify the IPSC instance (use
|
||||||
# something better than "IPSC1"...)
|
# something better than "IPSC1"...)
|
||||||
# ENABLED: Should we communiate with this network? Handy if you need to
|
# ENABLED: Should we communiate with this network? Handy if you need to
|
||||||
# shut one down but don't want to lose the config
|
# shut one down but don't want to lose the config
|
||||||
# RADIO_ID: This is the radio ID that DMRLink should use to communicate
|
# RADIO_ID: This is the radio ID that DMRLink should use to communicate
|
||||||
# IP: This is the local IPv4 address to listen on. It may be left
|
# IP: This is the local IPv4 address to listen on. It may be left
|
||||||
blank if you do not need or wish to specify. It is mostly
|
blank if you do not need or wish to specify. It is mostly
|
||||||
@ -103,10 +105,10 @@ LOG_NAME: DMRlink
|
|||||||
# ...Repeat the block for each IPSC network to join.
|
# ...Repeat the block for each IPSC network to join.
|
||||||
#
|
#
|
||||||
|
|
||||||
[IPSC1]
|
[SAMPLE_PEER]
|
||||||
ENABLED: True
|
ENABLED: True
|
||||||
RADIO_ID: 12345
|
RADIO_ID: 12345
|
||||||
IP: 4.3.2.1
|
IP: 127.0.0.1
|
||||||
PORT: 50000
|
PORT: 50000
|
||||||
ALIVE_TIMER: 5
|
ALIVE_TIMER: 5
|
||||||
MAX_MISSED: 20
|
MAX_MISSED: 20
|
||||||
@ -126,3 +128,31 @@ AUTH_ENABLED: True
|
|||||||
AUTH_KEY: 1A2B3C
|
AUTH_KEY: 1A2B3C
|
||||||
MASTER_IP: 1.2.3.4
|
MASTER_IP: 1.2.3.4
|
||||||
MASTER_PORT: 50000
|
MASTER_PORT: 50000
|
||||||
|
|
||||||
|
|
||||||
|
[SAMPLE_MASTER]
|
||||||
|
ENABLED: False
|
||||||
|
RADIO_ID: 54321
|
||||||
|
IP: 192.168.1.1
|
||||||
|
PORT: 50000
|
||||||
|
ALIVE_TIMER: 5
|
||||||
|
MAX_MISSED: 20
|
||||||
|
PEER_OPER: True
|
||||||
|
IPSC_MODE: DIGITAL
|
||||||
|
TS1_LINK: True
|
||||||
|
TS2_LINK: True
|
||||||
|
CSBK_CALL: False
|
||||||
|
RCM: True
|
||||||
|
CON_APP: True
|
||||||
|
XNL_CALL: False
|
||||||
|
XNL_MASTER: False
|
||||||
|
DATA_CALL: True
|
||||||
|
VOICE_CALL: True
|
||||||
|
MASTER_PEER: True
|
||||||
|
AUTH_ENABLED: True
|
||||||
|
AUTH_KEY: 1A2B3C
|
||||||
|
# Below not used for a Master
|
||||||
|
# MASTER_IP: 1.2.3.4
|
||||||
|
# MASTER_PORT: 50000
|
||||||
|
|
||||||
|
|
||||||
|
32
get_ids.sh
Normal file
32
get_ids.sh
Normal file
@ -0,0 +1,32 @@
|
|||||||
|
#! /bin/sh
|
||||||
|
|
||||||
|
#################################################
|
||||||
|
# #
|
||||||
|
# Create directory structure for DMRlink #
|
||||||
|
# #
|
||||||
|
#################################################
|
||||||
|
|
||||||
|
# To provide more readable output from DMRlink with current subscriber and repeater IDs, we download the CSV files from DMR-MARC
|
||||||
|
# If you are going to use this in a cron task, don't run it more then once a day.
|
||||||
|
# It might be good to find alternale a source as a backup.
|
||||||
|
|
||||||
|
# <http://www.dmr-marc.net/cgi-bin/trbo-database/datadump.cgi>
|
||||||
|
|
||||||
|
# wget -O users.csv -q "http://www.dmr-marc.net/cgi-bin/trbo-database/datadump.cgi?table=users&format=csv&header=0"
|
||||||
|
|
||||||
|
# Options are:
|
||||||
|
|
||||||
|
# table { users | repeaters }
|
||||||
|
|
||||||
|
# format { table | csv | csvq | json }
|
||||||
|
|
||||||
|
# header { 0 | 1 } (only applies to table and csv formats)
|
||||||
|
|
||||||
|
# id { nnnnnn } (query an individual record)
|
||||||
|
|
||||||
|
# Get the user IDs.
|
||||||
|
wget -O subscriber_ids.csv -q "http://www.dmr-marc.net/cgi-bin/trbo-database/datadump.cgi?table=users&format=csv&header=0"
|
||||||
|
|
||||||
|
# Get the peer IDs
|
||||||
|
wget -O peer_ids.csv -q "http://www.dmr-marc.net/cgi-bin/trbo-database/datadump.cgi?table=repeaters&format=csv&header=0"
|
||||||
|
|
64
init.d/bridge
Executable file
64
init.d/bridge
Executable file
@ -0,0 +1,64 @@
|
|||||||
|
#!/bin/sh
|
||||||
|
|
||||||
|
### BEGIN INIT INFO
|
||||||
|
# Provides: Mototrbo_IPSC_bridging
|
||||||
|
# Required-Start: $remote_fs $syslog
|
||||||
|
# Required-Stop: $remote_fs $syslog
|
||||||
|
# Default-Start: 2 3 4 5
|
||||||
|
# Default-Stop: 0 1 6
|
||||||
|
# Short-Description: DMRlink Bridge
|
||||||
|
# Description: Open Source IPSC bridging
|
||||||
|
### END INIT INFO
|
||||||
|
|
||||||
|
# Where is the directory containing DMRlink
|
||||||
|
DIR=/opt/dmrlink/bridge
|
||||||
|
# Filename of the python script
|
||||||
|
DAEMON=$DIR/bridge.py
|
||||||
|
# Daemon name
|
||||||
|
DAEMON_NAME=bridge
|
||||||
|
|
||||||
|
# Add any command line options for your daemon where
|
||||||
|
DAEMON_OPTS=""
|
||||||
|
|
||||||
|
# This next line determines what user the script runs as.
|
||||||
|
# Root generally not recommended but necessary if you are using the Raspberry Pi GPIO from Python.
|
||||||
|
DAEMON_USER=root
|
||||||
|
|
||||||
|
# The process ID of the script when it runs is stored here:
|
||||||
|
PIDFILE=/var/run/$DAEMON_NAME.pid
|
||||||
|
|
||||||
|
. /lib/lsb/init-functions
|
||||||
|
|
||||||
|
do_start () {
|
||||||
|
log_daemon_msg "Starting system $DAEMON_NAME daemon"
|
||||||
|
start-stop-daemon --start --background --pidfile $PIDFILE --make-pidfile --user $DAEMON_USER --chuid $DAEMON_USER --startas $DAEMON -- $DAEMON_OPTS
|
||||||
|
log_end_msg $?
|
||||||
|
}
|
||||||
|
do_stop () {
|
||||||
|
log_daemon_msg "Stopping system $DAEMON_NAME daemon"
|
||||||
|
start-stop-daemon --stop --pidfile $PIDFILE --remove-pidfile --retry 10
|
||||||
|
log_end_msg $?
|
||||||
|
}
|
||||||
|
|
||||||
|
case "$1" in
|
||||||
|
|
||||||
|
start|stop)
|
||||||
|
do_${1}
|
||||||
|
;;
|
||||||
|
|
||||||
|
restart|reload|force-reload)
|
||||||
|
do_stop
|
||||||
|
do_start
|
||||||
|
;;
|
||||||
|
|
||||||
|
status)
|
||||||
|
status_of_proc "$DAEMON_NAME" "$DAEMON" && exit 0 || exit $?
|
||||||
|
;;
|
||||||
|
*)
|
||||||
|
echo "Usage: /etc/init.d/$DAEMON_NAME {start|stop|restart|status}"
|
||||||
|
exit 1
|
||||||
|
;;
|
||||||
|
|
||||||
|
esac
|
||||||
|
exit 0
|
||||||
|
|
6
install.txt
Normal file
6
install.txt
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
To install DMRlink for DMRGateway
|
||||||
|
cd /srv
|
||||||
|
git clone https://github.com/N4IRS/DMRlink.git
|
||||||
|
cd DMRlink
|
||||||
|
./mk_dmrlink
|
||||||
|
|
115
mk_dmrlink
Executable file
115
mk_dmrlink
Executable file
@ -0,0 +1,115 @@
|
|||||||
|
#! /bin/sh
|
||||||
|
|
||||||
|
#################################################
|
||||||
|
# #
|
||||||
|
# Create directory structure for DMRlink #
|
||||||
|
# #
|
||||||
|
#################################################
|
||||||
|
|
||||||
|
apt-get install unzip -y
|
||||||
|
apt-get install python-twisted -y
|
||||||
|
|
||||||
|
# To allow multiple instances of DMRlink to run
|
||||||
|
# You need multiple ipsc directories, dmrlink.py and dmrlink.cfg
|
||||||
|
# The needed files are copied to /opt/dmrlink
|
||||||
|
|
||||||
|
# Make needed directories
|
||||||
|
mkdir -p /opt/dmrlink/ambe_audio/ipsc
|
||||||
|
mkdir -p /opt/dmrlink/bridge/ipsc
|
||||||
|
mkdir -p /opt/dmrlink/gateway/ipsc
|
||||||
|
mkdir -p /opt/dmrlink/log/ipsc
|
||||||
|
mkdir -p /opt/dmrlink/playback/ipsc
|
||||||
|
mkdir -p /opt/dmrlink/play_group/ipsc
|
||||||
|
mkdir -p /opt/dmrlink/record/ipsc
|
||||||
|
mkdir -p /opt/dmrlink/rcm/ipsc
|
||||||
|
mkdir -p /opt/dmrlink/doc
|
||||||
|
mkdir -p /opt/dmrlink/samples
|
||||||
|
mkdir -p /var/log/dmrlink
|
||||||
|
|
||||||
|
# Put scripts in /opt/dmrlink
|
||||||
|
cp /srv/DMRlink/get_ids.sh /opt/dmrlink # should be linked to /etc/cron.daily
|
||||||
|
cp /srv/DMRlink/init.d/bridge /opt/dmrlink/rc.bridge
|
||||||
|
|
||||||
|
# bitstring install
|
||||||
|
cd /opt/dmrlink
|
||||||
|
wget https://pypi.python.org/packages/source/b/bitstring/bitstring-3.1.3.zip
|
||||||
|
unzip bitstring-3.1.3.zip
|
||||||
|
cd bitstring-3.1.3
|
||||||
|
python setup.py install
|
||||||
|
cd /opt/dmrlink
|
||||||
|
rm bitstring-3.1.3.zip
|
||||||
|
|
||||||
|
# Put common files in /opt/dmrlink
|
||||||
|
cp /srv/DMRlink/subscriber_ids.csv /opt/dmrlink
|
||||||
|
cp /srv/DMRlink/talkgroup_ids.csv /opt/dmrlink
|
||||||
|
cp /srv/DMRlink/peer_ids.csv /opt/dmrlink
|
||||||
|
cp /srv/DMRlink/get_ids.sh /opt/dmrlink
|
||||||
|
|
||||||
|
# Copy ipsc directory into each app directory
|
||||||
|
cp /srv/DMRlink/ipsc/* /opt/dmrlink/ambe_audio/ipsc
|
||||||
|
cp /srv/DMRlink/ipsc/* /opt/dmrlink/bridge/ipsc
|
||||||
|
cp /srv/DMRlink/ipsc/* /opt/dmrlink/gateway/ipsc
|
||||||
|
cp /srv/DMRlink/ipsc/* /opt/dmrlink/log/ipsc
|
||||||
|
cp /srv/DMRlink/ipsc/* /opt/dmrlink/playback/ipsc
|
||||||
|
cp /srv/DMRlink/ipsc/* /opt/dmrlink/play_group/ipsc
|
||||||
|
cp /srv/DMRlink/ipsc/* /opt/dmrlink/record/ipsc
|
||||||
|
cp /srv/DMRlink/ipsc/* /opt/dmrlink/rcm/ipsc
|
||||||
|
|
||||||
|
# Put the samples together for easy ref
|
||||||
|
cp /srv/DMRlink/bridge_rules_SAMPLE.py /opt/dmrlink/samples
|
||||||
|
cp /srv/DMRlink/dmrlink_SAMPLE.cfg /opt/dmrlink/samples
|
||||||
|
cp /srv/DMRlink/known_bridges_SAMPLE.py /opt/dmrlink/samples
|
||||||
|
cp /srv/DMRlink/playback_config_SAMPLE.py /opt/dmrlink/samples
|
||||||
|
|
||||||
|
# Put the doc together for easy ref
|
||||||
|
cp /srv/DMRlink/FAQ.md /opt/dmrlink/doc
|
||||||
|
cp /srv/DMRlink/internal_data_decode.txt /opt/dmrlink/doc
|
||||||
|
cp /srv/DMRlink/LICENSE.txt /opt/dmrlink/doc
|
||||||
|
cp /srv/DMRlink/README.md /opt/dmrlink/doc
|
||||||
|
cp /srv/DMRlink/requirements.txt /opt/dmrlink/doc
|
||||||
|
|
||||||
|
# Stock ambe_audio
|
||||||
|
cp /srv/DMRlink/ambe_audio.cfg /opt/dmrlink/ambe_audio/
|
||||||
|
cp /srv/DMRlink/ambe_audio.py /opt/dmrlink/ambe_audio/
|
||||||
|
cp /srv/DMRlink/dmrlink.py /opt/dmrlink/ambe_audio/
|
||||||
|
cp /srv/DMRlink/dmrlink_SAMPLE.cfg /opt/dmrlink/ambe_audio/dmrlink.cfg
|
||||||
|
|
||||||
|
# ambe_audio for DMRGateway
|
||||||
|
cp /srv/DMRlink/ambe_audio.cfg /opt/dmrlink/gateway/
|
||||||
|
cp /srv/DMRlink/ambe_audio.py /opt/dmrlink/gateway/
|
||||||
|
cp /srv/DMRlink/dmrlink.py /opt/dmrlink/gateway/
|
||||||
|
cp /srv/DMRlink/dmrlink_SAMPLE.cfg /opt/dmrlink/gateway/dmrlink.cfg
|
||||||
|
|
||||||
|
# Bridge app
|
||||||
|
cp /srv/DMRlink/bridge.py /opt/dmrlink/bridge/
|
||||||
|
cp /srv/DMRlink/dmrlink.py /opt/dmrlink/bridge/
|
||||||
|
cp /srv/DMRlink/bridge_rules_SAMPLE.py /opt/dmrlink/bridge/
|
||||||
|
cp /srv/DMRlink/dmrlink_SAMPLE.cfg /opt/dmrlink/bridge/dmrlink.cfg
|
||||||
|
|
||||||
|
# Log app
|
||||||
|
cp /srv/DMRlink/log.py /opt/dmrlink/log/
|
||||||
|
cp /srv/DMRlink/dmrlink.py /opt/dmrlink/log/
|
||||||
|
cp /srv/DMRlink/dmrlink_SAMPLE.cfg /opt/dmrlink/log/dmrlink.cfg
|
||||||
|
|
||||||
|
# Playback (Parrot)
|
||||||
|
cp /srv/DMRlink/playback.py /opt/dmrlink/playback/
|
||||||
|
cp /srv/DMRlink/dmrlink.py /opt/dmrlink/playback/
|
||||||
|
cp /srv/DMRlink/dmrlink_SAMPLE.cfg /opt/dmrlink/playback/dmrlink.cfg
|
||||||
|
|
||||||
|
# Play Group app
|
||||||
|
cp /srv/DMRlink/play_group.py /opt/dmrlink/play_group/
|
||||||
|
cp /srv/DMRlink/dmrlink.py /opt/dmrlink/play_group/
|
||||||
|
cp /srv/DMRlink/dmrlink_SAMPLE.cfg /opt/dmrlink/play_group/dmrlink.cfg
|
||||||
|
|
||||||
|
# record app
|
||||||
|
cp /srv/DMRlink/record.py /opt/dmrlink/record/
|
||||||
|
cp /srv/DMRlink/dmrlink.py /opt/dmrlink/record/
|
||||||
|
cp /srv/DMRlink/dmrlink_SAMPLE.cfg /opt/dmrlink/record/dmrlink.cfg
|
||||||
|
|
||||||
|
# rcm app
|
||||||
|
cp /srv/DMRlink/rcm_db_log.py /opt/dmrlink/rcm/
|
||||||
|
cp /srv/DMRlink/rcm.py /opt/dmrlink/rcm/
|
||||||
|
cp /srv/DMRlink/dmrlink.py /opt/dmrlink/rcm/
|
||||||
|
cp /srv/DMRlink/dmrlink_SAMPLE.cfg /opt/dmrlink/rcm/dmrlink.cfg
|
||||||
|
cp /srv/DMRlink/pickle_stat_reader.py /opt/dmrlink/rcm/
|
||||||
|
|
1738
peer_ids.csv
1738
peer_ids.csv
File diff suppressed because one or more lines are too long
@ -1 +1,3 @@
|
|||||||
Twisted>=12.0.0
|
Twisted>=12.0.0
|
||||||
|
https://pypi.python.org/packages/source/b/bitstring/bitstring-3.1.3.zip
|
||||||
|
|
||||||
|
24492
subscriber_ids.csv
24492
subscriber_ids.csv
File diff suppressed because one or more lines are too long
@ -1 +1,11 @@
|
|||||||
Worldwide,1
Local,2
North America,3
T6-DCI Bridge,3100
Kansas Statewide,3120
Missouri,3129
Massachussetts,3125
Midwest,3169
Northeast,3172
|
Worldwide,1
Local,2
North America,3
|
||||||
|
DMRPlus,9
|
||||||
|
Worldwide English,13
|
||||||
|
TAC 310,310
|
||||||
|
DCI Bridge 2,3100
DCI 1,3160
|
||||||
|
Midwest,3169
Northeast,3172
|
||||||
|
Southeast,3174
|
||||||
|
Flordia,3112
|
||||||
|
Kansas Statewide,3120
Massachussetts,3125
Missouri,3129
|
||||||
|
DCI Comm 1,3777215
|
||||||
|
Echo Server,9998
|
|
Loading…
x
Reference in New Issue
Block a user