Merge pull request #272 from DJ2LS/ls-explorer

FreeDATA Explorer
This commit is contained in:
DJ2LS 2022-11-06 17:09:24 +01:00 committed by GitHub
commit 1a3188fc71
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
11 changed files with 202 additions and 9 deletions

View file

@ -217,7 +217,7 @@ exports.getDaemonState = function() {
// START TNC // START TNC
// ` `== multi line string // ` `== multi line string
exports.startTNC = function(mycall, mygrid, rx_audio, tx_audio, radiocontrol, devicename, deviceport, pttprotocol, pttport, serialspeed, data_bits, stop_bits, handshake, rigctld_ip, rigctld_port, enable_fft, enable_scatter, low_bandwidth_mode, tuning_range_fmin, tuning_range_fmax, enable_fsk, tx_audio_level, respond_to_cq, rx_buffer_size) { exports.startTNC = function(mycall, mygrid, rx_audio, tx_audio, radiocontrol, devicename, deviceport, pttprotocol, pttport, serialspeed, data_bits, stop_bits, handshake, rigctld_ip, rigctld_port, enable_fft, enable_scatter, low_bandwidth_mode, tuning_range_fmin, tuning_range_fmax, enable_fsk, tx_audio_level, respond_to_cq, rx_buffer_size, enable_explorer) {
var json_command = JSON.stringify({ var json_command = JSON.stringify({
type: 'set', type: 'set',
command: 'start_tnc', command: 'start_tnc',
@ -245,7 +245,8 @@ exports.startTNC = function(mycall, mygrid, rx_audio, tx_audio, radiocontrol, de
tuning_range_fmax : tuning_range_fmax, tuning_range_fmax : tuning_range_fmax,
tx_audio_level : tx_audio_level, tx_audio_level : tx_audio_level,
respond_to_cq : respond_to_cq, respond_to_cq : respond_to_cq,
rx_buffer_size : rx_buffer_size rx_buffer_size : rx_buffer_size,
enable_explorer : enable_explorer
}] }]
}) })

View file

@ -90,7 +90,8 @@ const configDefaultSettings = '{\
"tuning_range_fmin" : "-50.0",\ "tuning_range_fmin" : "-50.0",\
"tuning_range_fmax" : "50.0",\ "tuning_range_fmax" : "50.0",\
"respond_to_cq" : "True",\ "respond_to_cq" : "True",\
"rx_buffer_size" : "16" \ "rx_buffer_size" : "16", \
"enable_explorer" : "False" \
}'; }';
if (!fs.existsSync(configPath)) { if (!fs.existsSync(configPath)) {
@ -175,6 +176,7 @@ function createWindow() {
icon: 'src/img/icon.png', icon: 'src/img/icon.png',
webPreferences: { webPreferences: {
//preload: path.join(__dirname, 'preload-main.js'), //preload: path.join(__dirname, 'preload-main.js'),
backgroundThrottle: false,
preload: require.resolve('./preload-main.js'), preload: require.resolve('./preload-main.js'),
nodeIntegration: true, nodeIntegration: true,
contextIsolation: false, contextIsolation: false,

View file

@ -160,7 +160,13 @@ set_setting_switch("enable_hamlib_ptt_port", "hamlib_ptt_port", config.enable_ha
document.getElementById("respondCQSwitch").checked = true; document.getElementById("respondCQSwitch").checked = true;
} else { } else {
document.getElementById("respondCQSwitch").checked = false; document.getElementById("respondCQSwitch").checked = false;
} }
if(config.enable_explorer == 'True'){
document.getElementById("ExplorerSwitch").checked = true;
} else {
document.getElementById("ExplorerSwitch").checked = false;
}
// theme selector // theme selector
if(config.theme != 'default'){ if(config.theme != 'default'){
@ -872,7 +878,16 @@ document.getElementById('hamlib_rigctld_stop').addEventListener('click', () => {
} }
fs.writeFileSync(configPath, JSON.stringify(config, null, 2)); fs.writeFileSync(configPath, JSON.stringify(config, null, 2));
}); });
// enable explorer Switch clicked
document.getElementById("ExplorerSwitch").addEventListener("click", () => {
if(document.getElementById("ExplorerSwitch").checked == true){
config.enable_explorer = "True";
} else {
config.enable_explorer = "False";
}
fs.writeFileSync(configPath, JSON.stringify(config, null, 2));
});
// enable fsk Switch clicked // enable fsk Switch clicked
document.getElementById("fskModeSwitch").addEventListener("click", () => { document.getElementById("fskModeSwitch").addEventListener("click", () => {
@ -1008,6 +1023,11 @@ document.getElementById('hamlib_rigctld_stop').addEventListener('click', () => {
var respond_to_cq = "False"; var respond_to_cq = "False";
} }
if (document.getElementById("ExplorerSwitch").checked == true){
var enable_explorer = "True";
} else {
var enable_explorer = "False";
}
// loop through audio device list and select // loop through audio device list and select
for(i = 0; i < document.getElementById("audio_input_selectbox").length; i++) { for(i = 0; i < document.getElementById("audio_input_selectbox").length; i++) {
@ -1067,6 +1087,7 @@ document.getElementById('hamlib_rigctld_stop').addEventListener('click', () => {
config.tx_audio_level = tx_audio_level; config.tx_audio_level = tx_audio_level;
config.respond_to_cq = respond_to_cq; config.respond_to_cq = respond_to_cq;
config.rx_buffer_size = rx_buffer_size; config.rx_buffer_size = rx_buffer_size;
config.enable_explorer = enable_explorer;
fs.writeFileSync(configPath, JSON.stringify(config, null, 2)); fs.writeFileSync(configPath, JSON.stringify(config, null, 2));
@ -1085,7 +1106,7 @@ document.getElementById('hamlib_rigctld_stop').addEventListener('click', () => {
*/ */
daemon.startTNC(callsign_ssid, mygrid, rx_audio, tx_audio, radiocontrol, deviceid, deviceport, pttprotocol, pttport, serialspeed, data_bits, stop_bits, handshake, rigctld_ip, rigctld_port, enable_fft, enable_scatter, low_bandwidth_mode, tuning_range_fmin, tuning_range_fmax, enable_fsk, tx_audio_level, respond_to_cq, rx_buffer_size); daemon.startTNC(callsign_ssid, mygrid, rx_audio, tx_audio, radiocontrol, deviceid, deviceport, pttprotocol, pttport, serialspeed, data_bits, stop_bits, handshake, rigctld_ip, rigctld_port, enable_fft, enable_scatter, low_bandwidth_mode, tuning_range_fmin, tuning_range_fmax, enable_fsk, tx_audio_level, respond_to_cq, rx_buffer_size, enable_explorer);
}) })

View file

@ -1204,6 +1204,15 @@
</div> </div>
</label> </label>
</div> </div>
<div class="input-group input-group-sm mb-1">
<label class="input-group-text w-50">Enable Explorer Publishing</label>
<label class="input-group-text bg-white w-50">
<div class="form-check form-switch form-check-inline">
<input class="form-check-input" type="checkbox" id="ExplorerSwitch">
<label class="form-check-label" for="ExplorerSwitch">Publish</label>
</div>
</label>
</div>
<div class="input-group input-group-sm mb-1"> <div class="input-group input-group-sm mb-1">
<label class="input-group-text w-50">Respond to CQ</label> <label class="input-group-text w-50">Respond to CQ</label>
<label class="input-group-text bg-white w-50"> <label class="input-group-text bg-white w-50">

View file

@ -74,7 +74,8 @@ class CONFIG:
'fmin': data[19], 'fmin': data[19],
'fmax': data[20], 'fmax': data[20],
'qrv': data[23], 'qrv': data[23],
'rxbuffersize': data[24] 'rxbuffersize': data[24],
'explorer': data[25]
} }
try: try:
with open(self.config_name, 'w') as configfile: with open(self.config_name, 'w') as configfile:

View file

@ -248,6 +248,9 @@ class DAEMON:
options.append("--rx-buffer-size") options.append("--rx-buffer-size")
options.append(data[24]) options.append(data[24])
if data[25] == "True":
options.append("--explorer")
# safe data to config file # safe data to config file
config.write_entire_config(data) config.write_entire_config(data)

57
tnc/explorer.py Normal file
View file

@ -0,0 +1,57 @@
# -*- coding: UTF-8 -*-
"""
Created on 05.11.23
@author: DJ2LS
"""
# pylint: disable=invalid-name, line-too-long, c-extension-no-member
# pylint: disable=import-outside-toplevel, attribute-defined-outside-init
import requests
import threading
import time
import ujson as json
import structlog
import static
log = structlog.get_logger("explorer")
class explorer():
def __init__(self):
self.explorer_url = "https://explorer.freedata.app/api.php"
self.publish_interval = 15
self.interval_thread = threading.Thread(target=self.interval, name="interval", daemon=True)
self.interval_thread.start()
def interval(self):
while True:
self.push()
time.sleep(self.publish_interval)
def push(self):
if static.HAMLIB_FREQUENCY is not None:
frequency = static.HAMLIB_FREQUENCY
else:
frequency = 0
band = "USB"
callsign = str(static.MYCALLSIGN, "utf-8")
gridsquare = str(static.MYGRID, "utf-8")
version = str(static.VERSION)
bandwidth = str(static.LOW_BANDWIDTH_MODE)
log.info("[EXPLORER] publish", frequency=frequency, band=band, callsign=callsign, gridsquare=gridsquare, version=version, bandwidth=bandwidth)
headers = {"Content-Type": "application/json"}
station_data = {'callsign': callsign, 'gridsquare': gridsquare, 'frequency': frequency, 'band': band, 'version': version, 'bandwidth': bandwidth}
station_data = json.dumps(station_data)
try:
response = requests.post(self.explorer_url, json=station_data, headers=headers)
# print(response.status_code)
# print(response.content)
except Exception as e:
log.warning("[EXPLORER] connection lost")

View file

@ -7,6 +7,14 @@ Created on Tue Dec 22 16:58:45 2020
main module for running the tnc main module for running the tnc
""" """
# run tnc self test on startup before we are doing other things
import selftest
selftest.TEST()
# continue if we passed the test
import argparse import argparse
import multiprocessing import multiprocessing
import os import os
@ -22,10 +30,10 @@ import log_handler
import modem import modem
import static import static
import structlog import structlog
import explorer
log = structlog.get_logger("main") log = structlog.get_logger("main")
def signal_handler(sig, frame): def signal_handler(sig, frame):
""" """
a signal handler, which closes the network/socket when closing the application a signal handler, which closes the network/socket when closing the application
@ -246,7 +254,12 @@ if __name__ == "__main__":
help="Set the maximum size of rx buffer.", help="Set the maximum size of rx buffer.",
type=int, type=int,
) )
PARSER.add_argument(
"--explorer",
dest="enable_explorer",
action="store_true",
help="Enable sending tnc data to https://explorer.freedata.app",
)
ARGS = PARSER.parse_args() ARGS = PARSER.parse_args()
if ARGS.configfile: if ARGS.configfile:
# init config # init config
@ -285,6 +298,7 @@ if __name__ == "__main__":
static.TX_AUDIO_LEVEL = config['AUDIO']['txaudiolevel'] static.TX_AUDIO_LEVEL = config['AUDIO']['txaudiolevel']
static.RESPOND_TO_CQ = config['TNC']['qrv'] static.RESPOND_TO_CQ = config['TNC']['qrv']
static.RX_BUFFER_SIZE = config['TNC']['rxbuffersize'] static.RX_BUFFER_SIZE = config['TNC']['rxbuffersize']
static.ENABLE_EXPLORER = config['TNC']['explorer']
else: else:
@ -321,6 +335,7 @@ if __name__ == "__main__":
static.TX_AUDIO_LEVEL = ARGS.tx_audio_level static.TX_AUDIO_LEVEL = ARGS.tx_audio_level
static.RESPOND_TO_CQ = ARGS.enable_respond_to_cq static.RESPOND_TO_CQ = ARGS.enable_respond_to_cq
static.RX_BUFFER_SIZE = ARGS.rx_buffer_size static.RX_BUFFER_SIZE = ARGS.rx_buffer_size
static.ENABLE_EXPLORER = ARGS.enable_explorer
# we need to wait until we got all parameters from argparse first before we can load the other modules # we need to wait until we got all parameters from argparse first before we can load the other modules
import sock import sock
@ -358,6 +373,11 @@ if __name__ == "__main__":
# start modem # start modem
modem = modem.RF() modem = modem.RF()
# optionally start explorer module
if static.ENABLE_EXPLORER:
log.info("[EXPLORER] Publishing to https://explorer.freedata.app", state=static.ENABLE_EXPLORER)
explorer = explorer.explorer()
# --------------------------------------------START CMD SERVER # --------------------------------------------START CMD SERVER
try: try:
log.info("[TNC] Starting TCP/IP socket", port=static.PORT) log.info("[TNC] Starting TCP/IP socket", port=static.PORT)

74
tnc/selftest.py Normal file
View file

@ -0,0 +1,74 @@
"""
simple TNC self tests
"""
# -*- coding: utf-8 -*-
# pylint: disable=invalid-name, line-too-long, c-extension-no-member
# pylint: disable=import-outside-toplevel, attribute-defined-outside-init
import sys
import structlog
log = structlog.get_logger("selftest")
class TEST():
def __init__(self):
log.info("[selftest] running self tests...")
if self.run_tests():
log.info("[selftest] passed -> starting TNC")
else:
log.error("[selftest] failed -> closing TNC")
sys.exit(0)
def run_tests(self):
return bool(
self.check_imports()
and self.check_sounddevice()
and self.check_helpers()
)
def check_imports(self):
try:
import argparse
import atexit
import multiprocessing
import os
import signal
import socketserver
import sys
import threading
import time
import structlog
import crcengine
import ctypes
import glob
import enum
import numpy
import sounddevice
return True
except Exception as e:
log.info("[selftest] [check_imports] [failed]", e=e)
return False
def check_sounddevice(self):
try:
import audio
audio.get_audio_devices()
return True
except Exception as e:
log.info("[selftest] [check_sounddevice] [failed]", e=e)
return False
def check_helpers(self):
try:
import helpers
valid_crc24 = "f86ed0"
if helpers.get_crc_24(b"test").hex() == valid_crc24:
return True
else:
raise Exception
except Exception as e:
log.info("[selftest] [check_helpers] [failed]", e=e)
return False

View file

@ -608,6 +608,7 @@ def process_daemon_commands(data):
tx_audio_level = str(received_json["parameter"][0]["tx_audio_level"]) tx_audio_level = str(received_json["parameter"][0]["tx_audio_level"])
respond_to_cq = str(received_json["parameter"][0]["respond_to_cq"]) respond_to_cq = str(received_json["parameter"][0]["respond_to_cq"])
rx_buffer_size = str(received_json["parameter"][0]["rx_buffer_size"]) rx_buffer_size = str(received_json["parameter"][0]["rx_buffer_size"])
enable_explorer = str(received_json["parameter"][0]["enable_explorer"])
# print some debugging parameters # print some debugging parameters
for item in received_json["parameter"][0]: for item in received_json["parameter"][0]:
@ -643,6 +644,7 @@ def process_daemon_commands(data):
tx_audio_level, tx_audio_level,
respond_to_cq, respond_to_cq,
rx_buffer_size, rx_buffer_size,
enable_explorer,
] ]
) )
command_response("start_tnc", True) command_response("start_tnc", True)

View file

@ -13,6 +13,9 @@ from enum import Enum
VERSION = "0.6.1-alpha.1" VERSION = "0.6.1-alpha.1"
ENABLE_EXPLORER = False
# DAEMON # DAEMON
DAEMONPORT: int = 3001 DAEMONPORT: int = 3001
TNCSTARTED: bool = False TNCSTARTED: bool = False