Initial Upload

This commit is contained in:
Cort Buffington 2016-07-20 16:16:27 -05:00
commit c566ee76ef
6 changed files with 348 additions and 0 deletions

15
.gitignore vendored Normal file
View File

@ -0,0 +1,15 @@
.DS_Store
.dropbox
*.out
Icon
*.pyc
*.bak
*.lcl
*.conf
*.config
*.json
*.pickle
*.c
*.cpp
*.o
*.h

7
LICENSE.txt Normal file
View File

@ -0,0 +1,7 @@
Copyright (c) 2016 Cortney T. Buffington, N0MJS and the K0USY Group. n0mjs@me.com
This work is licensed under the Creative Commons Attribution-NonCommercial-ShareAlike
3.0 Unported License.To view a copy of this license, visit
http://creativecommons.org/licenses/by-nc-sa/3.0/ or send a letter to
Creative Commons, 444 Castro Street, Suite 900, Mountain View,
California, 94041, USA.

48
README.md Normal file
View File

@ -0,0 +1,48 @@
##PROJECT: Open Source HomeBrew Repeater Proctol Client/Master.
**PURPOSE:** Thanks to the work of Jonathan Naylor, G4KLX; Hans Barthen, DL5DI; Torsten Shultze, DG1HT we have an open protocol for internetworking DMR repeaters. Unfortunately, there's no generic client and/or master stacks. This project is to build an open-source, python-based implementation. This is a non-commercial license. Atribution is *required* if you use it.
For those who will ask: This is a piece of software that implements an open-source, amateur radio networking protocol. It is not a network. It is not indended to be a network. It is not intended to replace or circumvent a network. People do those things, code doesn't.
**PROPERTY:**
This work represents the author's interpretation of the HomeBrew Repeater Protocol, based on the 2015-07-26 documents from DMRplus, "IPSC Protocol Specs for homebrew DMR repeater" as written by Jonathan Naylor, G4KLX; Hans Barthen, DL5DI; Torsten Shultze, DG1HT, also licenced under Creative Commons BY-NC-SA license.
**WARRANTY**
None. The owners of this work make absolutley no warranty, express or implied. Use this software at your own risk.
**PRE-REQUISITE KNOWLEDGE:**
This document assumes the reader is familiar with the Python programming language and DMR.
**MORE DOCUMENTATION TO COME**
**PREFERRED ATTRIBUTION**
Attribution is requested to be given with the following information:
Title: "DMRlink"
Source: "http://github.com/n0mjs710/HBlink"
Author: "K0USY Group"
Link: "http://k0usy.strikingly.com"
License: "CC BY-NC-SA 3.0"
Link: "http://creativecommons.org/licenses/by-nc-sa/3.0/"
An example where Title, Author and License are hyperlinked:
["HBlink"](http://github.com/n0mjs710/HBlink) by [K0USY Group](http://k0usy.strikingly.com is licensed under [CC BY-NC-SA 3.0](http://creativecommons.org/licenses/by-nc-sa/3.0/)
An example where hyperlinks are not possible:
"HBlink" (http://github.com/n0mjs710/HBlink) by K0USY Group (http://k0usy.strikingly.com) is licensed under CC BY-NC-SA 3.0 (http://creativecommons.org/licenses/by-nc-sa/3.0/)
Attribution is requested to be made in any official public or private documentation about the use or derivative use of HBlink in conjunction with whatever function(s), operation(s) or feature(s) employ HBlink in whole or in part. Attribution need only be made once per any such "official" documentation and may be placed wherever deemed appropriate by the author of such documentation.
***73 DE N0MJS***
Copyright (c) 2016 Cortney T. Buffington, N0MJS and the K0USY Group. n0mjs@me.com
This work is licensed under the Creative Commons Attribution-NonCommercial-ShareAlike
3.0 Unported License.To view a copy of this license, visit
http://creativecommons.org/licenses/by-nc-sa/3.0/ or send a letter to
Creative Commons, 444 Castro Street, Suite 900, Mountain View,
California, 94041, USA.

21
hb_message_types.py Normal file
View File

@ -0,0 +1,21 @@
# Copyright (c) 2016 Cortney T. Buffington, N0MJS and the K0USY Group. n0mjs@me.com
#
# This work is licensed under the Creative Attribution-NonCommercial-ShareAlike
# 3.0 Unported License.To view a copy of this license, visit
# http://creativecommons.org/licenses/by-nc-sa/3.0/ or send a letter to
# Creative Commons, 444 Castro Street, Suite 900, Mountain View,
# California, 94041, USA.
# Known HomeBrew Repeater Message Types
# In message below, "ID" is taken to mean the 4-byte HEX repeater ID string of the repaeter(DMR Radio ID)
RPTL = 'RPTL' # Initial LOGIN, RPTL+ID
MSTNAK = 'MSTNAK' # Master negatvive ack, MSTNAK+ID
MSTACK = 'MSTACK' # Master acknowledgement, MSTACK+ID
# if in response to a login, MSTACK+ID+(random 32-bit integer (as a string))
RPTK = 'RPTK' # See explantation elsewhere about passphrase, ID and SHA-256 hash!
MSTPING = 'MSTPING' # From the repeater, MSTPING+ID
RPTPONG = 'MSTPONG' # From the master, MSTPONG+ID
MSTCL = 'MSTCL' # From the master, MSTCL+ID indicates close-down of the master
RPTCL = 'RPTCL' # From the repeater, RPTCL+ID indicates close-down of the repeater
RPTC = 'RPTC' # From the repeater, information packet about the repeater
DMRD = 'DMRD' # DMR data, format documented elsewhere

58
hblink.cfg Normal file
View File

@ -0,0 +1,58 @@
[GLOBAL]
PATH: ./
[LOGGER]
LOG_FILE: /tmp/hblink.log
LOG_HANDLERS: console-timed
LOG_LEVEL: INFO
LOG_NAME: HBlink
[MASTER]
ENABLED: True
IP:
PORT: 54000
PASSPHRASE:
[REPEATER-1]
ENABLED: True
IP:
PORT: 54001
MASTER_IP: 172.16.1.1
MASTER_PORT: 54000
PASSPHRASE: homebrew
CALLSIGN: W1ABC
RADIO_ID: 312000
RX_FREQ: 449.000000
TX_FREQ: 444.000000
TX_POWER: 25
ColorCode: 1
LATITUDE: 38.00000
LONGITUDE: -95.00000
HEIGHT: 75
LOCATION: Anywhere, USA
DESCRIPTION: This is a cool repeater
URL: www.w1abc.org
SOFTWARE_ID: HBlink v1.0
PACKAGE_ID: HBlink v1.0
[REPEATER-2]
ENABLED: True
IP:
PORT: 54002
MASTER_IP: 172.16.1.1
MASTER_PORT: 54000
PASSPHRASE: homebrew
CALLSIGN: K9ABC
RADIO_ID: 312001
RX_FREQ: 448.000000
TX_FREQ: 443.000000
TX_POWER: 40
ColorCode: 1
LATITUDE: 38.10000
LONGITUDE: -95.10000
HEIGHT: 50
LOCATION: Somewhere, USA
DESCRIPTION: This is a cooler repeater
URL: www.k9abc.org
SOFTWARE_ID: HBlink v1.0
PACKAGE_ID: HBlink v1.0

199
hblink.py Executable file
View File

@ -0,0 +1,199 @@
#!/usr/bin/env python
#
# This work is licensed under the Creative Attribution-NonCommercial-ShareAlike
# 3.0 Unported License.To view a copy of this license, visit
# http://creativecommons.org/licenses/by-nc-sa/3.0/ or send a letter to
# Creative Commons, 444 Castro Street, Suite 900, Mountain View,
# California, 94041, USA.
from __future__ import print_function
import ConfigParser
import argparse
import sys
import os
import logging
from logging.config import dictConfig
from binascii import b2a_hex as h
from socket import gethostbyname
from twisted.internet.protocol import DatagramProtocol
from twisted.internet import reactor
__author__ = 'Cortney T. Buffington, N0MJS'
__copyright__ = 'Copyright (c) 2013 - 2016 Cortney T. Buffington, N0MJS and the K0USY Group'
__credits__ = 'Steve Zingman, N4IRS; Mike Zingman, N4IRR; Jonathan Naylor, G4KLX; Hans Barthen, DL5DI; Torsten Shultze, DG1HT'
__license__ = 'Creative Commons Attribution-NonCommercial-ShareAlike 3.0 Unported'
__maintainer__ = 'Cort Buffington, N0MJS'
__email__ = 'n0mjs@me.com'
__status__ = 'pre-alpha'
# 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.add_argument('-c', '--config', action='store', dest='CFG_FILE', help='/full/path/to/config.file (usually hblink.cfg)')
cli_args = parser.parse_args()
#************************************************
# PARSE THE CONFIG FILE AND BUILD STRUCTURE
#************************************************
CLIENTS = {}
config = ConfigParser.ConfigParser()
if not cli_args.CFG_FILE:
cli_args.CFG_FILE = os.path.dirname(os.path.abspath(__file__))+'/hblink.cfg'
try:
if not config.read(cli_args.CFG_FILE):
sys.exit('Configuration file \''+cli_args.CFG_FILE+'\' is not a valid configuration file! Exiting...')
except:
sys.exit('Configuration file \''+cli_args.CFG_FILE+'\' is not a valid configuration file! Exiting...')
try:
for section in config.sections():
if section == 'GLOBAL':
# Process GLOBAL items in the configuration
PATH = config.get(section, 'PATH')
elif section == 'LOGGER':
# Process LOGGER items in the configuration
LOGGER = {
'LOG_FILE': config.get(section, 'LOG_FILE'),
'LOG_HANDLERS': config.get(section, 'LOG_HANDLERS'),
'LOG_LEVEL': config.get(section, 'LOG_LEVEL'),
'LOG_NAME': config.get(section, 'LOG_NAME')
}
elif section == 'MASTER':
# HomeBrew Master Configuration
MASTER = {
'ENABLED': config.getboolean(section, 'ENABLED'),
'IP': gethostbyname(config.get(section, 'IP')),
'PORT': config.getint(section, 'PORT'),
'PASSPHRASE': config.get(section, 'PASSPHRASE')
}
elif config.getboolean(section, 'ENABLED'):
# HomeBrew Client (Repeater) Configuration(s)
CLIENTS.update({section: {
'ENABLED': config.getboolean(section, 'ENABLED'),
'IP': gethostbyname(config.get(section, 'IP')),
'PORT': config.getint(section, 'PORT'),
'MASTER_IP': gethostbyname(config.get(section, 'MASTER_IP')),
'MASTER_PORT': config.getint(section, 'MASTER_PORT'),
'PASSPHRASE': config.get(section, 'PASSPHRASE'),
'CALLSIGN': config.get(section, 'CALLSIGN'),
'RADIO_ID': hex(int(config.get(section, 'RADIO_ID')))[2:].rjust(8,'0').decode('hex'),
'RX_FREQ': config.get(section, 'RX_FREQ'),
'TX_FREQ': config.get(section, 'TX_FREQ'),
'TX_POWER': config.get(section, 'TX_POWER'),
'COLORCODE': config.get(section, 'COLORCODE'),
'LATITUDE': config.get(section, 'LATITUDE'),
'LONGITUDE': config.get(section, 'LONGITUDE'),
'HEIGHT': config.get(section, 'HEIGHT'),
'LOCATION': config.get(section, 'LOCATION'),
'DESCRIPTION': config.get(section, 'DESCRIPTION'),
'URL': config.get(section, 'URL'),
'SOFTWARE_ID': config.get(section, 'SOFTWARE_ID'),
'PACKAGE_ID': config.get(section, 'PACKAGE_ID')
}})
except:
sys.exit('Could not parse configuration file, exiting...')
#************************************************
# CONFIGURE THE SYSTEM LOGGER
#************************************************
dictConfig({
'version': 1,
'disable_existing_loggers': False,
'filters': {
},
'formatters': {
'verbose': {
'format': '%(levelname)s %(asctime)s %(module)s %(process)d %(thread)d %(message)s'
},
'timed': {
'format': '%(levelname)s %(asctime)s %(message)s'
},
'simple': {
'format': '%(levelname)s %(message)s'
},
'syslog': {
'format': '%(name)s (%(process)d): %(levelname)s %(message)s'
}
},
'handlers': {
'null': {
'class': 'logging.NullHandler'
},
'console': {
'class': 'logging.StreamHandler',
'formatter': 'simple'
},
'console-timed': {
'class': 'logging.StreamHandler',
'formatter': 'timed'
},
'file': {
'class': 'logging.FileHandler',
'formatter': 'simple',
'filename': LOGGER['LOG_FILE'],
},
'file-timed': {
'class': 'logging.FileHandler',
'formatter': 'timed',
'filename': LOGGER['LOG_FILE'],
},
'syslog': {
'class': 'logging.handlers.SysLogHandler',
'formatter': 'syslog',
}
},
'loggers': {
LOGGER['LOG_NAME']: {
'handlers': LOGGER['LOG_HANDLERS'].split(','),
'level': LOGGER['LOG_LEVEL'],
'propagate': True,
}
}
})
logger = logging.getLogger(LOGGER['LOG_NAME'])
#************************************************
# HERE ARE THE IMPORTANT PARTS
#************************************************
class HBMASTER(DatagramProtocol):
def __init__(self, *args, **kwargs):
pass
class HBCLIENT(DatagramProtocol):
def __init__(self, *args, **kwargs):
pass
#************************************************
# MAIN PROGRAM LOOP STARTS HERE
#************************************************
if __name__ == '__main__':
logger.info('HBlink \'HBlink.py\' (c) 2016 N0MJS & the K0USY Group - SYSTEM STARTING...')
# HBlink Master
if MASTER:
hbmaster = HBMASTER()
reactor.listenUDP(MASTER['PORT'], hbmaster, interface=MASTER['IP'])
clients = {}
for client in CLIENTS:
if CLIENTS[client]['ENABLED']:
clients[client] = HBCLIENT(client)
reactor.listenUDP(CLIENTS[client]['PORT'], clients[client], interface=CLIENTS[client]['IP'])
reactor.run()