first commit
This commit is contained in:
commit
ddc8194bd6
27
README.md
Executable file
27
README.md
Executable file
|
@ -0,0 +1,27 @@
|
|||
DAPRS 1.0
|
||||
============================
|
||||
|
||||
|
||||
## Version en production 1.0
|
||||
|
||||
## 1.0
|
||||
|
||||
|
||||
placer:<br>
|
||||
le répertoire dashboard dans FreeDMR/script<br>
|
||||
puis les fichiers:<br>
|
||||
tout les fichier gps_* dans FreeDMR
|
||||
puis éditer fichier pour le service hbdaprs.service
|
||||
changer
|
||||
/opt/FreeDMR/gps_data.py par votre chemin
|
||||
puis copier le fichier hbdaprs.service
|
||||
dans
|
||||
/lib/systemd/system
|
||||
puis sudo systemctl enable hbdaprs.service
|
||||
|
||||
|
||||
73
|
||||
Xavier
|
||||
|
||||
|
||||
|
BIN
dashboard/__pycache__/dashboard_settings.cpython-310.pyc
Normal file
BIN
dashboard/__pycache__/dashboard_settings.cpython-310.pyc
Normal file
Binary file not shown.
BIN
dashboard/__pycache__/dashboard_settings.cpython-38.pyc
Normal file
BIN
dashboard/__pycache__/dashboard_settings.cpython-38.pyc
Normal file
Binary file not shown.
131
dashboard/dashboard.py
Normal file
131
dashboard/dashboard.py
Normal file
|
@ -0,0 +1,131 @@
|
|||
###############################################################################
|
||||
# HBLink - Copyright (C) 2020 Cortney T. Buffington, N0MJS <n0mjs@me.com>
|
||||
# GPS/Data - Copyright (C) 2020 Eric Craw, KF7EEL <kf7eel@qsl.net>
|
||||
#
|
||||
# This program is free software; you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
# the Free Software Foundation; either version 3 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program; if not, write to the Free Software Foundation,
|
||||
# Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
###############################################################################
|
||||
|
||||
'''
|
||||
This is a web dashboard for the GPS/Data application.
|
||||
'''
|
||||
|
||||
from flask import Flask, render_template
|
||||
import ast, os
|
||||
from dashboard_settings import *
|
||||
|
||||
app = Flask(__name__)
|
||||
|
||||
|
||||
tbl_hdr = '''
|
||||
<fieldset style="border-radius: 8px; background-color:#e0e0e0e0; text-algin: lef; margin-left:15px;margin-right:15px;font-size:14px;border-top-left-radius: 10px; border-top-right-radius: 10px;border-bottom-left-radius: 10px; border-bottom-right-radius: 10px;">
|
||||
<table style="width:100%; font: 10pt arial, sans-serif">
|
||||
'''
|
||||
|
||||
tbl_ftr = '''
|
||||
</table></fieldset>
|
||||
'''
|
||||
|
||||
def get_loc_data():
|
||||
try:
|
||||
dash_loc = ast.literal_eval(os.popen('cat /tmp/gps_data_user_loc.txt').read())
|
||||
tmp_loc = ''
|
||||
loc_hdr = '''
|
||||
<TR style=" height: 32px;font: 10pt arial, sans-serif; background-color:#CDCDCD; color:black">
|
||||
<TH>Indicatif</TH><TH>Latitude</TH><TH>Longitude</TH><TH>Date et heure</TH></TR>
|
||||
'''
|
||||
cpt=0
|
||||
for e in dash_loc:
|
||||
cpt = cpt + 1
|
||||
tmp_color="#f9f9f9f9"
|
||||
if cpt >= 2:
|
||||
tmp_color="#d9d9d9d9"
|
||||
cpt = 0
|
||||
tmp_loc = tmp_loc + '''
|
||||
<TR style="background-color:''' + str(tmp_color) + ''';">
|
||||
<TD><strong>''' + e['call'] + '''</strong></TD>
|
||||
<TD><strong> ''' + str(e['lat']) + ''' </strong></TD>
|
||||
<TD><strong> ''' + str(e['lon']) + ''' </strong></TD>
|
||||
<TD> ''' + e['time'] + ''' </TD>
|
||||
</TR>
|
||||
'''
|
||||
return str(str('<h1 style="text-align: center;">Positions Reçues</h1>') + tbl_hdr + loc_hdr + tmp_loc + tbl_ftr)
|
||||
except:
|
||||
return str('<h1 style="text-align: center;">Pas de données</h1>')
|
||||
|
||||
|
||||
def get_log_data():
|
||||
|
||||
#try:
|
||||
dash_log = (os.popen('cat /tmp/gps_data.log | nl | sort -n -r | cut -f2').read())
|
||||
dash_log = dash_log.replace('\n', '<br>')
|
||||
return str('<font color="#fff">'+dash_log+'</font>')
|
||||
#except:
|
||||
# return str('<font color="#fff">Pas de données</font>')
|
||||
|
||||
def get_bb_data():
|
||||
try:
|
||||
dash_bb = ast.literal_eval(os.popen('cat /tmp/gps_data_user_bb.txt').read())
|
||||
tmp_bb = ''
|
||||
|
||||
bb_hdr = '''
|
||||
<TR style=" height: 32px;font: 10pt arial, sans-serif; background-color:#CDCDCD; color:black">
|
||||
<TH>Indicatif</TH><TH>DMR id</TH><TH>Bulletin</TH><TH>Date et heure</TH></TR>
|
||||
'''
|
||||
|
||||
for e in dash_bb:
|
||||
tmp_bb = tmp_bb + '''
|
||||
<TR style="background-color:#f9f9f9f9;">
|
||||
<TD><strong> ''' + e['call'] + ''' </strong></TD>
|
||||
<TD>''' + str(e['dmr_id']) + '''</TD>
|
||||
<TD><strong> ''' + e['bulliten'] + ''' </strong></TD>
|
||||
<TD> ''' + e['time'] + ''' </TD>
|
||||
</TR>
|
||||
'''
|
||||
|
||||
return str('<h1 style="text-align: center;">Bulletins</h1>' + tbl_hdr + bb_hdr + tmp_bb + tbl_ftr)
|
||||
except:
|
||||
return str('<h1 style="text-align: center;">No data</h1>')
|
||||
|
||||
@app.route('/')
|
||||
def index():
|
||||
#return get_data()
|
||||
return render_template('index.html', title = dashboard_title, logo = logo)
|
||||
@app.route('/bulletin_board')
|
||||
def dash_bb():
|
||||
return get_bb_data()
|
||||
#return render_template('index.html', data = str(get_data()))
|
||||
@app.route('/positions')
|
||||
def dash_loc():
|
||||
return get_loc_data()
|
||||
#return render_template('index.html', data = str(get_data()))
|
||||
@app.route('/logdaprs')
|
||||
def dash_log():
|
||||
return get_log_data()
|
||||
##@app.route('/<string:page_name>/')
|
||||
##def render_static(page_name):
|
||||
## return render_template('%s.html' % page_name, title = dashboard_title, logo = logo, description = description)
|
||||
@app.route('/help/')
|
||||
def help():
|
||||
#return get_data()
|
||||
return render_template('help.html', title = dashboard_title, logo = logo, description = description, data_call_type = data_call_type, data_call_id = data_call_id, aprs_ssid = aprs_ssid)
|
||||
@app.route('/about/')
|
||||
def about():
|
||||
#return get_data()
|
||||
return render_template('about.html', title = dashboard_title, logo = logo, contact_name = contact_name, contact_call = contact_call, contact_email = contact_email, contact_website = contact_website)
|
||||
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
app.run(debug = True, port=dash_port, host=dash_host)
|
49
dashboard/dashboard_settings-SAMPLE.py
Normal file
49
dashboard/dashboard_settings-SAMPLE.py
Normal file
|
@ -0,0 +1,49 @@
|
|||
###############################################################################
|
||||
# HBLink - Copyright (C) 2020 Cortney T. Buffington, N0MJS <n0mjs@me.com>
|
||||
# GPS/Data - Copyright (C) 2020 Eric Craw, KF7EEL <kf7eel@qsl.net>
|
||||
#
|
||||
# This program is free software; you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
# the Free Software Foundation; either version 3 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program; if not, write to the Free Software Foundation,
|
||||
# Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
###############################################################################
|
||||
|
||||
'''
|
||||
Settings for web dashboard.
|
||||
'''
|
||||
|
||||
# Title of the Dashboard
|
||||
dashboard_title = 'HBLink3 D-APRS Dashboard'
|
||||
# Logo used on dashboard page
|
||||
logo = 'https://raw.githubusercontent.com/kf7eel/hblink3/gps/HBlink.png'
|
||||
# Port to run server
|
||||
dash_port = 8092
|
||||
# IP to run server on
|
||||
dash_host = '127.0.0.1'
|
||||
#Description of dashboard to show on main page
|
||||
description = '''
|
||||
Welcome to the ''' + dashboard_title + '''.
|
||||
'''
|
||||
# The following will generate a help page for your users.
|
||||
|
||||
# Data call type
|
||||
data_call_type = 'Private Call'
|
||||
# DMR ID of GPS/Data application
|
||||
data_call_id = '9099'
|
||||
# Default APRS ssid
|
||||
aprs_ssid = '15'
|
||||
|
||||
# Gateway contact info displayed on about page.
|
||||
contact_name = 'your name'
|
||||
contact_call = 'N0CALL'
|
||||
contact_email = 'email@example.org'
|
||||
contact_website = 'https://hbl.ink'
|
49
dashboard/dashboard_settings.py
Normal file
49
dashboard/dashboard_settings.py
Normal file
|
@ -0,0 +1,49 @@
|
|||
###############################################################################
|
||||
# HBLink - Copyright (C) 2020 Cortney T. Buffington, N0MJS <n0mjs@me.com>
|
||||
# GPS/Data - Copyright (C) 2020 Eric Craw, KF7EEL <kf7eel@qsl.net>
|
||||
#
|
||||
# This program is free software; you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
# the Free Software Foundation; either version 3 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program; if not, write to the Free Software Foundation,
|
||||
# Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
###############################################################################
|
||||
|
||||
'''
|
||||
Settings for web dashboard.
|
||||
'''
|
||||
|
||||
# Title of the Dashboard
|
||||
dashboard_title = 'D-APRS Dashboard'
|
||||
# Logo used on dashboard page
|
||||
logo = 'https://monsite.com/logo.png'
|
||||
# Port to run server
|
||||
dash_port = 8086
|
||||
# IP to run server on
|
||||
dash_host = '192.168.0.1'
|
||||
#Description of dashboard to show on main page
|
||||
description = '''
|
||||
Welcome to the ''' + dashboard_title + '''.
|
||||
'''
|
||||
# The following will generate a help page for your users.
|
||||
|
||||
# Data call type
|
||||
data_call_type = 'Private Call'
|
||||
# DMR ID of GPS/Data application
|
||||
data_call_id = '9099'
|
||||
# Default APRS ssid
|
||||
aprs_ssid = '15'
|
||||
|
||||
# Gateway contact info displayed on about page.
|
||||
contact_name = 'MYCALL'
|
||||
contact_call = 'MYCALL'
|
||||
contact_email = 'mail@free.fr'
|
||||
contact_website = 'http://www.monsite.com'
|
29
dashboard/templates/about.html
Normal file
29
dashboard/templates/about.html
Normal file
|
@ -0,0 +1,29 @@
|
|||
{% include 'page.html' %}
|
||||
{% include 'header.html' %}
|
||||
<fieldset style="background-color:#e0e0e0e0;text-algin: lef; margin-left:15px;margin-right:15px;font-size:14px;border-top-left-radius: 10px; border-top-right-radius: 10px;border-bottom-left-radius: 10px; border-bottom-right-radius: 10px;">
|
||||
<p> </p>
|
||||
<p>Nous contacter.</p>
|
||||
|
||||
<center>
|
||||
<fieldset style="border-radius: 8px; background-color:#e0e0e0e0; text-algin: lef; margin-left:15px;margin-right:15px;font-size:14px;border-top-left-radius: 10px; border-top-right-radius: 10px;border-bottom-left-radius: 10px; border-bottom-right-radius: 10px; width:50%;">
|
||||
<table style="width:80%; font: 10pt arial, sans-serif">
|
||||
<tr>
|
||||
<td><strong>Indicatif</strong></td>
|
||||
<td> {{contact_call}}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><strong>E-Mail</strong></td>
|
||||
<td> {{contact_email}}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><strong>Website</strong></td>
|
||||
<td> {{contact_website}}</td>
|
||||
</tr>
|
||||
|
||||
</table></fieldset>
|
||||
</center>
|
||||
|
||||
<p> </p>
|
||||
</fieldset>
|
||||
{% include 'footer.html' %}
|
||||
|
5
dashboard/templates/footer.html
Normal file
5
dashboard/templates/footer.html
Normal file
|
@ -0,0 +1,5 @@
|
|||
<div>
|
||||
<hr />
|
||||
<div style="text-align: center;">Copyright (c) 2016 - 2021. All rights reserved.</div>
|
||||
</body>
|
||||
</html>
|
14
dashboard/templates/header.html
Normal file
14
dashboard/templates/header.html
Normal file
|
@ -0,0 +1,14 @@
|
|||
<p><img style="display: block; margin-left: auto; margin-right: auto;" src="{{logo}}" alt="Logo" width="300" height="144" /></p>
|
||||
<h1 style="text-align: center;">{{title}}</h1>
|
||||
<hr />
|
||||
<table style="width: 1200px; margin-left: auto; margin-right: auto;" border="black" cellspacing="3" cellpadding="3">
|
||||
<tbody>
|
||||
<tr>
|
||||
<td style="text-align: center;"><input class="button link" type="button" value="APRS DashBoard" onclick="javascript:window.location.href='http://fra1od.freeboxos.fr:85';"></td>
|
||||
<td style="text-align: center;"><input class="button link" type="button" value="D-APRS DashBoard" onclick="javascript:window.location.href='/';"></td>
|
||||
<td style="text-align: center;"><input class="button link" type="button" value="D-APRS Help" onclick="javascript:window.location.href='/help';"></td>
|
||||
<td style="text-align: center;"><input class="button link" type="button" value="Contact" onclick="javascript:window.location.href='/about';"></td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
<hr />
|
96
dashboard/templates/help.html
Normal file
96
dashboard/templates/help.html
Normal file
|
@ -0,0 +1,96 @@
|
|||
{% include 'page.html' %}
|
||||
{% include 'header.html' %}
|
||||
<fieldset style="border-radius: 8px; background-color:#e0e0e0e0; text-algin: lef; margin-left:15px;margin-right:15px;font-size:14px;border-top-left-radius: 10px; border-top-right-radius: 10px;border-bottom-left-radius: 10px; border-bottom-right-radius: 10px;">
|
||||
<h2 style="text-align: center;">Attention:</h2>
|
||||
<p><span style="color: #ff0000;"><strong>Veuillez noter que de nombreuses radios DMR (sinon toutes) NE transmettent PAS votre indicatif lors de l'envoi d'une position GPS. Il est de votre responsabilité d'identifier votre station conformément aux réglementations de votre pays.</strong></span></p>
|
||||
<p> </p>
|
||||
<p>Configurez votre radio pour envoyer des positions GPS avec les paramètres suivants:</p>
|
||||
|
||||
<center>
|
||||
<fieldset style="border-radius: 8px; background-color:#e0e0e0e0; text-algin: lef; margin-left:15px;margin-right:15px;font-size:14px;border-top-left-radius: 10px; border-top-right-radius: 10px;border-bottom-left-radius: 10px; border-bottom-right-radius: 10px; width:30%;">
|
||||
<table style="width:100%; font: 10pt arial, sans-serif">
|
||||
|
||||
<TR style=" height: 32px;font: 10pt arial, sans-serif; background-color:#CDCDCD; color:black">
|
||||
<TH><strong>Data Call Type</strong></TH><TH><strong>SLOT</strong></TH><TH><strong>TG ID</strong></TH></TR>
|
||||
<tr>
|
||||
<td style="text-align: center;"> {{data_call_type}}</td>
|
||||
<td style="text-align: center;"> SL1 et SL2</td>
|
||||
<td style="text-align: center;"> {{data_call_id}}</td>
|
||||
</tr>
|
||||
</table></fieldset>
|
||||
</center>
|
||||
|
||||
<p> </p>
|
||||
<p>Lorsqu'une position est reçue par cette passerelle, elle extrait les coordonnées et crée un paquet de position APRS. Cette passerelle trouvera votre indicatif en fonction de l'identifiant DMR de votre radio. Il est essentiel d'avoir vos informations à jour avec Le réseaux DMR FRA et DMRNET>. Un SSID APRS prédéfini à <strong>{{aprs_ssid}}</strong> est ajouté à votre indicatif (il peut être personalisé). Le paquet de localisation APRS est ensuite téléchargé vers APRS-IS (la carte APRS). Aucune configuration ou création de compte n'est requise au préalable, il faut juste être enregistré sur le réseaux. C'est à peu près "plug and play"."</p>
|
||||
<p>Par exemple, N0CALL a un ID DMR de 1234567. La radio de N0CALL envoie une position à cette passerelle avec les paramètres ci-dessus. Cette passerelle interrogera le réseaux, et la base de données pour l'ID DMR 1234567. Le résultat sera un paquet d'emplacement APRS avec la position sur la carte APRS INDICATIF-SSID {{aprs_ssid}}. </p>
|
||||
<p>Vous pouvez modifier les paramètres APRS par défaut de votre radio par SMS.</p>
|
||||
<h2 style="text-align: center;">Les commandes</h2>
|
||||
|
||||
<center>
|
||||
<fieldset style="border-radius: 8px; background-color:#e0e0e0e0; text-algin: lef; margin-left:15px;margin-right:15px;font-size:14px;border-top-left-radius: 10px; border-top-right-radius: 10px;border-bottom-left-radius: 10px; border-bottom-right-radius: 10px; width:70%;">
|
||||
<table style="width:100%; font: 10pt arial, sans-serif">
|
||||
|
||||
<TR style=" height: 32px;font: 10pt arial, sans-serif; background-color:#CDCDCD; color:black">
|
||||
<TH><strong>Commande</strong></TH><TH><strong>Description</strong></TH><TH><strong>Exemple</strong></TH></TR>
|
||||
|
||||
<tr>
|
||||
<td><strong>TEST</strong></td>
|
||||
<td>Tester la reception de SMS sur le dashboard.</td>
|
||||
<td><em><code>TEST</code></em></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><strong>MAILTEST</strong></td>
|
||||
<td>Tester l'envoie de mail. Attention il faut activer la fonction voir @MAIL</td>
|
||||
<td><em><code>MAILTEST</code></em></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><strong>SOS</strong></td>
|
||||
<td>Envoyer à tous un mail de SOS</td>
|
||||
<td><em><code>SOS</code></em></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><strong>@COM</strong></td>
|
||||
<td>Changer le commentaire de l'APRS.</td>
|
||||
<td><em><code>@COM c'est un test.</code></em></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><strong>@ICON</strong></td>
|
||||
<td>Changer l'icon de la position APRS. La table est par défaut /. Voir <a href="http://fra1od.fr.to:85/aprs/aide.php" rel="nofollow">http://fra1od.fr.to:85/aprs/aide.php</a> pour la liste des icon APRS.</td>
|
||||
<td><em><code>@icon p</code></em></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><strong>@SSID</strong></td>
|
||||
<td>Changer votre SSID à votre indicatif idem voir lien dans la section @ICON.</td>
|
||||
<td><em><code>@SSID 7</code></em></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><strong>@MAIL</strong></td>
|
||||
<td>Activer et transmettre son mail..</td>
|
||||
<td><em><code>@MAIL mail@fai.extention</code></em></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><strong>@MH</strong></td>
|
||||
<td>Définissez votre emplacement par carré de grille <a href="https://k7fry.com/grid/">QTH Locator</a> Attention que 6 cararctères, et respecter MAJ MIN. Conçu pour les radios sans GPS ou qui ne sont pas encore compatibles.</td>
|
||||
<td><em><code>@MH JN33no</code></em></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><strong>@BB</strong></td>
|
||||
<td>Publier un message sur le tableau de bord Web.</td>
|
||||
<td><em><code>@BB c'est un bulletin de test.</code></em></td>
|
||||
</tr>
|
||||
<td><strong>@[CALLSIGN-SSID] A-[MESSAGE]</strong></td>
|
||||
<td>Envoyer un message à une autre station via APRS</td>
|
||||
<td><em><code>@N0CALL-15 A-C est un test.</code></em></td>
|
||||
</tr>
|
||||
</table></fieldset>
|
||||
</center>
|
||||
|
||||
<p> </p>
|
||||
<p>Envoyez un SMS à <strong>{{data_call_id}}</strong> comme un <strong>{{data_call_type}}</strong> avec la commande souhaitée suivie de la valeur. Par exemple, pour changer votre icône en chien, la commande serait <code>@ICON p</code> (voir le tableau des icônes pour les valeurs). Changer votre SSID est aussi simple que <code>@SSID 7</code>, et <code>@COM Testing 123</code> changera le commentaire.</p>
|
||||
<p>Envoyer <code>@BB Test</code> entraînera un message sur le buletin avec le message "Test".</p>
|
||||
<p><strong>Pour supprimer l'une des valeurs stockées, envoyez simplement la commande appropriée sans aucune entrée.</strong> <code>@COM</code> supprimera le commentaire stocké, <code>@ICON</code>
|
||||
supprimera l'icône stockée, et <code>@ICON</code> supprimera l'icône en chaîne. Tous les rapports de position envoyés auront les paramètres par défaut.
|
||||
<strong>Pour activer la notification mail: <code>@MAIL mail@fai.extention</code>. pour supprimer la notification de mail: <code>@MAIL</code>.
|
||||
</p>
|
||||
</fieldset>
|
||||
{% include 'footer.html' %}
|
160
dashboard/templates/index.html
Normal file
160
dashboard/templates/index.html
Normal file
|
@ -0,0 +1,160 @@
|
|||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta http-equiv="refresh" content="120" >
|
||||
<title>{{title}}</title>
|
||||
|
||||
<style>
|
||||
body {background-color: D3D3D3;}
|
||||
h1 {color: green;}
|
||||
p {
|
||||
padding: 10px;
|
||||
margin: 20px;
|
||||
|
||||
}
|
||||
.content {
|
||||
max-width: 1200px;
|
||||
min-width: 1200px;
|
||||
margin: auto;
|
||||
}
|
||||
a:link {
|
||||
color: #0066ff;
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
/* visited link */
|
||||
a:visited {
|
||||
color: #0066ff;
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
/* mouse over link */
|
||||
a:hover {
|
||||
color: hotpink;
|
||||
text-decoration: underline;
|
||||
}
|
||||
/* selected link */
|
||||
a:active {
|
||||
color: #0066ff;
|
||||
text-decoration: none;
|
||||
}
|
||||
.tooltip {
|
||||
position: relative;
|
||||
opacity: 1;
|
||||
display: inline-block;
|
||||
border-bottom: 1px dotted black;
|
||||
}
|
||||
|
||||
.tooltip .tooltiptext {
|
||||
visibility: hidden;
|
||||
width: 280px;
|
||||
background-color: #6E6E6E;
|
||||
box-shadow: 4px 4px 6px #800000;
|
||||
color: #FFFFFF;
|
||||
text-align: left;
|
||||
border-radius: 6px;
|
||||
padding: 8px 0;
|
||||
left: 100%
|
||||
opacity: 1;
|
||||
/* Position the tooltip */
|
||||
position: absolute;
|
||||
z-index: 1;
|
||||
}
|
||||
|
||||
.tooltip:hover .tooltiptext {
|
||||
right: 100%
|
||||
opacity: 1;
|
||||
visibility: visible;
|
||||
}
|
||||
.button {
|
||||
background-color: #356244;
|
||||
border: none;
|
||||
color: white;
|
||||
padding: 8px;
|
||||
text-align: center;
|
||||
text-decoration: none;
|
||||
display: inline-block;
|
||||
font-size: 14px;
|
||||
font-weight: 500;
|
||||
margin: 4px 2px;
|
||||
border-radius: 8px;
|
||||
box-shadow: 0px 8px 10px rgba(0,0,0,0.1);
|
||||
}
|
||||
.link {background-color: #356244;}
|
||||
.link:hover {background-color: #3e8e41;}
|
||||
|
||||
.dropbtn {
|
||||
background-color: #356244;
|
||||
border: none;
|
||||
color: white;
|
||||
padding: 8px;
|
||||
text-align: center;
|
||||
text-decoration: none;
|
||||
display: inline-block;
|
||||
font-size: 14px;
|
||||
font-weight: 500;
|
||||
margin: 4px 2px;
|
||||
border-radius: 8px;
|
||||
box-shadow: 0px 8px 10px rgba(0,0,0,0.1);
|
||||
}
|
||||
|
||||
/* The container <div> - needed to position the dropdown content */
|
||||
.dropdown {
|
||||
position: relative;
|
||||
display: inline-block;
|
||||
}
|
||||
|
||||
/* Dropdown Content (Hidden by Default) */
|
||||
.dropdown-content {
|
||||
display: none;
|
||||
position: absolute;
|
||||
background-color: #f1f1f1;
|
||||
min-width: 140px;
|
||||
box-shadow: 0px 8px 16px 0px rgba(0,0,0,0.2);
|
||||
z-index: 1;
|
||||
}
|
||||
|
||||
/* Links inside the dropdown */
|
||||
.dropdown-content a {
|
||||
color: black;
|
||||
padding: 6px 16px;
|
||||
text-decoration: none;
|
||||
display: block;
|
||||
}
|
||||
|
||||
/* Change color of dropdown links on hover */
|
||||
.dropdown-content a:hover {background-color: #ddd;}
|
||||
|
||||
/* Show the dropdown menu on hover */
|
||||
.dropdown:hover .dropdown-content {display: block;}
|
||||
|
||||
/* Change the background color of the dropdown button when the dropdown content is shown */
|
||||
.dropdown:hover .dropbtn {background-color: #3e8e41;}
|
||||
table, td, th {border: .5px solid #d0d0d0; padding: 2px; border-collapse: collapse; text-align:center;}
|
||||
</style>
|
||||
</head>
|
||||
<div class="content">
|
||||
<body background="https://fra1od.freeboxos.fr:4443/paper.png">
|
||||
{% include 'header.html' %}
|
||||
<p style="text-align: center;"><b><em>Actualisation automatique toutes les 2min.</em></b></p>
|
||||
<fieldset style="background-color:#e0e0e0e0;text-algin: lef; margin-left:15px;margin-right:15px;font-size:14px;border-top-left-radius: 10px; border-top-right-radius: 10px;border-bottom-left-radius: 10px; border-bottom-right-radius: 10px;">
|
||||
<table style="width:100%; font: 10pt arial, sans-serif">
|
||||
<tbody>
|
||||
<tr>
|
||||
<td><p align="center"><iframe style="border: none;" title="Bulletin" src="bulletin_board" width="620" height="550"></iframe></p></td>
|
||||
<td><p align="center"><iframe style="border: none;" title="Positions Reçues" src="positions" width="580" height="550"></iframe></p></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td colspan="2" align="center">
|
||||
<fieldset style="width: 1200px; margin-left:0px;margin-right:0px;font-size:14px;border-top-left-radius: 10px; border-top-right-radius: 10px;border-bottom-left-radius: 10px; border-bottom-right-radius: 10px;"><legend><b><font color="#000"> .: Log DMR Data :. </font></b></legend>
|
||||
<div style="height: 20em; text-align: left; font-size:13px; background-color: #000000; color:#b1eee9;" >
|
||||
<iframe style="border: none;" title="log" src="logdaprs" width="1200" height="250">
|
||||
</div>
|
||||
</fieldset>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</fieldset>
|
||||
{% include 'footer.html' %}
|
137
dashboard/templates/page.html
Normal file
137
dashboard/templates/page.html
Normal file
|
@ -0,0 +1,137 @@
|
|||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<title>{{title}}</title>
|
||||
<style>
|
||||
body {background-color: D3D3D3;}
|
||||
h1 {color: green;}
|
||||
p {
|
||||
padding: 10px;
|
||||
margin: 20px;
|
||||
|
||||
}
|
||||
.content {
|
||||
max-width: 1200px;
|
||||
min-width: 1200px;
|
||||
margin: auto;
|
||||
}
|
||||
a:link {
|
||||
color: #0066ff;
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
/* visited link */
|
||||
a:visited {
|
||||
color: #0066ff;
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
/* mouse over link */
|
||||
a:hover {
|
||||
color: hotpink;
|
||||
text-decoration: underline;
|
||||
}
|
||||
/* selected link */
|
||||
a:active {
|
||||
color: #0066ff;
|
||||
text-decoration: none;
|
||||
}
|
||||
.tooltip {
|
||||
position: relative;
|
||||
opacity: 1;
|
||||
display: inline-block;
|
||||
border-bottom: 1px dotted black;
|
||||
}
|
||||
|
||||
.tooltip .tooltiptext {
|
||||
visibility: hidden;
|
||||
width: 280px;
|
||||
background-color: #6E6E6E;
|
||||
box-shadow: 4px 4px 6px #800000;
|
||||
color: #FFFFFF;
|
||||
text-align: left;
|
||||
border-radius: 6px;
|
||||
padding: 8px 0;
|
||||
left: 100%
|
||||
opacity: 1;
|
||||
/* Position the tooltip */
|
||||
position: absolute;
|
||||
z-index: 1;
|
||||
}
|
||||
|
||||
.tooltip:hover .tooltiptext {
|
||||
right: 100%
|
||||
opacity: 1;
|
||||
visibility: visible;
|
||||
}
|
||||
.button {
|
||||
background-color: #356244;
|
||||
border: none;
|
||||
color: white;
|
||||
padding: 8px;
|
||||
text-align: center;
|
||||
text-decoration: none;
|
||||
display: inline-block;
|
||||
font-size: 14px;
|
||||
font-weight: 500;
|
||||
margin: 4px 2px;
|
||||
border-radius: 8px;
|
||||
box-shadow: 0px 8px 10px rgba(0,0,0,0.1);
|
||||
}
|
||||
.link {background-color: #356244;}
|
||||
.link:hover {background-color: #3e8e41;}
|
||||
|
||||
.dropbtn {
|
||||
background-color: #356244;
|
||||
border: none;
|
||||
color: white;
|
||||
padding: 8px;
|
||||
text-align: center;
|
||||
text-decoration: none;
|
||||
display: inline-block;
|
||||
font-size: 14px;
|
||||
font-weight: 500;
|
||||
margin: 4px 2px;
|
||||
border-radius: 8px;
|
||||
box-shadow: 0px 8px 10px rgba(0,0,0,0.1);
|
||||
}
|
||||
|
||||
/* The container <div> - needed to position the dropdown content */
|
||||
.dropdown {
|
||||
position: relative;
|
||||
display: inline-block;
|
||||
}
|
||||
|
||||
/* Dropdown Content (Hidden by Default) */
|
||||
.dropdown-content {
|
||||
display: none;
|
||||
position: absolute;
|
||||
background-color: #f1f1f1;
|
||||
min-width: 140px;
|
||||
box-shadow: 0px 8px 16px 0px rgba(0,0,0,0.2);
|
||||
z-index: 1;
|
||||
}
|
||||
|
||||
/* Links inside the dropdown */
|
||||
.dropdown-content a {
|
||||
color: black;
|
||||
padding: 6px 16px;
|
||||
text-decoration: none;
|
||||
display: block;
|
||||
}
|
||||
|
||||
/* Change color of dropdown links on hover */
|
||||
.dropdown-content a:hover {background-color: #ddd;}
|
||||
|
||||
/* Show the dropdown menu on hover */
|
||||
.dropdown:hover .dropdown-content {display: block;}
|
||||
|
||||
/* Change the background color of the dropdown button when the dropdown content is shown */
|
||||
.dropdown:hover .dropbtn {background-color: #3e8e41;}
|
||||
table, td, th {border: .5px solid #d0d0d0; padding: 2px; border-collapse: collapse; text-align:center;}
|
||||
</style>
|
||||
|
||||
</head>
|
||||
<div class="content">
|
||||
<body background="https://fra1od.fr.to:4443/paper.png">
|
242
gps_data-SAMPLE.cfg
Normal file
242
gps_data-SAMPLE.cfg
Normal file
|
@ -0,0 +1,242 @@
|
|||
# PROGRAM-WIDE PARAMETERS GO HERE
|
||||
# PATH - working path for files, leave it alone unless you NEED to change it
|
||||
# PING_TIME - the interval that peers will ping the master, and re-try registraion
|
||||
# - how often the Master maintenance loop runs
|
||||
# MAX_MISSED - how many pings are missed before we give up and re-register
|
||||
# - number of times the master maintenance loop runs before de-registering a peer
|
||||
#
|
||||
# ACLs:
|
||||
#
|
||||
# Access Control Lists are a very powerful tool for administering your system.
|
||||
# But they consume packet processing time. Disable them if you are not using them.
|
||||
# But be aware that, as of now, the configuration stanzas still need the ACL
|
||||
# sections configured even if you're not using them.
|
||||
#
|
||||
# REGISTRATION ACLS ARE ALWAYS USED, ONLY SUBSCRIBER AND TGID MAY BE DISABLED!!!
|
||||
#
|
||||
# The 'action' May be PERMIT|DENY
|
||||
# Each entry may be a single radio id, or a hypenated range (e.g. 1-2999)
|
||||
# Format:
|
||||
# ACL = 'action:id|start-end|,id|start-end,....'
|
||||
# --for example--
|
||||
# SUB_ACL: DENY:1,1000-2000,4500-60000,17
|
||||
#
|
||||
# ACL Types:
|
||||
# REG_ACL: peer radio IDs for registration (only used on HBP master systems)
|
||||
# SUB_ACL: subscriber IDs for end-users
|
||||
# TGID_TS1_ACL: destination talkgroup IDs on Timeslot 1
|
||||
# TGID_TS2_ACL: destination talkgroup IDs on Timeslot 2
|
||||
#
|
||||
# ACLs may be repeated for individual systems if needed for granularity
|
||||
# Global ACLs will be processed BEFORE the system level ACLs
|
||||
# Packets will be matched against all ACLs, GLOBAL first. If a packet 'passes'
|
||||
# All elements, processing continues. Packets are discarded at the first
|
||||
# negative match, or 'reject' from an ACL element.
|
||||
#
|
||||
# If you do not wish to use ACLs, set them to 'PERMIT:ALL'
|
||||
# TGID_TS1_ACL in the global stanza is used for OPENBRIDGE systems, since all
|
||||
# traffic is passed as TS 1 between OpenBridges
|
||||
[GLOBAL]
|
||||
PATH: ./
|
||||
PING_TIME: 5
|
||||
MAX_MISSED: 3
|
||||
USE_ACL: True
|
||||
REG_ACL: PERMIT:ALL
|
||||
SUB_ACL: DENY:1
|
||||
TGID_TS1_ACL: PERMIT:ALL
|
||||
TGID_TS2_ACL: PERMIT:ALL
|
||||
|
||||
|
||||
# NOT YET WORKING: NETWORK REPORTING CONFIGURATION
|
||||
# Enabling "REPORT" will configure a socket-based reporting
|
||||
# system that will send the configuration and other items
|
||||
# to a another process (local or remote) that may process
|
||||
# the information for some useful purpose, like a web dashboard.
|
||||
#
|
||||
# REPORT - True to enable, False to disable
|
||||
# REPORT_INTERVAL - Seconds between reports
|
||||
# REPORT_PORT - TCP port to listen on if "REPORT_NETWORKS" = NETWORK
|
||||
# REPORT_CLIENTS - comma separated list of IPs you will allow clients
|
||||
# to connect on. Entering a * will allow all.
|
||||
#
|
||||
# ****FOR NOW MUST BE TRUE - USE THE LOOPBACK IF YOU DON'T USE THIS!!!****
|
||||
[REPORTS]
|
||||
REPORT: True
|
||||
REPORT_INTERVAL: 60
|
||||
REPORT_PORT: 4323
|
||||
REPORT_CLIENTS: 127.0.0.1
|
||||
|
||||
|
||||
# SYSTEM LOGGER CONFIGURAITON
|
||||
# This allows the logger to be configured without chaning the individual
|
||||
# python logger stuff. LOG_FILE should be a complete path/filename for *your*
|
||||
# system -- use /dev/null for non-file handlers.
|
||||
# LOG_HANDLERS may be any of the following, please, no spaces in the
|
||||
# list if you use several:
|
||||
# null
|
||||
# console
|
||||
# console-timed
|
||||
# file
|
||||
# file-timed
|
||||
# syslog
|
||||
# LOG_LEVEL may be any of the standard syslog logging levels, though
|
||||
# as of now, DEBUG, INFO, WARNING and CRITICAL are the only ones
|
||||
# used.
|
||||
#
|
||||
[LOGGER]
|
||||
LOG_FILE: /tmp/gps_data.log
|
||||
LOG_HANDLERS: console-timed
|
||||
LOG_LEVEL: DEBUG
|
||||
LOG_NAME: HBlink3 GPS/Data
|
||||
|
||||
# DOWNLOAD AND IMPORT SUBSCRIBER, PEER and TGID ALIASES
|
||||
# Ok, not the TGID, there's no master list I know of to download
|
||||
# This is intended as a facility for other applcations built on top of
|
||||
# HBlink to use, and will NOT be used in HBlink directly.
|
||||
# STALE_DAYS is the number of days since the last download before we
|
||||
# download again. Don't be an ass and change this to less than a few days.
|
||||
[ALIASES]
|
||||
TRY_DOWNLOAD: True
|
||||
PATH: ./
|
||||
PEER_FILE: peer_ids.json
|
||||
SUBSCRIBER_FILE: subscriber_ids.json
|
||||
TGID_FILE: talkgroup_ids.json
|
||||
PEER_URL: https://www.radioid.net/static/rptrs.json
|
||||
SUBSCRIBER_URL: https://www.radioid.net/static/users.json
|
||||
STALE_DAYS: 1
|
||||
|
||||
# GPS/Data Application - by KF7EEL
|
||||
# Configure the settings for the DMR GPS to APRS position application here.
|
||||
#
|
||||
# DATA_DMR_ID - This is the DMR ID that users send DMR GPS data.
|
||||
# CALL_TYPE - gorup or unit. Group if you want users to send data to a talk group,
|
||||
# unit if you want users to send data as a private call.
|
||||
# USER_APRS_SSID - Default APRS SSID assigned to user APRS positions.
|
||||
# USER_APRS_COMMENT - Default Comment attached to user APRS positions.
|
||||
# APRS_LOGIN_CALL, PASSCODE, SERVER, and PORT - Login settings for APRS-IS.
|
||||
[GPS_DATA]
|
||||
DATA_DMR_ID: 9099
|
||||
CALL_TYPE: unit
|
||||
USER_APRS_SSID: 15
|
||||
USER_APRS_COMMENT: HBLink3 D-APRS -
|
||||
APRS_LOGIN_CALL: N0CALL
|
||||
APRS_LOGIN_PASSCODE: 12345
|
||||
APRS_SERVER: rotate.aprs2.net
|
||||
APRS_PORT: 14580
|
||||
# The following settings are only applicable if you are using the gps_data_beacon_igate script.
|
||||
# They do not affect the operation gps_data itself.
|
||||
# Time in minutes.
|
||||
IGATE_BEACON_TIME = 45
|
||||
IGATE_BEACON_COMMENT = HBLink3 D-APRS Gateway
|
||||
IGATE_BEACON_ICON = /I
|
||||
IGATE_LATITUDE = 0000.00N
|
||||
IGATE_LONGITUDE = 00000.00W
|
||||
|
||||
# OPENBRIDGE INSTANCES - DUPLICATE SECTION FOR MULTIPLE CONNECTIONS
|
||||
# OpenBridge is a protocol originall created by DMR+ for connection between an
|
||||
# IPSC2 server and Brandmeister. It has been implemented here at the suggestion
|
||||
# of the Brandmeister team as a way to legitimately connect HBlink to the
|
||||
# Brandemiester network.
|
||||
# It is recommended to name the system the ID of the Brandmeister server that
|
||||
# it connects to, but is not necessary. TARGET_IP and TARGET_PORT are of the
|
||||
# Brandmeister or IPSC2 server you are connecting to. PASSPHRASE is the password
|
||||
# that must be agreed upon between you and the operator of the server you are
|
||||
# connecting to. NETWORK_ID is a number in the format of a DMR Radio ID that
|
||||
# will be sent to the other server to identify this connection.
|
||||
# other parameters follow the other system types.
|
||||
#
|
||||
# ACLs:
|
||||
# OpenBridge does not 'register', so registration ACL is meaningless.
|
||||
# Proper OpenBridge passes all traffic on TS1.
|
||||
# HBlink can extend OPB to use both slots for unit calls only.
|
||||
# Setting "BOTH_SLOTS" True ONLY affects unit traffic!
|
||||
# Otherwise ACLs work as described in the global stanza
|
||||
|
||||
##[OBP-1]
|
||||
##MODE: OPENBRIDGE
|
||||
##ENABLED: True
|
||||
##IP:
|
||||
##PORT: 62035
|
||||
##NETWORK_ID: 3129100
|
||||
##PASSPHRASE: password
|
||||
##TARGET_IP: 1.2.3.4
|
||||
##TARGET_PORT: 62035
|
||||
##BOTH_SLOTS: True
|
||||
##USE_ACL: True
|
||||
##SUB_ACL: DENY:1
|
||||
##TGID_ACL: PERMIT:ALL
|
||||
|
||||
# MASTER INSTANCES - DUPLICATE SECTION FOR MULTIPLE MASTERS
|
||||
# HomeBrew Protocol Master instances go here.
|
||||
# IP may be left blank if there's one interface on your system.
|
||||
# Port should be the port you want this master to listen on. It must be unique
|
||||
# and unused by anything else.
|
||||
# Repeat - if True, the master repeats traffic to peers, False, it does nothing.
|
||||
#
|
||||
# MAX_PEERS -- maximun number of peers that may be connect to this master
|
||||
# at any given time. This is very handy if you're allowing hotspots to
|
||||
# connect, or using a limited computer like a Raspberry Pi.
|
||||
#
|
||||
# ACLs:
|
||||
# See comments in the GLOBAL stanza
|
||||
[MASTER-1]
|
||||
MODE: MASTER
|
||||
ENABLED: False
|
||||
REPEAT: True
|
||||
MAX_PEERS: 10
|
||||
EXPORT_AMBE: False
|
||||
IP:
|
||||
PORT: 54000
|
||||
PASSPHRASE: password
|
||||
GROUP_HANGTIME: 5
|
||||
USE_ACL: True
|
||||
REG_ACL: DENY:1
|
||||
SUB_ACL: DENY:1
|
||||
TGID_TS1_ACL: PERMIT:ALL
|
||||
TGID_TS2_ACL: PERMIT:ALL
|
||||
|
||||
# PEER INSTANCES - DUPLICATE SECTION FOR MULTIPLE PEERS
|
||||
# There are a LOT of errors in the HB Protocol specifications on this one!
|
||||
# MOST of these items are just strings and will be properly dealt with by the program
|
||||
# The TX & RX Frequencies are 9-digit numbers, and are the frequency in Hz.
|
||||
# Latitude is an 8-digit unsigned floating point number.
|
||||
# Longitude is a 9-digit signed floating point number.
|
||||
# Height is in meters
|
||||
# Setting Loose to True relaxes the validation on packets received from the master.
|
||||
# This will allow HBlink to connect to a non-compliant system such as XLXD, DMR+ etc.
|
||||
#
|
||||
# ACLs:
|
||||
# See comments in the GLOBAL stanza
|
||||
|
||||
[D-APRS]
|
||||
MODE: PEER
|
||||
ENABLED: True
|
||||
LOOSE: True
|
||||
EXPORT_AMBE: False
|
||||
IP:
|
||||
PORT: 54071
|
||||
MASTER_IP: localhost
|
||||
MASTER_PORT: 54070
|
||||
PASSPHRASE: passw0rd
|
||||
CALLSIGN: D-APRS
|
||||
RADIO_ID: 9099
|
||||
RX_FREQ: 000000000
|
||||
TX_FREQ: 000000000
|
||||
TX_POWER: 0
|
||||
COLORCODE: 1
|
||||
SLOTS: 1
|
||||
LATITUDE: 00.0000
|
||||
LONGITUDE: 000.0000
|
||||
HEIGHT: 0
|
||||
LOCATION: This Server
|
||||
DESCRIPTION: GPS to APRS
|
||||
URL: www.github.com/kf7eel/hblink3
|
||||
SOFTWARE_ID: 20170620
|
||||
PACKAGE_ID: MMDVM_HBlink
|
||||
GROUP_HANGTIME: 5
|
||||
OPTIONS:
|
||||
USE_ACL: True
|
||||
SUB_ACL: DENY:1
|
||||
TGID_TS1_ACL: PERMIT:ALL
|
||||
TGID_TS2_ACL: PERMIT:ALL
|
||||
|
287
gps_data.cfg
Normal file
287
gps_data.cfg
Normal file
|
@ -0,0 +1,287 @@
|
|||
# PROGRAM-WIDE PARAMETERS GO HERE
|
||||
# PATH - working path for files, leave it alone unless you NEED to change it
|
||||
# PING_TIME - the interval that peers will ping the master, and re-try registraion
|
||||
# - how often the Master maintenance loop runs
|
||||
# MAX_MISSED - how many pings are missed before we give up and re-register
|
||||
# - number of times the master maintenance loop runs before de-registering a peer
|
||||
#
|
||||
# ACLs:
|
||||
#
|
||||
# Access Control Lists are a very powerful tool for administering your system.
|
||||
# But they consume packet processing time. Disable them if you are not using them.
|
||||
# But be aware that, as of now, the configuration stanzas still need the ACL
|
||||
# sections configured even if you're not using them.
|
||||
#
|
||||
# REGISTRATION ACLS ARE ALWAYS USED, ONLY SUBSCRIBER AND TGID MAY BE DISABLED!!!
|
||||
#
|
||||
# The 'action' May be PERMIT|DENY
|
||||
# Each entry may be a single radio id, or a hypenated range (e.g. 1-2999)
|
||||
# Format:
|
||||
# ACL = 'action:id|start-end|,id|start-end,....'
|
||||
# --for example--
|
||||
# SUB_ACL: DENY:1,1000-2000,4500-60000,17
|
||||
#
|
||||
# ACL Types:
|
||||
# REG_ACL: peer radio IDs for registration (only used on HBP master systems)
|
||||
# SUB_ACL: subscriber IDs for end-users
|
||||
# TGID_TS1_ACL: destination talkgroup IDs on Timeslot 1
|
||||
# TGID_TS2_ACL: destination talkgroup IDs on Timeslot 2
|
||||
#
|
||||
# ACLs may be repeated for individual systems if needed for granularity
|
||||
# Global ACLs will be processed BEFORE the system level ACLs
|
||||
# Packets will be matched against all ACLs, GLOBAL first. If a packet 'passes'
|
||||
# All elements, processing continues. Packets are discarded at the first
|
||||
# negative match, or 'reject' from an ACL element.
|
||||
#
|
||||
# If you do not wish to use ACLs, set them to 'PERMIT:ALL'
|
||||
# TGID_TS1_ACL in the global stanza is used for OPENBRIDGE systems, since all
|
||||
# traffic is passed as TS 1 between OpenBridges
|
||||
[GLOBAL]
|
||||
PATH: ./
|
||||
PING_TIME: 5
|
||||
MAX_MISSED: 3
|
||||
USE_ACL: True
|
||||
REG_ACL: PERMIT:ALL
|
||||
SUB_ACL: DENY:1
|
||||
TGID_TS1_ACL: PERMIT:ALL
|
||||
TGID_TS2_ACL: PERMIT:ALL
|
||||
|
||||
|
||||
# NOT YET WORKING: NETWORK REPORTING CONFIGURATION
|
||||
# Enabling "REPORT" will configure a socket-based reporting
|
||||
# system that will send the configuration and other items
|
||||
# to a another process (local or remote) that may process
|
||||
# the information for some useful purpose, like a web dashboard.
|
||||
#
|
||||
# REPORT - True to enable, False to disable
|
||||
# REPORT_INTERVAL - Seconds between reports
|
||||
# REPORT_PORT - TCP port to listen on if "REPORT_NETWORKS" = NETWORK
|
||||
# REPORT_CLIENTS - comma separated list of IPs you will allow clients
|
||||
# to connect on. Entering a * will allow all.
|
||||
#
|
||||
# ****FOR NOW MUST BE TRUE - USE THE LOOPBACK IF YOU DON'T USE THIS!!!****
|
||||
[REPORTS]
|
||||
REPORT: True
|
||||
REPORT_INTERVAL: 60
|
||||
REPORT_PORT: 4323
|
||||
REPORT_CLIENTS: 127.0.0.1
|
||||
|
||||
|
||||
# SYSTEM LOGGER CONFIGURAITON
|
||||
# This allows the logger to be configured without chaning the individual
|
||||
# python logger stuff. LOG_FILE should be a complete path/filename for *your*
|
||||
# system -- use /dev/null for non-file handlers.
|
||||
# LOG_HANDLERS may be any of the following, please, no spaces in the
|
||||
# list if you use several:
|
||||
# null
|
||||
# console
|
||||
# console-timed
|
||||
# file
|
||||
# file-timed
|
||||
# syslog
|
||||
# LOG_LEVEL may be any of the standard syslog logging levels, though
|
||||
# as of now, DEBUG, INFO, WARNING and CRITICAL are the only ones
|
||||
# used.
|
||||
#
|
||||
[LOGGER]
|
||||
LOG_FILE: /tmp/gps_data.log
|
||||
LOG_HANDLERS: console-timed
|
||||
#LOG_LEVEL: DEBUG
|
||||
LOG_LEVEL: INFO
|
||||
LOG_NAME: GPS/Data
|
||||
|
||||
# DOWNLOAD AND IMPORT SUBSCRIBER, PEER and TGID ALIASES
|
||||
# Ok, not the TGID, there's no master list I know of to download
|
||||
# This is intended as a facility for other applcations built on top of
|
||||
# HBlink to use, and will NOT be used in HBlink directly.
|
||||
# STALE_DAYS is the number of days since the last download before we
|
||||
# download again. Don't be an ass and change this to less than a few days.
|
||||
[ALIASES]
|
||||
TRY_DOWNLOAD: True
|
||||
PATH: ./
|
||||
PEER_FILE: peer_ids.json
|
||||
SUBSCRIBER_FILE: subscriber_ids_aprs.json
|
||||
TGID_FILE: talkgroup_ids.json
|
||||
PEER_URL:
|
||||
SUBSCRIBER_URL:
|
||||
STALE_DAYS: 365
|
||||
|
||||
# GPS/Data Application - by KF7EEL
|
||||
# Configure the settings for the DMR GPS to APRS position application here.
|
||||
#
|
||||
# DATA_DMR_ID - This is the DMR ID that users send DMR GPS data.
|
||||
# CALL_TYPE - gorup or unit. Group if you want users to send data to a talk group,
|
||||
# unit if you want users to send data as a private call.
|
||||
# USER_APRS_SSID - Default APRS SSID assigned to user APRS positions.
|
||||
# USER_APRS_COMMENT - Default Comment attached to user APRS positions.
|
||||
# APRS_LOGIN_CALL, PASSCODE, SERVER, and PORT - Login settings for APRS-IS.
|
||||
[GPS_DATA]
|
||||
DATA_DMR_ID: 9099
|
||||
CALL_TYPE: unit
|
||||
USER_APRS_SSID: 15
|
||||
USER_APRS_COMMENT: D-APRS -
|
||||
APRS_LOGIN_CALL: FRA1OD
|
||||
APRS_LOGIN_PASSCODE: 15301
|
||||
APRS_SERVER: 192.168.0.129
|
||||
APRS_PORT: 14580
|
||||
# The following settings are only applicable if you are using the gps_data_beacon_igate script.
|
||||
# They do not affect the operation gps_data itself.
|
||||
# Time in minutes.
|
||||
IGATE_BEACON_TIME = 45
|
||||
IGATE_BEACON_COMMENT = D-APRS Gateway
|
||||
IGATE_BEACON_ICON = /I
|
||||
IGATE_LATITUDE = 0047.53N
|
||||
IGATE_LONGITUDE = 00007.11E
|
||||
|
||||
# OPENBRIDGE INSTANCES - DUPLICATE SECTION FOR MULTIPLE CONNECTIONS
|
||||
# OpenBridge is a protocol originall created by DMR+ for connection between an
|
||||
# IPSC2 server and Brandmeister. It has been implemented here at the suggestion
|
||||
# of the Brandmeister team as a way to legitimately connect HBlink to the
|
||||
# Brandemiester network.
|
||||
# It is recommended to name the system the ID of the Brandmeister server that
|
||||
# it connects to, but is not necessary. TARGET_IP and TARGET_PORT are of the
|
||||
# Brandmeister or IPSC2 server you are connecting to. PASSPHRASE is the password
|
||||
# that must be agreed upon between you and the operator of the server you are
|
||||
# connecting to. NETWORK_ID is a number in the format of a DMR Radio ID that
|
||||
# will be sent to the other server to identify this connection.
|
||||
# other parameters follow the other system types.
|
||||
#
|
||||
# ACLs:
|
||||
# OpenBridge does not 'register', so registration ACL is meaningless.
|
||||
# Proper OpenBridge passes all traffic on TS1.
|
||||
# HBlink can extend OPB to use both slots for unit calls only.
|
||||
# Setting "BOTH_SLOTS" True ONLY affects unit traffic!
|
||||
# Otherwise ACLs work as described in the global stanza
|
||||
|
||||
##[OBP-1]
|
||||
##MODE: OPENBRIDGE
|
||||
##ENABLED: True
|
||||
##IP:
|
||||
##PORT: 62035
|
||||
##NETWORK_ID: 3129100
|
||||
##PASSPHRASE: password
|
||||
##TARGET_IP: 1.2.3.4
|
||||
##TARGET_PORT: 62035
|
||||
##BOTH_SLOTS: True
|
||||
##USE_ACL: True
|
||||
##SUB_ACL: DENY:1
|
||||
##TGID_ACL: PERMIT:ALL
|
||||
|
||||
# MASTER INSTANCES - DUPLICATE SECTION FOR MULTIPLE MASTERS
|
||||
# HomeBrew Protocol Master instances go here.
|
||||
# IP may be left blank if there's one interface on your system.
|
||||
# Port should be the port you want this master to listen on. It must be unique
|
||||
# and unused by anything else.
|
||||
# Repeat - if True, the master repeats traffic to peers, False, it does nothing.
|
||||
#
|
||||
# MAX_PEERS -- maximun number of peers that may be connect to this master
|
||||
# at any given time. This is very handy if you're allowing hotspots to
|
||||
# connect, or using a limited computer like a Raspberry Pi.
|
||||
#
|
||||
# ACLs:
|
||||
# See comments in the GLOBAL stanza
|
||||
[MASTER-1]
|
||||
MODE: MASTER
|
||||
ENABLED: False
|
||||
REPEAT: True
|
||||
MAX_PEERS: 10
|
||||
EXPORT_AMBE: False
|
||||
IP:
|
||||
PORT: 54000
|
||||
PASSPHRASE: password
|
||||
GROUP_HANGTIME: 5
|
||||
USE_ACL: True
|
||||
REG_ACL: DENY:1
|
||||
SUB_ACL: DENY:1
|
||||
TGID_TS1_ACL: PERMIT:ALL
|
||||
TGID_TS2_ACL: PERMIT:ALL
|
||||
|
||||
# PEER INSTANCES - DUPLICATE SECTION FOR MULTIPLE PEERS
|
||||
# There are a LOT of errors in the HB Protocol specifications on this one!
|
||||
# MOST of these items are just strings and will be properly dealt with by the program
|
||||
# The TX & RX Frequencies are 9-digit numbers, and are the frequency in Hz.
|
||||
# Latitude is an 8-digit unsigned floating point number.
|
||||
# Longitude is a 9-digit signed floating point number.
|
||||
# Height is in meters
|
||||
# Setting Loose to True relaxes the validation on packets received from the master.
|
||||
# This will allow HBlink to connect to a non-compliant system such as XLXD, DMR+ etc.
|
||||
#
|
||||
# ACLs:
|
||||
# See comments in the GLOBAL stanza
|
||||
|
||||
[D-APRS]
|
||||
MODE: PEER
|
||||
ENABLED: True
|
||||
LOOSE: True
|
||||
EXPORT_AMBE: False
|
||||
IP:
|
||||
PORT: 54071
|
||||
MASTER_IP: localhost
|
||||
MASTER_PORT: 54070
|
||||
PASSPHRASE: passw0rd
|
||||
CALLSIGN: D-APRS
|
||||
RADIO_ID: 9099
|
||||
RX_FREQ: 000000000
|
||||
TX_FREQ: 000000000
|
||||
TX_POWER: 0
|
||||
COLORCODE: 1
|
||||
SLOTS: 1
|
||||
LATITUDE: 43.5300
|
||||
LONGITUDE: 007.1100
|
||||
HEIGHT: 0
|
||||
LOCATION: FRANCE
|
||||
DESCRIPTION: GPS to APRS
|
||||
URL: www.openpmr.c.la
|
||||
SOFTWARE_ID: 20210116
|
||||
PACKAGE_ID: MMDVM_HBlink
|
||||
GROUP_HANGTIME: 5
|
||||
OPTIONS:
|
||||
USE_ACL: True
|
||||
SUB_ACL: DENY:1
|
||||
TGID_TS1_ACL: PERMIT:ALL
|
||||
TGID_TS2_ACL: PERMIT:ALL
|
||||
DEFAULT_UA_TIMER: 10
|
||||
SINGLE_MODE: True
|
||||
VOICE_IDENT: True
|
||||
TS1_STATIC:
|
||||
TS2_STATIC:
|
||||
DEFAULT_REFLECTOR: 0
|
||||
|
||||
[D-APRS-NODEDMR]
|
||||
MODE: PEER
|
||||
ENABLED: True
|
||||
LOOSE: True
|
||||
EXPORT_AMBE: False
|
||||
IP:
|
||||
PORT: 54074
|
||||
MASTER_IP: 192.168.0.1
|
||||
MASTER_PORT: 62030
|
||||
PASSPHRASE: password
|
||||
CALLSIGN: D-APRS
|
||||
RADIO_ID: 9099
|
||||
RX_FREQ: 000000000
|
||||
TX_FREQ: 000000000
|
||||
TX_POWER: 0
|
||||
COLORCODE: 1
|
||||
SLOTS: 1
|
||||
LATITUDE: 43.5300
|
||||
LONGITUDE: 007.1100
|
||||
HEIGHT: 0
|
||||
LOCATION: FRANCE
|
||||
DESCRIPTION: GPS to APRS
|
||||
URL: www.openpmr.c.la
|
||||
SOFTWARE_ID: 20210116
|
||||
PACKAGE_ID: MMDVM_HBlink
|
||||
GROUP_HANGTIME: 5
|
||||
OPTIONS:
|
||||
USE_ACL: True
|
||||
SUB_ACL: DENY:1
|
||||
TGID_TS1_ACL: PERMIT:9099
|
||||
TGID_TS2_ACL: PERMIT:9099
|
||||
DEFAULT_UA_TIMER: 10
|
||||
SINGLE_MODE: True
|
||||
VOICE_IDENT: True
|
||||
TS1_STATIC:
|
||||
TS2_STATIC:
|
||||
DEFAULT_REFLECTOR: 0
|
||||
|
804
gps_data.py
Normal file
804
gps_data.py
Normal file
|
@ -0,0 +1,804 @@
|
|||
#!/usr/bin/env python3
|
||||
#
|
||||
###############################################################################
|
||||
# HBLink - Copyright (C) 2020 Cortney T. Buffington, N0MJS <n0mjs@me.com>
|
||||
# GPS/Data - Copyright (C) 2020 Eric Craw, KF7EEL <kf7eel@qsl.net>
|
||||
# Modification Copyright (C) 2021 Xavier FRS2013
|
||||
#
|
||||
# This program is free software; you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
# the Free Software Foundation; either version 3 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program; if not, write to the Free Software Foundation,
|
||||
# Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
###############################################################################
|
||||
|
||||
'''
|
||||
This is a GPS and Data application. It decodes and reassambles DMR GPS packets and
|
||||
uploads them th APRS-IS.
|
||||
'''
|
||||
|
||||
# Python modules we need
|
||||
import sys
|
||||
from bitarray import bitarray
|
||||
from time import time
|
||||
from importlib import import_module
|
||||
from types import ModuleType
|
||||
|
||||
# Twisted is pretty important, so I keep it separate
|
||||
from twisted.internet.protocol import Factory, Protocol
|
||||
from twisted.protocols.basic import NetstringReceiver
|
||||
from twisted.internet import reactor, task
|
||||
|
||||
# Things we import from the main hblink module
|
||||
from hblink import HBSYSTEM, OPENBRIDGE, systems, hblink_handler, reportFactory, REPORT_OPCODES, config_reports, mk_aliases, acl_check
|
||||
from dmr_utils3.utils import bytes_3, int_id, get_alias
|
||||
from dmr_utils3 import decode, bptc, const
|
||||
import config
|
||||
import log
|
||||
import const
|
||||
|
||||
# The module needs logging logging, but handlers, etc. are controlled by the parent
|
||||
import logging
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
# Other modules we need for data and GPS
|
||||
from bitarray import bitarray
|
||||
from binascii import b2a_hex as ahex
|
||||
import re
|
||||
##from binascii import a2b_hex as bhex
|
||||
import aprslib
|
||||
import datetime
|
||||
from bitarray.util import ba2int as ba2num
|
||||
from bitarray.util import ba2hex as ba2hx
|
||||
import codecs
|
||||
import time
|
||||
#Needed for working with NMEA
|
||||
import pynmea2
|
||||
|
||||
# Modules for executing commands/scripts
|
||||
import os
|
||||
from gps_functions import cmd_list
|
||||
|
||||
# Module for maidenhead grids
|
||||
import maidenhead as mh
|
||||
|
||||
#Modules for APRS settings
|
||||
import ast
|
||||
from pathlib import Path
|
||||
|
||||
#For Mail
|
||||
import smtplib, ssl
|
||||
|
||||
|
||||
# Does anybody read this stuff? There's a PEP somewhere that says I should do this.
|
||||
__author__ = 'Cortney T. Buffington, N0MJS; Eric Craw, KF7EEL'
|
||||
__copyright__ = 'Copyright (c) 2020 Cortney T. Buffington'
|
||||
__credits__ = 'Colin Durbridge, G4EML, Steve Zingman, N4IRS; Mike Zingman, N4IRR; Jonathan Naylor, G4KLX; Hans Barthen, DL5DI; Torsten Shultze, DG1HT'
|
||||
__license__ = 'GNU GPLv3'
|
||||
__maintainer__ = 'Eric Craw, KF7EEL'
|
||||
__email__ = 'kf7eel@qsl.net'
|
||||
__status__ = 'pre-alpha'
|
||||
|
||||
# Known to work with: AT-D878
|
||||
|
||||
# Must have the following at line 1054 in bridge.py to forward group vcsbk, also there is a typo there:
|
||||
# self.group_received(_peer_id, _rf_src, _dst_id, _seq, _slot, _frame_type, _dtype_vseq, _stream_id, _data)
|
||||
|
||||
##################################################################################################
|
||||
|
||||
# Headers for GPS by model of radio:
|
||||
# AT-D878 - Compressed UDP
|
||||
# MD-380 - Unified Data Transport
|
||||
hdr_type = ''
|
||||
btf = ''
|
||||
ssid = ''
|
||||
|
||||
# From dmr_utils3, modified to decode entire packet. Works for 1/2 rate coded data.
|
||||
def decode_full(_data):
|
||||
binlc = bitarray(endian='big')
|
||||
binlc.extend([_data[136],_data[121],_data[106],_data[91], _data[76], _data[61], _data[46], _data[31]])
|
||||
binlc.extend([_data[152],_data[137],_data[122],_data[107],_data[92], _data[77], _data[62], _data[47], _data[32], _data[17], _data[2] ])
|
||||
binlc.extend([_data[123],_data[108],_data[93], _data[78], _data[63], _data[48], _data[33], _data[18], _data[3], _data[184],_data[169]])
|
||||
binlc.extend([_data[94], _data[79], _data[64], _data[49], _data[34], _data[19], _data[4], _data[185],_data[170],_data[155],_data[140]])
|
||||
binlc.extend([_data[65], _data[50], _data[35], _data[20], _data[5], _data[186],_data[171],_data[156],_data[141],_data[126],_data[111]])
|
||||
binlc.extend([_data[36], _data[21], _data[6], _data[187],_data[172],_data[157],_data[142],_data[127],_data[112],_data[97], _data[82] ])
|
||||
binlc.extend([_data[7], _data[188],_data[173],_data[158],_data[143],_data[128],_data[113],_data[98], _data[83]])
|
||||
#This is the rest of the Full LC data -- the RS1293 FEC that we don't need
|
||||
# This is extremely important for SMS and GPS though.
|
||||
binlc.extend([_data[68],_data[53],_data[174],_data[159],_data[144],_data[129],_data[114],_data[99],_data[84],_data[69],_data[54],_data[39]])
|
||||
binlc.extend([_data[24],_data[145],_data[130],_data[115],_data[100],_data[85],_data[70],_data[55],_data[40],_data[25],_data[10],_data[191]])
|
||||
return binlc
|
||||
|
||||
|
||||
n_packet_assembly = 0
|
||||
|
||||
packet_assembly = ''
|
||||
|
||||
final_packet = ''
|
||||
|
||||
#Convert DMR packet to binary from MMDVM packet and remove Slot Type and EMB Sync stuff to allow for BPTC 196,96 decoding
|
||||
def bptc_decode(_data):
|
||||
binary_packet = bitarray(decode.to_bits(_data[20:]))
|
||||
del binary_packet[98:166]
|
||||
return decode_full(binary_packet)
|
||||
# Placeholder for future header id
|
||||
def header_ID(_data):
|
||||
hex_hdr = str(ahex(bptc_decode(_data)))
|
||||
return hex_hdr[2:6]
|
||||
# Work in progress, used to determine data format
|
||||
## pass
|
||||
|
||||
def aprs_send(packet):
|
||||
AIS = aprslib.IS(aprs_callsign, passwd=aprs_passcode,host=aprs_server, port=aprs_port)
|
||||
AIS.connect()
|
||||
AIS.sendall(packet)
|
||||
AIS.close()
|
||||
|
||||
def dashboard_loc_write(call, lat, lon, time):
|
||||
#try:
|
||||
dash_entries = ast.literal_eval(os.popen('cat /tmp/gps_data_user_loc.txt').read())
|
||||
# except:
|
||||
# dash_entries = []
|
||||
dash_entries.insert(0, {'call': call, 'lat': lat, 'lon': lon, 'time':time})
|
||||
with open("/tmp/gps_data_user_loc.txt", 'w') as user_loc_file:
|
||||
user_loc_file.write(str(dash_entries[:20]))
|
||||
user_loc_file.close()
|
||||
logger.info('User location saved for dashboard')
|
||||
#logger.info(dash_entries)
|
||||
|
||||
def dashboard_bb_write(call, dmr_id, time, bulletin):
|
||||
#try:
|
||||
dash_bb = ast.literal_eval(os.popen('cat /tmp/gps_data_user_bb.txt').read())
|
||||
# except:
|
||||
# dash_entries = []
|
||||
dash_bb.insert(0, {'call': call, 'dmr_id': dmr_id, 'time': time, 'bulliten':bulletin})
|
||||
with open("/tmp/gps_data_user_bb.txt", 'w') as user_bb_file:
|
||||
user_bb_file.write(str(dash_bb[:20]))
|
||||
user_bb_file.close()
|
||||
logger.info('User bulletin entry saved.')
|
||||
#logger.info(dash_bb)
|
||||
|
||||
|
||||
# Thanks for this forum post for this - https://stackoverflow.com/questions/2579535/convert-dd-decimal-degrees-to-dms-degrees-minutes-seconds-in-python
|
||||
|
||||
def decdeg2dms(dd):
|
||||
is_positive = dd >= 0
|
||||
dd = abs(dd)
|
||||
minutes,seconds = divmod(dd*3600,60)
|
||||
degrees,minutes = divmod(minutes,60)
|
||||
degrees = degrees if is_positive else -degrees
|
||||
return (degrees,minutes,seconds)
|
||||
|
||||
def user_setting_write(dmr_id, setting, value):
|
||||
# Open file and load as dict for modification
|
||||
with open("./user_settings.txt", 'r') as f:
|
||||
user_dict = ast.literal_eval(f.read())
|
||||
logger.info('Current settings: ' + str(user_dict))
|
||||
if dmr_id not in user_dict:
|
||||
user_dict[dmr_id] = [{'call': str(get_alias((dmr_id), subscriber_ids))}, {'ssid': ''}, {'icon': ''}, {'comment': ''},{'mail': ''}]
|
||||
|
||||
if setting.upper() == 'ICON':
|
||||
user_dict[dmr_id][2]['icon'] = value
|
||||
if setting.upper() == 'SSID':
|
||||
user_dict[dmr_id][1]['ssid'] = value
|
||||
if setting.upper() == 'COM':
|
||||
user_comment = user_dict[dmr_id][3]['comment'] = value[0:35]
|
||||
if setting.upper() == 'MAIL':
|
||||
user_dict[dmr_id][4]['mail'] = value
|
||||
|
||||
f.close()
|
||||
logger.info('Loaded user settings. Preparing to write...')
|
||||
# Write modified dict to file
|
||||
with open("./user_settings.txt", 'w') as user_dict_file:
|
||||
user_dict_file.write(str(user_dict))
|
||||
user_dict_file.close()
|
||||
logger.info('User setting saved')
|
||||
f.close()
|
||||
packet_assembly = ''
|
||||
|
||||
##def retrieve_aprs_settings(_rf_src):
|
||||
## user_settings = ast.literal_eval(os.popen('cat ./user_settings.txt').read())
|
||||
## if int_id(_rf_src) not in user_settings:
|
||||
## aprs_loc_packet = str(get_alias(int_id(_rf_src), subscriber_ids)) + '-' + str(user_ssid) + '>APRS,TCPIP*:/' + str(datetime.datetime.utcnow().strftime("%H%M%Sh")) + str(loc.lat[0:7]) + str(loc.lat_dir) + '/' + str(loc.lon[0:8]) + str(loc.lon_dir) + '[' + str(round(loc.true_course)).zfill(3) + '/' + str(round(loc.spd_over_grnd)).zfill(3) + '/' + aprs_comment + ' DMR ID: ' + str(int_id(_rf_src))
|
||||
## else:
|
||||
## if user_settings[int_id(_rf_src)][1]['ssid'] == '':
|
||||
## ssid = user_ssid
|
||||
## if user_settings[int_id(_rf_src)][3]['comment'] == '':
|
||||
## comment = aprs_comment + ' DMR ID: ' + str(int_id(_rf_src))
|
||||
## if user_settings[int_id(_rf_src)][2]['icon'] == '':
|
||||
## icon_table = '/'
|
||||
## icon_icon = '['
|
||||
## if user_settings[int_id(_rf_src)][2]['icon'] != '':
|
||||
## icon_table = user_settings[int_id(_rf_src)][2]['icon'][0]
|
||||
## icon_icon = user_settings[int_id(_rf_src)][2]['icon'][1]
|
||||
## if user_settings[int_id(_rf_src)][1]['ssid'] != '':
|
||||
## ssid = user_settings[int_id(_rf_src)][1]['ssid']
|
||||
## if user_settings[int_id(_rf_src)][3]['comment'] != '':
|
||||
## comment = user_settings[int_id(_rf_src)][3]['comment']
|
||||
## return ssid, icon, comment
|
||||
##
|
||||
|
||||
|
||||
#Process Mail
|
||||
|
||||
def process_mail(mailtoto,msg_mail):
|
||||
logger.debug('Mailto:'+str(mailtoto))
|
||||
logger.debug('MsgMail:'+str(msg_mail))
|
||||
try:
|
||||
os.system('echo "' + msg_mail + '" > fichier.txt')
|
||||
os.system('mutt -s "FRA1OD DMRD:" ' + str(mailtoto) +' < fichier.txt')
|
||||
os.system('rm fichier.txt')
|
||||
return (str(' Mail send'))
|
||||
except:
|
||||
return (str(' Mail not send'))
|
||||
|
||||
# Process SMS, do something bases on message
|
||||
|
||||
def process_sms(_rf_src, sms):
|
||||
if sms == 'ID':
|
||||
logger.info(str(get_alias(int_id(from_id), subscriber_ids)) + ' - ' + str(int_id(from_id)))
|
||||
elif sms == 'TEST':
|
||||
logger.info('It works!')
|
||||
dashboard_bb_write(get_alias(int_id(_rf_src), subscriber_ids), int_id(_rf_src), time.strftime('%H:%M:%S - %d/%m/%y'),sms)
|
||||
elif sms == 'MAILTEST':
|
||||
logger.info('Mail It works!')
|
||||
user_settings = ast.literal_eval(os.popen('cat ./user_settings.txt').read())
|
||||
if int_id(_rf_src) in user_settings:
|
||||
if user_settings[int_id(_rf_src)][4]['mail'] != '':
|
||||
mailto = user_settings[int_id(_rf_src)][4]['mail']
|
||||
#Send mail at int_id(_rf_src) alias get_alias(int_id(_rf_src)
|
||||
error_mail = process_mail(mailto,'Vous recevez ce message du Réseau FRA1OD\nTest MAIL')
|
||||
dashboard_bb_write(get_alias(int_id(_rf_src), subscriber_ids), int_id(_rf_src), time.strftime('%H:%M:%S - %d/%m/%y'),sms + str(error_mail))
|
||||
else:
|
||||
dashboard_bb_write(get_alias(int_id(_rf_src), subscriber_ids), int_id(_rf_src), time.strftime('%H:%M:%S - %d/%m/%y'),sms + "not active")
|
||||
|
||||
elif 'SOS' in sms:
|
||||
|
||||
with open("./user_settings.txt", 'r') as f:
|
||||
user_dict = ast.literal_eval(f.read())
|
||||
|
||||
for x in user_dict:
|
||||
logger.info('ID: ' + str(x))
|
||||
logger.info('Mail to: ' + str(user_dict[x][4]['mail']))
|
||||
mailto = user_dict[x][4]['mail']
|
||||
if (user_dict[x][4]['mail'] !=''):
|
||||
error_mail = process_mail(mailto,'Vous recevez ce message du Réseau FRA1OD\nSOS:')
|
||||
logger.info('BB Mail : ' + str(error_mail))
|
||||
f.close()
|
||||
dashboard_bb_write(get_alias(int_id(_rf_src), subscriber_ids), int_id(_rf_src), time.strftime('%H:%M:%S - %d/%m/%y'), re.sub('@BB|@BB ','BB:',sms))
|
||||
|
||||
|
||||
|
||||
elif '@ICON' in sms:
|
||||
user_setting_write(int_id(_rf_src), re.sub(' .*|@','',sms), '/' + re.sub('@ICON| ','',sms))
|
||||
elif '@SSID' in sms:
|
||||
user_setting_write(int_id(_rf_src), re.sub(' .*|@','',sms), re.sub('@SSID| ','',sms))
|
||||
elif '@COM' in sms:
|
||||
user_setting_write(int_id(_rf_src), re.sub(' .*|@','',sms), re.sub('@COM |@COM','',sms))
|
||||
elif '@MAIL' in sms:
|
||||
user_setting_write(int_id(_rf_src), re.sub(' .*|@','',sms), re.sub('@MAIL |@MAIL','',sms))
|
||||
|
||||
elif '@BB' in sms:
|
||||
|
||||
with open("./user_settings.txt", 'r') as f:
|
||||
user_dict = ast.literal_eval(f.read())
|
||||
|
||||
for x in user_dict:
|
||||
logger.info('ID: ' + str(x))
|
||||
logger.info('Mail to: ' + str(user_dict[x][4]['mail']))
|
||||
mailto = user_dict[x][4]['mail']
|
||||
if (user_dict[x][4]['mail'] !=''):
|
||||
error_mail = process_mail(mailto,re.sub('@BB|@BB ','Vous recevez ce message du Réseau FRA1OD\nBB:',sms))
|
||||
logger.info('BB Mail : ' + str(error_mail))
|
||||
f.close()
|
||||
dashboard_bb_write(get_alias(int_id(_rf_src), subscriber_ids), int_id(_rf_src), time.strftime('%H:%M:%S - %d/%m/%y'), re.sub('@BB|@BB ','BB:',sms))
|
||||
|
||||
elif '@MH' in sms:
|
||||
grid_square = re.sub('@MH ', '', sms)
|
||||
grid_square = re.sub(' |\n|\r|\t', '', grid_square)
|
||||
dashboard_bb_write(get_alias(int_id(_rf_src), subscriber_ids), int_id(_rf_src), time.strftime('%H:%M:%S - %d/%m/%y'), re.sub('@BB|@BB ','','QTH Locator : ' + grid_square))
|
||||
#logger.info('grid_square='+str(grid_square))
|
||||
#logger.info('len grid_square='+str(len(grid_square)))
|
||||
if len(grid_square) == 6:
|
||||
|
||||
logger.info('Decode QTH Locator'+str(grid_square))
|
||||
positionmh = mh.to_location(grid_square)
|
||||
#logger.debug('positionmh='+ str(positionmh))
|
||||
#logger.debug('lat='+str(mh.to_location(grid_square)[0]))
|
||||
#logger.debug('lon='+str(mh.to_location(grid_square)[1]))
|
||||
lat = decdeg2dms(mh.to_location(grid_square)[0])
|
||||
lon = decdeg2dms(mh.to_location(grid_square)[1])
|
||||
|
||||
logger.debug('Latitude: ' + str(lat))
|
||||
logger.debug('Longitude: ' + str(lon))
|
||||
lon_dir = 'E'
|
||||
lat_dir = 'N'
|
||||
if lon[0] < 1:
|
||||
lon_dir = 'W'
|
||||
if lon[0] > 0:
|
||||
lon_dir = 'E'
|
||||
if lat[0] < 1:
|
||||
lat_dir = 'S'
|
||||
if lat[0] > 0:
|
||||
lat_dir = 'N'
|
||||
|
||||
aprs_lat = (lat[0] * 100) + lat[1]
|
||||
if (lat[2] > 1):
|
||||
aprs_lat = aprs_lat + (lat[2]/100)
|
||||
aprs_lat = str("%4.2f" % aprs_lat) + lat_dir
|
||||
logger.debug('Latitude: ' + str(aprs_lat))
|
||||
|
||||
aprs_lon = (lon[0] * 100) + lon[1]
|
||||
if (lon[2] > 1):
|
||||
aprs_lon = aprs_lon + (lon[2]/100)
|
||||
aprs_lon = str("%5.2f" % aprs_lon) + lon_dir
|
||||
if ((lon[0] > -1) and (lon[0] < 1)):
|
||||
aprs_lon = "0" + str(aprs_lon)
|
||||
if ((lon[0] * 100) < 1000):
|
||||
aprs_lon = "00" + str(aprs_lon)
|
||||
if ( (lon[0] * 100) > 1000):
|
||||
aprs_lon = "0" + str(aprs_lon)
|
||||
logger.debug('Longitude: ' + str(aprs_lon))
|
||||
|
||||
|
||||
logger.info(mh.to_location(grid_square))
|
||||
logger.info(str(lat) + ', ' + str(lon))
|
||||
logger.info('Latitude: ' + str(aprs_lat))
|
||||
logger.info('Longitude: ' + str(aprs_lon))
|
||||
user_settings = ast.literal_eval(os.popen('cat ./user_settings.txt').read())
|
||||
if int_id(_rf_src) not in user_settings:
|
||||
ssid = str(user_ssid)
|
||||
icon_table = '/'
|
||||
icon_icon = '['
|
||||
comment = aprs_comment + ' DMR ID: ' + str(int_id(_rf_src))
|
||||
else:
|
||||
if user_settings[int_id(_rf_src)][1]['ssid'] == '':
|
||||
ssid = user_ssid
|
||||
if user_settings[int_id(_rf_src)][3]['comment'] == '':
|
||||
comment = aprs_comment + ' DMR ID: ' + str(int_id(_rf_src))
|
||||
if user_settings[int_id(_rf_src)][2]['icon'] == '':
|
||||
icon_table = '/'
|
||||
icon_icon = '['
|
||||
if user_settings[int_id(_rf_src)][2]['icon'] != '':
|
||||
icon_table = user_settings[int_id(_rf_src)][2]['icon'][0]
|
||||
icon_icon = user_settings[int_id(_rf_src)][2]['icon'][1]
|
||||
if user_settings[int_id(_rf_src)][1]['ssid'] != '':
|
||||
ssid = user_settings[int_id(_rf_src)][1]['ssid']
|
||||
if user_settings[int_id(_rf_src)][3]['comment'] != '':
|
||||
comment = user_settings[int_id(_rf_src)][3]['comment']
|
||||
try:
|
||||
aprs_loc_packet = str(get_alias(int_id(_rf_src), subscriber_ids)) + '-' + ssid + '>APHBL3,TCPIP*:/' + str(datetime.datetime.utcnow().strftime("%H%M%Sh")) + str(aprs_lat) + icon_table + str(aprs_lon) + icon_icon + '/' + str(comment)
|
||||
logger.info(aprs_loc_packet)
|
||||
aprslib.parse(aprs_loc_packet)
|
||||
aprs_send(aprs_loc_packet)
|
||||
dashboard_loc_write(str(get_alias(int_id(_rf_src), subscriber_ids)) + '-' + ssid, aprs_lat, aprs_lon, time.strftime('%H:%M:%S - %d/%m/%y'))
|
||||
logger.info('Sent manual position QTH locator to APRS')
|
||||
except:
|
||||
logger.info('Exception. Not uploaded')
|
||||
packet_assembly = ''
|
||||
else :
|
||||
pass
|
||||
elif 'A-' in sms and '@' in sms:
|
||||
#Example SMS text: @ARMDS A-This is a test.
|
||||
aprs_dest = re.sub('@| A-.*','',sms)
|
||||
aprs_msg = re.sub('@.* A-|','',sms)
|
||||
logger.info('APRS message to ' + aprs_dest.upper() + '. Message: ' + aprs_msg)
|
||||
user_settings = ast.literal_eval(os.popen('cat ./user_settings.txt').read())
|
||||
if int_id(_rf_src) in user_settings and user_settings[int_id(_rf_src)][1]['ssid'] != '':
|
||||
ssid = user_settings[int_id(_rf_src)][1]['ssid']
|
||||
else:
|
||||
ssid = user_ssid
|
||||
try:
|
||||
aprs_msg_pkt = str(get_alias(int_id(_rf_src), subscriber_ids)) + '-' + str(ssid) + '>APHBL3,TCPIP*::' + str(aprs_dest).ljust(9).upper() + ':' + aprs_msg[0:73]
|
||||
logger.info(aprs_msg_pkt)
|
||||
aprslib.parse(aprs_msg_pkt)
|
||||
aprs_send(aprs_msg_pkt)
|
||||
logger.info('Packet sent.')
|
||||
except:
|
||||
logger.info('Error uploading MSG packet.')
|
||||
dashboard_bb_write(get_alias(int_id(_rf_src), subscriber_ids), int_id(_rf_src), time.strftime('%H:%M:%S - %d/%m/%y'), re.sub('@|@A- ','','Send DAPRS for: ' + sms))
|
||||
'''
|
||||
try:
|
||||
if sms in cmd_list:
|
||||
logger.info('Executing command /script.')
|
||||
os.popen(cmd_list[sms]).read()
|
||||
packet_assembly = ''
|
||||
except:
|
||||
logger.info('Exception. Command possibly not in list, or other error.')
|
||||
packet_assembly = ''
|
||||
|
||||
else:
|
||||
pass
|
||||
'''
|
||||
################
|
||||
## CLASS DATA ##
|
||||
################
|
||||
|
||||
class DATA_SYSTEM(HBSYSTEM):
|
||||
def __init__(self, _name, _config, _report):
|
||||
HBSYSTEM.__init__(self, _name, _config, _report)
|
||||
def dmrd_received(self, _peer_id, _rf_src, _dst_id, _seq, _slot, _call_type, _frame_type, _dtype_vseq, _stream_id, _data):
|
||||
# Capture data headers
|
||||
#global n_packet_assembly, hdr_type, btf_6_stop
|
||||
global n_packet_assembly, hdr_type
|
||||
logger.info('Start:'+time.strftime('%H:%M:%S - %d/%m/%y'))
|
||||
#logger.debug('Special debug for developement:')
|
||||
#logger.debug('data_hex:'+ str(ahex(bptc_decode(_data))))
|
||||
#logger.debug('Type: '+str(hdr_type))
|
||||
#logger.debug('data8:12: '+str(ba2num(bptc_decode(_data)[8:12])))
|
||||
#logger.debug('dtype_vseq=: '+str(_dtype_vseq))
|
||||
#logger.debug('DST_ID=' + str(int_id(_dst_id)))
|
||||
#logger.debug('CALL_TYPE=' + str(_call_type))
|
||||
if type(_seq) is bytes:
|
||||
pckt_seq = int.from_bytes(_seq, 'big')
|
||||
else:
|
||||
pckt_seq = _seq
|
||||
#logger.debug('PCKT_SEQ=' + str(pckt_seq))
|
||||
|
||||
if int_id(_dst_id) == data_id:
|
||||
#logger.info(type(_seq))
|
||||
if type(_seq) is bytes:
|
||||
pckt_seq = int.from_bytes(_seq, 'big')
|
||||
else:
|
||||
pckt_seq = _seq
|
||||
# Try to classify header
|
||||
# UDT header has DPF of 0101, which is 5.
|
||||
# If 5 is at position 3, then this should be a UDT header for MD-380 type radios.
|
||||
# Coordinates are usually in the very next block after the header, we will discard the rest.
|
||||
#logger.info(ahex(bptc_decode(_data)[0:10]))
|
||||
if _call_type == call_type and header_ID(_data)[3] == '5' and ba2num(bptc_decode(_data)[69:72]) == 0 and ba2num(bptc_decode(_data)[8:12]) == 0 or (_call_type == 'vcsbk' and header_ID(_data)[3] == '5' and ba2num(bptc_decode(_data)[69:72]) == 0 and ba2num(bptc_decode(_data)[8:12]) == 0):
|
||||
global udt_block
|
||||
logger.info('MD-380 type UDT header detected. Very next packet should be location.')
|
||||
hdr_type = '380'
|
||||
if _dtype_vseq == 6 and hdr_type == '380' or _dtype_vseq == 'group' and hdr_type == '380':
|
||||
udt_block = 1
|
||||
if _dtype_vseq == 7 and hdr_type == '380':
|
||||
udt_block = udt_block - 1
|
||||
if udt_block == 0:
|
||||
logger.info('MD-380 type packet. This should contain the GPS location.')
|
||||
logger.info('Packet: ' + str(ahex(bptc_decode(_data))))
|
||||
if ba2num(bptc_decode(_data)[1:2]) == 1:
|
||||
lat_dir = 'N'
|
||||
if ba2num(bptc_decode(_data)[1:2]) == 0:
|
||||
lat_dir = 'S'
|
||||
if ba2num(bptc_decode(_data)[2:3]) == 1:
|
||||
lon_dir = 'E'
|
||||
if ba2num(bptc_decode(_data)[2:3]) == 0:
|
||||
lon_dir = 'W'
|
||||
lat_deg = ba2num(bptc_decode(_data)[11:18])
|
||||
lon_deg = ba2num(bptc_decode(_data)[38:46])
|
||||
lat_min = ba2num(bptc_decode(_data)[18:24])
|
||||
lon_min = ba2num(bptc_decode(_data)[46:52])
|
||||
lat_min_dec = ba2num(bptc_decode(_data)[24:38])
|
||||
lon_min_dec = ba2num(bptc_decode(_data)[52:66])
|
||||
aprs_lat = str(str(lat_deg) + str(lat_min) + '.' + str(lat_min_dec)[0:2]).zfill(7) + lat_dir
|
||||
aprs_lon = str(str(lon_deg) + str(lon_min) + '.' + str(lon_min_dec)[0:2]).zfill(8) + lon_dir
|
||||
# Form APRS packet
|
||||
# For future use below
|
||||
#aprs_loc_packet = str(get_alias(int_id(_rf_src), subscriber_ids)) + '-' + ssid + '>APHBL3,TCPIP*:/' + str(datetime.datetime.utcnow().strftime("%H%M%Sh")) + str(aprs_lat) + icon_table + str(aprs_lon) + icon_icon + '/' + str(comment)
|
||||
|
||||
#logger.info(aprs_loc_packet)
|
||||
logger.info('Lat: ' + str(aprs_lat) + ' Lon: ' + str(aprs_lon))
|
||||
user_settings = ast.literal_eval(os.popen('cat ./user_settings.txt').read())
|
||||
if int_id(_rf_src) not in user_settings:
|
||||
ssid = str(user_ssid)
|
||||
icon_table = '/'
|
||||
icon_icon = '['
|
||||
comment = aprs_comment + ' DMR ID: ' + str(int_id(_rf_src))
|
||||
user_setting_write(int_id(_rf_src), 'SSID', ssid)
|
||||
user_setting_write(int_id(_rf_src), 'ICON', icon_table + icon_icon)
|
||||
user_setting_write(int_id(_rf_src), 'COM', comment)
|
||||
|
||||
else:
|
||||
if user_settings[int_id(_rf_src)][1]['ssid'] == '':
|
||||
ssid = user_ssid
|
||||
if user_settings[int_id(_rf_src)][3]['comment'] == '':
|
||||
comment = aprs_comment + ' DMR ID: ' + str(int_id(_rf_src))
|
||||
if user_settings[int_id(_rf_src)][2]['icon'] == '':
|
||||
icon_table = '/'
|
||||
icon_icon = '['
|
||||
if user_settings[int_id(_rf_src)][2]['icon'] != '':
|
||||
icon_table = user_settings[int_id(_rf_src)][2]['icon'][0]
|
||||
icon_icon = user_settings[int_id(_rf_src)][2]['icon'][1]
|
||||
if user_settings[int_id(_rf_src)][1]['ssid'] != '':
|
||||
ssid = user_settings[int_id(_rf_src)][1]['ssid']
|
||||
if user_settings[int_id(_rf_src)][3]['comment'] != '':
|
||||
comment = user_settings[int_id(_rf_src)][3]['comment']
|
||||
# Attempt to prevent malformed packets from being uploaded.
|
||||
try:
|
||||
aprs_loc_packet = str(get_alias(int_id(_rf_src), subscriber_ids)) + '-' + ssid + '>APHBL3,TCPIP*:/' + str(datetime.datetime.utcnow().strftime("%H%M%Sh")) + str(aprs_lat) + icon_table + str(aprs_lon) + icon_icon + '/' + str(comment)
|
||||
aprslib.parse(aprs_loc_packet)
|
||||
float(lat_deg) < 91
|
||||
float(lon_deg) < 121
|
||||
aprs_send(aprs_loc_packet)
|
||||
dashboard_loc_write(str(get_alias(int_id(_rf_src), subscriber_ids)) + '-' + ssid, str(loc.lat[0:7]) + str(loc.lat_dir), str(loc.lon[0:8]) + str(loc.lon_dir), time.strftime('%H:%M:%S - %d/%m/%y'))
|
||||
logger.info('Sent APRS packet')
|
||||
logger.info(aprs_loc_packet)
|
||||
logger.info('comment: ' + comment)
|
||||
logger.info('SSID: ' + ssid)
|
||||
logger.info('icon: ' + icon_table + icon_icon)
|
||||
|
||||
except:
|
||||
logger.info('Error. Failed to send packet. Packet may be malformed.')
|
||||
udt_block = 1
|
||||
hdr_type = ''
|
||||
else:
|
||||
pass
|
||||
#NMEA type packets for Anytone like radios.
|
||||
elif _call_type == call_type or (_call_type == 'vcsbk' and pckt_seq > 3) or (_call_type == 'group' and pckt_seq > 3): #int.from_bytes(_seq, 'big') > 3 ):
|
||||
global packet_assembly, btf
|
||||
|
||||
if _dtype_vseq == 6 or _dtype_vseq == 'group':
|
||||
#global btf, hdr_start
|
||||
global hdr_start
|
||||
hdr_start = str(header_ID(_data))
|
||||
logger.info('Header from ' + str(get_alias(int_id(_rf_src), subscriber_ids)) + '. DMR ID: ' + str(int_id(_rf_src)))
|
||||
logger.info(ahex(bptc_decode(_data)))
|
||||
logger.info('Blocks to follow: ' + str(ba2num(bptc_decode(_data)[65:72])))
|
||||
btf = ba2num(bptc_decode(_data)[65:72])
|
||||
# Try resetting packet_assembly
|
||||
packet_assembly = ''
|
||||
|
||||
# Data blocks at 1/2 rate, see https://github.com/g4klx/MMDVM/blob/master/DMRDefines.h for data types. _dtype_seq defined here also
|
||||
if _dtype_vseq == 7 :
|
||||
#try:
|
||||
btf = btf - 1
|
||||
#except:
|
||||
# btf = 5
|
||||
#logger.info('Block#: ' + str(btf))
|
||||
#logger.info('SEQ='+str(_seq))
|
||||
#logger.info('Data block from ' + str(get_alias(int_id(_rf_src), subscriber_ids)) + '. DMR ID: ' + str(int_id(_rf_src)))
|
||||
#logger.info(ahex(bptc_decode(_data)))
|
||||
if _seq == 0:
|
||||
n_packet_assembly = 0
|
||||
packet_assembly = ''
|
||||
#if btf < btf + 1:
|
||||
n_packet_assembly = n_packet_assembly + 1
|
||||
packet_assembly = packet_assembly + str(bptc_decode(_data)) #str((decode_full_lc(b_packet)).strip('bitarray('))
|
||||
#logger.info('packet_assembly'+str(packet_assembly))
|
||||
|
||||
# Use block 0 as trigger. $GPRMC must also be in string to indicate NMEA.
|
||||
# This triggers the APRS upload
|
||||
if btf == 0 : # _seq ==12:
|
||||
final_packet = str(bitarray(re.sub("\)|\(|bitarray|'", '', packet_assembly)).tobytes().decode('utf-8', 'ignore'))
|
||||
sms_hex = str(ba2hx(bitarray(re.sub("\)|\(|bitarray|'", '', packet_assembly))))
|
||||
sms_hex_string = re.sub("b'|'", '', str(sms_hex))
|
||||
#NMEA GPS sentence
|
||||
#logger.info('Final packet:'+final_packet + '\n')
|
||||
if '$GPRMC' in final_packet or '$GNRMC' in final_packet:
|
||||
#logger.info(final_packet + '\n')
|
||||
nmea_parse = re.sub('A\*.*|.*\$', '', str(final_packet))
|
||||
nmea_parse = nmea_parse.replace('*','')
|
||||
#logger.debug('NMEA=' +str(nmea_parse))
|
||||
loc = pynmea2.parse(nmea_parse, check=False)
|
||||
#logger.debug('LOC='+str(loc))
|
||||
logger.info('Latitude: ' + str(loc.lat[0:7]) + str(loc.lat_dir) + ' Longitude: ' + str(loc.lon[0:8]) + str(loc.lon_dir) + ' Direction: ' + str(loc.true_course) + ' Speed: ' + str(loc.spd_over_grnd) + '\n')
|
||||
# Begin APRS format and upload
|
||||
##aprs_loc_packet = str(get_alias(int_id(_rf_src), subscriber_ids)) + '-' + str(user_ssid) + '>APRS,TCPIP*:/' + str(datetime.datetime.utcnow().strftime("%H%M%Sh")) + str(final_packet[29:36]) + str(final_packet[39]) + '/' + str(re.sub(',', '', final_packet[41:49])) + str(final_packet[52]) + '[/' + aprs_comment + ' DMR ID: ' + str(int_id(_rf_src))
|
||||
##try:
|
||||
# Disable opening file for reading to reduce "collision" or reading and writing at same time.
|
||||
with open("./user_settings.txt", 'r') as f:
|
||||
user_settings = ast.literal_eval(f.read())
|
||||
user_settings = ast.literal_eval(os.popen('cat ./user_settings.txt').read())
|
||||
if int_id(_rf_src) not in user_settings:
|
||||
ssid = str(user_ssid)
|
||||
icon_table = '/'
|
||||
icon_icon = '['
|
||||
comment = aprs_comment + ' DMR ID: ' + str(int_id(_rf_src))
|
||||
user_setting_write(int_id(_rf_src), 'SSID', ssid)
|
||||
user_setting_write(int_id(_rf_src), 'ICON', icon_table + icon_icon)
|
||||
user_setting_write(int_id(_rf_src), 'COM', comment)
|
||||
else:
|
||||
if user_settings[int_id(_rf_src)][1]['ssid'] == '':
|
||||
ssid = user_ssid
|
||||
if user_settings[int_id(_rf_src)][3]['comment'] == '':
|
||||
comment = aprs_comment + ' DMR ID: ' + str(int_id(_rf_src))
|
||||
if user_settings[int_id(_rf_src)][2]['icon'] == '':
|
||||
icon_table = '/'
|
||||
icon_icon = '['
|
||||
if user_settings[int_id(_rf_src)][2]['icon'] != '':
|
||||
icon_table = user_settings[int_id(_rf_src)][2]['icon'][0]
|
||||
icon_icon = user_settings[int_id(_rf_src)][2]['icon'][1]
|
||||
if user_settings[int_id(_rf_src)][1]['ssid'] != '':
|
||||
ssid = user_settings[int_id(_rf_src)][1]['ssid']
|
||||
if user_settings[int_id(_rf_src)][3]['comment'] != '':
|
||||
comment = user_settings[int_id(_rf_src)][3]['comment']
|
||||
#logger.info(retrieve_aprs_settings(_rf_src))
|
||||
try:
|
||||
aprs_loc_packet = str(get_alias(int_id(_rf_src), subscriber_ids)) + '-' + ssid + '>APHBL3,TCPIP*:/' + str(datetime.datetime.utcnow().strftime("%H%M%Sh")) + str(loc.lat[0:7]) + str(loc.lat_dir) + icon_table + str(loc.lon[0:8]) + str(loc.lon_dir) + icon_icon + str(round(loc.true_course)).zfill(3) + '/' + str(round(loc.spd_over_grnd)).zfill(3) + '/' + str(comment)
|
||||
# Try parse of APRS packet. If it fails, it will not upload to APRS-IS
|
||||
aprslib.parse(aprs_loc_packet)
|
||||
# Float values of lat and lon. Anything that is not a number will cause it to fail.
|
||||
float(loc.lat)
|
||||
float(loc.lon)
|
||||
aprs_send(aprs_loc_packet)
|
||||
dashboard_loc_write(str(get_alias(int_id(_rf_src), subscriber_ids)) + '-' + ssid, str(loc.lat[0:7]) + str(loc.lat_dir), str(loc.lon[0:8]) + str(loc.lon_dir), time.strftime('%H:%M:%S - %d/%m/%y'))
|
||||
|
||||
logger.info(aprs_loc_packet)
|
||||
logger.info('Sent APRS packet')
|
||||
logger.info('comment: ' + comment)
|
||||
logger.info('SSID: ' + ssid)
|
||||
logger.info('icon: ' + icon_table + icon_icon)
|
||||
|
||||
except:
|
||||
logger.info('Error or user settings file not found, proceeding with default settings.')
|
||||
|
||||
# End APRS-IS upload
|
||||
|
||||
# Assume this is an SMS message
|
||||
elif '$GPRMC' not in final_packet or '$GNRMC' not in final_packet:
|
||||
|
||||
#### # Motorola type SMS header
|
||||
## if '824a' in hdr_start or '024a' in hdr_start:
|
||||
## logger.info('\nMotorola type SMS')
|
||||
## sms = codecs.decode(bytes.fromhex(''.join(sms_hex[74:-8].split('00'))), 'utf-8')
|
||||
## logger.info('\n\n' + 'Received SMS from ' + str(get_alias(int_id(_rf_src), subscriber_ids)) + ', DMR ID: ' + str(int_id(_rf_src)) + ': ' + str(sms) + '\n')
|
||||
## process_sms(_rf_src, sms)
|
||||
## packet_assembly = ''
|
||||
## # ETSI? type SMS header
|
||||
## elif '0244' in hdr_start or '8244' in hdr_start:
|
||||
## logger.info('ETSI? type SMS')
|
||||
## sms = codecs.decode(bytes.fromhex(''.join(sms_hex[64:-8].split('00'))), 'utf-8')
|
||||
## logger.info('\n\n' + 'Received SMS from ' + str(get_alias(int_id(_rf_src), subscriber_ids)) + ', DMR ID: ' + str(int_id(_rf_src)) + ': ' + str(sms) + '\n')
|
||||
## #logger.info(final_packet)
|
||||
## #logger.info(sms_hex[64:-8])
|
||||
## process_sms(_rf_src, sms)
|
||||
## packet_assembly = ''
|
||||
####
|
||||
## else:
|
||||
|
||||
logger.info('\nSMS detected. Attempting to parse.')
|
||||
#logger.info(final_packet)
|
||||
#logger.info('SMS HEX='+sms_hex)
|
||||
#logger.info(type(sms_hex))
|
||||
logger.info('Attempting to find command...')
|
||||
sms = codecs.decode(bytes.fromhex(''.join(sms_hex_string[:-8].split('00'))), 'utf-8', 'ignore')
|
||||
logger.info('SMS='+ str(sms) + '\n')
|
||||
msg_found = re.sub('.*\n', '', sms)
|
||||
logger.info('\n\n' + 'Received SMS from ' + str(get_alias(int_id(_rf_src), subscriber_ids)) + ', DMR ID: ' + str(int_id(_rf_src)) + ': ' + str(msg_found) + '\n')
|
||||
process_sms(_rf_src, msg_found)
|
||||
#logger.info(bitarray(re.sub("\)|\(|bitarray|'", '', str(bptc_decode(_data)).tobytes().decode('utf-8', 'ignore'))))
|
||||
#logger.info('\n\n' + 'Received SMS from ' + str(get_alias(int_id(_rf_src), subscriber_ids)) + ', DMR ID: ' + str(int_id(_rf_src)) + ': ' + str(sms) + '\n')
|
||||
# Reset the packet assembly to prevent old data from returning.
|
||||
hdr_start = ''
|
||||
n_packet_assembly = 0
|
||||
packet_assembly = ''
|
||||
btf = 0
|
||||
#packet_assembly = '' #logger.info(_dtype_vseq)
|
||||
#logger.info(ahex(bptc_decode(_data)).decode('utf-8', 'ignore'))
|
||||
#logger.info(bitarray(re.sub("\)|\(|bitarray|'", '', str(bptc_decode(_data)).tobytes().decode('utf-8', 'ignore'))))
|
||||
|
||||
else:
|
||||
pass
|
||||
#logger.debug('nbpacket:'+ str(n_packet_assembly))
|
||||
|
||||
#************************************************
|
||||
# MAIN PROGRAM LOOP STARTS HERE
|
||||
#************************************************
|
||||
|
||||
if __name__ == '__main__':
|
||||
#global aprs_callsign, aprs_passcode, aprs_server, aprs_port, user_ssid, aprs_comment, call_type, data_id
|
||||
import argparse
|
||||
import sys
|
||||
import os
|
||||
import signal
|
||||
from dmr_utils3.utils import try_download, mk_id_dict
|
||||
# Change the current directory to the location of the application
|
||||
os.chdir(os.path.dirname(os.path.realpath(sys.argv[0])))
|
||||
# Check if user_settings (for APRS settings of users) exists. Creat it if not.
|
||||
if Path('./user_settings.txt').is_file():
|
||||
pass
|
||||
else:
|
||||
Path('./user_settings.txt').touch()
|
||||
with open("./user_settings.txt", 'w') as user_dict_file:
|
||||
user_dict_file.write("{1: [{'call': 'N0CALL'}, {'ssid': ''}, {'icon': ''}, {'comment': ''}]}")
|
||||
user_dict_file.close()
|
||||
# Check to see if dashboard files exist
|
||||
if Path('/tmp/gps_data_user_loc.txt').is_file():
|
||||
pass
|
||||
else:
|
||||
Path('/tmp/gps_data_user_loc.txt').touch()
|
||||
with open("/tmp/gps_data_user_loc.txt", 'w') as user_loc_file:
|
||||
user_loc_file.write("[]")
|
||||
user_loc_file.close()
|
||||
if Path('/tmp/gps_data_user_bb.txt').is_file():
|
||||
pass
|
||||
else:
|
||||
Path('/tmp/gps_data_user_bb.txt').touch()
|
||||
with open("/tmp/gps_data_user_bb.txt", 'w') as user_bb_file:
|
||||
user_bb_file.write("[]")
|
||||
user_bb_file.close()
|
||||
# CLI argument parser - handles picking up the config file from the command line, and sending a "help" message
|
||||
parser = argparse.ArgumentParser()
|
||||
parser.add_argument('-c', '--config', action='store', dest='CONFIG_FILE', help='/full/path/to/config.file (usually gps_data.cfg)')
|
||||
parser.add_argument('-l', '--logging', action='store', dest='LOG_LEVEL', help='Override config file logging level.')
|
||||
cli_args = parser.parse_args()
|
||||
|
||||
# Ensure we have a path for the config file, if one wasn't specified, then use the default (top of file)
|
||||
if not cli_args.CONFIG_FILE:
|
||||
cli_args.CONFIG_FILE = os.path.dirname(os.path.abspath(__file__))+'/gps_data.cfg'
|
||||
|
||||
# Call the external routine to build the configuration dictionary
|
||||
CONFIG = config.build_config(cli_args.CONFIG_FILE)
|
||||
|
||||
data_id = int(CONFIG['GPS_DATA']['DATA_DMR_ID'])
|
||||
|
||||
# Group call or Unit (private) call
|
||||
call_type = CONFIG['GPS_DATA']['CALL_TYPE']
|
||||
# APRS-IS login information
|
||||
aprs_callsign = CONFIG['GPS_DATA']['APRS_LOGIN_CALL']
|
||||
aprs_passcode = int(CONFIG['GPS_DATA']['APRS_LOGIN_PASSCODE'])
|
||||
aprs_server = CONFIG['GPS_DATA']['APRS_SERVER']
|
||||
aprs_port = int(CONFIG['GPS_DATA']['APRS_PORT'])
|
||||
user_ssid = CONFIG['GPS_DATA']['USER_APRS_SSID']
|
||||
aprs_comment = CONFIG['GPS_DATA']['USER_APRS_COMMENT']
|
||||
|
||||
# Start the system logger
|
||||
if cli_args.LOG_LEVEL:
|
||||
CONFIG['LOGGER']['LOG_LEVEL'] = cli_args.LOG_LEVEL
|
||||
logger = log.config_logging(CONFIG['LOGGER'])
|
||||
btf_6_stop = 0
|
||||
logger.setLevel(logging.DEBUG)
|
||||
# create file handler which logs even debug messages
|
||||
fh = logging.FileHandler('/tmp/gps_data.log')
|
||||
fh.setLevel(logging.DEBUG)
|
||||
# create console handler with a higher log level
|
||||
ch = logging.StreamHandler()
|
||||
ch.setLevel(logging.ERROR)
|
||||
# create formatter and add it to the handlers
|
||||
formatter = logging.Formatter('%(asctime)s - %(name)s - %(levelname)s - %(message)s')
|
||||
fh.setFormatter(formatter)
|
||||
ch.setFormatter(formatter)
|
||||
# add the handlers to the logger
|
||||
logger.addHandler(fh)
|
||||
logger.addHandler(ch)
|
||||
|
||||
logger.info('\n\nCopyright (c) 2013, 2014, 2015, 2016, 2018, 2019, 2021\n\tThe Regents of the K0USY Group. All rights reserved.\n GPS and Data decoding by Eric, KF7EEL Modified by Xavier FRS2013')
|
||||
logger.debug('Logging system started, anything from here on gets logged')
|
||||
|
||||
# Set up the signal handler
|
||||
def sig_handler(_signal, _frame):
|
||||
logger.info('SHUTDOWN: >>>GPS and Data Decoder<<< IS TERMINATING WITH SIGNAL %s', str(_signal))
|
||||
hblink_handler(_signal, _frame)
|
||||
logger.info('SHUTDOWN: ALL SYSTEM HANDLERS EXECUTED - STOPPING REACTOR')
|
||||
reactor.stop()
|
||||
|
||||
# Set signal handers so that we can gracefully exit if need be
|
||||
for sig in [signal.SIGTERM, signal.SIGINT]:
|
||||
signal.signal(sig, sig_handler)
|
||||
|
||||
# Create the name-number mapping dictionaries
|
||||
peer_ids, subscriber_ids, talkgroup_ids = mk_aliases(CONFIG)
|
||||
|
||||
|
||||
# INITIALIZE THE REPORTING LOOP
|
||||
if CONFIG['REPORTS']['REPORT']:
|
||||
report_server = config_reports(CONFIG, reportFactory)
|
||||
else:
|
||||
report_server = None
|
||||
logger.info('(REPORT) TCP Socket reporting not configured')
|
||||
|
||||
# HBlink instance creation
|
||||
logger.info('HBlink \'gps_data.py\' -- SYSTEM STARTING...')
|
||||
for system in CONFIG['SYSTEMS']:
|
||||
if CONFIG['SYSTEMS'][system]['ENABLED']:
|
||||
if CONFIG['SYSTEMS'][system]['MODE'] == 'OPENBRIDGE':
|
||||
systems[system] = OPENBRIDGE(system, CONFIG, report_server)
|
||||
else:
|
||||
systems[system] = DATA_SYSTEM(system, CONFIG, report_server)
|
||||
|
||||
reactor.listenUDP(CONFIG['SYSTEMS'][system]['PORT'], systems[system], interface=CONFIG['SYSTEMS'][system]['IP'])
|
||||
logger.debug('%s instance created: %s, %s', CONFIG['SYSTEMS'][system]['MODE'], system, systems[system])
|
||||
reactor.run()
|
||||
|
||||
|
||||
# John 3:16 - For God so loved the world, that he gave his only Son,
|
||||
# that whoever believes in him should not perish but have eternal life.
|
60
gps_data_igate_beacon.py
Normal file
60
gps_data_igate_beacon.py
Normal file
|
@ -0,0 +1,60 @@
|
|||
#!/usr/bin/env python
|
||||
#
|
||||
###############################################################################
|
||||
# HBLink - Copyright (C) 2020 Cortney T. Buffington, N0MJS <n0mjs@me.com>
|
||||
# GPS/Data - Copyright (C) 2020 Eric Craw, KF7EEL <kf7eel@qsl.net>
|
||||
#
|
||||
# This program is free software; you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
# the Free Software Foundation; either version 3 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program; if not, write to the Free Software Foundation,
|
||||
# Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
###############################################################################
|
||||
|
||||
'''
|
||||
This script uploads a beacon to APRS-IS for the GPS/Data Application itself, cuasing it to appear as an igate
|
||||
an aprs.fi.
|
||||
'''
|
||||
|
||||
import aprslib
|
||||
import argparse
|
||||
import os
|
||||
import config
|
||||
import time
|
||||
|
||||
parser = argparse.ArgumentParser()
|
||||
parser.add_argument('-c', '--config', action='store', dest='CONFIG_FILE', help='/full/path/to/config.file (usually gps_data.cfg)')
|
||||
cli_args = parser.parse_args()
|
||||
|
||||
if not cli_args.CONFIG_FILE:
|
||||
cli_args.CONFIG_FILE = os.path.dirname(os.path.abspath(__file__))+'/gps_data.cfg'
|
||||
CONFIG = config.build_config(cli_args.CONFIG_FILE)
|
||||
|
||||
print('''
|
||||
GPS/Data Application Beacon Script by Eric, KF7EEL. https://github.com/kf7eel/hblink3 \n
|
||||
This script will upload an APRS position for the GPS/Data Application. This usually causes most APRS-IS clients to see the GPS/Data Application
|
||||
as an i-gate. \n
|
||||
Using the following setting to upload beacon.\n
|
||||
Callsign: ''' + CONFIG['GPS_DATA']['APRS_LOGIN_CALL'] + ''' - Position comment: ''' + CONFIG['GPS_DATA']['IGATE_BEACON_COMMENT'] + '''
|
||||
Beacon time: ''' + CONFIG['GPS_DATA']['IGATE_BEACON_TIME'] + '''
|
||||
''')
|
||||
|
||||
beacon_packet = CONFIG['GPS_DATA']['APRS_LOGIN_CALL'] + '>APHBL3,TCPIP*:!' + CONFIG['GPS_DATA']['IGATE_LATITUDE'] + str(CONFIG['GPS_DATA']['IGATE_BEACON_ICON'][0]) + CONFIG['GPS_DATA']['IGATE_LONGITUDE'] + str(CONFIG['GPS_DATA']['IGATE_BEACON_ICON'][1]) + '/' + CONFIG['GPS_DATA']['IGATE_BEACON_COMMENT']
|
||||
#print(beacon_packet)
|
||||
AIS = aprslib.IS(CONFIG['GPS_DATA']['APRS_LOGIN_CALL'], passwd=CONFIG['GPS_DATA']['APRS_LOGIN_PASSCODE'],host=CONFIG['GPS_DATA']['APRS_SERVER'], port=CONFIG['GPS_DATA']['APRS_PORT'])
|
||||
|
||||
while int(CONFIG['GPS_DATA']['IGATE_BEACON_TIME']) > 15:
|
||||
|
||||
AIS.connect()
|
||||
AIS.sendall(beacon_packet)
|
||||
print(beacon_packet)
|
||||
AIS.close()
|
||||
time.sleep(int(CONFIG['GPS_DATA']['IGATE_BEACON_TIME'])*60)
|
11
gps_functions.py
Normal file
11
gps_functions.py
Normal file
|
@ -0,0 +1,11 @@
|
|||
# Various configurable options used in the GPS/Data application.
|
||||
|
||||
# SMS message that triggers command
|
||||
|
||||
|
||||
cmd_list = {
|
||||
|
||||
'COMMAND' : 'echo "command to execute"',
|
||||
'UPTIME' : 'uptime'
|
||||
|
||||
}
|
14
hbdaprs.service
Normal file
14
hbdaprs.service
Normal file
|
@ -0,0 +1,14 @@
|
|||
[Unit]
|
||||
|
||||
Description=Start DAPRS
|
||||
|
||||
After=multi-user.target
|
||||
|
||||
[Service]
|
||||
|
||||
ExecStart=/usr/bin/python3 /opt/FreeDMR/gps_data.py
|
||||
|
||||
[Install]
|
||||
|
||||
WantedBy=multi-user.target
|
||||
|
Loading…
Reference in New Issue
Block a user