diff --git a/tnc/audio.py b/tnc/audio.py index 23c438dd..5ec339f8 100644 --- a/tnc/audio.py +++ b/tnc/audio.py @@ -1,4 +1,3 @@ - import atexit import json import multiprocessing @@ -8,15 +7,16 @@ import sounddevice as sd atexit.register(sd._terminate) + def get_audio_devices(): """ return list of input and output audio devices in own process to avoid crashes of portaudio on raspberry pi also uses a process data manager """ - # we need to run this on windows for multiprocessing support + # we need to run this on Windows for multiprocessing support # multiprocessing.freeze_support() - #multiprocessing.get_context('spawn') + # multiprocessing.get_context("spawn") # we need to reset and initialize sounddevice before running the multiprocessing part. # If we are not doing this at this early point, not all devices will be displayed @@ -26,41 +26,43 @@ def get_audio_devices(): with multiprocessing.Manager() as manager: proxy_input_devices = manager.list() proxy_output_devices = manager.list() - #print(multiprocessing.get_start_method()) - p = multiprocessing.Process(target=fetch_audio_devices, args=(proxy_input_devices, proxy_output_devices)) - p.start() - p.join() + # print(multiprocessing.get_start_method()) + proc = multiprocessing.Process( + target=fetch_audio_devices, args=(proxy_input_devices, proxy_output_devices) + ) + proc.start() + proc.join() return list(proxy_input_devices), list(proxy_output_devices) + def fetch_audio_devices(input_devices, output_devices): """ get audio devices from portaudio Args: input_devices: proxy variable for input devices - output_devices: proxy variable for outout devices + output_devices: proxy variable for output devices Returns: """ devices = sd.query_devices(device=None, kind=None) for index, device in enumerate(devices): - #for i in range(0, p.get_device_count()): - # we need to do a try exception, beacuse for windows theres no audio device range + # Use a try/except block beacuse Windows doesn't have an audio device range try: name = device["name"] - maxOutputChannels = device["max_output_channels"] - maxInputChannels = device["max_input_channels"] + max_output_channels = device["max_output_channels"] + max_input_channels = device["max_input_channels"] - except Exception as e: - print(e) - maxInputChannels = 0 - maxOutputChannels = 0 - name = '' + except Exception as err: + print(err) + max_input_channels = 0 + max_output_channels = 0 + name = "" - if maxInputChannels > 0: + if max_input_channels > 0: input_devices.append({"id": index, "name": name}) - if maxOutputChannels > 0: + if max_output_channels > 0: output_devices.append({"id": index, "name": name}) diff --git a/tnc/codec2.py b/tnc/codec2.py index d1c10af4..be458ff6 100644 --- a/tnc/codec2.py +++ b/tnc/codec2.py @@ -16,6 +16,7 @@ from threading import Lock import numpy as np import structlog +log = structlog.get_logger(__file__) # Enum for codec2 modes class FREEDV_MODE(Enum): @@ -65,7 +66,7 @@ if hasattr(sys, "_MEIPASS"): else: sys.path.append(os.path.abspath(".")) -structlog.get_logger("structlog").info("[C2 ] Searching for libcodec2...") +log.info("[C2 ] Searching for libcodec2...") if sys.platform == "linux": files = glob.glob(r"**/*libcodec2*", recursive=True) files.append("libcodec2.so") @@ -80,16 +81,14 @@ api = None for file in files: try: api = ctypes.CDLL(file) - structlog.get_logger("structlog").info("[C2 ] Libcodec2 loaded", path=file) + log.info("[C2 ] Libcodec2 loaded", path=file) break - except OSError as e: - structlog.get_logger("structlog").warning( - "[C2 ] Libcodec2 found but not loaded", path=file, e=e - ) + except OSError as err: + log.warning("[C2 ] Libcodec2 found but not loaded", path=file, e=err) # Quit module if codec2 cant be loaded if api is None or "api" not in locals(): - structlog.get_logger("structlog").critical("[C2 ] Libcodec2 not loaded") + log.critical("[C2 ] Libcodec2 not loaded - Exiting") sys.exit(1) # ctypes function init @@ -271,7 +270,7 @@ api.rx_sync_flags_to_text = [ # type: ignore # Audio buffer --------------------------------------------------------- class audio_buffer: """ - Thread safe audio buffer, which fits to needs of codec2 + Thread-safe audio buffer, which fits the needs of codec2 made by David Rowe, VK5DGR """ @@ -279,9 +278,7 @@ class audio_buffer: # A buffer of int16 samples, using a fixed length numpy array self.buffer for storage # self.nbuffer is the current number of samples in the buffer def __init__(self, size): - structlog.get_logger("structlog").debug( - "[C2 ] Creating audio buffer", size=size - ) + log.debug("[C2 ] Creating audio buffer", size=size) self.size = size self.buffer = np.zeros(size, dtype=np.int16) self.nbuffer = 0 @@ -343,7 +340,7 @@ class resampler: MEM48 = api.FDMDV_OS_TAPS_48K def __init__(self): - structlog.get_logger("structlog").debug("[C2 ] Create 48<->8 kHz resampler") + log.debug("[C2 ] Create 48<->8 kHz resampler") self.filter_mem8 = np.zeros(self.MEM8, dtype=np.int16) self.filter_mem48 = np.zeros(self.MEM48) diff --git a/tnc/daemon.py b/tnc/daemon.py index 9b7c166d..4297f7a8 100755 --- a/tnc/daemon.py +++ b/tnc/daemon.py @@ -15,8 +15,6 @@ import argparse import atexit import multiprocessing import os -import queue -import re import signal import socketserver import subprocess @@ -24,17 +22,14 @@ import sys import threading import time -import crcengine -import psutil -import serial.tools.list_ports -import structlog -import ujson as json - import audio -import helpers +import crcengine import log_handler +import serial.tools.list_ports import sock import static +import structlog +import ujson as json # signal handler for closing aplication @@ -47,26 +42,35 @@ def signal_handler(sig, frame): Returns: system exit """ - print('Closing daemon...') + print("Closing daemon...") sock.CLOSE_SIGNAL = True sys.exit(0) + signal.signal(signal.SIGINT, signal_handler) -class DAEMON(): + +class DAEMON: """ Daemon class """ + + log = structlog.get_logger(__name__) + def __init__(self): # load crc engine - self.crc_algorithm = crcengine.new('crc16-ccitt-false') # load crc8 library + self.crc_algorithm = crcengine.new("crc16-ccitt-false") # load crc8 library self.daemon_queue = sock.DAEMON_QUEUE - update_audio_devices = threading.Thread(target=self.update_audio_devices, name="UPDATE_AUDIO_DEVICES", daemon=True) + update_audio_devices = threading.Thread( + target=self.update_audio_devices, name="UPDATE_AUDIO_DEVICES", daemon=True + ) update_audio_devices.start() - update_serial_devices = threading.Thread(target=self.update_serial_devices, name="UPDATE_SERIAL_DEVICES", daemon=True) + update_serial_devices = threading.Thread( + target=self.update_serial_devices, name="UPDATE_SERIAL_DEVICES", daemon=True + ) update_serial_devices.start() worker = threading.Thread(target=self.worker, name="WORKER", daemon=True) @@ -79,9 +83,15 @@ class DAEMON(): while 1: try: if not static.TNCSTARTED: - static.AUDIO_INPUT_DEVICES, static.AUDIO_OUTPUT_DEVICES = audio.get_audio_devices() - except Exception as e: - structlog.get_logger("structlog").error("[DMN] update_audio_devices: Exception gathering audio devices:", e=e) + ( + static.AUDIO_INPUT_DEVICES, + static.AUDIO_OUTPUT_DEVICES, + ) = audio.get_audio_devices() + except Exception as err1: + self.log.error( + "[DMN] update_audio_devices: Exception gathering audio devices:", + e=err1, + ) # print(e) time.sleep(1) @@ -91,21 +101,26 @@ class DAEMON(): """ while 1: try: - #print("update serial") + # print("update serial") serial_devices = [] ports = serial.tools.list_ports.comports() for port, desc, hwid in ports: # calculate hex of hwid if we have unique names - crc_hwid = self.crc_algorithm(bytes(hwid, encoding='utf-8')) - crc_hwid = crc_hwid.to_bytes(2, byteorder='big') + crc_hwid = self.crc_algorithm(bytes(hwid, encoding="utf-8")) + crc_hwid = crc_hwid.to_bytes(2, byteorder="big") crc_hwid = crc_hwid.hex() description = f"{desc} [{crc_hwid}]" - serial_devices.append({"port": str(port), "description": str(description) }) + serial_devices.append( + {"port": str(port), "description": str(description)} + ) static.SERIAL_DEVICES = serial_devices time.sleep(1) - except Exception as e: - structlog.get_logger("structlog").error("[DMN] update_serial_devices: Exception gathering serial devices:", e=e) + except Exception as err1: + self.log.error( + "[DMN] update_serial_devices: Exception gathering serial devices:", + e=err1, + ) # print(e) def worker(self): @@ -140,131 +155,131 @@ class DAEMON(): # data[22] tx-audio-level # data[23] respond_to_cq - if data[0] == 'STARTTNC': - structlog.get_logger("structlog").warning("[DMN] Starting TNC", rig=data[5], port=data[6]) + if data[0] == "STARTTNC": + self.log.warning("[DMN] Starting TNC", rig=data[5], port=data[6]) # list of parameters, necessary for running subprocess command as a list options = [] - options.append('--port') + options.append("--port") options.append(str(static.DAEMONPORT - 1)) - options.append('--mycall') + options.append("--mycall") options.append(data[1]) - options.append('--mygrid') + options.append("--mygrid") options.append(data[2]) - options.append('--rx') + options.append("--rx") options.append(data[3]) - options.append('--tx') + options.append("--tx") options.append(data[4]) # if radiocontrol != disabled # this should hopefully avoid a ton of problems if we are just running in # disabled mode - if data[13] != 'disabled': - options.append('--devicename') + if data[13] != "disabled": + options.append("--devicename") options.append(data[5]) - options.append('--deviceport') + options.append("--deviceport") options.append(data[6]) - options.append('--serialspeed') + options.append("--serialspeed") options.append(data[7]) - options.append('--pttprotocol') + options.append("--pttprotocol") options.append(data[8]) - options.append('--pttport') + options.append("--pttport") options.append(data[9]) - options.append('--data_bits') + options.append("--data_bits") options.append(data[10]) - options.append('--stop_bits') + options.append("--stop_bits") options.append(data[11]) - options.append('--handshake') + options.append("--handshake") options.append(data[12]) - options.append('--radiocontrol') + options.append("--radiocontrol") options.append(data[13]) - if data[13] == 'rigctld': - options.append('--rigctld_ip') + if data[13] == "rigctld": + options.append("--rigctld_ip") options.append(data[14]) - options.append('--rigctld_port') + options.append("--rigctld_port") options.append(data[15]) - if data[16] == 'True': - options.append('--scatter') + if data[16] == "True": + options.append("--scatter") - if data[17] == 'True': - options.append('--fft') + if data[17] == "True": + options.append("--fft") - if data[18] == 'True': - options.append('--500hz') + if data[18] == "True": + options.append("--500hz") - options.append('--tuning_range_fmin') + options.append("--tuning_range_fmin") options.append(data[19]) - options.append('--tuning_range_fmax') + options.append("--tuning_range_fmax") options.append(data[20]) # overriding FSK mode - #if data[21] == 'True': - # options.append('--fsk') + # if data[21] == "True": + # options.append("--fsk") - options.append('--tx-audio-level') + options.append("--tx-audio-level") options.append(data[22]) - if data[23] == 'True': - options.append('--qrv') + if data[23] == "True": + options.append("--qrv") # Try running tnc from binary, else run from source # This helps running the tnc in a developer environment try: command = [] - if sys.platform in ['linux', 'darwin']: - command.append('./freedata-tnc') - elif sys.platform in ['win32', 'win64']: - command.append('freedata-tnc.exe') + if sys.platform in ["linux", "darwin"]: + command.append("./freedata-tnc") + elif sys.platform in ["win32", "win64"]: + command.append("freedata-tnc.exe") command += options p = subprocess.Popen(command) atexit.register(p.kill) - structlog.get_logger("structlog").info("[DMN] TNC started", path="binary") - except FileNotFoundError as e: - structlog.get_logger("structlog").error("[DMN] worker: Exception:", e=e) + self.log.info("[DMN] TNC started", path="binary") + except FileNotFoundError as err1: + self.log.info("[DMN] worker: ", e=err1) command = [] - if sys.platform in ['linux', 'darwin']: - command.append('python3') - elif sys.platform in ['win32', 'win64']: - command.append('python') + if sys.platform in ["linux", "darwin"]: + command.append("python3") + elif sys.platform in ["win32", "win64"]: + command.append("python") - command.append('main.py') + command.append("main.py") command += options p = subprocess.Popen(command) atexit.register(p.kill) - structlog.get_logger("structlog").info("[DMN] TNC started", path="source") + self.log.info("[DMN] TNC started", path="source") static.TNCPROCESS = p # .pid static.TNCSTARTED = True - ''' + """ # WE HAVE THIS PART in SOCKET - if data[0] == 'STOPTNC': + if data[0] == "STOPTNC": static.TNCPROCESS.kill() - structlog.get_logger("structlog").warning("[DMN] Stopping TNC") + self.log.warning("[DMN] Stopping TNC") #os.kill(static.TNCPROCESS, signal.SIGKILL) static.TNCSTARTED = False - ''' + """ # data[1] devicename # data[2] deviceport # data[3] serialspeed @@ -276,7 +291,7 @@ class DAEMON(): # data[9] radiocontrol # data[10] rigctld_ip # data[11] rigctld_port - if data[0] == 'TEST_HAMLIB': + if data[0] == "TEST_HAMLIB": devicename = data[1] deviceport = data[2] serialspeed = data[3] @@ -290,19 +305,28 @@ class DAEMON(): rigctld_port = data[11] # check how we want to control the radio - if radiocontrol == 'direct': + if radiocontrol == "direct": import rig - elif radiocontrol == 'rigctl': + elif radiocontrol == "rigctl": import rigctl as rig - elif radiocontrol == 'rigctld': + elif radiocontrol == "rigctld": import rigctld as rig else: import rigdummy as rig hamlib = rig.radio() - hamlib.open_rig(devicename=devicename, deviceport=deviceport, hamlib_ptt_type=pttprotocol, - serialspeed=serialspeed, pttport=pttport, data_bits=data_bits, stop_bits=stop_bits, - handshake=handshake, rigctld_ip=rigctld_ip, rigctld_port = rigctld_port) + hamlib.open_rig( + devicename=devicename, + deviceport=deviceport, + hamlib_ptt_type=pttprotocol, + serialspeed=serialspeed, + pttport=pttport, + data_bits=data_bits, + stop_bits=stop_bits, + handshake=handshake, + rigctld_ip=rigctld_ip, + rigctld_port=rigctld_port, + ) hamlib_version = rig.hamlib_version @@ -310,14 +334,14 @@ class DAEMON(): pttstate = hamlib.get_ptt() if pttstate: - structlog.get_logger("structlog").info("[DMN] Hamlib PTT", status='SUCCESS') - response = {'command': 'test_hamlib', 'result': 'SUCCESS'} + self.log.info("[DMN] Hamlib PTT", status="SUCCESS") + response = {"command": "test_hamlib", "result": "SUCCESS"} elif not pttstate: - structlog.get_logger("structlog").warning("[DMN] Hamlib PTT", status='NO SUCCESS') - response = {'command': 'test_hamlib', 'result': 'NOSUCCESS'} + self.log.warning("[DMN] Hamlib PTT", status="NO SUCCESS") + response = {"command": "test_hamlib", "result": "NOSUCCESS"} else: - structlog.get_logger("structlog").error("[DMN] Hamlib PTT", status='FAILED') - response = {'command': 'test_hamlib', 'result': 'FAILED'} + self.log.error("[DMN] Hamlib PTT", status="FAILED") + response = {"command": "test_hamlib", "result": "FAILED"} hamlib.set_ptt(False) hamlib.close_rig() @@ -325,51 +349,74 @@ class DAEMON(): jsondata = json.dumps(response) sock.SOCKET_QUEUE.put(jsondata) - except Exception as e: - structlog.get_logger("structlog").error("[DMN] worker: Exception: ", e=e) + except Exception as err1: + self.log.error("[DMN] worker: Exception: ", e=err1) # print(e) -if __name__ == '__main__': + +if __name__ == "__main__": + mainlog = structlog.get_logger(__file__) # we need to run this on windows for multiprocessing support multiprocessing.freeze_support() # --------------------------------------------GET PARAMETER INPUTS - PARSER = argparse.ArgumentParser(description='FreeDATA Daemon') - PARSER.add_argument('--port', dest="socket_port", default=3001, help="Socket port in the range of 1024-65536", type=int) + PARSER = argparse.ArgumentParser(description="FreeDATA Daemon") + PARSER.add_argument( + "--port", + dest="socket_port", + default=3001, + help="Socket port in the range of 1024-65536", + type=int, + ) ARGS = PARSER.parse_args() static.DAEMONPORT = ARGS.socket_port try: - if sys.platform == 'linux': - logging_path = os.getenv("HOME") + '/.config/' + 'FreeDATA/' + 'daemon' + if sys.platform == "linux": + logging_path = os.getenv("HOME") + "/.config/" + "FreeDATA/" + "daemon" - if sys.platform == 'darwin': - logging_path = os.getenv("HOME") + '/Library/' + 'Application Support/' + 'FreeDATA/' + 'daemon' + if sys.platform == "darwin": + logging_path = ( + os.getenv("HOME") + + "/Library/" + + "Application Support/" + + "FreeDATA/" + + "daemon" + ) - if sys.platform in ['win32', 'win64']: - logging_path = os.getenv('APPDATA') + '/' + 'FreeDATA/' + 'daemon' + if sys.platform in ["win32", "win64"]: + logging_path = os.getenv("APPDATA") + "/" + "FreeDATA/" + "daemon" if not os.path.exists(logging_path): os.makedirs(logging_path) log_handler.setup_logging(logging_path) - except Exception as e: - structlog.get_logger("structlog").error("[DMN] logger init error", exception=e) + except Exception as err: + mainlog.error("[DMN] logger init error", exception=err) try: - structlog.get_logger("structlog").info("[DMN] Starting TCP/IP socket", port=static.DAEMONPORT) + mainlog.info("[DMN] Starting TCP/IP socket", port=static.DAEMONPORT) # https://stackoverflow.com/a/16641793 socketserver.TCPServer.allow_reuse_address = True - cmdserver = sock.ThreadedTCPServer((static.HOST, static.DAEMONPORT), sock.ThreadedTCPRequestHandler) + cmdserver = sock.ThreadedTCPServer( + (static.HOST, static.DAEMONPORT), sock.ThreadedTCPRequestHandler + ) server_thread = threading.Thread(target=cmdserver.serve_forever) server_thread.daemon = True server_thread.start() - except Exception as e: - structlog.get_logger("structlog").error("[DMN] Starting TCP/IP socket failed", port=static.DAEMONPORT, e=e) + except Exception as err: + mainlog.error( + "[DMN] Starting TCP/IP socket failed", port=static.DAEMONPORT, e=err + ) sys.exit(1) daemon = DAEMON() - structlog.get_logger("structlog").info("[DMN] Starting FreeDATA Daemon", author="DJ2LS", year="2022", version=static.VERSION) + mainlog.info( + "[DMN] Starting FreeDATA Daemon", + author="DJ2LS", + year="2022", + version=static.VERSION, + ) while True: time.sleep(1) diff --git a/tnc/data_handler.py b/tnc/data_handler.py index d5e00e33..875094b7 100644 --- a/tnc/data_handler.py +++ b/tnc/data_handler.py @@ -81,7 +81,7 @@ class DATA: self.data_channel_max_retries = 5 self.datachannel_timeout = False - # List of codec2 modes to use in 'low bandwidth' mode. + # List of codec2 modes to use in "low bandwidth" mode. self.mode_list_low_bw = [ codec2.FREEDV_MODE.datac0.value, codec2.FREEDV_MODE.datac3.value, @@ -89,7 +89,7 @@ class DATA: # List for time to wait for corresponding mode in seconds self.time_list_low_bw = [3, 7] - # List of codec2 modes to use in 'high bandwidth' mode. + # List of codec2 modes to use in "high bandwidth" mode. self.mode_list_high_bw = [ codec2.FREEDV_MODE.datac0.value, codec2.FREEDV_MODE.datac3.value, @@ -101,13 +101,13 @@ class DATA: # Mode list for selecting between low bandwidth ( 500Hz ) and modes with higher bandwidth # but ability to fall back to low bandwidth modes if needed. if static.LOW_BANDWITH_MODE: - # List of codec2 modes to use in 'low bandwidth' mode. + # List of codec2 modes to use in "low bandwidth" mode. self.mode_list = self.mode_list_low_bw # list of times to wait for corresponding mode in seconds self.time_list = self.time_list_low_bw else: - # List of codec2 modes to use in 'high bandwidth' mode. + # List of codec2 modes to use in "high bandwidth" mode. self.mode_list = self.mode_list_high_bw # list of times to wait for corresponding mode in seconds self.time_list = self.time_list_high_bw @@ -968,7 +968,7 @@ class DATA: break # break retry loop # We need this part for leaving the repeat loop - # static.ARQ_STATE == 'DATA' --> when stopping transmission manually + # static.ARQ_STATE == "DATA" --> when stopping transmission manually if not static.ARQ_STATE: # print("not ready for data...leaving loop....") break @@ -1205,10 +1205,7 @@ class DATA: # ############################################################################################################ def arq_session_handler(self) -> bool: """ - Create a session with `callsign` and wait until the session is open. - - Args: - callsign: + Create a session with `static.DXCALLSIGN` and wait until the session is open. Returns: True if the session was opened successfully @@ -1233,7 +1230,7 @@ class DATA: static.ARQ_SESSION_STATE = "connecting" if static.ARQ_SESSION and static.ARQ_SESSION_STATE == "connected": - # static.ARQ_SESSION_STATE = 'connected' + # static.ARQ_SESSION_STATE = "connected" return True static.ARQ_SESSION_STATE = "failed" @@ -1278,7 +1275,6 @@ class DATA: time.sleep(0.01) # Stop waiting if data channel is opened if static.ARQ_SESSION: - # eventuell einfach nur return true um die nächste break ebene zu vermeiden? return True # Session connect timeout, send close_session frame to @@ -1392,8 +1388,8 @@ class DATA: def transmit_session_heartbeat(self) -> None: """Send ARQ sesion heartbeat while connected""" # static.ARQ_SESSION = True - # static.TNC_STATE = 'BUSY' - # static.ARQ_SESSION_STATE = 'connected' + # static.TNC_STATE = "BUSY" + # static.ARQ_SESSION_STATE = "connected" connection_frame = bytearray(14) connection_frame[:1] = bytes([222]) @@ -1933,9 +1929,7 @@ class DATA: if static.ENABLE_FSK: self.enqueue_frame_for_tx( beacon_frame, - c2_mode=codec2.freedv_get_mode_value_by_name( - "FSK_LDPC_0" - ), + c2_mode=codec2.FREEDV_MODE.fsk_ldpc_0.value, ) else: self.enqueue_frame_for_tx(beacon_frame) @@ -2010,7 +2004,7 @@ class DATA: if static.ENABLE_FSK: self.enqueue_frame_for_tx( - cq_frame, c2_mode=codec2.freedv_get_mode_value_by_name("FSK_LDPC_0") + cq_frame, c2_mode=codec2.FREEDV_MODE.fsk_ldpc_0.value ) else: self.enqueue_frame_for_tx(cq_frame) @@ -2074,7 +2068,7 @@ class DATA: if static.ENABLE_FSK: self.enqueue_frame_for_tx( - qrv_frame, c2_mode=codec2.freedv_get_mode_value_by_name("FSK_LDPC_0") + qrv_frame, c2_mode=codec2.FREEDV_MODE.fsk_ldpc_0.value ) else: self.enqueue_frame_for_tx(qrv_frame) @@ -2299,18 +2293,16 @@ class DATA: """ # set modes we want to listen to - mode_name = codec2.freedv_get_mode_name_by_value(mode) - - if mode_name == "datac1": + if mode == codec2.FREEDV_MODE.datac1.value: modem.RECEIVE_DATAC1 = True self.log.debug("[TNC] Changing listening data mode", mode="datac1") - elif mode_name == "datac3": + elif mode == codec2.FREEDV_MODE.datac3.value: modem.RECEIVE_DATAC3 = True self.log.debug("[TNC] Changing listening data mode", mode="datac3") - elif mode_name == "fsk_ldpc_1": + elif mode == codec2.FREEDV_MODE.fsk_ldpc_1.value: modem.RECEIVE_FSK_LDPC_1 = True self.log.debug("[TNC] Changing listening data mode", mode="fsk_ldpc_1") - elif mode_name == "allmodes": + elif mode == codec2.FREEDV_MODE.allmodes.value: modem.RECEIVE_DATAC1 = True modem.RECEIVE_DATAC3 = True modem.RECEIVE_FSK_LDPC_1 = True diff --git a/tnc/helpers.py b/tnc/helpers.py index 00378f7d..06471ab8 100644 --- a/tnc/helpers.py +++ b/tnc/helpers.py @@ -8,9 +8,10 @@ Created on Fri Dec 25 21:25:14 2020 import time import crcengine +import static import structlog -import static +log = structlog.get_logger(__file__) def wait(seconds: float) -> bool: @@ -27,6 +28,7 @@ def wait(seconds: float) -> bool: time.sleep(0.01) return True + def get_crc_8(data) -> bytes: """Author: DJ2LS @@ -40,11 +42,12 @@ def get_crc_8(data) -> bytes: Returns: CRC-8 (CCITT) of the provided data as bytes """ - crc_algorithm = crcengine.new('crc8-ccitt') # load crc8 library + crc_algorithm = crcengine.new("crc8-ccitt") # load crc8 library crc_data = crc_algorithm(data) - crc_data = crc_data.to_bytes(1, byteorder='big') + crc_data = crc_data.to_bytes(1, byteorder="big") return crc_data + def get_crc_16(data) -> bytes: """Author: DJ2LS @@ -58,11 +61,12 @@ def get_crc_16(data) -> bytes: Returns: CRC-16 (CCITT) of the provided data as bytes """ - crc_algorithm = crcengine.new('crc16-ccitt-false') # load crc16 library + crc_algorithm = crcengine.new("crc16-ccitt-false") # load crc16 library crc_data = crc_algorithm(data) - crc_data = crc_data.to_bytes(2, byteorder='big') + crc_data = crc_data.to_bytes(2, byteorder="big") return crc_data + def get_crc_24(data) -> bytes: """Author: DJ2LS @@ -77,13 +81,20 @@ def get_crc_24(data) -> bytes: Returns: CRC-24 (OpenPGP) of the provided data as bytes """ - crc_algorithm = crcengine.create(0x864cfb, 24, 0xb704ce, ref_in=False, - ref_out=False, xor_out=0, - name='crc-24-openpgp') + crc_algorithm = crcengine.create( + 0x864CFB, + 24, + 0xB704CE, + ref_in=False, + ref_out=False, + xor_out=0, + name="crc-24-openpgp", + ) crc_data = crc_algorithm(data) - crc_data = crc_data.to_bytes(3, byteorder='big') + crc_data = crc_data.to_bytes(3, byteorder="big") return crc_data + def get_crc_32(data: bytes) -> bytes: """Author: DJ2LS @@ -97,11 +108,12 @@ def get_crc_32(data: bytes) -> bytes: Returns: CRC-32 of the provided data as bytes """ - crc_algorithm = crcengine.new('crc32') # load crc32 library + crc_algorithm = crcengine.new("crc32") # load crc32 library crc_data = crc_algorithm(data) - crc_data = crc_data.to_bytes(4, byteorder='big') + crc_data = crc_data.to_bytes(4, byteorder="big") return crc_data + def add_to_heard_stations(dxcallsign, dxgrid, datatype, snr, offset, frequency): """ @@ -118,24 +130,46 @@ def add_to_heard_stations(dxcallsign, dxgrid, datatype, snr, offset, frequency): """ # check if buffer empty if len(static.HEARD_STATIONS) == 0: - static.HEARD_STATIONS.append([dxcallsign, dxgrid, int(time.time()), datatype, snr, offset, frequency]) + static.HEARD_STATIONS.append( + [dxcallsign, dxgrid, int(time.time()), datatype, snr, offset, frequency] + ) # if not, we search and update else: for i in range(len(static.HEARD_STATIONS)): # Update callsign with new timestamp if static.HEARD_STATIONS[i].count(dxcallsign) > 0: - static.HEARD_STATIONS[i] = [dxcallsign, dxgrid, int(time.time()), datatype, snr, offset, frequency] + static.HEARD_STATIONS[i] = [ + dxcallsign, + dxgrid, + int(time.time()), + datatype, + snr, + offset, + frequency, + ] break # Insert if nothing found if i == len(static.HEARD_STATIONS) - 1: - static.HEARD_STATIONS.append([dxcallsign, dxgrid, int(time.time()), datatype, snr, offset, frequency]) + static.HEARD_STATIONS.append( + [ + dxcallsign, + dxgrid, + int(time.time()), + datatype, + snr, + offset, + frequency, + ] + ) break + # for idx, item in enumerate(static.HEARD_STATIONS): # if dxcallsign in item: # item = [dxcallsign, int(time.time())] # static.HEARD_STATIONS[idx] = item + def callsign_to_bytes(callsign) -> bytes: """ @@ -146,48 +180,49 @@ def callsign_to_bytes(callsign) -> bytes: """ # http://www.aprs.org/aprs11/SSIDs.txt - #-0 Your primary station usually fixed and message capable - #-1 generic additional station, digi, mobile, wx, etc - #-2 generic additional station, digi, mobile, wx, etc - #-3 generic additional station, digi, mobile, wx, etc - #-4 generic additional station, digi, mobile, wx, etc - #-5 Other networks (Dstar, Iphones, Androids, Blackberry's etc) - #-6 Special activity, Satellite ops, camping or 6 meters, etc - #-7 walkie talkies, HT's or other human portable - #-8 boats, sailboats, RV's or second main mobile - #-9 Primary Mobile (usually message capable) - #-10 internet, Igates, echolink, winlink, AVRS, APRN, etc - #-11 balloons, aircraft, spacecraft, etc - #-12 APRStt, DTMF, RFID, devices, one-way trackers*, etc - #-13 Weather stations - #-14 Truckers or generally full time drivers - #-15 generic additional station, digi, mobile, wx, etc + # -0 Your primary station usually fixed and message capable + # -1 generic additional station, digi, mobile, wx, etc + # -2 generic additional station, digi, mobile, wx, etc + # -3 generic additional station, digi, mobile, wx, etc + # -4 generic additional station, digi, mobile, wx, etc + # -5 Other networks (Dstar, Iphones, Androids, Blackberry's etc) + # -6 Special activity, Satellite ops, camping or 6 meters, etc + # -7 walkie talkies, HT's or other human portable + # -8 boats, sailboats, RV's or second main mobile + # -9 Primary Mobile (usually message capable) + # -10 internet, Igates, echolink, winlink, AVRS, APRN, etc + # -11 balloons, aircraft, spacecraft, etc + # -12 APRStt, DTMF, RFID, devices, one-way trackers*, etc + # -13 Weather stations + # -14 Truckers or generally full time drivers + # -15 generic additional station, digi, mobile, wx, etc # Try converting to bytestring if possible type string try: - callsign = bytes(callsign, 'utf-8') - except TypeError as e: - structlog.get_logger("structlog").debug("[HLP] callsign_to_bytes: Exception converting callsign to bytes:", e=e) - pass + callsign = bytes(callsign, "utf-8") + except TypeError as err: + log.debug("[HLP] callsign_to_bytes: Error converting callsign to bytes:", e=err) - # Need this step to reduce the needed payload by the callsign (stripping "-" out of the callsign) - callsign = callsign.split(b'-') + # Need this step to reduce the needed payload by the callsign + # (stripping "-" out of the callsign) + callsign = callsign.split(b"-") ssid = 0 try: ssid = int(callsign[1]) - except IndexError as e: - structlog.get_logger("structlog").debug("[HLP] callsign_to_bytes: Error callsign SSID to integer:", e=e) + except IndexError as err: + log.debug("[HLP] callsign_to_bytes: Error callsign SSID to integer:", e=err) - #callsign = callsign[0] - #bytestring = bytearray(8) - #bytestring[:len(callsign)] = callsign - #bytestring[7:8] = bytes([ssid]) + # callsign = callsign[0] + # bytestring = bytearray(8) + # bytestring[:len(callsign)] = callsign + # bytestring[7:8] = bytes([ssid]) # ---- callsign with encoding always 6 bytes long callsign = callsign[0].decode("utf-8") ssid = bytes([ssid]).decode("utf-8") return encode_call(callsign + ssid) - #return bytes(bytestring) + # return bytes(bytestring) + def bytes_to_callsign(bytestring: bytes) -> bytes: """ @@ -200,25 +235,25 @@ def bytes_to_callsign(bytestring: bytes) -> bytes: bytes """ # http://www.aprs.org/aprs11/SSIDs.txt - #-0 Your primary station usually fixed and message capable - #-1 generic additional station, digi, mobile, wx, etc - #-2 generic additional station, digi, mobile, wx, etc - #-3 generic additional station, digi, mobile, wx, etc - #-4 generic additional station, digi, mobile, wx, etc - #-5 Other networks (Dstar, Iphones, Androids, Blackberry's etc) - #-6 Special activity, Satellite ops, camping or 6 meters, etc - #-7 walkie talkies, HT's or other human portable - #-8 boats, sailboats, RV's or second main mobile - #-9 Primary Mobile (usually message capable) - #-10 internet, Igates, echolink, winlink, AVRS, APRN, etc - #-11 balloons, aircraft, spacecraft, etc - #-12 APRStt, DTMF, RFID, devices, one-way trackers*, etc - #-13 Weather stations - #-14 Truckers or generally full time drivers - #-15 generic additional station, digi, mobile, wx, etc + # -0 Your primary station usually fixed and message capable + # -1 generic additional station, digi, mobile, wx, etc + # -2 generic additional station, digi, mobile, wx, etc + # -3 generic additional station, digi, mobile, wx, etc + # -4 generic additional station, digi, mobile, wx, etc + # -5 Other networks (Dstar, Iphones, Androids, Blackberry's etc) + # -6 Special activity, Satellite ops, camping or 6 meters, etc + # -7 walkie talkies, HT's or other human portable + # -8 boats, sailboats, RV's or second main mobile + # -9 Primary Mobile (usually message capable) + # -10 internet, Igates, echolink, winlink, AVRS, APRN, etc + # -11 balloons, aircraft, spacecraft, etc + # -12 APRStt, DTMF, RFID, devices, one-way trackers*, etc + # -13 Weather stations + # -14 Truckers or generally full time drivers + # -15 generic additional station, digi, mobile, wx, etc # we need to do this step to reduce the needed paypload by the callsign ( stripping "-" out of the callsign ) - ''' + """ callsign = bytes(bytestring[:7]) callsign = callsign.rstrip(b'\x00') ssid = int.from_bytes(bytes(bytestring[7:8]), "big") @@ -229,15 +264,17 @@ def bytes_to_callsign(bytestring: bytes) -> bytes: callsign = callsign.encode('utf-8') return bytes(callsign) - ''' + """ decoded = decode_call(bytestring) callsign = decoded[:-1] ssid = ord(bytes(decoded[-1], "utf-8")) return bytes(f"{callsign}-{ssid}", "utf-8") -def check_callsign(callsign:bytes, crc_to_check:bytes): + +def check_callsign(callsign: bytes, crc_to_check: bytes): """ - Funktion to check a crc against a callsign to calculate the ssid by generating crc until we got it + Function to check a crc against a callsign to calculate the + ssid by generating crc until we find the correct SSID Args: callsign: Callsign which we want to check @@ -249,18 +286,18 @@ def check_callsign(callsign:bytes, crc_to_check:bytes): """ # print(callsign) - structlog.get_logger("structlog").debug("[HLP] check_callsign: Checking:", callsign=callsign) + log.debug("[HLP] check_callsign: Checking:", callsign=callsign) try: # We want the callsign without SSID - callsign = callsign.split(b'-')[0] + callsign = callsign.split(b"-")[0] - except Exception as e: - structlog.get_logger("structlog").debug("[HLP] check_callsign: Error callsign SSIG to integer:", e=e) + except Exception as err: + log.debug("[HLP] check_callsign: Error callsign SSID to integer:", e=err) for ssid in static.SSID_LIST: call_with_ssid = bytearray(callsign) - call_with_ssid.extend('-'.encode('utf-8')) - call_with_ssid.extend(str(ssid).encode('utf-8')) + call_with_ssid.extend("-".encode("utf-8")) + call_with_ssid.extend(str(ssid).encode("utf-8")) callsign_crc = get_crc_24(call_with_ssid) @@ -270,6 +307,7 @@ def check_callsign(callsign:bytes, crc_to_check:bytes): return [False, ""] + def encode_grid(grid): """ @auther: DB1UJ @@ -280,30 +318,31 @@ def encode_grid(grid): """ out_code_word = 0 - grid = grid.upper() # upper case to be save + grid = grid.upper() # upper case to be save - int_first = ord(grid[0]) - 65 # -65 offset for 'A' become zero, utf8 table - int_sec = ord(grid[1]) - 65 # -65 offset for 'A' become zero, utf8 table + int_first = ord(grid[0]) - 65 # -65 offset for 'A' become zero, utf8 table + int_sec = ord(grid[1]) - 65 # -65 offset for 'A' become zero, utf8 table - int_val = (int_first * 18) + int_sec # encode for modulo devision, 2 numbers in 1 + int_val = (int_first * 18) + int_sec # encode for modulo devision, 2 numbers in 1 - out_code_word = (int_val & 0b111111111) # only 9 bit LSB A - R * A - R is needed - out_code_word <<= 9 # shift 9 bit left having space next bits, letter A-R * A-R + out_code_word = int_val & 0b111111111 # only 9 bit LSB A - R * A - R is needed + out_code_word <<= 9 # shift 9 bit left having space next bits, letter A-R * A-R - int_val = int(grid[2:4]) # number string to number int, highest value 99 - out_code_word |= (int_val & 0b1111111) # using bit OR to add new value - out_code_word <<= 7 # shift 7 bit left having space next bits, letter A-X + int_val = int(grid[2:4]) # number string to number int, highest value 99 + out_code_word |= int_val & 0b1111111 # using bit OR to add new value + out_code_word <<= 7 # shift 7 bit left having space next bits, letter A-X - int_val = ord(grid[4]) - 65 # -65 offset for 'A' become zero, utf8 table - out_code_word |= (int_val & 0b11111) # using bit OR to add new value - out_code_word <<= 5 # shift 5 bit left having space next bits, letter A-X + int_val = ord(grid[4]) - 65 # -65 offset for 'A' become zero, utf8 table + out_code_word |= int_val & 0b11111 # using bit OR to add new value + out_code_word <<= 5 # shift 5 bit left having space next bits, letter A-X - int_val = ord(grid[5]) - 65 # -65 offset for 'A' become zero, utf8 table - out_code_word |= (int_val & 0b11111) # using bit OR to add new value + int_val = ord(grid[5]) - 65 # -65 offset for 'A' become zero, utf8 table + out_code_word |= int_val & 0b11111 # using bit OR to add new value - return out_code_word.to_bytes(length=4, byteorder='big') + return out_code_word.to_bytes(length=4, byteorder="big") -def decode_grid(b_code_word:bytes): + +def decode_grid(b_code_word: bytes): """ @auther: DB1UJ Args: @@ -311,7 +350,7 @@ def decode_grid(b_code_word:bytes): Returns: grid:str: upper case maidenhead QTH locater [A-R][A-R][0-9][0-9][A-X][A-X] """ - code_word = int.from_bytes(b_code_word, byteorder='big', signed=False) + code_word = int.from_bytes(b_code_word, byteorder="big", signed=False) grid = chr((code_word & 0b11111) + 65) code_word >>= 5 @@ -321,7 +360,7 @@ def decode_grid(b_code_word:bytes): grid = str(int(code_word & 0b1111111)) + grid if (code_word & 0b1111111) < 10: - grid = f'0{grid}' + grid = f"0{grid}" code_word >>= 9 int_val = int(code_word & 0b111111111) @@ -332,6 +371,7 @@ def decode_grid(b_code_word:bytes): return grid + def encode_call(call): """ @auther: DB1UJ @@ -339,23 +379,27 @@ def encode_call(call): call:string: ham radio call sign [A-Z,0-9], last char SSID 0-63 Returns: - 6 bytes contains 6 bits/sign encoded 8 char call sign with binary SSID (only upper letters + numbers, SSID) + 6 bytes contains 6 bits/sign encoded 8 char call sign with binary SSID + (only upper letters + numbers, SSID) """ out_code_word = 0 - call = call.upper() # upper case to be save + call = call.upper() # upper case to be save - for x in call: - int_val = ord(x) - 48 # -48 reduce bits, begin with first number utf8 table - out_code_word <<= 6 # shift left 6 bit, making space for a new char - out_code_word |= (int_val & 0b111111) # bit OR adds the new char, masked with AND 0b111111 - out_code_word >>= 6 # clean last char - out_code_word <<= 6 # make clean space - out_code_word |= (ord(call[-1]) & 0b111111) # add the SSID uncoded only 0 - 63 + for char in call: + int_val = ord(char) - 48 # -48 reduce bits, begin with first number utf8 table + out_code_word <<= 6 # shift left 6 bit, making space for a new char + out_code_word |= ( + int_val & 0b111111 + ) # bit OR adds the new char, masked with AND 0b111111 + out_code_word >>= 6 # clean last char + out_code_word <<= 6 # make clean space + out_code_word |= ord(call[-1]) & 0b111111 # add the SSID uncoded only 0 - 63 - return out_code_word.to_bytes(length=6, byteorder='big') + return out_code_word.to_bytes(length=6, byteorder="big") -def decode_call(b_code_word:bytes): + +def decode_call(b_code_word: bytes): """ @auther: DB1UJ Args: @@ -364,14 +408,14 @@ def decode_call(b_code_word:bytes): Returns: call:str: upper case ham radio call sign [A-Z,0-9] + binary SSID """ - code_word = int.from_bytes(b_code_word, byteorder='big', signed=False) - ssid = chr(code_word & 0b111111) # save the uncoded binary SSID + code_word = int.from_bytes(b_code_word, byteorder="big", signed=False) + ssid = chr(code_word & 0b111111) # save the uncoded binary SSID call = str() while code_word != 0: - call = chr((code_word & 0b111111)+48) + call + call = chr((code_word & 0b111111) + 48) + call code_word >>= 6 - call = call[:-1] + ssid # remove the last char from call and replace with SSID + call = call[:-1] + ssid # remove the last char from call and replace with SSID return call diff --git a/tnc/log_handler.py b/tnc/log_handler.py index 6d489cca..bda36caf 100644 --- a/tnc/log_handler.py +++ b/tnc/log_handler.py @@ -1,3 +1,8 @@ +import logging.config + +import structlog + + # https://www.structlog.org/en/stable/standard-library.html def setup_logging(filename): """ @@ -8,8 +13,6 @@ def setup_logging(filename): Returns: """ - import logging.config - import structlog timestamper = structlog.processors.TimeStamper(fmt="%Y-%m-%d %H:%M:%S") pre_chain = [ @@ -19,7 +22,8 @@ def setup_logging(filename): timestamper, ] - logging.config.dictConfig({ + logging.config.dictConfig( + { "version": 1, "disable_existing_loggers": False, "formatters": { @@ -43,7 +47,7 @@ def setup_logging(filename): "file": { "level": "DEBUG", "class": "logging.handlers.WatchedFileHandler", - "filename": filename + '.log', + "filename": f"{filename}.log", "formatter": "plain", }, }, @@ -53,8 +57,9 @@ def setup_logging(filename): "level": "DEBUG", "propagate": True, }, - } - }) + }, + } + ) structlog.configure( processors=[ structlog.stdlib.add_log_level, diff --git a/tnc/main.py b/tnc/main.py index 6e08dfbd..35200b82 100755 --- a/tnc/main.py +++ b/tnc/main.py @@ -16,13 +16,14 @@ import sys import threading import time -import structlog - import data_handler import helpers import log_handler import modem import static +import structlog + +log = structlog.get_logger(__file__) # signal handler for closing aplication def signal_handler(sig, frame): @@ -35,56 +36,208 @@ def signal_handler(sig, frame): Returns: system exit """ - print('Closing TNC...') + print("Closing TNC...") sock.CLOSE_SIGNAL = True sys.exit(0) + signal.signal(signal.SIGINT, signal_handler) -if __name__ == '__main__': +if __name__ == "__main__": # we need to run this on windows for multiprocessing support multiprocessing.freeze_support() # --------------------------------------------GET PARAMETER INPUTS - PARSER = argparse.ArgumentParser(description='FreeDATA TNC') - PARSER.add_argument('--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", type=str) - PARSER.add_argument('--mygrid', dest="mygrid", default="JN12AA", help="My gridsquare", type=str) - PARSER.add_argument('--rx', dest="audio_input_device", default=0, help="listening sound card", type=int) - PARSER.add_argument('--tx', dest="audio_output_device", default=0, help="transmitting sound card", type=int) - PARSER.add_argument('--port', dest="socket_port", default=3000, help="Socket port in the range of 1024-65536", type=int) - PARSER.add_argument('--deviceport', dest="hamlib_device_port", default="/dev/ttyUSB0", help="Hamlib device port", type=str) - PARSER.add_argument('--devicename', dest="hamlib_device_name", default="2028", help="Hamlib device name", type=str) - PARSER.add_argument('--serialspeed', dest="hamlib_serialspeed", choices=[1200, 2400, 4800, 9600, 19200, 38400, 57600, 115200], default=9600, help="Serialspeed", type=int) - PARSER.add_argument('--pttprotocol', dest="hamlib_ptt_type", choices=['USB', 'RIG', 'RTS', 'DTR', 'CM108', 'MICDATA', 'PARALLEL', 'DTR-H', 'DTR-L', 'NONE'], default='USB', help="PTT Type", type=str) - PARSER.add_argument('--pttport', dest="hamlib_ptt_port", default="/dev/ttyUSB0", help="PTT Port", type=str) - PARSER.add_argument('--data_bits', dest="hamlib_data_bits", choices=[7, 8], default=8, help="Hamlib data bits", type=int) - PARSER.add_argument('--stop_bits', dest="hamlib_stop_bits", choices=[1, 2], default=1, help="Hamlib stop bits", type=int) - PARSER.add_argument('--handshake', dest="hamlib_handshake", default="None", help="Hamlib handshake", type=str) - 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_bandwith_mode", action="store_true", help="Enable low bandwith 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 = argparse.ArgumentParser(description="FreeDATA TNC") + PARSER.add_argument( + "--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", + type=str, + ) + PARSER.add_argument( + "--mygrid", dest="mygrid", default="JN12AA", help="My gridsquare", type=str + ) + PARSER.add_argument( + "--rx", + dest="audio_input_device", + default=0, + help="listening sound card", + type=int, + ) + PARSER.add_argument( + "--tx", + dest="audio_output_device", + default=0, + help="transmitting sound card", + type=int, + ) + PARSER.add_argument( + "--port", + dest="socket_port", + default=3000, + help="Socket port in the range of 1024-65536", + type=int, + ) + PARSER.add_argument( + "--deviceport", + dest="hamlib_device_port", + default="/dev/ttyUSB0", + help="Hamlib device port", + type=str, + ) + PARSER.add_argument( + "--devicename", + dest="hamlib_device_name", + default="2028", + help="Hamlib device name", + type=str, + ) + PARSER.add_argument( + "--serialspeed", + dest="hamlib_serialspeed", + choices=[1200, 2400, 4800, 9600, 19200, 38400, 57600, 115200], + default=9600, + help="Serialspeed", + type=int, + ) + PARSER.add_argument( + "--pttprotocol", + dest="hamlib_ptt_type", + choices=[ + "USB", + "RIG", + "RTS", + "DTR", + "CM108", + "MICDATA", + "PARALLEL", + "DTR-H", + "DTR-L", + "NONE", + ], + default="USB", + help="PTT Type", + type=str, + ) + PARSER.add_argument( + "--pttport", + dest="hamlib_ptt_port", + default="/dev/ttyUSB0", + help="PTT Port", + type=str, + ) + PARSER.add_argument( + "--data_bits", + dest="hamlib_data_bits", + choices=[7, 8], + default=8, + help="Hamlib data bits", + type=int, + ) + PARSER.add_argument( + "--stop_bits", + dest="hamlib_stop_bits", + choices=[1, 2], + default=1, + help="Hamlib stop bits", + type=int, + ) + PARSER.add_argument( + "--handshake", + dest="hamlib_handshake", + default="None", + help="Hamlib handshake", + type=str, + ) + 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_bandwith_mode", + action="store_true", + help="Enable low bandwith 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, + ) ARGS = PARSER.parse_args() # additional step for beeing sure our callsign is correctly # in case we are not getting a station ssid # then we are forcing a station ssid = 0 - mycallsign = bytes(ARGS.mycall.upper(), 'utf-8') + mycallsign = bytes(ARGS.mycall.upper(), "utf-8") mycallsign = helpers.callsign_to_bytes(mycallsign) static.MYCALLSIGN = helpers.bytes_to_callsign(mycallsign) static.MYCALLSIGN_CRC = helpers.get_crc_24(static.MYCALLSIGN) static.SSID_LIST = ARGS.ssid_list - static.MYGRID = bytes(ARGS.mygrid, 'utf-8') + static.MYGRID = bytes(ARGS.mygrid, "utf-8") static.AUDIO_INPUT_DEVICE = ARGS.audio_input_device static.AUDIO_OUTPUT_DEVICE = ARGS.audio_output_device static.PORT = ARGS.socket_port @@ -113,22 +266,30 @@ if __name__ == '__main__': # config logging try: - if sys.platform == 'linux': - logging_path = os.getenv("HOME") + '/.config/' + 'FreeDATA/' + 'tnc' + if sys.platform == "linux": + logging_path = os.getenv("HOME") + "/.config/" + "FreeDATA/" + "tnc" - if sys.platform == 'darwin': - logging_path = os.getenv("HOME") + '/Library/' + 'Application Support/' + 'FreeDATA/' + 'tnc' + if sys.platform == "darwin": + logging_path = ( + os.getenv("HOME") + + "/Library/" + + "Application Support/" + + "FreeDATA/" + + "tnc" + ) - if sys.platform in ['win32', 'win64']: - logging_path = os.getenv('APPDATA') + '/' + 'FreeDATA/' + 'tnc' + if sys.platform in ["win32", "win64"]: + logging_path = os.getenv("APPDATA") + "/" + "FreeDATA/" + "tnc" if not os.path.exists(logging_path): os.makedirs(logging_path) log_handler.setup_logging(logging_path) - except Exception as e: - structlog.get_logger("structlog").error("[DMN] logger init error", exception=e) + except Exception as err: + log.error("[DMN] logger init error", exception=err) - structlog.get_logger("structlog").info("[TNC] Starting FreeDATA", author="DJ2LS", year="2022", version=static.VERSION) + log.info( + "[TNC] Starting FreeDATA", author="DJ2LS", year="2022", version=static.VERSION + ) # start data handler data_handler.DATA() @@ -138,17 +299,19 @@ if __name__ == '__main__': # --------------------------------------------START CMD SERVER try: - structlog.get_logger("structlog").info("[TNC] Starting TCP/IP socket", port=static.PORT) + log.info("[TNC] Starting TCP/IP socket", port=static.PORT) # https://stackoverflow.com/a/16641793 socketserver.TCPServer.allow_reuse_address = True - cmdserver = sock.ThreadedTCPServer((static.HOST, static.PORT), sock.ThreadedTCPRequestHandler) + cmdserver = sock.ThreadedTCPServer( + (static.HOST, static.PORT), sock.ThreadedTCPRequestHandler + ) server_thread = threading.Thread(target=cmdserver.serve_forever) server_thread.daemon = True server_thread.start() - except Exception as e: - structlog.get_logger("structlog").error("[TNC] Starting TCP/IP socket failed", port=static.PORT, e=e) + except Exception as err: + log.error("[TNC] Starting TCP/IP socket failed", port=static.PORT, e=err) sys.exit(1) while 1: time.sleep(1) diff --git a/tnc/modem.py b/tnc/modem.py index 1e2cb1c7..7b19e9d4 100644 --- a/tnc/modem.py +++ b/tnc/modem.py @@ -16,7 +16,6 @@ import sys import threading import time from collections import deque -from typing import Union import codec2 import data_handler @@ -45,6 +44,8 @@ RECEIVE_FSK_LDPC_1 = False class RF: """Class to encapsulate interactions between the audio device and codec2""" + log = structlog.get_logger(__name__) + def __init__(self) -> None: self.sampler_avg = 0 @@ -188,7 +189,7 @@ class RF: self.datac3_nin = codec2.api.freedv_nin(self.datac3_freedv) self.fsk_ldpc_nin_0 = codec2.api.freedv_nin(self.fsk_ldpc_freedv_0) self.fsk_ldpc_nin_1 = codec2.api.freedv_nin(self.fsk_ldpc_freedv_1) - # structlog.get_logger("structlog").debug("[MDM] RF: ",datac0_nin=self.datac0_nin) + # self.log.debug("[MDM] RF: ",datac0_nin=self.datac0_nin) # --------------------------------------------CREATE PYAUDIO INSTANCE if not TESTMODE: @@ -202,24 +203,24 @@ class RF: blocksize=4800, ) atexit.register(self.stream.stop) - structlog.get_logger("structlog").info( + self.log.info( "[MDM] init: opened audio devices" ) - except Exception as e: - structlog.get_logger("structlog").error( - "[MDM] init: can't open audio device. Exit", e=e + except Exception as err: + self.log.error( + "[MDM] init: can't open audio device. Exit", e=err ) sys.exit(1) try: - structlog.get_logger("structlog").debug( + self.log.debug( "[MDM] init: starting pyaudio callback" ) # self.audio_stream.start_stream() self.stream.start() - except Exception as e: - structlog.get_logger("structlog").error( - "[MDM] init: starting pyaudio callback failed", e=e + except Exception as err: + self.log.error( + "[MDM] init: starting pyaudio callback failed", e=err ) else: @@ -235,9 +236,9 @@ class RF: try: os.mkfifo(RXCHANNEL) os.mkfifo(TXCHANNEL) - except Exception as e: - structlog.get_logger("structlog").error( - f"[MDM] init:mkfifo: Exception: {e}" + except Exception as err: + self.log.error( + f"[MDM] init:mkfifo: Exception: {err}" ) mkfifo_write_callback_thread = threading.Thread( @@ -247,7 +248,7 @@ class RF: ) mkfifo_write_callback_thread.start() - structlog.get_logger("structlog").debug( + self.log.debug( "[MDM] Starting mkfifo_read_callback" ) mkfifo_read_callback_thread = threading.Thread( @@ -320,7 +321,7 @@ class RF: ) hamlib_thread.start() - # structlog.get_logger("structlog").debug("[MDM] Starting worker_receive") + # self.log.debug("[MDM] Starting worker_receive") worker_received = threading.Thread( target=self.worker_received, name="WORKER_THREAD", daemon=True ) @@ -397,7 +398,7 @@ class RF: status: """ - # structlog.get_logger("structlog").debug("[MDM] callback") + # self.log.debug("[MDM] callback") x = np.frombuffer(data_in48k, dtype=np.int16) x = self.resampler.resample48_to_8(x) @@ -429,8 +430,10 @@ class RF: try: outdata[:] = data_out48k[:frames] - except IndexError as e: - structlog.get_logger("structlog").debug(f"[MDM] callback: IndexError: {e}") + except IndexError as err: + self.log.debug( + f"[MDM] callback: IndexError: {err}" + ) # return (data_out48k, audio.pyaudio.paContinue) @@ -447,7 +450,7 @@ class RF: frames: """ - structlog.get_logger("structlog").debug("[MDM] transmit", mode=mode) + self.log.debug("[MDM] transmit", mode=mode) static.TRANSMITTING = True # Toggle ptt early to save some time and send ptt state via socket static.PTT_STATE = self.hamlib.set_ptt(True) @@ -487,7 +490,7 @@ class RF: mod_out_silence = ctypes.create_string_buffer(data_delay * 2) txbuffer = bytes(mod_out_silence) - structlog.get_logger("structlog").debug( + self.log.debug( "[MDM] TRANSMIT", mode=self.MODE, payload=payload_bytes_per_frame ) @@ -625,7 +628,7 @@ class RF: audiobuffer.pop(nin) nin = codec2.api.freedv_nin(freedv) if nbytes == bytes_per_frame: - structlog.get_logger("structlog").debug( + self.log.debug( "[MDM] [demod_audio] Pushing received data to received_queue" ) self.modem_received_queue.put([bytes_out, freedv, bytes_per_frame]) @@ -688,7 +691,7 @@ class RF: while True: data = self.modem_transmit_queue.get() - structlog.get_logger("structlog").debug( + self.log.debug( "[MDM] worker_transmit", mode=data[0] ) self.transmit( @@ -700,7 +703,7 @@ class RF: """Worker for FIFO queue for processing received frames""" while True: data = self.modem_received_queue.get() - structlog.get_logger("structlog").debug( + self.log.debug( "[MDM] worker_received: received data!" ) # data[0] = bytes_out @@ -783,15 +786,15 @@ class RF: modem_stats_sync = modem_stats_sync.value snr = round(modem_stats_snr, 1) - structlog.get_logger("structlog").info("[MDM] calculate_snr: ", snr=snr) + self.log.info("[MDM] calculate_snr: ", snr=snr) # static.SNR = np.clip(snr, 0, 255) # limit to max value of 255 static.SNR = np.clip( snr, -128, 128 ) # limit to max value of -128/128 as a possible fix of #188 return static.SNR - except Exception as e: - structlog.get_logger("structlog").error( - f"[MDM] calculate_snr: Exception: {e}" + except Exception as err: + self.log.error( + f"[MDM] calculate_snr: Exception: {err}" ) static.SNR = 0 return static.SNR @@ -813,7 +816,7 @@ class RF: def calculate_fft(self) -> None: """ Calculate an average signal strength of the channel to assess - whether the channel is 'busy.' + whether the channel is "busy." """ # Initialize channel_busy_delay counter channel_busy_delay = 0 @@ -870,11 +873,11 @@ class RF: dfftlist = dfft.tolist() static.FFT = dfftlist[:320] # 320 --> bandwidth 3000 - except Exception as e: - structlog.get_logger("structlog").error( - f"[MDM] calculate_fft: Exception: {e}" + except Exception as err: + self.log.error( + f"[MDM] calculate_fft: Exception: {err}" ) - structlog.get_logger("structlog").debug("[MDM] Setting fft=0") + self.log.debug("[MDM] Setting fft=0") # else 0 static.FFT = [0] diff --git a/tnc/rig.py b/tnc/rig.py index cdbf879f..6f78b6c9 100644 --- a/tnc/rig.py +++ b/tnc/rig.py @@ -1,11 +1,12 @@ -#!/usr/bin/env python3 - -import sys -import re -import structlog import atexit -import subprocess import os +import re +import subprocess +import sys + +import structlog + +mainlog = structlog.get_logger(__file__) # set global hamlib version hamlib_version = 0 @@ -20,26 +21,26 @@ else: # try importing hamlib try: # get python version - python_version = str(sys.version_info[0]) + "." + str(sys.version_info[1]) + python_version = f"{str(sys.version_info[0])}.{str(sys.version_info[1])}" # installation path for Ubuntu 20.04 LTS python modules - #sys.path.append('/usr/local/lib/python'+ python_version +'/site-packages') + # sys.path.append(f"/usr/local/lib/python{python_version}/site-packages") # installation path for Ubuntu 20.10 + - sys.path.append('/usr/local/lib/') + sys.path.append("/usr/local/lib/") # installation path for Suse - sys.path.append('/usr/local/lib64/python'+ python_version +'/site-packages') + sys.path.append(f"/usr/local/lib64/python{python_version}/site-packages") # everything else... not nice, but an attempt to see how its running within app bundle # this is not needed as python will be shipped with app bundle - sys.path.append('/usr/local/lib/python3.6/site-packages') - sys.path.append('/usr/local/lib/python3.7/site-packages') - sys.path.append('/usr/local/lib/python3.8/site-packages') - sys.path.append('/usr/local/lib/python3.9/site-packages') - sys.path.append('/usr/local/lib/python3.10/site-packages') + sys.path.append("/usr/local/lib/python3.6/site-packages") + sys.path.append("/usr/local/lib/python3.7/site-packages") + sys.path.append("/usr/local/lib/python3.8/site-packages") + sys.path.append("/usr/local/lib/python3.9/site-packages") + sys.path.append("/usr/local/lib/python3.10/site-packages") - sys.path.append('lib/hamlib/linux/python3.8/site-packages') + sys.path.append("lib/hamlib/linux/python3.8/site-packages") import Hamlib # https://stackoverflow.com/a/4703409 @@ -48,43 +49,68 @@ try: min_hamlib_version = 4.1 if hamlib_version > min_hamlib_version: - structlog.get_logger("structlog").info("[RIG] Hamlib found", version=hamlib_version) + mainlog.info("[RIG] Hamlib found", version=hamlib_version) else: - structlog.get_logger("structlog").warning("[RIG] Hamlib outdated", found=hamlib_version, recommend=min_hamlib_version) -except Exception as e: - structlog.get_logger("structlog").warning("[RIG] Python Hamlib binding not found", error=e) + mainlog.warning( + "[RIG] Hamlib outdated", found=hamlib_version, recommend=min_hamlib_version + ) +except Exception as err: + mainlog.warning("[RIG] Python Hamlib binding not found", error=err) try: - structlog.get_logger("structlog").warning("[RIG] Trying to open rigctl") - rigctl = subprocess.Popen("rigctl -V",shell=True, stdin=subprocess.PIPE, stdout=subprocess.PIPE, text=True) + mainlog.warning("[RIG] Trying to open rigctl") + rigctl = subprocess.Popen( + "rigctl -V", + shell=True, + stdin=subprocess.PIPE, + stdout=subprocess.PIPE, + text=True, + ) hamlib_version = rigctl.stdout.readline() - hamlib_version = hamlib_version.split(' ') + hamlib_version = hamlib_version.split(" ") - if hamlib_version[1] == 'Hamlib': - structlog.get_logger("structlog").warning("[RIG] Rigctl found! Please try using this", version=hamlib_version[2]) + if hamlib_version[1] == "Hamlib": + mainlog.warning( + "[RIG] Rigctl found! Please try using this", version=hamlib_version[2] + ) sys.exit() else: raise Exception - except Exception as e: - structlog.get_logger("structlog").critical("[RIG] HAMLIB NOT INSTALLED", error=e) + except Exception as err1: + mainlog.critical("[RIG] HAMLIB NOT INSTALLED", error=err1) hamlib_version = 0 sys.exit() class radio: """ """ - def __init__(self): - self.devicename = '' - self.devicenumber = '' - self.deviceport = '' - self.serialspeed = '' - self.hamlib_ptt_type = '' - self.my_rig = '' - self.pttport = '' - self.data_bits = '' - self.stop_bits = '' - self.handshake = '' - def open_rig(self, devicename, deviceport, hamlib_ptt_type, serialspeed, pttport, data_bits, stop_bits, handshake, rigctld_port, rigctld_ip): + log = structlog.get_logger(__name__) + + def __init__(self): + self.devicename = "" + self.devicenumber = "" + self.deviceport = "" + self.serialspeed = "" + self.hamlib_ptt_type = "" + self.my_rig = "" + self.pttport = "" + self.data_bits = "" + self.stop_bits = "" + self.handshake = "" + + def open_rig( + self, + devicename, + deviceport, + hamlib_ptt_type, + serialspeed, + pttport, + data_bits, + stop_bits, + handshake, + rigctld_port, + rigctld_ip, + ): """ Args: @@ -99,12 +125,11 @@ class radio: rigctld_port: rigctld_ip: - Returns: - """ self.devicename = devicename self.deviceport = str(deviceport) - self.serialspeed = str(serialspeed) # we need to ensure this is a str, otherwise set_conf functions are crashing + # we need to ensure this is a str, otherwise set_conf functions are crashing + self.serialspeed = str(serialspeed) self.hamlib_ptt_type = str(hamlib_ptt_type) self.pttport = str(pttport) self.data_bits = str(data_bits) @@ -118,8 +143,8 @@ class radio: # get devicenumber by looking for deviceobject in Hamlib module try: self.devicenumber = int(getattr(Hamlib, self.devicename)) - except: - structlog.get_logger("structlog").error("[RIG] Hamlib: rig not supported...") + except Exception: + self.log.error("[RIG] Hamlib: rig not supported...") self.devicenumber = 0 self.my_rig = Hamlib.Rig(self.devicenumber) @@ -131,73 +156,84 @@ class radio: self.my_rig.set_conf("data_bits", self.data_bits) self.my_rig.set_conf("ptt_pathname", self.pttport) - if self.hamlib_ptt_type == 'RIG': + if self.hamlib_ptt_type == "RIG": self.hamlib_ptt_type = Hamlib.RIG_PTT_RIG - self.my_rig.set_conf("ptt_type", 'RIG') + self.my_rig.set_conf("ptt_type", "RIG") - elif self.hamlib_ptt_type == 'USB': + elif self.hamlib_ptt_type == "USB": self.hamlib_ptt_type = Hamlib.RIG_PORT_USB - self.my_rig.set_conf("ptt_type", 'USB') + self.my_rig.set_conf("ptt_type", "USB") - elif self.hamlib_ptt_type == 'DTR-H': + elif self.hamlib_ptt_type == "DTR-H": self.hamlib_ptt_type = Hamlib.RIG_PTT_SERIAL_DTR self.my_rig.set_conf("dtr_state", "HIGH") self.my_rig.set_conf("ptt_type", "DTR") - elif self.hamlib_ptt_type == 'DTR-L': + elif self.hamlib_ptt_type == "DTR-L": self.hamlib_ptt_type = Hamlib.RIG_PTT_SERIAL_DTR self.my_rig.set_conf("dtr_state", "LOW") self.my_rig.set_conf("ptt_type", "DTR") - elif self.hamlib_ptt_type == 'RTS': + elif self.hamlib_ptt_type == "RTS": self.hamlib_ptt_type = Hamlib.RIG_PTT_SERIAL_RTS self.my_rig.set_conf("dtr_state", "OFF") self.my_rig.set_conf("ptt_type", "RTS") - elif self.hamlib_ptt_type == 'PARALLEL': + elif self.hamlib_ptt_type == "PARALLEL": self.hamlib_ptt_type = Hamlib.RIG_PTT_PARALLEL - elif self.hamlib_ptt_type == 'MICDATA': + elif self.hamlib_ptt_type == "MICDATA": self.hamlib_ptt_type = Hamlib.RIG_PTT_RIG_MICDATA - elif self.hamlib_ptt_type == 'CM108': + elif self.hamlib_ptt_type == "CM108": self.hamlib_ptt_type = Hamlib.RIG_PTT_CM108 - elif self.hamlib_ptt_type == 'RIG_PTT_NONE': + elif self.hamlib_ptt_type == "RIG_PTT_NONE": self.hamlib_ptt_type = Hamlib.RIG_PTT_NONE - else: #self.hamlib_ptt_type == 'RIG_PTT_NONE': + else: # self.hamlib_ptt_type == "RIG_PTT_NONE": self.hamlib_ptt_type = Hamlib.RIG_PTT_NONE - structlog.get_logger("structlog").info("[RIG] Opening...", device=self.devicenumber, path=self.my_rig.get_conf("rig_pathname"), serial_speed=self.my_rig.get_conf("serial_speed"), serial_handshake=self.my_rig.get_conf("serial_handshake"), stop_bits=self.my_rig.get_conf("stop_bits"), data_bits=self.my_rig.get_conf("data_bits"), ptt_pathname=self.my_rig.get_conf("ptt_pathname")) + self.log.info( + "[RIG] Opening...", + device=self.devicenumber, + path=self.my_rig.get_conf("rig_pathname"), + serial_speed=self.my_rig.get_conf("serial_speed"), + serial_handshake=self.my_rig.get_conf("serial_handshake"), + stop_bits=self.my_rig.get_conf("stop_bits"), + data_bits=self.my_rig.get_conf("data_bits"), + ptt_pathname=self.my_rig.get_conf("ptt_pathname"), + ) self.my_rig.open() atexit.register(self.my_rig.close) try: # lets determine the error message when opening rig - error = str(Hamlib.rigerror(my_rig.error_status)).splitlines() - error = error[1].split('err=') + error = str(Hamlib.rigerror(self.my_rig.error_status)).splitlines() + error = error[1].split("err=") error = error[1] - if error == 'Permission denied': - structlog.get_logger("structlog").error("[RIG] Hamlib has no permissions", e = error) - help_url = 'https://github.com/DJ2LS/FreeDATA/wiki/UBUNTU-Manual-installation#1-permissions' - structlog.get_logger("structlog").error("[RIG] HELP:", check = help_url) - except: - structlog.get_logger("structlog").info("[RIG] Hamlib device opened", status='SUCCESS') + if error == "Permission denied": + self.log.error("[RIG] Hamlib has no permissions", e=error) + help_url = "https://github.com/DJ2LS/FreeDATA/wiki/UBUNTU-Manual-installation#1-permissions" + self.log.error("[RIG] HELP:", check=help_url) + except Exception: + self.log.info("[RIG] Hamlib device opened", status="SUCCESS") # set ptt to false if ptt is stuck for some reason self.set_ptt(False) # set rig mode to USB # temporarly outcommented because of possible problems. - #self.my_rig.set_mode(Hamlib.RIG_MODE_USB) + # self.my_rig.set_mode(Hamlib.RIG_MODE_USB) # self.my_rig.set_mode(Hamlib.RIG_MODE_PKTUSB) return True - except Exception as e: - structlog.get_logger("structlog").error("[RIG] Hamlib - can't open rig", error=e, e=sys.exc_info()[0]) + except Exception as err2: + mainlog.error( + "[RIG] Hamlib - can't open rig", error=err2, e=sys.exc_info()[0] + ) return False def get_frequency(self): @@ -206,16 +242,16 @@ class radio: def get_mode(self): """ """ - (hamlib_mode, bandwith) = self.my_rig.get_mode() + (hamlib_mode, bandwidth) = self.my_rig.get_mode() return Hamlib.rig_strrmode(hamlib_mode) def get_bandwith(self): """ """ - (hamlib_mode, bandwith) = self.my_rig.get_mode() - return bandwith + (hamlib_mode, bandwidth) = self.my_rig.get_mode() + return bandwidth # not needed yet beacuse of some possible problems - #def set_mode(self, mode): + # def set_mode(self, mode): # return 0 def get_ptt(self): diff --git a/tnc/rigctl.py b/tnc/rigctl.py index 319d511a..c8cfa827 100644 --- a/tnc/rigctl.py +++ b/tnc/rigctl.py @@ -1,4 +1,3 @@ -#!/usr/bin/env python3 # Intially created by Franco Spinelli, IW2DHW, 01/2022 # Updated by DJ2LS # @@ -22,19 +21,35 @@ hamlib_version = 0 class radio: """ """ - def __init__(self): - self.devicename = '' - self.devicenumber = '' - self.deviceport = '' - self.serialspeed = '' - self.hamlib_ptt_type = '' - self.my_rig = '' - self.pttport = '' - self.data_bits = '' - self.stop_bits = '' - self.handshake = '' - def open_rig(self, devicename, deviceport, hamlib_ptt_type, serialspeed, pttport, data_bits, stop_bits, handshake, rigctld_ip, rigctld_port): + log = structlog.get_logger(__name__) + + def __init__(self): + self.devicename = "" + self.devicenumber = "" + self.deviceport = "" + self.serialspeed = "" + self.hamlib_ptt_type = "" + self.my_rig = "" + self.pttport = "" + self.data_bits = "" + self.stop_bits = "" + self.handshake = "" + self.cmd = "" + + def open_rig( + self, + devicename, + deviceport, + hamlib_ptt_type, + serialspeed, + pttport, + data_bits, + stop_bits, + handshake, + rigctld_ip, + rigctld_port, + ): """ Args: @@ -54,14 +69,15 @@ class radio: """ self.devicename = devicename self.deviceport = deviceport - self.serialspeed = str(serialspeed) # we need to ensure this is a str, otherwise set_conf functions are crashing + # we need to ensure this is a str, otherwise set_conf functions are crashing + self.serialspeed = str(serialspeed) self.hamlib_ptt_type = hamlib_ptt_type self.pttport = pttport self.data_bits = data_bits self.stop_bits = stop_bits self.handshake = handshake - # check if we are running in a pyinstaller environment + # check if we are running in a pyinstaller environment if hasattr(sys, "_MEIPASS"): sys.path.append(getattr(sys, "_MEIPASS")) else: @@ -70,27 +86,41 @@ class radio: # get devicenumber by looking for deviceobject in Hamlib module try: import Hamlib + self.devicenumber = int(getattr(Hamlib, self.devicename)) - except Exception as e: + except Exception as err: if int(self.devicename): self.devicenumber = int(self.devicename) else: - self.devicenumber = 6 #dummy - structlog.get_logger("structlog").warning("[RIGCTL] Radio not found. Using DUMMY!", error=e) + self.devicenumber = 6 # dummy + self.log.warning("[RIGCTL] Radio not found. Using DUMMY!", error=err) # set deviceport to dummy port, if we selected dummy model - if self.devicenumber == 1 or self.devicenumber == 6: - self.deviceport = '/dev/ttyUSB0' + if self.devicenumber in {1, 6}: + self.deviceport = "/dev/ttyUSB0" print(self.devicenumber, self.deviceport, self.serialspeed) # select precompiled executable for win32/win64 rigctl # this is really a hack...somewhen we need a native hamlib integration for windows - if sys.platform in ['win32', 'win64']: - self.cmd = app_path + 'lib\\hamlib\\'+sys.platform+'\\rigctl -m %d -r %s -s %d ' % (int(self.devicenumber), self.deviceport, int(self.serialspeed)) + if sys.platform in ["win32", "win64"]: + self.cmd = ( + app_path + + "lib\\hamlib\\" + + sys.platform + + ( + f"\\rigctl -m {self.devicenumber} " + f"-r {self.deviceport} " + f"-s {int(self.serialspeed)} " + ) + ) else: - self.cmd = 'rigctl -m %d -r %s -s %d ' % (int(self.devicenumber), self.deviceport, int(self.serialspeed)) + self.cmd = "rigctl -m %d -r %s -s %d " % ( + self.devicenumber, + self.deviceport, + int(self.serialspeed), + ) # eseguo semplicemente rigctl con il solo comando T 1 o T 0 per # il set e t per il get @@ -101,33 +131,35 @@ class radio: def get_frequency(self): """ """ - cmd = self.cmd + ' f' - sw_proc = subprocess.Popen(cmd, shell=True, stdin=subprocess.PIPE, stdout=subprocess.PIPE, text=True) + cmd = f"{self.cmd} f" + sw_proc = subprocess.Popen( + cmd, shell=True, stdin=subprocess.PIPE, stdout=subprocess.PIPE, text=True + ) time.sleep(0.5) freq = sw_proc.communicate()[0] - #print('get_frequency', freq, sw_proc.communicate()) + # print("get_frequency", freq, sw_proc.communicate()) try: return int(freq) - except: + except Exception: return False def get_mode(self): """ """ - #(hamlib_mode, bandwith) = self.my_rig.get_mode() - #return Hamlib.rig_strrmode(hamlib_mode) + # (hamlib_mode, bandwith) = self.my_rig.get_mode() + # return Hamlib.rig_strrmode(hamlib_mode) try: - return 'PKTUSB' - except: + return "PKTUSB" + except Exception: return False def get_bandwith(self): """ """ - #(hamlib_mode, bandwith) = self.my_rig.get_mode() + # (hamlib_mode, bandwith) = self.my_rig.get_mode() bandwith = 2700 try: return bandwith - except: + except Exception: return False def set_mode(self, mode): @@ -144,14 +176,16 @@ class radio: def get_ptt(self): """ """ - cmd = self.cmd + ' t' - sw_proc = subprocess.Popen(cmd, shell=True, stdin=subprocess.PIPE, stdout=subprocess.PIPE, text=True) + cmd = f"{self.cmd} t" + sw_proc = subprocess.Popen( + cmd, shell=True, stdin=subprocess.PIPE, stdout=subprocess.PIPE, text=True + ) time.sleep(0.5) status = sw_proc.communicate()[0] try: return status - except: + except Exception: return False def set_ptt(self, state): @@ -163,21 +197,18 @@ class radio: Returns: """ - cmd = self.cmd + ' T ' - print('set_ptt', state) - if state: - cmd = cmd + '1' - else: - cmd = cmd + '0' - print('set_ptt', cmd) + cmd = f"{self.cmd} T " + print("set_ptt", state) + cmd = f"{cmd}1" if state else f"{cmd}0" + print("set_ptt", cmd) sw_proc = subprocess.Popen(cmd, shell=True, text=True) try: return state - except: + except Exception: return False def close_rig(self): """ """ - #self.my_rig.close() + # self.my_rig.close() return diff --git a/tnc/rigctld.py b/tnc/rigctld.py index 9405b019..b5e6f674 100644 --- a/tnc/rigctld.py +++ b/tnc/rigctld.py @@ -4,34 +4,45 @@ # # modified and adjusted to FreeDATA needs by DJ2LS -import logging import socket import time import structlog -import log_handler -import static - # set global hamlib version hamlib_version = 0 -class radio(): +class radio: """rigctld (hamlib) communication class""" + # Note: This is a massive hack. + log = structlog.get_logger(__name__) + def __init__(self, hostname="localhost", port=4532, poll_rate=5, timeout=5): - """ Open a connection to rotctld, and test it for validity """ + """Open a connection to rotctld, and test it for validity""" self.sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) - #self.sock.settimeout(timeout) + # self.sock.settimeout(timeout) self.connected = False self.hostname = hostname self.port = port self.connection_attempts = 5 - def open_rig(self, devicename, deviceport, hamlib_ptt_type, serialspeed, pttport, data_bits, stop_bits, handshake, rigctld_ip, rigctld_port): + def open_rig( + self, + devicename, + deviceport, + hamlib_ptt_type, + serialspeed, + pttport, + data_bits, + stop_bits, + handshake, + rigctld_ip, + rigctld_port, + ): """ Args: @@ -51,26 +62,35 @@ class radio(): """ self.hostname = rigctld_ip self.port = int(rigctld_port) - + if self.connect(): - logging.debug("Rigctl intialized") + self.log.debug("Rigctl initialized") return True - structlog.get_logger("structlog").error("[RIGCTLD] Can't connect to rigctld!", ip=self.hostname, port=self.port) + self.log.error( + "[RIGCTLD] Can't connect to rigctld!", ip=self.hostname, port=self.port + ) return False def connect(self): """Connect to rigctld instance""" if not self.connected: try: - self.connection = socket.create_connection((self.hostname,self.port)) + self.connection = socket.create_connection((self.hostname, self.port)) self.connected = True - structlog.get_logger("structlog").info("[RIGCTLD] Connected to rigctld!", ip=self.hostname, port=self.port) + self.log.info( + "[RIGCTLD] Connected to rigctld!", ip=self.hostname, port=self.port + ) return True - except Exception as e: + except Exception as err: # ConnectionRefusedError: [Errno 111] Connection refused self.close_rig() - structlog.get_logger("structlog").warning("[RIGCTLD] Connection to rigctld refused! Reconnect...", ip=self.hostname, port=self.port, e=e) + self.log.warning( + "[RIGCTLD] Connection to rigctld refused! Reconnect...", + ip=self.hostname, + port=self.port, + e=err, + ) return False def close_rig(self): @@ -85,20 +105,28 @@ class radio(): Args: command: - Returns: - """ if self.connected: try: - self.connection.sendall(command+b'\n') - except: - structlog.get_logger("structlog").warning("[RIGCTLD] Command not executed!", command=command, ip=self.hostname, port=self.port) + self.connection.sendall(command + b"\n") + except Exception: + self.log.warning( + "[RIGCTLD] Command not executed!", + command=command, + ip=self.hostname, + port=self.port, + ) self.connected = False try: return self.connection.recv(1024) - except: - structlog.get_logger("structlog").warning("[RIGCTLD] No command response!", command=command, ip=self.hostname, port=self.port) + except Exception: + self.log.warning( + "[RIGCTLD] No command response!", + command=command, + ip=self.hostname, + port=self.port, + ) self.connected = False else: @@ -110,20 +138,20 @@ class radio(): """ """ try: data = self.send_command(b"m") - data = data.split(b'\n') + data = data.split(b"\n") mode = data[0] return mode.decode("utf-8") - except: + except Exception: return 0 def get_bandwith(self): """ """ try: data = self.send_command(b"m") - data = data.split(b'\n') + data = data.split(b"\n") bandwith = data[1] return bandwith.decode("utf-8") - except: + except Exception: return 0 def get_frequency(self): @@ -131,14 +159,14 @@ class radio(): try: frequency = self.send_command(b"f") return frequency.decode("utf-8") - except: + except Exception: return 0 def get_ptt(self): """ """ try: return self.send_command(b"t") - except: + except Exception: return False def set_ptt(self, state): @@ -152,9 +180,9 @@ class radio(): """ try: if state: - self.send_command(b"T 1") + self.send_command(b"T 1") else: - self.send_command(b"T 0") + self.send_command(b"T 0") return state - except: + except Exception: return False diff --git a/tnc/rigdummy.py b/tnc/rigdummy.py index 445cfb58..22572356 100644 --- a/tnc/rigdummy.py +++ b/tnc/rigdummy.py @@ -1,12 +1,9 @@ -#!/usr/bin/env python3 - -import structlog - hamlib_version = 0 class radio: """ """ + def __init__(self): pass diff --git a/tnc/sock.py b/tnc/sock.py index 604e41de..fcaa86f6 100644 --- a/tnc/sock.py +++ b/tnc/sock.py @@ -1,4 +1,3 @@ -#!/usr/bin/env python3 # -*- coding: utf-8 -*- """ Created on Fri Dec 25 21:25:14 2020 @@ -21,23 +20,17 @@ Created on Fri Dec 25 21:25:14 2020 """ import atexit import base64 -import logging -import os import queue import socketserver import sys import threading import time -import psutil -import structlog -import ujson as json - -import audio import data_handler import helpers -import log_handler import static +import structlog +import ujson as json SOCKET_QUEUE = queue.Queue() DAEMON_QUEUE = queue.Queue() @@ -50,19 +43,22 @@ class ThreadedTCPServer(socketserver.ThreadingMixIn, socketserver.TCPServer): """ the socket handler base class """ + pass class ThreadedTCPRequestHandler(socketserver.StreamRequestHandler): """ """ + connection_alive = False + log = structlog.get_logger(__name__) def send_to_client(self): """ function called by socket handler send data to a network client if available """ - tempdata = b'' + tempdata = b"" while self.connection_alive and not CLOSE_SIGNAL: # send tnc state as network stream # check server port against daemon port and send corresponding data @@ -80,24 +76,24 @@ class ThreadedTCPRequestHandler(socketserver.StreamRequestHandler): while not SOCKET_QUEUE.empty(): data = SOCKET_QUEUE.get() - sock_data = bytes(data, 'utf-8') - sock_data += b'\n' # append line limiter + sock_data = bytes(data, "utf-8") + sock_data += b"\n" # append line limiter # send data to all clients - #try: + # try: for client in CONNECTED_CLIENTS: try: client.send(sock_data) - except Exception as e: + except Exception as err: # print("connection lost...") - structlog.get_logger("structlog").info("[SCK] Connection lost", e=e) + self.log.info("[SCK] Connection lost", e=err) self.connection_alive = False # we want to transmit scatter data only once to reduce network traffic static.SCATTER = [] # we want to display INFO messages only once static.INFO = [] - #self.request.sendall(sock_data) + # self.request.sendall(sock_data) time.sleep(0.15) def receive_from_client(self): @@ -111,15 +107,15 @@ class ThreadedTCPRequestHandler(socketserver.StreamRequestHandler): chunk = self.request.recv(1024) data += chunk - if chunk == b'': - #print("connection broken. Closing...") + if chunk == b"": + # print("connection broken. Closing...") self.connection_alive = False - if data.startswith(b'{') and data.endswith(b'}\n'): + if data.startswith(b"{") and data.endswith(b"}\n"): # split data by \n if we have multiple commands in socket buffer - data = data.split(b'\n') + data = data.split(b"\n") # remove empty data - data.remove(b'') + data.remove(b"") # iterate thorugh data list for commands in data: @@ -137,8 +133,13 @@ class ThreadedTCPRequestHandler(socketserver.StreamRequestHandler): # finally delete our rx buffer to be ready for new commands data = bytes() - except Exception as e: - structlog.get_logger("structlog").info("[SCK] Connection closed", ip=self.client_address[0], port=self.client_address[1], e=e) + except Exception as err: + self.log.info( + "[SCK] Connection closed", + ip=self.client_address[0], + port=self.client_address[1], + e=err, + ) self.connection_alive = False def handle(self): @@ -147,11 +148,19 @@ class ThreadedTCPRequestHandler(socketserver.StreamRequestHandler): """ CONNECTED_CLIENTS.add(self.request) - structlog.get_logger("structlog").debug("[SCK] Client connected", ip=self.client_address[0], port=self.client_address[1]) + self.log.debug( + "[SCK] Client connected", + ip=self.client_address[0], + port=self.client_address[1], + ) self.connection_alive = True - self.sendThread = threading.Thread(target=self.send_to_client, args=[],daemon=True).start() - self.receiveThread = threading.Thread(target=self.receive_from_client, args=[],daemon=True).start() + self.sendThread = threading.Thread( + target=self.send_to_client, args=[], daemon=True + ).start() + self.receiveThread = threading.Thread( + target=self.receive_from_client, args=[], daemon=True + ).start() # keep connection alive until we close it while self.connection_alive and not CLOSE_SIGNAL: @@ -159,11 +168,19 @@ class ThreadedTCPRequestHandler(socketserver.StreamRequestHandler): def finish(self): """ """ - structlog.get_logger("structlog").warning("[SCK] Closing client socket", ip=self.client_address[0], port=self.client_address[1]) + self.log.warning( + "[SCK] Closing client socket", + ip=self.client_address[0], + port=self.client_address[1], + ) try: CONNECTED_CLIENTS.remove(self.request) - except: - structlog.get_logger("structlog").warning("[SCK] client connection already removed from client list", client=self.request) + except Exception: + self.log.warning( + "[SCK] client connection already removed from client list", + client=self.request, + ) + def process_tnc_commands(data): """ @@ -175,64 +192,82 @@ def process_tnc_commands(data): Returns: """ + log = structlog.get_logger(__name__) + # we need to do some error handling in case of socket timeout or decoding issue try: # convert data to json object received_json = json.loads(data) - structlog.get_logger("structlog").debug("[SCK] CMD", command=received_json) + log.debug("[SCK] CMD", command=received_json) # SET TX AUDIO LEVEL ----------------------------------------------------- - if received_json["type"] == "set" and received_json["command"] == "tx_audio_level": + if ( + received_json["type"] == "set" + and received_json["command"] == "tx_audio_level" + ): try: static.TX_AUDIO_LEVEL = int(received_json["value"]) command_response("tx_audio_level", True) - except Exception as e: + except Exception as err: command_response("tx_audio_level", False) - structlog.get_logger("structlog").warning("[SCK] command execution error", e=e, command=received_json) + log.warning( + "[SCK] command execution error", e=err, command=received_json + ) # TRANSMIT SINE WAVE ----------------------------------------------------- - if received_json["type"] == "set" and received_json["command"] == "send_test_frame": + if ( + received_json["type"] == "set" + and received_json["command"] == "send_test_frame" + ): try: - data_handler.DATA_QUEUE_TRANSMIT.put(['SEND_TEST_FRAME']) + data_handler.DATA_QUEUE_TRANSMIT.put(["SEND_TEST_FRAME"]) command_response("send_test_frame", True) - except Exception as e: + except Exception as err: command_response("send_test_frame", False) - structlog.get_logger("structlog").warning("[SCK] command execution error", e=e, command=received_json) + log.warning( + "[SCK] command execution error", e=err, command=received_json + ) # CQ CQ CQ ----------------------------------------------------- if received_json["command"] == "cqcqcq": try: - data_handler.DATA_QUEUE_TRANSMIT.put(['CQ']) + data_handler.DATA_QUEUE_TRANSMIT.put(["CQ"]) command_response("cqcqcq", True) - except Exception as e: + except Exception as err: command_response("cqcqcq", False) - structlog.get_logger("structlog").warning("[SCK] command execution error", e=e, command=received_json) + log.warning( + "[SCK] command execution error", e=err, command=received_json + ) # START_BEACON ----------------------------------------------------- if received_json["command"] == "start_beacon": try: static.BEACON_STATE = True interval = int(received_json["parameter"]) - data_handler.DATA_QUEUE_TRANSMIT.put(['BEACON', interval, True]) + data_handler.DATA_QUEUE_TRANSMIT.put(["BEACON", interval, True]) command_response("start_beacon", True) - except Exception as e: + except Exception as err: command_response("start_beacon", False) - structlog.get_logger("structlog").warning("[SCK] command execution error", e=e, command=received_json) + log.warning( + "[SCK] command execution error", e=err, command=received_json + ) # STOP_BEACON ----------------------------------------------------- if received_json["command"] == "stop_beacon": try: - structlog.get_logger("structlog").warning("[TNC] Stopping beacon!") + log.warning("[TNC] Stopping beacon!") static.BEACON_STATE = False - data_handler.DATA_QUEUE_TRANSMIT.put(['BEACON', None, False]) + data_handler.DATA_QUEUE_TRANSMIT.put(["BEACON", None, False]) command_response("stop_beacon", True) - except Exception as e: + except Exception as err: command_response("stop_beacon", False) - structlog.get_logger("structlog").warning("[SCK] command execution error", e=e, command=received_json) + log.warning( + "[SCK] command execution error", e=err, command=received_json + ) # PING ---------------------------------------------------------- - if received_json["type"] == 'ping' and received_json["command"] == "ping": + if received_json["type"] == "ping" and received_json["command"] == "ping": # send ping frame and wait for ACK try: dxcallsign = received_json["dxcallsign"] @@ -243,14 +278,16 @@ def process_tnc_commands(data): dxcallsign = helpers.callsign_to_bytes(dxcallsign) dxcallsign = helpers.bytes_to_callsign(dxcallsign) - data_handler.DATA_QUEUE_TRANSMIT.put(['PING', dxcallsign]) + data_handler.DATA_QUEUE_TRANSMIT.put(["PING", dxcallsign]) command_response("ping", True) - except Exception as e: + except Exception as err: command_response("ping", False) - structlog.get_logger("structlog").warning("[SCK] command execution error", e=e, command=received_json) + log.warning( + "[SCK] command execution error", e=err, command=received_json + ) # CONNECT ---------------------------------------------------------- - if received_json["type"] == 'arq' and received_json["command"] == "connect": + if received_json["type"] == "arq" and received_json["command"] == "connect": static.BEACON_PAUSE = True # send ping frame and wait for ACK try: @@ -265,24 +302,28 @@ def process_tnc_commands(data): static.DXCALLSIGN = dxcallsign static.DXCALLSIGN_CRC = helpers.get_crc_24(static.DXCALLSIGN) - data_handler.DATA_QUEUE_TRANSMIT.put(['CONNECT', dxcallsign]) + data_handler.DATA_QUEUE_TRANSMIT.put(["CONNECT", dxcallsign]) command_response("connect", True) - except Exception as e: + except Exception as err: command_response("connect", False) - structlog.get_logger("structlog").warning("[SCK] command execution error", e=e, command=received_json) + log.warning( + "[SCK] command execution error", e=err, command=received_json + ) # DISCONNECT ---------------------------------------------------------- - if received_json["type"] == 'arq' and received_json["command"] == "disconnect": + if received_json["type"] == "arq" and received_json["command"] == "disconnect": # send ping frame and wait for ACK try: - data_handler.DATA_QUEUE_TRANSMIT.put(['DISCONNECT']) + data_handler.DATA_QUEUE_TRANSMIT.put(["DISCONNECT"]) command_response("disconnect", True) - except Exception as e: + except Exception as err: command_response("disconnect", False) - structlog.get_logger("structlog").warning("[SCK] command execution error", e=e, command=received_json) + log.warning( + "[SCK] command execution error", e=err, command=received_json + ) # TRANSMIT RAW DATA ------------------------------------------- - if received_json["type"] == 'arq' and received_json["command"] == "send_raw": + if received_json["type"] == "arq" and received_json["command"] == "send_raw": static.BEACON_PAUSE = True try: if not static.ARQ_SESSION: @@ -306,39 +347,48 @@ def process_tnc_commands(data): # check if specific callsign is set with different SSID than the TNC is initialized try: mycallsign = received_json["parameter"][0]["mycallsign"] - except: + except Exception: mycallsign = static.MYCALLSIGN # check if transmission uuid provided else set no-uuid try: arq_uuid = received_json["uuid"] - except: - arq_uuid = 'no-uuid' + except Exception: + arq_uuid = "no-uuid" if not len(base64data) % 4: binarydata = base64.b64decode(base64data) - data_handler.DATA_QUEUE_TRANSMIT.put(['ARQ_RAW', binarydata, mode, n_frames, arq_uuid, mycallsign]) + data_handler.DATA_QUEUE_TRANSMIT.put( + ["ARQ_RAW", binarydata, mode, n_frames, arq_uuid, mycallsign] + ) else: raise TypeError - except Exception as e: + except Exception as err: command_response("send_raw", False) - structlog.get_logger("structlog").warning("[SCK] command execution error", e=e, command=received_json) + log.warning( + "[SCK] command execution error", e=err, command=received_json + ) # STOP TRANSMISSION ---------------------------------------------------------- - if received_json["type"] == 'arq' and received_json["command"] == "stop_transmission": + if ( + received_json["type"] == "arq" + and received_json["command"] == "stop_transmission" + ): try: - if static.TNC_STATE == 'BUSY' or static.ARQ_STATE: - data_handler.DATA_QUEUE_TRANSMIT.put(['STOP']) - structlog.get_logger("structlog").warning("[TNC] Stopping transmission!") - static.TNC_STATE = 'IDLE' + if static.TNC_STATE == "BUSY" or static.ARQ_STATE: + data_handler.DATA_QUEUE_TRANSMIT.put(["STOP"]) + log.warning("[TNC] Stopping transmission!") + static.TNC_STATE = "IDLE" static.ARQ_STATE = False command_response("stop_transmission", True) - except Exception as e: + except Exception as err: command_response("stop_transmission", False) - structlog.get_logger("structlog").warning("[SCK] command execution error", e=e, command=received_json) + log.warning( + "[SCK] command execution error", e=err, command=received_json + ) - if received_json["type"] == 'get' and received_json["command"] == 'rx_buffer': + if received_json["type"] == "get" and received_json["command"] == "rx_buffer": try: output = { "command": "rx_buffer", @@ -346,37 +396,53 @@ def process_tnc_commands(data): } for i in range(len(static.RX_BUFFER)): - #print(static.RX_BUFFER[i][4]) - #rawdata = json.loads(static.RX_BUFFER[i][4]) + # print(static.RX_BUFFER[i][4]) + # rawdata = json.loads(static.RX_BUFFER[i][4]) base64_data = static.RX_BUFFER[i][4] - output["data-array"].append({"uuid": static.RX_BUFFER[i][0],"timestamp": static.RX_BUFFER[i][1], "dxcallsign": str(static.RX_BUFFER[i][2], 'utf-8'), "dxgrid": str(static.RX_BUFFER[i][3], 'utf-8'), "data": base64_data}) + output["data-array"].append( + { + "uuid": static.RX_BUFFER[i][0], + "timestamp": static.RX_BUFFER[i][1], + "dxcallsign": str(static.RX_BUFFER[i][2], "utf-8"), + "dxgrid": str(static.RX_BUFFER[i][3], "utf-8"), + "data": base64_data, + } + ) jsondata = json.dumps(output) - #self.request.sendall(bytes(jsondata, encoding)) + # self.request.sendall(bytes(jsondata, encoding)) SOCKET_QUEUE.put(jsondata) command_response("rx_buffer", True) - except Exception as e: + except Exception as err: command_response("rx_buffer", False) - structlog.get_logger("structlog").warning("[SCK] command execution error", e=e, command=received_json) + log.warning( + "[SCK] command execution error", e=err, command=received_json + ) - if received_json["type"] == 'set' and received_json["command"] == 'del_rx_buffer': + if ( + received_json["type"] == "set" + and received_json["command"] == "del_rx_buffer" + ): try: static.RX_BUFFER = [] command_response("del_rx_buffer", True) - except Exception as e: + except Exception as err: command_response("del_rx_buffer", False) - structlog.get_logger("structlog").warning("[SCK] command execution error", e=e, command=received_json) + log.warning( + "[SCK] command execution error", e=err, command=received_json + ) # exception, if JSON cant be decoded - except Exception as e: - structlog.get_logger("structlog").error("[TNC] JSON decoding error", e=e) + except Exception as err: + log.error("[TNC] JSON decoding error", e=err) + def send_tnc_state(): """ send the tnc state to network """ - encoding = 'utf-8' + encoding = "utf-8" output = { "command": "tnc_state", @@ -401,8 +467,8 @@ def send_tnc_state(): "arq_compression_factor": str(static.ARQ_COMPRESSION_FACTOR), "arq_transmission_percent": str(static.ARQ_TRANSMISSION_PERCENT), "total_bytes": str(static.TOTAL_BYTES), - "info" : static.INFO, - "beacon_state" : str(static.BEACON_STATE), + "info": static.INFO, + "beacon_state": str(static.BEACON_STATE), "stations": [], "mycallsign": str(static.MYCALLSIGN, encoding), "dxcallsign": str(static.DXCALLSIGN, encoding), @@ -411,17 +477,21 @@ def send_tnc_state(): # add heard stations to heard stations object for heard in static.HEARD_STATIONS: - output["stations"].append({ - "dxcallsign": str(heard[0], 'utf-8'), - "dxgrid": str(heard[1], 'utf-8'), - "timestamp": heard[2], - "datatype": heard[3], - "snr": heard[4], - "offset": heard[5], - "frequency": heard[6]}) + output["stations"].append( + { + "dxcallsign": str(heard[0], "utf-8"), + "dxgrid": str(heard[1], "utf-8"), + "timestamp": heard[2], + "datatype": heard[3], + "snr": heard[4], + "offset": heard[5], + "frequency": heard[6], + } + ) return json.dumps(output) + def process_daemon_commands(data): """ process daemon commands @@ -432,41 +502,55 @@ def process_daemon_commands(data): Returns: """ + log = structlog.get_logger(__name__) + # convert data to json object received_json = json.loads(data) - structlog.get_logger("structlog").debug("[SCK] CMD", command=received_json) - if received_json["type"] == 'set' and received_json["command"] == 'mycallsign': + log.debug("[SCK] CMD", command=received_json) + if received_json["type"] == "set" and received_json["command"] == "mycallsign": try: callsign = received_json["parameter"] - if bytes(callsign, 'utf-8') == b'': - self.request.sendall(b'INVALID CALLSIGN') - structlog.get_logger("structlog").warning("[DMN] SET MYCALL FAILED", call=static.MYCALLSIGN, crc=static.MYCALLSIGN_CRC) + if bytes(callsign, "utf-8") == b"": + self.request.sendall(b"INVALID CALLSIGN") + log.warning( + "[DMN] SET MYCALL FAILED", + call=static.MYCALLSIGN, + crc=static.MYCALLSIGN_CRC, + ) else: - static.MYCALLSIGN = bytes(callsign, 'utf-8') + static.MYCALLSIGN = bytes(callsign, "utf-8") static.MYCALLSIGN_CRC = helpers.get_crc_24(static.MYCALLSIGN) command_response("mycallsign", True) - structlog.get_logger("structlog").info("[DMN] SET MYCALL", call=static.MYCALLSIGN, crc=static.MYCALLSIGN_CRC) - except Exception as e: + log.info( + "[DMN] SET MYCALL", + call=static.MYCALLSIGN, + crc=static.MYCALLSIGN_CRC, + ) + except Exception as err: command_response("mycallsign", False) - structlog.get_logger("structlog").warning("[SCK] command execution error", e=e, command=received_json) + log.warning("[SCK] command execution error", e=err, command=received_json) - if received_json["type"] == 'set' and received_json["command"] == 'mygrid': + if received_json["type"] == "set" and received_json["command"] == "mygrid": try: mygrid = received_json["parameter"] - if bytes(mygrid, 'utf-8') == b'': - self.request.sendall(b'INVALID GRID') + if bytes(mygrid, "utf-8") == b"": + self.request.sendall(b"INVALID GRID") else: - static.MYGRID = bytes(mygrid, 'utf-8') - structlog.get_logger("structlog").info("[SCK] SET MYGRID", grid=static.MYGRID) + static.MYGRID = bytes(mygrid, "utf-8") + log.info("[SCK] SET MYGRID", grid=static.MYGRID) command_response("mygrid", True) - except Exception as e: + except Exception as err: command_response("mygrid", False) - structlog.get_logger("structlog").warning("[SCK] command execution error", e=e, command=received_json) + log.warning("[SCK] command execution error", e=err, command=received_json) - if received_json["type"] == 'set' and received_json["command"] == 'start_tnc' and not static.TNCSTARTED: + if ( + received_json["type"] == "set" + and received_json["command"] == "start_tnc" + and not static.TNCSTARTED + ): try: mycall = str(received_json["parameter"][0]["mycall"]) mygrid = str(received_json["parameter"][0]["mygrid"]) @@ -494,40 +578,45 @@ def process_daemon_commands(data): # print some debugging parameters for item in received_json["parameter"][0]: - structlog.get_logger("structlog").debug("[DMN] TNC Startup Config : " + item, value=received_json["parameter"][0][item]) + log.debug( + f"[DMN] TNC Startup Config : {item}", + value=received_json["parameter"][0][item], + ) - DAEMON_QUEUE.put(['STARTTNC', - mycall, - mygrid, - rx_audio, - tx_audio, - devicename, - deviceport, - serialspeed, - pttprotocol, - pttport, - data_bits, - stop_bits, - handshake, - radiocontrol, - rigctld_ip, - rigctld_port, - enable_scatter, - enable_fft, - low_bandwith_mode, - tuning_range_fmin, - tuning_range_fmax, - enable_fsk, - tx_audio_level, - respond_to_cq, - ]) + DAEMON_QUEUE.put( + [ + "STARTTNC", + mycall, + mygrid, + rx_audio, + tx_audio, + devicename, + deviceport, + serialspeed, + pttprotocol, + pttport, + data_bits, + stop_bits, + handshake, + radiocontrol, + rigctld_ip, + rigctld_port, + enable_scatter, + enable_fft, + low_bandwith_mode, + tuning_range_fmin, + tuning_range_fmax, + enable_fsk, + tx_audio_level, + respond_to_cq, + ] + ) command_response("start_tnc", True) - except Exception as e: + except Exception as err: command_response("start_tnc", False) - structlog.get_logger("structlog").warning("[SCK] command execution error", e=e, command=received_json) - - if received_json["type"] == 'get' and received_json["command"] == 'test_hamlib': + log.warning("[SCK] command execution error", e=err, command=received_json) + if received_json["type"] == "get" and received_json["command"] == "test_hamlib": try: devicename = str(received_json["parameter"][0]["devicename"]) deviceport = str(received_json["parameter"][0]["deviceport"]) @@ -541,56 +630,62 @@ def process_daemon_commands(data): rigctld_ip = str(received_json["parameter"][0]["rigctld_ip"]) rigctld_port = str(received_json["parameter"][0]["rigctld_port"]) - DAEMON_QUEUE.put(['TEST_HAMLIB', - devicename, - deviceport, - serialspeed, - pttprotocol, - pttport, - data_bits, - stop_bits, - handshake, - radiocontrol, - rigctld_ip, - rigctld_port, - ]) + DAEMON_QUEUE.put( + [ + "TEST_HAMLIB", + devicename, + deviceport, + serialspeed, + pttprotocol, + pttport, + data_bits, + stop_bits, + handshake, + radiocontrol, + rigctld_ip, + rigctld_port, + ] + ) command_response("test_hamlib", True) - except Exception as e: + except Exception as err: command_response("test_hamlib", False) - structlog.get_logger("structlog").warning("[SCK] command execution error", e=e, command=received_json) + log.warning("[SCK] command execution error", e=err, command=received_json) - if received_json["type"] == 'set' and received_json["command"] == 'stop_tnc': + if received_json["type"] == "set" and received_json["command"] == "stop_tnc": try: static.TNCPROCESS.kill() # unregister process from atexit to avoid process zombies atexit.unregister(static.TNCPROCESS.kill) - structlog.get_logger("structlog").warning("[DMN] Stopping TNC") + log.warning("[DMN] Stopping TNC") static.TNCSTARTED = False command_response("stop_tnc", True) - except Exception as e: + except Exception as err: command_response("stop_tnc", False) - structlog.get_logger("structlog").warning("[SCK] command execution error", e=e, command=received_json) + log.warning("[SCK] command execution error", e=err, command=received_json) + def send_daemon_state(): """ send the daemon state to network """ + log = structlog.get_logger(__name__) + try: python_version = f"{str(sys.version_info[0])}.{str(sys.version_info[1])}" output = { - 'command': 'daemon_state', - 'daemon_state': [], - 'python_version': str(python_version), - 'hamlib_version': static.HAMLIB_VERSION, - 'input_devices': static.AUDIO_INPUT_DEVICES, - 'output_devices': static.AUDIO_OUTPUT_DEVICES, - 'serial_devices': static.SERIAL_DEVICES, + "command": "daemon_state", + "daemon_state": [], + "python_version": str(python_version), + "hamlib_version": static.HAMLIB_VERSION, + "input_devices": static.AUDIO_INPUT_DEVICES, + "output_devices": static.AUDIO_OUTPUT_DEVICES, + "serial_devices": static.SERIAL_DEVICES, #'cpu': str(psutil.cpu_percent()), #'ram': str(psutil.virtual_memory().percent), - 'version': '0.1' - } + "version": "0.1", + } if static.TNCSTARTED: output["daemon_state"].append({"status": "running"}) @@ -600,12 +695,13 @@ def send_daemon_state(): jsondata = json.dumps(output) return jsondata - except Exception as e: - structlog.get_logger("structlog").warning("[SCK] error", e=e) + except Exception as err: + log.warning("[SCK] error", e=err) return None + def command_response(command, status): s_status = "OK" if status else "Failed" - jsondata = {"command_response": command, "status" : s_status} + jsondata = {"command_response": command, "status": s_status} data_out = json.dumps(jsondata) SOCKET_QUEUE.put(data_out) diff --git a/tnc/static.py b/tnc/static.py index 07586fb9..26348fe8 100644 --- a/tnc/static.py +++ b/tnc/static.py @@ -8,7 +8,7 @@ Here we are saving application wide variables and stats, which have to be access Not nice, suggestions are appreciated :-) """ -VERSION = '0.4.0-alpha' +VERSION = "0.4.0-alpha" # DAEMON DAEMONPORT = 3001 @@ -16,16 +16,16 @@ TNCSTARTED = False TNCPROCESS = 0 # Operator Defaults -MYCALLSIGN = b'AA0AA' -MYCALLSIGN_CRC = b'A' +MYCALLSIGN = b"AA0AA" +MYCALLSIGN_CRC = b"A" -DXCALLSIGN = b'AA0AA' -DXCALLSIGN_CRC = b'A' +DXCALLSIGN = b"AA0AA" +DXCALLSIGN_CRC = b"A" -MYGRID = b'' -DXGRID = b'' +MYGRID = b"" +DXGRID = b"" -SSID_LIST = [] # ssid list we are responding to +SSID_LIST = [] # ssid list we are responding to LOW_BANDWITH_MODE = False # --------------------------------- @@ -33,7 +33,7 @@ LOW_BANDWITH_MODE = False # Server Defaults HOST = "0.0.0.0" PORT = 3000 -SOCKET_TIMEOUT = 1 # seconds +SOCKET_TIMEOUT = 1 # seconds # --------------------------------- SERIAL_DEVICES = [] # --------------------------------- @@ -41,21 +41,21 @@ SERIAL_DEVICES = [] PTT_STATE = False TRANSMITTING = False -HAMLIB_VERSION = '0' -HAMLIB_PTT_TYPE = 'RTS' -HAMLIB_DEVICE_NAME = 'RIG_MODEL_DUMMY_NOVFO' -HAMLIB_DEVICE_PORT = '/dev/ttyUSB0' -HAMLIB_SERIAL_SPEED = '9600' -HAMLIB_PTT_PORT = '/dev/ttyUSB0' -HAMLIB_STOP_BITS = '1' -HAMLIB_DATA_BITS = '8' -HAMLIB_HANDSHAKE = 'None' -HAMLIB_RADIOCONTROL = 'direct' -HAMLIB_RIGCTLD_IP = '127.0.0.1' -HAMLIB_RIGCTLD_PORT = '4532' +HAMLIB_VERSION = "0" +HAMLIB_PTT_TYPE = "RTS" +HAMLIB_DEVICE_NAME = "RIG_MODEL_DUMMY_NOVFO" +HAMLIB_DEVICE_PORT = "/dev/ttyUSB0" +HAMLIB_SERIAL_SPEED = "9600" +HAMLIB_PTT_PORT = "/dev/ttyUSB0" +HAMLIB_STOP_BITS = "1" +HAMLIB_DATA_BITS = "8" +HAMLIB_HANDSHAKE = "None" +HAMLIB_RADIOCONTROL = "direct" +HAMLIB_RIGCTLD_IP = "127.0.0.1" +HAMLIB_RIGCTLD_PORT = "4532" HAMLIB_FREQUENCY = 0 -HAMLIB_MODE = '' +HAMLIB_MODE = "" HAMLIB_BANDWIDTH = 0 # ------------------------- # FreeDV Defaults @@ -74,7 +74,7 @@ AUDIO_INPUT_DEVICES = [] AUDIO_OUTPUT_DEVICES = [] AUDIO_INPUT_DEVICE = -2 AUDIO_OUTPUT_DEVICE = -2 -BUFFER_OVERFLOW_COUNTER = [0,0,0,0,0] +BUFFER_OVERFLOW_COUNTER = [0, 0, 0, 0, 0] AUDIO_RMS = 0 FFT = [0] @@ -94,11 +94,12 @@ ARQ_TRANSMISSION_PERCENT = 0 ARQ_SPEED_LEVEL = 0 TOTAL_BYTES = 0 -#CHANNEL_STATE = 'RECEIVING_SIGNALLING' -TNC_STATE = 'IDLE' +# CHANNEL_STATE = 'RECEIVING_SIGNALLING' +TNC_STATE = "IDLE" ARQ_STATE = False ARQ_SESSION = False -ARQ_SESSION_STATE = 'disconnected' # disconnected, connecting, connected, disconnecting, failed +# disconnected, connecting, connected, disconnecting, failed +ARQ_SESSION_STATE = "disconnected" # BEACON STATE BEACON_STATE = False @@ -108,8 +109,8 @@ BEACON_PAUSE = False RX_BUFFER = [] RX_MSG_BUFFER = [] RX_BURST_BUFFER = [] -RX_FRAME_BUFFER = b'' -#RX_BUFFER_SIZE = 0 +RX_FRAME_BUFFER = b"" +# RX_BUFFER_SIZE = 0 # ------- HEARD STATIOS BUFFER HEARD_STATIONS = []