Incorporate some changes from pep8_improvements.

Simplify structlog calls.
Other refactoring.
This commit is contained in:
Paul Kronenwetter 2022-05-25 21:23:30 -04:00
parent 4c16efaf2c
commit 98c1030c24
14 changed files with 1140 additions and 698 deletions

View file

@ -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})

View file

@ -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)

View file

@ -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)

View file

@ -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

View file

@ -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

View file

@ -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,

View file

@ -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)

View file

@ -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]

View file

@ -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):

View file

@ -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

View file

@ -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

View file

@ -1,12 +1,9 @@
#!/usr/bin/env python3
import structlog
hamlib_version = 0
class radio:
""" """
def __init__(self):
pass

View file

@ -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)

View file

@ -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 = []