FreeDATA/tnc/main.py

429 lines
14 KiB
Python
Raw Normal View History

2020-12-26 10:02:14 +00:00
#!/usr/bin/python3
2020-12-23 16:48:22 +00:00
# -*- coding: utf-8 -*-
"""
Created on Tue Dec 22 16:58:45 2020
@author: DJ2LS
main module for running the tnc
2020-12-23 16:48:22 +00:00
"""
2022-11-05 21:27:33 +00:00
# run tnc self test on startup before we are doing other things
2022-11-06 17:37:47 +00:00
# import selftest
# selftest.TEST()
2022-11-05 21:27:33 +00:00
# continue if we passed the test
2020-12-23 17:25:50 +00:00
import argparse
import multiprocessing
import os
import signal
import socketserver
import sys
import threading
import time
2022-09-20 09:34:28 +00:00
import config
import data_handler
import helpers
import log_handler
import modem
import static
2023-04-27 19:43:56 +00:00
from static import ARQ, AudioParam, Beacon, Channel, Daemon, HamlibParam, ModemParam, Station, Statistics, TCIParam, TNC
import structlog
2022-11-05 21:27:33 +00:00
import explorer
2022-12-29 17:57:52 +00:00
import json
2022-06-01 00:35:35 +00:00
log = structlog.get_logger("main")
2021-11-18 18:40:22 +00:00
def signal_handler(sig, frame):
"""
a signal handler, which closes the network/socket when closing the application
Args:
sig: signal
2022-05-09 00:41:49 +00:00
frame:
Returns: system exit
"""
print("Closing TNC...")
sock.CLOSE_SIGNAL = True
sys.exit(0)
2022-05-09 00:41:49 +00:00
signal.signal(signal.SIGINT, signal_handler)
if __name__ == "__main__":
# This is for Windows multiprocessing support
multiprocessing.freeze_support()
2021-03-12 13:14:36 +00:00
# --------------------------------------------GET PARAMETER INPUTS
PARSER = argparse.ArgumentParser(description="FreeDATA TNC")
2022-09-20 09:34:28 +00:00
2022-12-10 12:34:26 +00:00
#PARSER.add_argument(
# "--use-config",
# dest="configfile",
# action="store_true",
# help="Use the default config file config.ini",
#)
2022-09-20 09:34:28 +00:00
PARSER.add_argument(
"--use-config",
dest="configfile",
2022-12-10 12:34:26 +00:00
default=False,
type=str,
2022-09-20 09:34:28 +00:00
help="Use the default config file config.ini",
)
PARSER.add_argument(
"--save-to-folder",
dest="savetofolder",
default=False,
action="store_true",
help="Save received data to local folder",
)
PARSER.add_argument(
2022-09-20 09:34:28 +00:00
"--mycall",
dest="mycall",
default="AA0AA",
help="My callsign",
type=str
)
PARSER.add_argument(
"--ssid",
dest="ssid_list",
nargs="*",
default=[0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15],
help="SSID list we are responding to",
2022-11-29 06:45:17 +00:00
type=int,
)
PARSER.add_argument(
2022-09-20 09:34:28 +00:00
"--mygrid",
dest="mygrid",
default="JN12AA",
help="My gridsquare",
type=str
)
PARSER.add_argument(
"--rx",
dest="audio_input_device",
default=0,
help="listening sound card",
2022-12-04 15:12:43 +00:00
type=str,
)
PARSER.add_argument(
"--tx",
dest="audio_output_device",
default=0,
help="transmitting sound card",
2022-12-04 15:12:43 +00:00
type=str,
)
PARSER.add_argument(
"--port",
dest="socket_port",
default=3000,
help="Socket port in the range of 1024-65536",
type=int,
)
PARSER.add_argument(
"--radiocontrol",
dest="hamlib_radiocontrol",
choices=["disabled", "direct", "rigctl", "rigctld"],
default="disabled",
help="Set how you want to control your radio",
)
PARSER.add_argument(
"--rigctld_port",
dest="rigctld_port",
default=4532,
type=int,
help="Set rigctld port",
)
PARSER.add_argument(
"--rigctld_ip", dest="rigctld_ip", default="localhost", help="Set rigctld ip"
)
PARSER.add_argument(
"--scatter",
dest="send_scatter",
action="store_true",
help="Send scatter information via network",
)
PARSER.add_argument(
"--fft",
dest="send_fft",
action="store_true",
help="Send fft information via network",
)
PARSER.add_argument(
"--500hz",
dest="low_bandwidth_mode",
action="store_true",
help="Enable low bandwidth mode ( 500 Hz only )",
)
PARSER.add_argument(
"--fsk",
dest="enable_fsk",
action="store_true",
help="Enable FSK mode for ping, beacon and CQ",
)
PARSER.add_argument(
"--qrv",
dest="enable_respond_to_cq",
action="store_true",
help="Enable sending a QRV frame if CQ received",
)
PARSER.add_argument(
"--tuning_range_fmin",
dest="tuning_range_fmin",
choices=[-50.0, -100.0, -150.0, -200.0, -250.0],
default=-50.0,
help="Tuning range fmin",
type=float,
)
PARSER.add_argument(
"--tuning_range_fmax",
dest="tuning_range_fmax",
choices=[50.0, 100.0, 150.0, 200.0, 250.0],
default=50.0,
help="Tuning range fmax",
type=float,
)
PARSER.add_argument(
"--tx-audio-level",
dest="tx_audio_level",
default=50,
help="Set the tx audio level at an early stage",
type=int,
)
PARSER.add_argument(
"--rx-buffer-size",
dest="rx_buffer_size",
default=16,
help="Set the maximum size of rx buffer.",
type=int,
)
2022-11-05 21:27:33 +00:00
PARSER.add_argument(
"--explorer",
dest="enable_explorer",
action="store_true",
help="Enable sending tnc data to https://explorer.freedata.app",
)
2023-02-01 12:08:15 +00:00
PARSER.add_argument(
"--tune",
dest="enable_audio_auto_tune",
action="store_true",
help="Enable auto tuning of audio level with ALC information form hamlib",
)
2023-02-02 17:03:22 +00:00
PARSER.add_argument(
"--stats",
dest="enable_stats",
action="store_true",
help="Enable publishing stats to https://freedata.app",
)
2023-02-14 20:28:53 +00:00
PARSER.add_argument(
"--tci",
dest="audio_enable_tci",
action="store_true",
help="Enable TCI as audio source",
)
PARSER.add_argument(
"--tci-ip",
dest="tci_ip",
default='127.0.0.1',
type=str,
help="Set tci destination ip",
)
PARSER.add_argument(
"--tci-port",
dest="tci_port",
default=9000,
type=int,
help="Set tci destination port",
)
2023-03-06 11:48:27 +00:00
PARSER.add_argument(
"--tx-delay",
dest="tx_delay",
default=0,
help="delay in ms before modulation is pushed to audio device",
type=int,
)
2021-03-11 17:03:48 +00:00
ARGS = PARSER.parse_args()
# set save to folder state for allowing downloading files to local file system
2023-04-27 19:43:56 +00:00
ARQ.arq_save_to_folder = ARGS.savetofolder
2022-12-10 12:34:26 +00:00
if not ARGS.configfile:
2022-09-20 09:34:28 +00:00
# additional step for being sure our callsign is correctly
# in case we are not getting a station ssid
# then we are forcing a station ssid = 0
try:
mycallsign = bytes(ARGS.mycall.upper(), "utf-8")
mycallsign = helpers.callsign_to_bytes(mycallsign)
2023-04-27 19:43:56 +00:00
Station.mycallsign = helpers.bytes_to_callsign(mycallsign)
Station.mycallsign_crc = helpers.get_crc_24(Station.mycallsign)
Station.ssid_list = ARGS.ssid_list
# check if own ssid is always part of ssid list
2023-04-27 19:43:56 +00:00
own_ssid = int(Station.mycallsign.split(b"-")[1])
if own_ssid not in Station.ssid_list:
Station.ssid_list.append(own_ssid)
2023-04-27 19:43:56 +00:00
Station.mygrid = bytes(ARGS.mygrid, "utf-8")
# check if we have an int or str as device name
try:
2023-04-27 19:43:56 +00:00
AudioParam.audio_input_device = int(ARGS.audio_input_device)
except ValueError:
2023-04-27 19:43:56 +00:00
AudioParam.audio_input_device = ARGS.audio_input_device
try:
2023-04-27 19:43:56 +00:00
AudioParam.audio_output_device = int(ARGS.audio_output_device)
except ValueError:
2023-04-27 19:43:56 +00:00
AudioParam.audio_output_device = ARGS.audio_output_device
2023-04-26 17:54:53 +00:00
TNC.port = ARGS.socket_port
2023-04-27 19:43:56 +00:00
HamlibParam.hamlib_radiocontrol = ARGS.hamlib_radiocontrol
HamlibParam.hamlib_rigctld_ip = ARGS.rigctld_ip
HamlibParam.hamlib_rigctld_port = str(ARGS.rigctld_port)
ModemParam.enable_scatter = ARGS.send_scatter
AudioParam.enable_fft = ARGS.send_fft
TNC.enable_fsk = ARGS.enable_fsk
TNC.low_bandwidth_mode = ARGS.low_bandwidth_mode
ModemParam.tuning_range_fmin = ARGS.tuning_range_fmin
ModemParam.tuning_range_fmax = ARGS.tuning_range_fmax
AudioParam.tx_audio_level = ARGS.tx_audio_level
TNC.respond_to_cq = ARGS.enable_respond_to_cq
ARQ.rx_buffer_size = ARGS.rx_buffer_size
TNC.enable_explorer = ARGS.enable_explorer
AudioParam.audio_auto_tune = ARGS.enable_audio_auto_tune
TNC.enable_stats = ARGS.enable_stats
AudioParam.audio_enable_tci = ARGS.audio_enable_tci
TCIParam.ip = ARGS.tci_ip
TCIParam.port = ARGS.tci_port
ModemParam.tx_delay = ARGS.tx_delay
2023-02-01 12:08:15 +00:00
except Exception as e:
log.error("[DMN] Error reading config file", exception=e)
2022-05-09 00:41:49 +00:00
2022-12-10 12:34:26 +00:00
else:
configfile = ARGS.configfile
# init config
2023-02-18 13:51:26 +00:00
conf = config.CONFIG(configfile)
2022-12-10 12:34:26 +00:00
try:
# additional step for being sure our callsign is correctly
# in case we are not getting a station ssid
# then we are forcing a station ssid = 0
2023-02-18 13:51:26 +00:00
mycallsign = bytes(conf.get('STATION', 'mycall', 'AA0AA'), "utf-8")
2022-12-10 12:34:26 +00:00
mycallsign = helpers.callsign_to_bytes(mycallsign)
2023-04-27 19:43:56 +00:00
Station.mycallsign = helpers.bytes_to_callsign(mycallsign)
Station.mycallsign_crc = helpers.get_crc_24(Station.mycallsign)
2022-12-10 12:34:26 +00:00
2022-12-29 17:57:52 +00:00
#json.loads = for converting str list to list
2023-04-27 19:43:56 +00:00
Station.ssid_list = json.loads(conf.get('STATION', 'ssid_list', '[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]'))
2022-12-29 17:57:52 +00:00
2023-04-27 19:43:56 +00:00
Station.mygrid = bytes(conf.get('STATION', 'mygrid', 'JN12aa'), "utf-8")
2022-12-10 12:34:26 +00:00
# check if we have an int or str as device name
try:
2023-04-27 19:43:56 +00:00
AudioParam.audio_input_device = int(conf.get('AUDIO', 'rx', '0'))
2022-12-10 12:34:26 +00:00
except ValueError:
2023-04-27 19:43:56 +00:00
AudioParam.audio_input_device = conf.get('AUDIO', 'rx', '0')
2022-12-10 12:34:26 +00:00
try:
2023-04-27 19:43:56 +00:00
AudioParam.audio_output_device = int(conf.get('AUDIO', 'tx', '0'))
2022-12-10 12:34:26 +00:00
except ValueError:
2023-04-27 19:43:56 +00:00
AudioParam.audio_output_device = conf.get('AUDIO', 'tx', '0')
2022-12-10 12:34:26 +00:00
2023-04-26 17:54:53 +00:00
TNC.port = int(conf.get('NETWORK', 'tncport', '3000'))
2023-04-27 19:43:56 +00:00
HamlibParam.hamlib_radiocontrol = conf.get('RADIO', 'radiocontrol', 'rigctld')
HamlibParam.hamlib_rigctld_ip = conf.get('RADIO', 'rigctld_ip', '127.0.0.1')
HamlibParam.hamlib_rigctld_port = str(conf.get('RADIO', 'rigctld_port', '4532'))
ModemParam.enable_scatter = conf.get('TNC', 'scatter', 'True')
AudioParam.enable_fft = conf.get('TNC', 'fft', 'True')
TNC.enable_fsk = conf.get('TNC', 'fsk', 'False')
TNC.low_bandwidth_mode = conf.get('TNC', 'narrowband', 'False')
ModemParam.tuning_range_fmin = float(conf.get('TNC', 'fmin', '-50.0'))
ModemParam.tuning_range_fmax = float(conf.get('TNC', 'fmax', '50.0'))
AudioParam.tx_audio_level = int(conf.get('AUDIO', 'txaudiolevel', '100'))
TNC.respond_to_cq = conf.get('TNC', 'qrv', 'True')
2023-05-07 08:34:23 +00:00
ARQ.rx_buffer_size = int(conf.get('TNC', 'rx_buffer_size', '16'))
2023-04-27 19:43:56 +00:00
TNC.enable_explorer = conf.get('TNC', 'explorer', 'False')
AudioParam.audio_auto_tune = conf.get('AUDIO', 'auto_tune', 'False')
TNC.enable_stats = conf.get('TNC', 'stats', 'False')
AudioParam.audio_enable_tci = conf.get('AUDIO', 'enable_tci', 'False')
2023-05-04 16:41:24 +00:00
TCIParam.ip = str(conf.get('TCI', 'tci_ip', 'localhost'))
TCIParam.port = int(conf.get('TCI', 'tci_port', '50001'))
2023-04-27 19:43:56 +00:00
ModemParam.tx_delay = int(conf.get('TNC', 'tx_delay', '0'))
2022-12-10 12:34:26 +00:00
except KeyError as e:
log.warning("[CFG] Error reading config file near", key=str(e))
except Exception as e:
log.warning("[CFG] Error", e=e)
2022-12-29 17:57:52 +00:00
# make sure the own ssid is always part of the ssid list
2023-04-27 19:43:56 +00:00
my_ssid = int(Station.mycallsign.split(b'-')[1])
if my_ssid not in Station.ssid_list:
Station.ssid_list.append(my_ssid)
2022-12-29 17:57:52 +00:00
# we need to wait until we got all parameters from argparse first before we can load the other modules
2022-05-09 00:41:49 +00:00
import sock
# config logging
try:
if sys.platform == "linux":
logging_path = os.getenv("HOME") + "/.config/" + "FreeDATA/" + "tnc"
2022-05-09 00:41:49 +00:00
if sys.platform == "darwin":
logging_path = (
os.getenv("HOME")
+ "/Library/"
+ "Application Support/"
+ "FreeDATA/"
+ "tnc"
)
2022-05-09 00:41:49 +00:00
if sys.platform in ["win32", "win64"]:
logging_path = os.getenv("APPDATA") + "/" + "FreeDATA/" + "tnc"
2022-05-09 00:41:49 +00:00
2022-02-21 16:58:44 +00:00
if not os.path.exists(logging_path):
os.makedirs(logging_path)
log_handler.setup_logging(logging_path)
except Exception as err:
log.error("[DMN] logger init error", exception=err)
2022-05-09 00:41:49 +00:00
log.info(
2023-04-26 16:23:49 +00:00
"[TNC] Starting FreeDATA", author="DJ2LS", version=TNC.version
)
2022-05-09 00:41:49 +00:00
# start data handler
data_handler.DATA()
2022-05-09 00:41:49 +00:00
# start modem
modem = modem.RF()
2022-11-05 21:32:04 +00:00
# optionally start explorer module
2023-04-27 19:43:56 +00:00
if TNC.enable_explorer:
log.info("[EXPLORER] Publishing to https://explorer.freedata.app", state=TNC.enable_explorer)
2022-11-05 21:27:33 +00:00
explorer = explorer.explorer()
2021-03-12 13:14:36 +00:00
# --------------------------------------------START CMD SERVER
try:
2023-04-26 17:54:53 +00:00
log.info("[TNC] Starting TCP/IP socket", port=TNC.port)
# https://stackoverflow.com/a/16641793
socketserver.TCPServer.allow_reuse_address = True
cmdserver = sock.ThreadedTCPServer(
2023-04-26 17:54:53 +00:00
(TNC.host, TNC.port), sock.ThreadedTCPRequestHandler
)
server_thread = threading.Thread(target=cmdserver.serve_forever)
server_thread.daemon = True
server_thread.start()
except Exception as err:
2023-04-26 17:54:53 +00:00
log.error("[TNC] Starting TCP/IP socket failed", port=TNC.port, e=err)
sys.exit(1)
while True:
2023-04-26 17:54:53 +00:00
threading.Event().wait(1)