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
|
|
|
|
|
2023-10-20 12:12:20 +00:00
|
|
|
main module for running the modem
|
2020-12-23 16:48:22 +00:00
|
|
|
"""
|
2022-11-05 21:27:33 +00:00
|
|
|
|
|
|
|
|
2023-10-20 12:12:20 +00:00
|
|
|
# run modem 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
|
|
|
|
|
2022-05-11 22:10:59 +00:00
|
|
|
import multiprocessing
|
|
|
|
import os
|
|
|
|
import signal
|
2022-01-06 21:15:14 +00:00
|
|
|
import socketserver
|
2022-05-11 22:10:59 +00:00
|
|
|
import sys
|
|
|
|
import threading
|
2023-11-06 14:20:09 +00:00
|
|
|
import argparse
|
2022-09-20 09:34:28 +00:00
|
|
|
import config
|
2022-05-11 22:10:59 +00:00
|
|
|
import data_handler
|
|
|
|
import helpers
|
2022-04-11 09:10:32 +00:00
|
|
|
import log_handler
|
|
|
|
import modem
|
2023-10-20 12:12:20 +00:00
|
|
|
from global_instances import ARQ, AudioParam, Beacon, Channel, Daemon, HamlibParam, ModemParam, Station, Statistics, TCIParam, Modem, MeshParam
|
2022-05-26 01:23:30 +00:00
|
|
|
import structlog
|
2022-11-05 21:27:33 +00:00
|
|
|
import explorer
|
2022-12-29 17:57:52 +00:00
|
|
|
import json
|
2023-05-26 12:25:48 +00:00
|
|
|
import mesh
|
2023-11-04 14:20:47 +00:00
|
|
|
from os.path import exists
|
|
|
|
|
2022-06-01 00:35:35 +00:00
|
|
|
log = structlog.get_logger("main")
|
2021-11-18 18:40:22 +00:00
|
|
|
|
2022-02-16 08:11:32 +00:00
|
|
|
def signal_handler(sig, frame):
|
2022-03-04 15:50:32 +00:00
|
|
|
"""
|
|
|
|
a signal handler, which closes the network/socket when closing the application
|
|
|
|
Args:
|
|
|
|
sig: signal
|
2022-05-09 00:41:49 +00:00
|
|
|
frame:
|
2022-03-04 15:50:32 +00:00
|
|
|
|
|
|
|
Returns: system exit
|
|
|
|
|
|
|
|
"""
|
2023-10-20 12:12:20 +00:00
|
|
|
print("Closing Modem...")
|
2023-11-12 22:22:53 +00:00
|
|
|
deprecated_sock.CLOSE_SIGNAL = True
|
2022-02-16 08:11:32 +00:00
|
|
|
sys.exit(0)
|
2022-05-09 00:41:49 +00:00
|
|
|
|
2022-06-24 18:55:59 +00:00
|
|
|
|
2022-02-16 08:11:32 +00:00
|
|
|
signal.signal(signal.SIGINT, signal_handler)
|
|
|
|
|
2023-11-04 14:20:47 +00:00
|
|
|
# This is for Windows multiprocessing support
|
|
|
|
multiprocessing.freeze_support()
|
2022-09-20 09:34:28 +00:00
|
|
|
|
2023-11-06 14:20:09 +00:00
|
|
|
parser = argparse.ArgumentParser()
|
|
|
|
parser.add_argument("--use-config",
|
|
|
|
help = "Specify a config file",
|
|
|
|
default = 'config.ini')
|
|
|
|
args = parser.parse_args()
|
2023-02-02 17:03:22 +00:00
|
|
|
|
2023-11-04 14:20:47 +00:00
|
|
|
# init config
|
2023-11-06 14:20:09 +00:00
|
|
|
config_file = args.use_config
|
2023-11-04 14:20:47 +00:00
|
|
|
if not exists(config_file):
|
|
|
|
print("Config file %s not found. Exiting." % config_file)
|
|
|
|
exit(1)
|
2023-03-06 11:48:27 +00:00
|
|
|
|
2023-11-04 14:20:47 +00:00
|
|
|
conf = config.CONFIG(config_file)
|
2023-08-06 21:08:55 +00:00
|
|
|
|
2023-11-04 14:20:47 +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
|
|
|
|
mycallsign = bytes(conf.get('STATION', 'mycall', 'AA0AA'), "utf-8")
|
|
|
|
mycallsign = helpers.callsign_to_bytes(mycallsign)
|
|
|
|
Station.mycallsign = helpers.bytes_to_callsign(mycallsign)
|
|
|
|
Station.mycallsign_crc = helpers.get_crc_24(Station.mycallsign)
|
2023-10-31 08:53:31 +00:00
|
|
|
|
2023-11-04 14:20:47 +00:00
|
|
|
#json.loads = for converting str list to list
|
|
|
|
Station.ssid_list = json.loads(conf.get('STATION', 'ssid_list', '[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]'))
|
2023-10-31 08:53:31 +00:00
|
|
|
|
2023-11-04 14:20:47 +00:00
|
|
|
# init config
|
|
|
|
conf = config.CONFIG(config_file)
|
|
|
|
try:
|
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
|
2023-11-04 14:20:47 +00:00
|
|
|
mycallsign = bytes(conf.get('STATION', 'mycall', 'AA0AA'), "utf-8")
|
|
|
|
mycallsign = helpers.callsign_to_bytes(mycallsign)
|
|
|
|
Station.mycallsign = helpers.bytes_to_callsign(mycallsign)
|
|
|
|
Station.mycallsign_crc = helpers.get_crc_24(Station.mycallsign)
|
|
|
|
|
|
|
|
#json.loads = for converting str list to list
|
|
|
|
Station.ssid_list = json.loads(conf.get('STATION', 'ssid_list', '[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]'))
|
|
|
|
|
|
|
|
Station.mygrid = bytes(conf.get('STATION', 'mygrid', 'JN12aa'), "utf-8")
|
|
|
|
# check if we have an int or str as device name
|
2022-11-09 11:23:59 +00:00
|
|
|
try:
|
2023-11-04 14:20:47 +00:00
|
|
|
AudioParam.audio_input_device = int(conf.get('AUDIO', 'rx', '0'))
|
|
|
|
except ValueError:
|
|
|
|
AudioParam.audio_input_device = conf.get('AUDIO', 'rx', '0')
|
2022-12-10 12:34:26 +00:00
|
|
|
try:
|
2023-11-04 14:20:47 +00:00
|
|
|
AudioParam.audio_output_device = int(conf.get('AUDIO', 'tx', '0'))
|
|
|
|
except ValueError:
|
|
|
|
AudioParam.audio_output_device = conf.get('AUDIO', 'tx', '0')
|
|
|
|
|
|
|
|
Modem.port = int(conf.get('NETWORK', 'modemport', '3000'))
|
|
|
|
HamlibParam.hamlib_radiocontrol = conf.get('RADIO', 'radiocontrol', 'disabled')
|
|
|
|
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('Modem', 'scatter', 'True')
|
|
|
|
AudioParam.enable_fft = conf.get('Modem', 'fft', 'True')
|
|
|
|
Modem.enable_fsk = conf.get('Modem', 'fsk', 'False')
|
|
|
|
Modem.low_bandwidth_mode = conf.get('Modem', 'narrowband', 'False')
|
|
|
|
ModemParam.tuning_range_fmin = float(conf.get('Modem', 'fmin', '-50.0'))
|
|
|
|
ModemParam.tuning_range_fmax = float(conf.get('Modem', 'fmax', '50.0'))
|
|
|
|
AudioParam.tx_audio_level = int(conf.get('AUDIO', 'txaudiolevel', '0'))
|
|
|
|
AudioParam.rx_audio_level = int(conf.get('AUDIO', 'rxaudiolevel', '0'))
|
|
|
|
Modem.respond_to_cq = conf.get('Modem', 'qrv', 'True')
|
|
|
|
ARQ.rx_buffer_size = int(conf.get('Modem', 'rx_buffer_size', '16'))
|
|
|
|
Modem.enable_explorer = conf.get('Modem', 'explorer', 'False')
|
|
|
|
AudioParam.audio_auto_tune = conf.get('AUDIO', 'auto_tune', 'False')
|
|
|
|
Modem.enable_stats = conf.get('Modem', 'stats', 'False')
|
|
|
|
TCIParam.ip = str(conf.get('TCI', 'tci_ip', 'localhost'))
|
|
|
|
TCIParam.port = int(conf.get('TCI', 'tci_port', '50001'))
|
|
|
|
ModemParam.tx_delay = int(conf.get('Modem', 'tx_delay', '0'))
|
|
|
|
MeshParam.enable_protocol = conf.get('MESH','mesh_enable','False')
|
|
|
|
MeshParam.transmit_morse_identifier = conf.get('Modem','transmit_morse_identifier','False')
|
|
|
|
|
|
|
|
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-10 12:34:26 +00:00
|
|
|
|
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
|
|
|
|
2023-11-04 14:20:47 +00:00
|
|
|
Station.mygrid = bytes(conf.get('STATION', 'mygrid', 'JN12aa'), "utf-8")
|
|
|
|
# check if we have an int or str as device name
|
|
|
|
|
2021-05-13 15:11:26 +00:00
|
|
|
# we need to wait until we got all parameters from argparse first before we can load the other modules
|
2023-11-12 22:22:53 +00:00
|
|
|
import deprecated_sock
|
2022-05-09 00:41:49 +00:00
|
|
|
|
|
|
|
try:
|
2023-11-04 14:20:47 +00:00
|
|
|
AudioParam.audio_input_device = int(conf.get('AUDIO', 'rx', '0'))
|
|
|
|
except ValueError:
|
|
|
|
AudioParam.audio_input_device = conf.get('AUDIO', 'rx', '0')
|
|
|
|
try:
|
|
|
|
AudioParam.audio_output_device = int(conf.get('AUDIO', 'tx', '0'))
|
|
|
|
except ValueError:
|
|
|
|
AudioParam.audio_output_device = conf.get('AUDIO', 'tx', '0')
|
|
|
|
|
|
|
|
Modem.port = int(conf.get('NETWORK', 'modemport', '3000'))
|
|
|
|
HamlibParam.hamlib_radiocontrol = conf.get('RADIO', 'radiocontrol', 'disabled')
|
|
|
|
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('Modem', 'scatter', 'True')
|
|
|
|
AudioParam.enable_fft = conf.get('Modem', 'fft', 'True')
|
|
|
|
Modem.enable_fsk = conf.get('Modem', 'fsk', 'False')
|
|
|
|
Modem.low_bandwidth_mode = conf.get('Modem', 'narrowband', 'False')
|
|
|
|
ModemParam.tuning_range_fmin = float(conf.get('Modem', 'fmin', '-50.0'))
|
|
|
|
ModemParam.tuning_range_fmax = float(conf.get('Modem', 'fmax', '50.0'))
|
|
|
|
AudioParam.tx_audio_level = int(conf.get('AUDIO', 'txaudiolevel', '100'))
|
|
|
|
Modem.respond_to_cq = conf.get('Modem', 'qrv', 'True')
|
|
|
|
ARQ.rx_buffer_size = int(conf.get('Modem', 'rx_buffer_size', '16'))
|
|
|
|
ARQ.arq_save_to_folder = conf.get('Modem', 'save_to_folder', 'False')
|
|
|
|
Modem.enable_explorer = conf.get('Modem', 'explorer', 'False')
|
|
|
|
AudioParam.audio_auto_tune = conf.get('AUDIO', 'auto_tune', 'False')
|
|
|
|
Modem.enable_stats = conf.get('Modem', 'stats', 'False')
|
|
|
|
TCIParam.ip = str(conf.get('TCI', 'tci_ip', 'localhost'))
|
|
|
|
TCIParam.port = int(conf.get('TCI', 'tci_port', '50001'))
|
|
|
|
ModemParam.tx_delay = int(conf.get('Modem', 'tx_delay', '0'))
|
|
|
|
MeshParam.enable_protocol = conf.get('MESH','mesh_enable','False')
|
|
|
|
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)
|
|
|
|
|
|
|
|
# make sure the own ssid is always part of the ssid list
|
|
|
|
my_ssid = int(Station.mycallsign.split(b'-')[1])
|
|
|
|
if my_ssid not in Station.ssid_list:
|
|
|
|
Station.ssid_list.append(my_ssid)
|
|
|
|
|
|
|
|
# we need to wait until we got all parameters from argparse first before we can load the other modules
|
2023-11-12 22:22:53 +00:00
|
|
|
import deprecated_sock
|
2023-11-04 14:20:47 +00:00
|
|
|
|
|
|
|
# config logging
|
|
|
|
try:
|
|
|
|
if sys.platform == "linux":
|
|
|
|
logging_path = os.getenv("HOME") + "/.config/" + "FreeDATA/" + "modem"
|
|
|
|
|
|
|
|
if sys.platform == "darwin":
|
|
|
|
logging_path = (
|
|
|
|
os.getenv("HOME")
|
|
|
|
+ "/Library/"
|
|
|
|
+ "Application Support/"
|
|
|
|
+ "FreeDATA/"
|
|
|
|
+ "modem"
|
|
|
|
)
|
2022-05-09 00:41:49 +00:00
|
|
|
|
2023-11-04 14:20:47 +00:00
|
|
|
if sys.platform in ["win32", "win64"]:
|
|
|
|
logging_path = os.getenv("APPDATA") + "/" + "FreeDATA/" + "modem"
|
2022-05-09 00:41:49 +00:00
|
|
|
|
2023-11-04 14:20:47 +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-01-07 10:25:28 +00:00
|
|
|
|
2023-11-04 14:20:47 +00:00
|
|
|
log.info(
|
|
|
|
"[Modem] Starting FreeDATA", author="DJ2LS", version=Modem.version
|
|
|
|
)
|
2023-05-26 12:25:48 +00:00
|
|
|
|
2023-11-04 14:20:47 +00:00
|
|
|
# start data handler
|
2023-11-07 10:36:49 +00:00
|
|
|
data_handler.DATA(conf.config)
|
2022-11-05 21:27:33 +00:00
|
|
|
|
2023-11-04 14:20:47 +00:00
|
|
|
# start modem
|
2023-11-07 10:36:49 +00:00
|
|
|
modem = modem.RF(conf.config)
|
2023-11-04 14:20:47 +00:00
|
|
|
|
|
|
|
# start mesh protocol only if enabled
|
|
|
|
if MeshParam.enable_protocol:
|
|
|
|
log.info("[MESH] loading module")
|
|
|
|
# start mesh module
|
|
|
|
mesh = mesh.MeshRouter()
|
|
|
|
|
|
|
|
# optionally start explorer module
|
|
|
|
if Modem.enable_explorer:
|
|
|
|
log.info("[EXPLORER] Publishing to https://explorer.freedata.app", state=Modem.enable_explorer)
|
|
|
|
explorer = explorer.explorer()
|
|
|
|
|
|
|
|
# --------------------------------------------START CMD SERVER
|
|
|
|
try:
|
|
|
|
log.info("[Modem] Starting TCP/IP socket", port=Modem.port)
|
|
|
|
# https://stackoverflow.com/a/16641793
|
|
|
|
socketserver.TCPServer.allow_reuse_address = True
|
|
|
|
cmdserver = sock.ThreadedTCPServer(
|
|
|
|
(Modem.host, Modem.port), sock.ThreadedTCPRequestHandler
|
|
|
|
)
|
|
|
|
server_thread = threading.Thread(target=cmdserver.serve_forever)
|
|
|
|
|
|
|
|
server_thread.daemon = True
|
|
|
|
server_thread.start()
|
2022-02-16 08:11:32 +00:00
|
|
|
|
2023-11-04 14:20:47 +00:00
|
|
|
except Exception as err:
|
|
|
|
log.error("[Modem] Starting TCP/IP socket failed", port=Modem.port, e=err)
|
|
|
|
sys.exit(1)
|
2022-01-06 21:15:14 +00:00
|
|
|
|
2023-11-04 14:20:47 +00:00
|
|
|
server_thread.join()
|