mirror of
https://github.com/DJ2LS/FreeDATA
synced 2024-05-14 08:04:33 +00:00
Merge pull request #187 from DJ2LS/refactor_N2KIQ_202205
Refactor TNC modules
This commit is contained in:
commit
2a109844e3
14 changed files with 1741 additions and 1995 deletions
23
tnc/audio.py
23
tnc/audio.py
|
@ -1,16 +1,14 @@
|
|||
|
||||
import json
|
||||
import sys
|
||||
import multiprocessing
|
||||
import sounddevice as sd
|
||||
import atexit
|
||||
import json
|
||||
import multiprocessing
|
||||
import sys
|
||||
|
||||
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
|
||||
|
||||
|
@ -26,7 +24,6 @@ def get_audio_devices():
|
|||
sd._initialize()
|
||||
|
||||
with multiprocessing.Manager() as manager:
|
||||
|
||||
proxy_input_devices = manager.list()
|
||||
proxy_output_devices = manager.list()
|
||||
#print(multiprocessing.get_start_method())
|
||||
|
@ -47,10 +44,8 @@ def fetch_audio_devices(input_devices, output_devices):
|
|||
Returns:
|
||||
|
||||
"""
|
||||
|
||||
devices = sd.query_devices(device=None, kind=None)
|
||||
index = 0
|
||||
for device in devices:
|
||||
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
|
||||
try:
|
||||
|
@ -66,8 +61,6 @@ def fetch_audio_devices(input_devices, output_devices):
|
|||
name = ''
|
||||
|
||||
if maxInputChannels > 0:
|
||||
input_devices.append({"id": index, "name": str(name)})
|
||||
input_devices.append({"id": index, "name": name})
|
||||
if maxOutputChannels > 0:
|
||||
output_devices.append({"id": index, "name": str(name)})
|
||||
index += 1
|
||||
|
||||
output_devices.append({"id": index, "name": name})
|
||||
|
|
216
tnc/codec2.py
216
tnc/codec2.py
|
@ -1,21 +1,24 @@
|
|||
#!/usr/bin/env python3
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
# pylint: disable=invalid-name, line-too-long, c-extension-no-member
|
||||
# pylint: disable=import-outside-toplevel
|
||||
|
||||
import ctypes
|
||||
from ctypes import *
|
||||
import sys
|
||||
import os
|
||||
from enum import Enum
|
||||
import numpy as np
|
||||
from threading import Lock
|
||||
import glob
|
||||
import os
|
||||
import sys
|
||||
from enum import Enum
|
||||
from threading import Lock
|
||||
|
||||
import numpy as np
|
||||
import structlog
|
||||
|
||||
|
||||
# Enum for codec2 modes
|
||||
class FREEDV_MODE(Enum):
|
||||
"""
|
||||
enum for codec2 modes and names
|
||||
Enumeration for codec2 modes and names
|
||||
"""
|
||||
fsk_ldpc_0 = 200
|
||||
fsk_ldpc_1 = 201
|
||||
|
@ -25,51 +28,50 @@ class FREEDV_MODE(Enum):
|
|||
datac3 = 12
|
||||
allmodes = 255
|
||||
|
||||
# function for returning the mode value
|
||||
def freedv_get_mode_value_by_name(mode):
|
||||
|
||||
# Function for returning the mode value
|
||||
def freedv_get_mode_value_by_name(mode: str) -> int:
|
||||
"""
|
||||
get the codec2 mode by entering its string
|
||||
Get the codec2 mode by entering its string
|
||||
|
||||
Args:
|
||||
mode:
|
||||
|
||||
Returns: int
|
||||
|
||||
Returns:
|
||||
int
|
||||
"""
|
||||
return FREEDV_MODE[mode].value
|
||||
|
||||
# function for returning the mode name
|
||||
def freedv_get_mode_name_by_value(mode):
|
||||
# Function for returning the mode name
|
||||
def freedv_get_mode_name_by_value(mode: int) -> str:
|
||||
"""
|
||||
get the codec2 mode name as string
|
||||
Args:
|
||||
mode:
|
||||
|
||||
Returns: string
|
||||
|
||||
Returns:
|
||||
string
|
||||
"""
|
||||
return FREEDV_MODE(mode).name
|
||||
|
||||
|
||||
# check if we are running in a pyinstaller environment
|
||||
try:
|
||||
app_path = sys._MEIPASS
|
||||
except:
|
||||
app_path = os.path.abspath(".")
|
||||
sys.path.append(app_path)
|
||||
# Check if we are running in a pyinstaller environment
|
||||
if hasattr(sys, "_MEIPASS"):
|
||||
sys.path.append(getattr(sys, "_MEIPASS"))
|
||||
else:
|
||||
sys.path.append(os.path.abspath("."))
|
||||
|
||||
structlog.get_logger("structlog").info("[C2 ] Searching for libcodec2...")
|
||||
if sys.platform == 'linux':
|
||||
files = glob.glob('**/*libcodec2*',recursive=True)
|
||||
files = glob.glob(r'**/*libcodec2*',recursive=True)
|
||||
files.append('libcodec2.so')
|
||||
elif sys.platform == 'darwin':
|
||||
files = glob.glob('**/*libcodec2*.dylib',recursive=True)
|
||||
|
||||
elif sys.platform == 'win32' or sys.platform == 'win64':
|
||||
files = glob.glob('**\*libcodec2*.dll',recursive=True)
|
||||
files = glob.glob(r'**/*libcodec2*.dylib',recursive=True)
|
||||
elif sys.platform in ['win32', 'win64']:
|
||||
files = glob.glob(r'**\*libcodec2*.dll',recursive=True)
|
||||
else:
|
||||
files = []
|
||||
|
||||
|
||||
api = None
|
||||
for file in files:
|
||||
try:
|
||||
api = ctypes.CDLL(file)
|
||||
|
@ -78,67 +80,63 @@ for file in files:
|
|||
except Exception as e:
|
||||
structlog.get_logger("structlog").warning("[C2 ] Libcodec2 found but not loaded", path=file, e=e)
|
||||
|
||||
|
||||
# quit module if codec2 cant be loaded
|
||||
if not 'api' in locals():
|
||||
structlog.get_logger("structlog").critical("[C2 ] Libcodec2 not loaded", path=file)
|
||||
os._exit(1)
|
||||
|
||||
|
||||
|
||||
# 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")
|
||||
sys.exit(1)
|
||||
|
||||
# ctypes function init
|
||||
|
||||
#api.freedv_set_tuning_range.restype = c_int
|
||||
#api.freedv_set_tuning_range.argype = [c_void_p, c_float, c_float]
|
||||
#api.freedv_set_tuning_range.restype = ctypes.c_int
|
||||
#api.freedv_set_tuning_range.argype = [ctypes.c_void_p, ctypes.c_float, ctypes.c_float]
|
||||
|
||||
api.freedv_open.argype = [c_int]
|
||||
api.freedv_open.restype = c_void_p
|
||||
api.freedv_open.argype = [ctypes.c_int]
|
||||
api.freedv_open.restype = ctypes.c_void_p
|
||||
|
||||
api.freedv_open_advanced.argtype = [c_int, c_void_p]
|
||||
api.freedv_open_advanced.restype = c_void_p
|
||||
api.freedv_open_advanced.argtype = [ctypes.c_int, ctypes.c_void_p]
|
||||
api.freedv_open_advanced.restype = ctypes.c_void_p
|
||||
|
||||
api.freedv_get_bits_per_modem_frame.argtype = [c_void_p]
|
||||
api.freedv_get_bits_per_modem_frame.restype = c_int
|
||||
api.freedv_get_bits_per_modem_frame.argtype = [ctypes.c_void_p]
|
||||
api.freedv_get_bits_per_modem_frame.restype = ctypes.c_int
|
||||
|
||||
api.freedv_nin.argtype = [c_void_p]
|
||||
api.freedv_nin.restype = c_int
|
||||
api.freedv_nin.argtype = [ctypes.c_void_p]
|
||||
api.freedv_nin.restype = ctypes.c_int
|
||||
|
||||
api.freedv_rawdatarx.argtype = [c_void_p, c_char_p, c_char_p]
|
||||
api.freedv_rawdatarx.restype = c_int
|
||||
api.freedv_rawdatarx.argtype = [ctypes.c_void_p, ctypes.c_char_p, ctypes.c_char_p]
|
||||
api.freedv_rawdatarx.restype = ctypes.c_int
|
||||
|
||||
api.freedv_rawdatatx.argtype = [c_void_p, c_char_p, c_char_p]
|
||||
api.freedv_rawdatatx.restype = c_int
|
||||
api.freedv_rawdatatx.argtype = [ctypes.c_void_p, ctypes.c_char_p, ctypes.c_char_p]
|
||||
api.freedv_rawdatatx.restype = ctypes.c_int
|
||||
|
||||
api.freedv_rawdatapostambletx.argtype = [c_void_p, c_char_p, c_char_p]
|
||||
api.freedv_rawdatapostambletx.restype = c_int
|
||||
api.freedv_rawdatapostambletx.argtype = [ctypes.c_void_p, ctypes.c_char_p, ctypes.c_char_p]
|
||||
api.freedv_rawdatapostambletx.restype = ctypes.c_int
|
||||
|
||||
api.freedv_rawdatapreambletx.argtype = [c_void_p, c_char_p, c_char_p]
|
||||
api.freedv_rawdatapreambletx.restype = c_int
|
||||
api.freedv_rawdatapreambletx.argtype = [ctypes.c_void_p, ctypes.c_char_p, ctypes.c_char_p]
|
||||
api.freedv_rawdatapreambletx.restype = ctypes.c_int
|
||||
|
||||
api.freedv_get_n_max_modem_samples.argtype = [c_void_p]
|
||||
api.freedv_get_n_max_modem_samples.restype = c_int
|
||||
api.freedv_get_n_max_modem_samples.argtype = [ctypes.c_void_p]
|
||||
api.freedv_get_n_max_modem_samples.restype = ctypes.c_int
|
||||
|
||||
api.freedv_set_frames_per_burst.argtype = [c_void_p, c_int]
|
||||
api.freedv_set_frames_per_burst.restype = c_void_p
|
||||
api.freedv_set_frames_per_burst.argtype = [ctypes.c_void_p, ctypes.c_int]
|
||||
api.freedv_set_frames_per_burst.restype = ctypes.c_void_p
|
||||
|
||||
api.freedv_get_rx_status.argtype = [c_void_p]
|
||||
api.freedv_get_rx_status.restype = c_int
|
||||
api.freedv_get_rx_status.argtype = [ctypes.c_void_p]
|
||||
api.freedv_get_rx_status.restype = ctypes.c_int
|
||||
|
||||
api.freedv_get_modem_stats.argtype = [c_void_p, c_void_p, c_void_p]
|
||||
api.freedv_get_modem_stats.restype = c_int
|
||||
api.freedv_get_modem_stats.argtype = [ctypes.c_void_p, ctypes.c_void_p, ctypes.c_void_p]
|
||||
api.freedv_get_modem_stats.restype = ctypes.c_int
|
||||
|
||||
api.freedv_get_n_tx_postamble_modem_samples.argtype = [c_void_p]
|
||||
api.freedv_get_n_tx_postamble_modem_samples.restype = c_int
|
||||
api.freedv_get_n_tx_postamble_modem_samples.argtype = [ctypes.c_void_p]
|
||||
api.freedv_get_n_tx_postamble_modem_samples.restype = ctypes.c_int
|
||||
|
||||
api.freedv_get_n_tx_preamble_modem_samples.argtype = [c_void_p]
|
||||
api.freedv_get_n_tx_preamble_modem_samples.restype = c_int
|
||||
api.freedv_get_n_tx_preamble_modem_samples.argtype = [ctypes.c_void_p]
|
||||
api.freedv_get_n_tx_preamble_modem_samples.restype = ctypes.c_int
|
||||
|
||||
api.freedv_get_n_tx_modem_samples.argtype = [c_void_p]
|
||||
api.freedv_get_n_tx_modem_samples.restype = c_int
|
||||
api.freedv_get_n_tx_modem_samples.argtype = [ctypes.c_void_p]
|
||||
api.freedv_get_n_tx_modem_samples.restype = ctypes.c_int
|
||||
|
||||
api.freedv_get_n_max_modem_samples.argtype = [c_void_p]
|
||||
api.freedv_get_n_max_modem_samples.restype = c_int
|
||||
api.freedv_get_n_max_modem_samples.argtype = [ctypes.c_void_p]
|
||||
api.freedv_get_n_max_modem_samples.restype = ctypes.c_int
|
||||
|
||||
api.FREEDV_FS_8000 = 8000
|
||||
api.FREEDV_MODE_DATAC1 = 10
|
||||
|
@ -148,7 +146,7 @@ api.FREEDV_MODE_FSK_LDPC = 9
|
|||
|
||||
# -------------------------------- FSK LDPC MODE SETTINGS
|
||||
|
||||
# advanced structure for fsk modes
|
||||
# Advanced structure for fsk modes
|
||||
class ADVANCED(ctypes.Structure):
|
||||
""" """
|
||||
_fields_ = [
|
||||
|
@ -203,7 +201,6 @@ api.FREEDV_MODE_FSK_LDPC_1_ADV.tone_spacing = 200
|
|||
api.FREEDV_MODE_FSK_LDPC_1_ADV.codename = 'H_256_512_4'.encode('utf-8') # code word
|
||||
|
||||
# ------- MODEM STATS STRUCTURES
|
||||
|
||||
MODEM_STATS_NC_MAX = 50 + 1
|
||||
MODEM_STATS_NR_MAX = 160
|
||||
MODEM_STATS_ET_MAX = 8
|
||||
|
@ -211,7 +208,8 @@ MODEM_STATS_EYE_IND_MAX = 160
|
|||
MODEM_STATS_NSPEC = 512
|
||||
MODEM_STATS_MAX_F_HZ = 4000
|
||||
MODEM_STATS_MAX_F_EST = 4
|
||||
# modem stats structure
|
||||
|
||||
# Modem stats structure
|
||||
class MODEMSTATS(ctypes.Structure):
|
||||
""" """
|
||||
_fields_ = [
|
||||
|
@ -233,8 +231,6 @@ class MODEMSTATS(ctypes.Structure):
|
|||
("fft_buf", (ctypes.c_float * MODEM_STATS_NSPEC * 2)),
|
||||
]
|
||||
|
||||
|
||||
|
||||
# Return code flags for freedv_get_rx_status() function
|
||||
api.FREEDV_RX_TRIAL_SYNC = 0x1 # demodulator has trial sync
|
||||
api.FREEDV_RX_SYNC = 0x2 # demodulator has sync
|
||||
|
@ -259,22 +255,22 @@ api.rx_sync_flags_to_text = [
|
|||
"EBS-",
|
||||
"EBST"]
|
||||
|
||||
# audio buffer ---------------------------------------------------------
|
||||
|
||||
# Audio buffer ---------------------------------------------------------
|
||||
class audio_buffer:
|
||||
"""
|
||||
thread safe audio buffer, which fits to needs of codec2
|
||||
Thread safe audio buffer, which fits to needs of codec2
|
||||
|
||||
made by David Rowe, VK5DGR
|
||||
"""
|
||||
# a buffer of int16 samples, using a fixed length numpy array self.buffer for storage
|
||||
# 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)
|
||||
structlog.get_logger("structlog").debug("[C2 ] Creating audio buffer", size=size)
|
||||
self.size = size
|
||||
self.buffer = np.zeros(size, dtype=np.int16)
|
||||
self.nbuffer = 0
|
||||
self.mutex = Lock()
|
||||
|
||||
def push(self,samples):
|
||||
"""
|
||||
Push new data to buffer
|
||||
|
@ -283,14 +279,15 @@ class audio_buffer:
|
|||
samples:
|
||||
|
||||
Returns:
|
||||
|
||||
Nothing
|
||||
"""
|
||||
self.mutex.acquire()
|
||||
# add samples at the end of the buffer
|
||||
# Add samples at the end of the buffer
|
||||
assert self.nbuffer+len(samples) <= self.size
|
||||
self.buffer[self.nbuffer:self.nbuffer+len(samples)] = samples
|
||||
self.nbuffer += len(samples)
|
||||
self.mutex.release()
|
||||
|
||||
def pop(self,size):
|
||||
"""
|
||||
get data from buffer in size of NIN
|
||||
|
@ -298,90 +295,89 @@ class audio_buffer:
|
|||
size:
|
||||
|
||||
Returns:
|
||||
|
||||
Nothing
|
||||
"""
|
||||
self.mutex.acquire()
|
||||
# remove samples from the start of the buffer
|
||||
self.nbuffer -= size;
|
||||
# Remove samples from the start of the buffer
|
||||
self.nbuffer -= size
|
||||
self.buffer[:self.nbuffer] = self.buffer[size:size+self.nbuffer]
|
||||
assert self.nbuffer >= 0
|
||||
self.mutex.release()
|
||||
|
||||
# resampler ---------------------------------------------------------
|
||||
# Resampler ---------------------------------------------------------
|
||||
|
||||
api.FDMDV_OS_48 = int(6) # oversampling rate
|
||||
api.FDMDV_OS_TAPS_48K = int(48) # number of OS filter taps at 48kHz
|
||||
api.FDMDV_OS_TAPS_48_8K = int(api.FDMDV_OS_TAPS_48K/api.FDMDV_OS_48) # number of OS filter taps at 8kHz
|
||||
api.fdmdv_8_to_48_short.argtype = [c_void_p, c_void_p, c_int]
|
||||
api.fdmdv_48_to_8_short.argtype = [c_void_p, c_void_p, c_int]
|
||||
api.fdmdv_8_to_48_short.argtype = [ctypes.c_void_p, ctypes.c_void_p, ctypes.c_int]
|
||||
api.fdmdv_48_to_8_short.argtype = [ctypes.c_void_p, ctypes.c_void_p, ctypes.c_int]
|
||||
|
||||
class resampler:
|
||||
"""
|
||||
resampler class
|
||||
Re-sampler class
|
||||
"""
|
||||
# resample an array of variable length, we just store the filter memories here
|
||||
# Re-sample an array of variable length, we just store the filter memories here
|
||||
MEM8 = api.FDMDV_OS_TAPS_48_8K
|
||||
MEM48 = api.FDMDV_OS_TAPS_48K
|
||||
|
||||
def __init__(self):
|
||||
structlog.get_logger("structlog").debug("[C2 ] create 48<->8 kHz resampler")
|
||||
structlog.get_logger("structlog").debug("[C2 ] Create 48<->8 kHz resampler")
|
||||
self.filter_mem8 = np.zeros(self.MEM8, dtype=np.int16)
|
||||
self.filter_mem48 = np.zeros(self.MEM48)
|
||||
|
||||
|
||||
def resample48_to_8(self, in48):
|
||||
"""
|
||||
audio resampler integration from codec2
|
||||
downsample audio from 48000Hz to 8000Hz
|
||||
Audio resampler integration from codec2
|
||||
Downsample audio from 48000Hz to 8000Hz
|
||||
Args:
|
||||
in48: input data as np.int16
|
||||
|
||||
Returns: downsampled 8000Hz data as np.int16
|
||||
|
||||
Returns:
|
||||
Downsampled 8000Hz data as np.int16
|
||||
"""
|
||||
assert in48.dtype == np.int16
|
||||
# length of input vector must be an integer multiple of api.FDMDV_OS_48
|
||||
assert(len(in48) % api.FDMDV_OS_48 == 0)
|
||||
# Length of input vector must be an integer multiple of api.FDMDV_OS_48
|
||||
assert len(in48) % api.FDMDV_OS_48 == 0
|
||||
|
||||
# concat filter memory and input samples
|
||||
# Concatenate filter memory and input samples
|
||||
in48_mem = np.zeros(self.MEM48+len(in48), dtype=np.int16)
|
||||
in48_mem[:self.MEM48] = self.filter_mem48
|
||||
in48_mem[self.MEM48:] = in48
|
||||
|
||||
# In C: pin48=&in48_mem[MEM48]
|
||||
pin48 = byref(np.ctypeslib.as_ctypes(in48_mem), 2*self.MEM48)
|
||||
pin48 = ctypes.byref(np.ctypeslib.as_ctypes(in48_mem), 2 * self.MEM48)
|
||||
n8 = int(len(in48) / api.FDMDV_OS_48)
|
||||
out8 = np.zeros(n8, dtype=np.int16)
|
||||
api.fdmdv_48_to_8_short(out8.ctypes, pin48, n8);
|
||||
api.fdmdv_48_to_8_short(out8.ctypes, pin48, n8)
|
||||
|
||||
# store memory for next time
|
||||
# Store memory for next time
|
||||
self.filter_mem48 = in48_mem[:self.MEM48]
|
||||
|
||||
return out8
|
||||
|
||||
def resample8_to_48(self, in8):
|
||||
"""
|
||||
audio resampler integration from codec2
|
||||
resample audio from 8000Hz to 48000Hz
|
||||
Audio resampler integration from codec2
|
||||
Re-sample audio from 8000Hz to 48000Hz
|
||||
Args:
|
||||
in8: input data as np.int16
|
||||
|
||||
Returns: 48000Hz audio as np.int16
|
||||
|
||||
Returns:
|
||||
48000Hz audio as np.int16
|
||||
"""
|
||||
assert in8.dtype == np.int16
|
||||
|
||||
# concat filter memory and input samples
|
||||
# Concatenate filter memory and input samples
|
||||
in8_mem = np.zeros(self.MEM8+len(in8), dtype=np.int16)
|
||||
in8_mem[:self.MEM8] = self.filter_mem8
|
||||
in8_mem[self.MEM8:] = in8
|
||||
|
||||
# In C: pin8=&in8_mem[MEM8]
|
||||
pin8 = byref(np.ctypeslib.as_ctypes(in8_mem), 2*self.MEM8)
|
||||
pin8 = ctypes.byref(np.ctypeslib.as_ctypes(in8_mem), 2 * self.MEM8)
|
||||
out48 = np.zeros(api.FDMDV_OS_48*len(in8), dtype=np.int16)
|
||||
api.fdmdv_8_to_48_short(out48.ctypes, pin8, len(in8));
|
||||
|
||||
# store memory for next time
|
||||
# Store memory for next time
|
||||
self.filter_mem8 = in8_mem[:self.MEM8]
|
||||
|
||||
return out48
|
||||
|
|
112
tnc/daemon.py
112
tnc/daemon.py
|
@ -8,55 +8,57 @@ Author: DJ2LS, January 2022
|
|||
daemon for providing basic information for the tnc like audio or serial devices
|
||||
|
||||
"""
|
||||
# pylint: disable=invalid-name, line-too-long, c-extension-no-member
|
||||
# pylint: disable=import-outside-toplevel
|
||||
|
||||
import argparse
|
||||
import threading
|
||||
import socketserver
|
||||
import time
|
||||
import sys
|
||||
import subprocess
|
||||
import ujson as json
|
||||
import psutil
|
||||
import serial.tools.list_ports
|
||||
import static
|
||||
import crcengine
|
||||
import re
|
||||
import structlog
|
||||
import log_handler
|
||||
import helpers
|
||||
import atexit
|
||||
import multiprocessing
|
||||
import os
|
||||
import queue
|
||||
import audio
|
||||
import sock
|
||||
import atexit
|
||||
import re
|
||||
import signal
|
||||
import multiprocessing
|
||||
import socketserver
|
||||
import subprocess
|
||||
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 log_handler
|
||||
import sock
|
||||
import static
|
||||
|
||||
|
||||
# signal handler for closing aplication
|
||||
def signal_handler(sig, frame):
|
||||
"""
|
||||
signal handler for closing the network socket on app exit
|
||||
Signal handler for closing the network socket on app exit
|
||||
Args:
|
||||
sig:
|
||||
frame:
|
||||
|
||||
Returns: system exit
|
||||
|
||||
"""
|
||||
print('Closing daemon...')
|
||||
sock.CLOSE_SIGNAL = True
|
||||
sys.exit(0)
|
||||
|
||||
signal.signal(signal.SIGINT, signal_handler)
|
||||
|
||||
|
||||
|
||||
class DAEMON():
|
||||
"""
|
||||
daemon class
|
||||
Daemon class
|
||||
|
||||
"""
|
||||
def __init__(self):
|
||||
|
||||
# load crc engine
|
||||
self.crc_algorithm = crcengine.new('crc16-ccitt-false') # load crc8 library
|
||||
|
||||
|
@ -70,25 +72,22 @@ class DAEMON():
|
|||
worker = threading.Thread(target=self.worker, name="WORKER", daemon=True)
|
||||
worker.start()
|
||||
|
||||
|
||||
|
||||
def update_audio_devices(self):
|
||||
"""
|
||||
update audio devices and set to static
|
||||
Update audio devices and set to static
|
||||
"""
|
||||
while 1:
|
||||
try:
|
||||
if not static.TNCSTARTED:
|
||||
|
||||
static.AUDIO_INPUT_DEVICES, static.AUDIO_OUTPUT_DEVICES = audio.get_audio_devices()
|
||||
except Exception as e:
|
||||
print(e)
|
||||
structlog.get_logger("structlog").error("[DMN] update_audio_devices: Exception gathering audio devices:", e=e)
|
||||
# print(e)
|
||||
time.sleep(1)
|
||||
|
||||
|
||||
def update_serial_devices(self):
|
||||
"""
|
||||
update serial devices and set to static
|
||||
Update serial devices and set to static
|
||||
"""
|
||||
while 1:
|
||||
try:
|
||||
|
@ -96,26 +95,25 @@ class DAEMON():
|
|||
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 = crc_hwid.hex()
|
||||
description = desc + ' [' + crc_hwid + ']'
|
||||
description = f"{desc} [{crc_hwid}]"
|
||||
serial_devices.append({"port": str(port), "description": str(description) })
|
||||
|
||||
static.SERIAL_DEVICES = serial_devices
|
||||
time.sleep(1)
|
||||
except Exception as e:
|
||||
print(e)
|
||||
structlog.get_logger("structlog").error("[DMN] update_serial_devices: Exception gathering serial devices:", e=e)
|
||||
# print(e)
|
||||
|
||||
def worker(self):
|
||||
"""
|
||||
a worker for the received commands
|
||||
Worker to handle the received commands
|
||||
"""
|
||||
while 1:
|
||||
try:
|
||||
|
||||
data = self.daemon_queue.get()
|
||||
|
||||
# data[1] mycall
|
||||
|
@ -168,7 +166,6 @@ class DAEMON():
|
|||
# disabled mode
|
||||
|
||||
if data[13] != 'disabled':
|
||||
|
||||
options.append('--devicename')
|
||||
options.append(data[5])
|
||||
|
||||
|
@ -212,7 +209,6 @@ class DAEMON():
|
|||
if data[18] == 'True':
|
||||
options.append('--500hz')
|
||||
|
||||
|
||||
options.append('--tuning_range_fmin')
|
||||
options.append(data[19])
|
||||
|
||||
|
@ -229,15 +225,13 @@ class DAEMON():
|
|||
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 running tnc from binary, else run from source
|
||||
# This helps running the tnc in a developer environment
|
||||
try:
|
||||
command = []
|
||||
if sys.platform == 'linux' or sys.platform == 'darwin':
|
||||
if sys.platform in ['linux', 'darwin']:
|
||||
command.append('./freedata-tnc')
|
||||
elif sys.platform == 'win32' or sys.platform == 'win64':
|
||||
elif sys.platform in ['win32', 'win64']:
|
||||
command.append('freedata-tnc.exe')
|
||||
|
||||
command += options
|
||||
|
@ -246,11 +240,12 @@ class DAEMON():
|
|||
atexit.register(p.kill)
|
||||
|
||||
structlog.get_logger("structlog").info("[DMN] TNC started", path="binary")
|
||||
except:
|
||||
except FileNotFoundError as e:
|
||||
structlog.get_logger("structlog").error("[DMN] worker: Exception:", e=e)
|
||||
command = []
|
||||
if sys.platform == 'linux' or sys.platform == 'darwin':
|
||||
if sys.platform in ['linux', 'darwin']:
|
||||
command.append('python3')
|
||||
elif sys.platform == 'win32' or sys.platform == 'win64':
|
||||
elif sys.platform in ['win32', 'win64']:
|
||||
command.append('python')
|
||||
|
||||
command.append('main.py')
|
||||
|
@ -282,7 +277,6 @@ class DAEMON():
|
|||
# data[10] rigctld_ip
|
||||
# data[11] rigctld_port
|
||||
if data[0] == 'TEST_HAMLIB':
|
||||
|
||||
devicename = data[1]
|
||||
deviceport = data[2]
|
||||
serialspeed = data[3]
|
||||
|
@ -295,8 +289,6 @@ class DAEMON():
|
|||
rigctld_ip = data[10]
|
||||
rigctld_port = data[11]
|
||||
|
||||
|
||||
|
||||
# check how we want to control the radio
|
||||
if radiocontrol == 'direct':
|
||||
import rig
|
||||
|
@ -308,7 +300,9 @@ class DAEMON():
|
|||
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
|
||||
|
||||
|
@ -332,15 +326,13 @@ class DAEMON():
|
|||
sock.SOCKET_QUEUE.put(jsondata)
|
||||
|
||||
except Exception as e:
|
||||
print(e)
|
||||
|
||||
|
||||
structlog.get_logger("structlog").error("[DMN] worker: Exception: ", e=e)
|
||||
# print(e)
|
||||
|
||||
if __name__ == '__main__':
|
||||
# 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)
|
||||
|
@ -348,7 +340,6 @@ if __name__ == '__main__':
|
|||
|
||||
static.DAEMONPORT = ARGS.socket_port
|
||||
|
||||
|
||||
try:
|
||||
if sys.platform == 'linux':
|
||||
logging_path = os.getenv("HOME") + '/.config/' + 'FreeDATA/' + 'daemon'
|
||||
|
@ -356,14 +347,14 @@ if __name__ == '__main__':
|
|||
if sys.platform == 'darwin':
|
||||
logging_path = os.getenv("HOME") + '/Library/' + 'Application Support/' + 'FreeDATA/' + 'daemon'
|
||||
|
||||
if sys.platform == 'win32' or sys.platform == 'win64':
|
||||
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:
|
||||
structlog.get_logger("structlog").error("[DMN] logger init error")
|
||||
except Exception as e:
|
||||
structlog.get_logger("structlog").error("[DMN] logger init error", exception=e)
|
||||
|
||||
try:
|
||||
structlog.get_logger("structlog").info("[DMN] Starting TCP/IP socket", port=static.DAEMONPORT)
|
||||
|
@ -376,10 +367,9 @@ if __name__ == '__main__':
|
|||
|
||||
except Exception as e:
|
||||
structlog.get_logger("structlog").error("[DMN] Starting TCP/IP socket failed", port=static.DAEMONPORT, e=e)
|
||||
os._exit(1)
|
||||
sys.exit(1)
|
||||
daemon = DAEMON()
|
||||
|
||||
|
||||
structlog.get_logger("structlog").info("[DMN] Starting FreeDATA Daemon", author="DJ2LS", year="2022", version=static.VERSION)
|
||||
while True:
|
||||
time.sleep(1)
|
||||
|
|
File diff suppressed because it is too large
Load diff
126
tnc/helpers.py
126
tnc/helpers.py
|
@ -5,21 +5,21 @@ Created on Fri Dec 25 21:25:14 2020
|
|||
|
||||
@author: DJ2LS
|
||||
"""
|
||||
|
||||
import time
|
||||
|
||||
import crcengine
|
||||
import structlog
|
||||
|
||||
import static
|
||||
|
||||
|
||||
|
||||
def wait(seconds):
|
||||
def wait(seconds: float) -> bool:
|
||||
"""
|
||||
|
||||
Args:
|
||||
seconds:
|
||||
|
||||
Returns:
|
||||
|
||||
"""
|
||||
timeout = time.time() + seconds
|
||||
|
||||
|
@ -27,9 +27,7 @@ def wait(seconds):
|
|||
time.sleep(0.01)
|
||||
return True
|
||||
|
||||
|
||||
|
||||
def get_crc_8(data):
|
||||
def get_crc_8(data) -> bytes:
|
||||
"""Author: DJ2LS
|
||||
|
||||
Get the CRC8 of a byte string
|
||||
|
@ -40,15 +38,14 @@ def get_crc_8(data):
|
|||
data:
|
||||
|
||||
Returns:
|
||||
|
||||
CRC-8 (CCITT) of the provided data as bytes
|
||||
"""
|
||||
crc_algorithm = crcengine.new('crc8-ccitt') # load crc8 library
|
||||
crc_data = crc_algorithm(data)
|
||||
crc_data = crc_data.to_bytes(1, byteorder='big')
|
||||
return crc_data
|
||||
|
||||
|
||||
def get_crc_16(data):
|
||||
def get_crc_16(data) -> bytes:
|
||||
"""Author: DJ2LS
|
||||
|
||||
Get the CRC16 of a byte string
|
||||
|
@ -59,14 +56,14 @@ def get_crc_16(data):
|
|||
data:
|
||||
|
||||
Returns:
|
||||
|
||||
CRC-16 (CCITT) of the provided data as bytes
|
||||
"""
|
||||
crc_algorithm = crcengine.new('crc16-ccitt-false') # load crc16 library
|
||||
crc_data = crc_algorithm(data)
|
||||
crc_data = crc_data.to_bytes(2, byteorder='big')
|
||||
return crc_data
|
||||
|
||||
def get_crc_24(data):
|
||||
def get_crc_24(data) -> bytes:
|
||||
"""Author: DJ2LS
|
||||
|
||||
Get the CRC24-OPENPGP of a byte string
|
||||
|
@ -78,7 +75,7 @@ def get_crc_24(data):
|
|||
data:
|
||||
|
||||
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,
|
||||
|
@ -87,8 +84,7 @@ def get_crc_24(data):
|
|||
crc_data = crc_data.to_bytes(3, byteorder='big')
|
||||
return crc_data
|
||||
|
||||
|
||||
def get_crc_32(data):
|
||||
def get_crc_32(data: bytes) -> bytes:
|
||||
"""Author: DJ2LS
|
||||
|
||||
Get the CRC32 of a byte string
|
||||
|
@ -99,14 +95,13 @@ def get_crc_32(data):
|
|||
data:
|
||||
|
||||
Returns:
|
||||
|
||||
CRC-32 of the provided data as bytes
|
||||
"""
|
||||
crc_algorithm = crcengine.new('crc32') # load crc16 library
|
||||
crc_algorithm = crcengine.new('crc32') # load crc32 library
|
||||
crc_data = crc_algorithm(data)
|
||||
crc_data = crc_data.to_bytes(4, byteorder='big')
|
||||
return crc_data
|
||||
|
||||
|
||||
def add_to_heard_stations(dxcallsign, dxgrid, datatype, snr, offset, frequency):
|
||||
"""
|
||||
|
||||
|
@ -119,33 +114,29 @@ def add_to_heard_stations(dxcallsign, dxgrid, datatype, snr, offset, frequency):
|
|||
frequency:
|
||||
|
||||
Returns:
|
||||
|
||||
Nothing
|
||||
"""
|
||||
|
||||
# check if buffer empty
|
||||
if len(static.HEARD_STATIONS) == 0:
|
||||
static.HEARD_STATIONS.append([dxcallsign, dxgrid, int(time.time()), datatype, snr, offset, frequency])
|
||||
# if not, we search and update
|
||||
else:
|
||||
for i in range(0, len(static.HEARD_STATIONS)):
|
||||
# update callsign with new timestamp
|
||||
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]
|
||||
break
|
||||
# insert if nothing found
|
||||
# Insert if nothing found
|
||||
if i == len(static.HEARD_STATIONS) - 1:
|
||||
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):
|
||||
def callsign_to_bytes(callsign) -> bytes:
|
||||
"""
|
||||
|
||||
Args:
|
||||
|
@ -172,18 +163,20 @@ def callsign_to_bytes(callsign):
|
|||
#-14 Truckers or generally full time drivers
|
||||
#-15 generic additional station, digi, mobile, wx, etc
|
||||
|
||||
# try converting to bytestring if possible type string
|
||||
# Try converting to bytestring if possible type string
|
||||
try:
|
||||
callsign = bytes(callsign, 'utf-8')
|
||||
except:
|
||||
except TypeError as e:
|
||||
structlog.get_logger("structlog").debug("[HLP] callsign_to_bytes: Exception converting callsign to bytes:", e=e)
|
||||
pass
|
||||
|
||||
# we need to do this step to reduce the needed paypload by the callsign ( stripping "-" out of the callsign )
|
||||
# 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:
|
||||
ssid = 0
|
||||
except IndexError as e:
|
||||
structlog.get_logger("structlog").debug("[HLP] callsign_to_bytes: Error callsign SSID to integer:", e=e)
|
||||
|
||||
#callsign = callsign[0]
|
||||
#bytestring = bytearray(8)
|
||||
|
@ -194,11 +187,9 @@ def callsign_to_bytes(callsign):
|
|||
callsign = callsign[0].decode("utf-8")
|
||||
ssid = bytes([ssid]).decode("utf-8")
|
||||
return encode_call(callsign + ssid)
|
||||
|
||||
|
||||
#return bytes(bytestring)
|
||||
|
||||
def bytes_to_callsign(bytestring):
|
||||
def bytes_to_callsign(bytestring: bytes) -> bytes:
|
||||
"""
|
||||
Convert our callsign, received by a frame to a callsign in a human readable format
|
||||
|
||||
|
@ -207,9 +198,7 @@ def bytes_to_callsign(bytestring):
|
|||
|
||||
Returns:
|
||||
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
|
||||
|
@ -244,9 +233,7 @@ def bytes_to_callsign(bytestring):
|
|||
decoded = decode_call(bytestring)
|
||||
callsign = decoded[:-1]
|
||||
ssid = ord(bytes(decoded[-1], "utf-8"))
|
||||
return bytes(callsign + "-" + str(ssid), "utf-8")
|
||||
|
||||
|
||||
return bytes(f"{callsign}-{ssid}", "utf-8")
|
||||
|
||||
def check_callsign(callsign:bytes, crc_to_check:bytes):
|
||||
"""
|
||||
|
@ -261,13 +248,14 @@ def check_callsign(callsign:bytes, crc_to_check:bytes):
|
|||
False
|
||||
"""
|
||||
|
||||
print(callsign)
|
||||
# print(callsign)
|
||||
structlog.get_logger("structlog").debug("[HLP] check_callsign: Checking:", callsign=callsign)
|
||||
try:
|
||||
callsign = callsign.split(b'-')
|
||||
callsign = callsign[0] # we want the callsign without SSID
|
||||
# We want the callsign without SSID
|
||||
callsign = callsign.split(b'-')[0]
|
||||
|
||||
except:
|
||||
callsign = callsign
|
||||
except Exception as e:
|
||||
structlog.get_logger("structlog").debug("[HLP] check_callsign: Error callsign SSIG to integer:", e=e)
|
||||
|
||||
for ssid in static.SSID_LIST:
|
||||
call_with_ssid = bytearray(callsign)
|
||||
|
@ -282,8 +270,6 @@ def check_callsign(callsign:bytes, crc_to_check:bytes):
|
|||
|
||||
return [False, ""]
|
||||
|
||||
|
||||
|
||||
def encode_grid(grid):
|
||||
"""
|
||||
@auther: DB1UJ
|
||||
|
@ -292,7 +278,7 @@ def encode_grid(grid):
|
|||
Returns:
|
||||
4 bytes contains 26 bit valid data with encoded grid locator
|
||||
"""
|
||||
out_code_word = int(0)
|
||||
out_code_word = 0
|
||||
|
||||
grid = grid.upper() # upper case to be save
|
||||
|
||||
|
@ -302,18 +288,18 @@ def encode_grid(grid):
|
|||
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 = out_code_word << 9 # shift 9 bit left having space next bits, letter A-R * A-R
|
||||
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 = out_code_word | (int_val & 0b1111111) # using bit OR to add new value
|
||||
out_code_word = out_code_word << 7 # shift 7 bit left having space next bits, letter A-X
|
||||
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 = out_code_word | (int_val & 0b11111) # using bit OR to add new value
|
||||
out_code_word = out_code_word << 5 # shift 5 bit left having space next bits, letter A-X
|
||||
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 = out_code_word | (int_val & 0b11111) # using bit OR to add new value
|
||||
out_code_word |= (int_val & 0b11111) # using bit OR to add new value
|
||||
|
||||
return out_code_word.to_bytes(length=4, byteorder='big')
|
||||
|
||||
|
@ -328,25 +314,24 @@ def decode_grid(b_code_word:bytes):
|
|||
code_word = int.from_bytes(b_code_word, byteorder='big', signed=False)
|
||||
|
||||
grid = chr((code_word & 0b11111) + 65)
|
||||
code_word = code_word >> 5
|
||||
code_word >>= 5
|
||||
|
||||
grid = chr((code_word & 0b11111) + 65) + grid
|
||||
code_word = code_word >> 7
|
||||
code_word >>= 7
|
||||
|
||||
grid = str(int(code_word & 0b1111111)) + grid
|
||||
if (code_word & 0b1111111) < 10:
|
||||
grid = '0' + grid
|
||||
code_word = code_word >> 9
|
||||
grid = f'0{grid}'
|
||||
code_word >>= 9
|
||||
|
||||
int_val = int(code_word & 0b111111111)
|
||||
int_first = int_val // 18
|
||||
int_sec = int_val % 18
|
||||
int_first, int_sec = divmod(int_val, 18)
|
||||
# int_first = int_val // 18
|
||||
# int_sec = int_val % 18
|
||||
grid = chr(int(int_first) + 65) + chr(int(int_sec) + 65) + grid
|
||||
|
||||
return grid
|
||||
|
||||
|
||||
|
||||
def encode_call(call):
|
||||
"""
|
||||
@auther: DB1UJ
|
||||
|
@ -356,17 +341,17 @@ def encode_call(call):
|
|||
Returns:
|
||||
6 bytes contains 6 bits/sign encoded 8 char call sign with binary SSID (only upper letters + numbers, SSID)
|
||||
"""
|
||||
out_code_word = int(0)
|
||||
out_code_word = 0
|
||||
|
||||
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 = out_code_word << 6 # shift left 6 bit, making space for a new char
|
||||
out_code_word = out_code_word | (int_val & 0b111111) # bit OR adds the new char, masked with AND 0b111111
|
||||
out_code_word = out_code_word >> 6 # clean last char
|
||||
out_code_word = out_code_word << 6 # make clean space
|
||||
out_code_word = out_code_word | (ord(call[-1]) & 0b111111) # add the SSID uncoded only 0 - 63
|
||||
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')
|
||||
|
||||
|
@ -385,9 +370,8 @@ def decode_call(b_code_word:bytes):
|
|||
call = str()
|
||||
while code_word != 0:
|
||||
call = chr((code_word & 0b111111)+48) + call
|
||||
code_word = code_word >> 6
|
||||
code_word >>= 6
|
||||
|
||||
call = call[0:-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
|
||||
|
||||
|
|
|
@ -8,7 +8,6 @@ def setup_logging(filename):
|
|||
Returns:
|
||||
|
||||
"""
|
||||
|
||||
import logging.config
|
||||
import structlog
|
||||
|
||||
|
|
42
tnc/main.py
42
tnc/main.py
|
@ -6,26 +6,23 @@ Created on Tue Dec 22 16:58:45 2020
|
|||
@author: DJ2LS
|
||||
|
||||
main module for running the tnc
|
||||
|
||||
|
||||
"""
|
||||
|
||||
|
||||
import argparse
|
||||
import threading
|
||||
import static
|
||||
import socketserver
|
||||
import helpers
|
||||
import data_handler
|
||||
import structlog
|
||||
import log_handler
|
||||
import modem
|
||||
import sys
|
||||
import multiprocessing
|
||||
import os
|
||||
import signal
|
||||
import socketserver
|
||||
import sys
|
||||
import threading
|
||||
import time
|
||||
import multiprocessing
|
||||
|
||||
import structlog
|
||||
|
||||
import data_handler
|
||||
import helpers
|
||||
import log_handler
|
||||
import modem
|
||||
import static
|
||||
|
||||
# signal handler for closing aplication
|
||||
def signal_handler(sig, frame):
|
||||
|
@ -75,7 +72,6 @@ if __name__ == '__main__':
|
|||
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
|
||||
|
@ -101,8 +97,8 @@ if __name__ == '__main__':
|
|||
static.HAMLIB_STOP_BITS = str(ARGS.hamlib_stop_bits)
|
||||
static.HAMLIB_HANDSHAKE = ARGS.hamlib_handshake
|
||||
static.HAMLIB_RADIOCONTROL = ARGS.hamlib_radiocontrol
|
||||
static.HAMLIB_RGICTLD_IP = ARGS.rigctld_ip
|
||||
static.HAMLIB_RGICTLD_PORT = str(ARGS.rigctld_port)
|
||||
static.HAMLIB_RIGCTLD_IP = ARGS.rigctld_ip
|
||||
static.HAMLIB_RIGCTLD_PORT = str(ARGS.rigctld_port)
|
||||
static.ENABLE_SCATTER = ARGS.send_scatter
|
||||
static.ENABLE_FFT = ARGS.send_fft
|
||||
static.ENABLE_FSK = ARGS.enable_fsk
|
||||
|
@ -123,16 +119,14 @@ if __name__ == '__main__':
|
|||
if sys.platform == 'darwin':
|
||||
logging_path = os.getenv("HOME") + '/Library/' + 'Application Support/' + 'FreeDATA/' + 'tnc'
|
||||
|
||||
if sys.platform == 'win32' or sys.platform == 'win64':
|
||||
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:
|
||||
structlog.get_logger("structlog").error("[DMN] logger init error")
|
||||
|
||||
|
||||
except Exception as e:
|
||||
structlog.get_logger("structlog").error("[DMN] logger init error", exception=e)
|
||||
|
||||
structlog.get_logger("structlog").info("[TNC] Starting FreeDATA", author="DJ2LS", year="2022", version=static.VERSION)
|
||||
|
||||
|
@ -142,9 +136,7 @@ if __name__ == '__main__':
|
|||
# start modem
|
||||
modem = modem.RF()
|
||||
|
||||
|
||||
# --------------------------------------------START CMD SERVER
|
||||
|
||||
try:
|
||||
structlog.get_logger("structlog").info("[TNC] Starting TCP/IP socket", port=static.PORT)
|
||||
# https://stackoverflow.com/a/16641793
|
||||
|
@ -157,6 +149,6 @@ if __name__ == '__main__':
|
|||
|
||||
except Exception as e:
|
||||
structlog.get_logger("structlog").error("[TNC] Starting TCP/IP socket failed", port=static.PORT, e=e)
|
||||
os._exit(1)
|
||||
sys.exit(1)
|
||||
while 1:
|
||||
time.sleep(1)
|
||||
|
|
439
tnc/modem.py
439
tnc/modem.py
|
@ -5,51 +5,55 @@ Created on Wed Dec 23 07:04:24 2020
|
|||
|
||||
@author: DJ2LS
|
||||
"""
|
||||
import sys
|
||||
import os
|
||||
import ctypes
|
||||
from ctypes import *
|
||||
import pathlib
|
||||
import logging, structlog, log_handler
|
||||
import time
|
||||
import threading
|
||||
# pylint: disable=invalid-name, line-too-long, c-extension-no-member
|
||||
# pylint: disable=import-outside-toplevel
|
||||
|
||||
import atexit
|
||||
import numpy as np
|
||||
import helpers
|
||||
import static
|
||||
import data_handler
|
||||
import ujson as json
|
||||
import sock
|
||||
import re
|
||||
import ctypes
|
||||
import logging
|
||||
import os
|
||||
import pathlib
|
||||
import queue
|
||||
import codec2
|
||||
import audio
|
||||
|
||||
import sounddevice as sd
|
||||
|
||||
import re
|
||||
import sys
|
||||
import threading
|
||||
import time
|
||||
from collections import deque
|
||||
|
||||
import numpy as np
|
||||
import sounddevice as sd
|
||||
import structlog
|
||||
import ujson as json
|
||||
|
||||
# init FIFO queue to store received frames in
|
||||
import audio
|
||||
import codec2
|
||||
import data_handler
|
||||
import helpers
|
||||
import log_handler
|
||||
import sock
|
||||
import static
|
||||
|
||||
TESTMODE = False
|
||||
RXCHANNEL = ''
|
||||
TXCHANNEL = ''
|
||||
|
||||
# Initialize FIFO queue to store received frames
|
||||
MODEM_RECEIVED_QUEUE = queue.Queue()
|
||||
MODEM_TRANSMIT_QUEUE = queue.Queue()
|
||||
static.TRANSMITTING = False
|
||||
|
||||
# receive only specific modes to reduce cpu load
|
||||
# Receive only specific modes to reduce CPU load
|
||||
RECEIVE_DATAC1 = False
|
||||
RECEIVE_DATAC3 = False
|
||||
RECEIVE_FSK_LDPC_1 = False
|
||||
|
||||
|
||||
class RF():
|
||||
""" """
|
||||
|
||||
def __init__(self):
|
||||
|
||||
self.sampler_avg = 0
|
||||
self.buffer_avg = 0
|
||||
|
||||
|
||||
self.AUDIO_SAMPLE_RATE_RX = 48000
|
||||
self.AUDIO_SAMPLE_RATE_TX = 48000
|
||||
self.MODEM_SAMPLE_RATE = codec2.api.FREEDV_FS_8000
|
||||
|
@ -58,65 +62,64 @@ class RF():
|
|||
self.AUDIO_CHUNKS = 48 # 8 * (self.AUDIO_SAMPLE_RATE_RX/self.MODEM_SAMPLE_RATE) #48
|
||||
self.AUDIO_CHANNELS = 1
|
||||
|
||||
# locking state for mod out so buffer will be filled before we can use it
|
||||
# Locking state for mod out so buffer will be filled before we can use it
|
||||
# https://github.com/DJ2LS/FreeDATA/issues/127
|
||||
# https://github.com/DJ2LS/FreeDATA/issues/99
|
||||
self.mod_out_locked = True
|
||||
|
||||
# make sure our resampler will work
|
||||
# Make sure our resampler will work
|
||||
assert (self.AUDIO_SAMPLE_RATE_RX / self.MODEM_SAMPLE_RATE) == codec2.api.FDMDV_OS_48
|
||||
|
||||
# small hack for initializing codec2 via codec2.py module
|
||||
# TODO: we need to change the entire modem module to integrate codec2 module
|
||||
# Small hack for initializing codec2 via codec2.py module
|
||||
# TODO: Need to change the entire modem module to integrate codec2 module
|
||||
self.c_lib = codec2.api
|
||||
self.resampler = codec2.resampler()
|
||||
|
||||
self.modem_transmit_queue = MODEM_TRANSMIT_QUEUE
|
||||
self.modem_received_queue = MODEM_RECEIVED_QUEUE
|
||||
|
||||
# init FIFO queue to store modulation out in
|
||||
# Init FIFO queue to store modulation out in
|
||||
self.modoutqueue = deque()
|
||||
|
||||
# define fft_data buffer
|
||||
# Define fft_data buffer
|
||||
self.fft_data = bytes()
|
||||
|
||||
# open codec2 instance
|
||||
self.datac0_freedv = cast(codec2.api.freedv_open(codec2.api.FREEDV_MODE_DATAC0), c_void_p)
|
||||
self.c_lib.freedv_set_tuning_range(self.datac0_freedv, c_float(static.TUNING_RANGE_FMIN), c_float(static.TUNING_RANGE_FMAX))
|
||||
# Open codec2 instances
|
||||
self.datac0_freedv = ctypes.cast(codec2.api.freedv_open(codec2.api.FREEDV_MODE_DATAC0), ctypes.c_void_p)
|
||||
self.c_lib.freedv_set_tuning_range(self.datac0_freedv, ctypes.c_float(static.TUNING_RANGE_FMIN), ctypes.c_float(static.TUNING_RANGE_FMAX))
|
||||
self.datac0_bytes_per_frame = int(codec2.api.freedv_get_bits_per_modem_frame(self.datac0_freedv) / 8)
|
||||
self.datac0_payload_per_frame = self.datac0_bytes_per_frame - 2
|
||||
self.datac0_n_nom_modem_samples = self.c_lib.freedv_get_n_nom_modem_samples(self.datac0_freedv)
|
||||
self.datac0_n_tx_modem_samples = self.c_lib.freedv_get_n_tx_modem_samples(self.datac0_freedv)
|
||||
self.datac0_n_tx_preamble_modem_samples = self.c_lib.freedv_get_n_tx_preamble_modem_samples(self.datac0_freedv)
|
||||
self.datac0_n_tx_postamble_modem_samples = self.c_lib.freedv_get_n_tx_postamble_modem_samples(self.datac0_freedv)
|
||||
self.datac0_bytes_out = create_string_buffer(self.datac0_bytes_per_frame)
|
||||
self.datac0_bytes_out = ctypes.create_string_buffer(self.datac0_bytes_per_frame)
|
||||
codec2.api.freedv_set_frames_per_burst(self.datac0_freedv, 1)
|
||||
self.datac0_buffer = codec2.audio_buffer(2*self.AUDIO_FRAMES_PER_BUFFER_RX)
|
||||
|
||||
self.datac1_freedv = cast(codec2.api.freedv_open(codec2.api.FREEDV_MODE_DATAC1), c_void_p)
|
||||
self.c_lib.freedv_set_tuning_range(self.datac1_freedv, c_float(static.TUNING_RANGE_FMIN), c_float(static.TUNING_RANGE_FMAX))
|
||||
self.datac1_freedv = ctypes.cast(codec2.api.freedv_open(codec2.api.FREEDV_MODE_DATAC1), ctypes.c_void_p)
|
||||
self.c_lib.freedv_set_tuning_range(self.datac1_freedv, ctypes.c_float(static.TUNING_RANGE_FMIN), ctypes.c_float(static.TUNING_RANGE_FMAX))
|
||||
self.datac1_bytes_per_frame = int(codec2.api.freedv_get_bits_per_modem_frame(self.datac1_freedv) / 8)
|
||||
self.datac1_bytes_out = create_string_buffer(self.datac1_bytes_per_frame)
|
||||
self.datac1_bytes_out = ctypes.create_string_buffer(self.datac1_bytes_per_frame)
|
||||
codec2.api.freedv_set_frames_per_burst(self.datac1_freedv, 1)
|
||||
self.datac1_buffer = codec2.audio_buffer(2*self.AUDIO_FRAMES_PER_BUFFER_RX)
|
||||
|
||||
self.datac3_freedv = cast(codec2.api.freedv_open(codec2.api.FREEDV_MODE_DATAC3), c_void_p)
|
||||
self.c_lib.freedv_set_tuning_range(self.datac3_freedv, c_float(static.TUNING_RANGE_FMIN), c_float(static.TUNING_RANGE_FMAX))
|
||||
self.datac3_freedv = ctypes.cast(codec2.api.freedv_open(codec2.api.FREEDV_MODE_DATAC3), ctypes.c_void_p)
|
||||
self.c_lib.freedv_set_tuning_range(self.datac3_freedv, ctypes.c_float(static.TUNING_RANGE_FMIN), ctypes.c_float(static.TUNING_RANGE_FMAX))
|
||||
self.datac3_bytes_per_frame = int(codec2.api.freedv_get_bits_per_modem_frame(self.datac3_freedv) / 8)
|
||||
self.datac3_bytes_out = create_string_buffer(self.datac3_bytes_per_frame)
|
||||
self.datac3_bytes_out = ctypes.create_string_buffer(self.datac3_bytes_per_frame)
|
||||
codec2.api.freedv_set_frames_per_burst(self.datac3_freedv, 1)
|
||||
self.datac3_buffer = codec2.audio_buffer(2*self.AUDIO_FRAMES_PER_BUFFER_RX)
|
||||
|
||||
|
||||
self.fsk_ldpc_freedv_0 = cast(codec2.api.freedv_open_advanced(codec2.api.FREEDV_MODE_FSK_LDPC, ctypes.byref(codec2.api.FREEDV_MODE_FSK_LDPC_0_ADV)), c_void_p)
|
||||
self.fsk_ldpc_freedv_0 = ctypes.cast(codec2.api.freedv_open_advanced(codec2.api.FREEDV_MODE_FSK_LDPC, ctypes.byref(codec2.api.FREEDV_MODE_FSK_LDPC_0_ADV)), ctypes.c_void_p)
|
||||
self.fsk_ldpc_bytes_per_frame_0 = int(codec2.api.freedv_get_bits_per_modem_frame(self.fsk_ldpc_freedv_0) / 8)
|
||||
self.fsk_ldpc_bytes_out_0 = create_string_buffer(self.fsk_ldpc_bytes_per_frame_0)
|
||||
self.fsk_ldpc_bytes_out_0 = ctypes.create_string_buffer(self.fsk_ldpc_bytes_per_frame_0)
|
||||
#codec2.api.freedv_set_frames_per_burst(self.fsk_ldpc_freedv_0, 1)
|
||||
self.fsk_ldpc_buffer_0 = codec2.audio_buffer(self.AUDIO_FRAMES_PER_BUFFER_RX)
|
||||
|
||||
self.fsk_ldpc_freedv_1 = cast(codec2.api.freedv_open_advanced(codec2.api.FREEDV_MODE_FSK_LDPC, ctypes.byref(codec2.api.FREEDV_MODE_FSK_LDPC_1_ADV)), c_void_p)
|
||||
self.fsk_ldpc_freedv_1 = ctypes.cast(codec2.api.freedv_open_advanced(codec2.api.FREEDV_MODE_FSK_LDPC, ctypes.byref(codec2.api.FREEDV_MODE_FSK_LDPC_1_ADV)), ctypes.c_void_p)
|
||||
self.fsk_ldpc_bytes_per_frame_1 = int(codec2.api.freedv_get_bits_per_modem_frame(self.fsk_ldpc_freedv_1) / 8)
|
||||
self.fsk_ldpc_bytes_out_1 = create_string_buffer(self.fsk_ldpc_bytes_per_frame_1)
|
||||
self.fsk_ldpc_bytes_out_1 = ctypes.create_string_buffer(self.fsk_ldpc_bytes_per_frame_1)
|
||||
#codec2.api.freedv_set_frames_per_burst(self.fsk_ldpc_freedv_0, 1)
|
||||
self.fsk_ldpc_buffer_1 = codec2.audio_buffer(self.AUDIO_FRAMES_PER_BUFFER_RX)
|
||||
|
||||
|
@ -127,66 +130,47 @@ class RF():
|
|||
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)
|
||||
# --------------------------------------------CREATE PYAUDIO INSTANCE
|
||||
|
||||
'''
|
||||
if not TESTMODE:
|
||||
try:
|
||||
# we need to "try" this, because sometimes libasound.so isn't in the default place
|
||||
# try to supress error messages
|
||||
with audio.noalsaerr(): # https://github.com/DJ2LS/FreeDATA/issues/22
|
||||
self.p = audio.pyaudio.PyAudio()
|
||||
# else do it the default way
|
||||
except:
|
||||
self.p = audio.pyaudio.PyAudio()
|
||||
atexit.register(self.p.terminate)
|
||||
|
||||
# --------------------------------------------OPEN RX AUDIO CHANNEL
|
||||
# optional auto selection of loopback device if using in testmode
|
||||
if static.AUDIO_INPUT_DEVICE == -2:
|
||||
loopback_list = []
|
||||
for dev in range(0,self.p.get_device_count()):
|
||||
if 'Loopback: PCM' in self.p.get_device_info_by_index(dev)["name"]:
|
||||
loopback_list.append(dev)
|
||||
if len(loopback_list) >= 2:
|
||||
static.AUDIO_INPUT_DEVICE = loopback_list[0] #0 = RX
|
||||
static.AUDIO_OUTPUT_DEVICE = loopback_list[1] #1 = TX
|
||||
print(f"loopback_list rx: {loopback_list}", file=sys.stderr)
|
||||
|
||||
'''
|
||||
try:
|
||||
'''
|
||||
self.audio_stream = self.p.open(format=audio.pyaudio.paInt16,
|
||||
channels=self.AUDIO_CHANNELS,
|
||||
rate=self.AUDIO_SAMPLE_RATE_RX,
|
||||
frames_per_buffer=self.AUDIO_FRAMES_PER_BUFFER_RX,
|
||||
input=True,
|
||||
output=True,
|
||||
input_device_index=static.AUDIO_INPUT_DEVICE,
|
||||
output_device_index=static.AUDIO_OUTPUT_DEVICE,
|
||||
stream_callback=self.audio_callback
|
||||
)
|
||||
'''
|
||||
|
||||
self.stream = sd.RawStream(channels=1, dtype='int16', callback=self.callback, device=(static.AUDIO_INPUT_DEVICE, static.AUDIO_OUTPUT_DEVICE), samplerate = self.AUDIO_SAMPLE_RATE_RX, blocksize=4800)
|
||||
|
||||
|
||||
atexit.register(self.stream.stop)
|
||||
|
||||
structlog.get_logger("structlog").info("opened audio devices")
|
||||
structlog.get_logger("structlog").info("[MDM] init: opened audio devices")
|
||||
|
||||
except Exception as e:
|
||||
structlog.get_logger("structlog").error("can't open audio device. Exit", e=e)
|
||||
os._exit(1)
|
||||
structlog.get_logger("structlog").error("[MDM] init: can't open audio device. Exit", e=e)
|
||||
sys.exit(1)
|
||||
|
||||
try:
|
||||
structlog.get_logger("structlog").debug("[TNC] starting pyaudio callback")
|
||||
structlog.get_logger("structlog").debug("[MDM] init: starting pyaudio callback")
|
||||
#self.audio_stream.start_stream()
|
||||
self.stream.start()
|
||||
|
||||
except Exception as e:
|
||||
structlog.get_logger("structlog").error("[TNC] starting pyaudio callback failed", e=e)
|
||||
structlog.get_logger("structlog").error("[MDM] init: starting pyaudio callback failed", e=e)
|
||||
|
||||
else:
|
||||
# create a stream object for simulating audio stream
|
||||
class Object(object):
|
||||
pass
|
||||
self.stream = Object()
|
||||
self.stream.active = True
|
||||
|
||||
# create mkfifo buffer
|
||||
try:
|
||||
os.mkfifo(RXCHANNEL)
|
||||
os.mkfifo(TXCHANNEL)
|
||||
except Exception as e:
|
||||
structlog.get_logger("structlog").error(f"[MDM] init:mkfifo: Exception: {e}")
|
||||
pass
|
||||
|
||||
mkfifo_write_callback_thread = threading.Thread(target=self.mkfifo_write_callback, name="MKFIFO WRITE CALLBACK THREAD",daemon=True)
|
||||
mkfifo_write_callback_thread.start()
|
||||
|
||||
mkfifo_read_callback_thread = threading.Thread(target=self.mkfifo_read_callback, name="MKFIFO READ CALLBACK THREAD",daemon=True)
|
||||
mkfifo_read_callback_thread.start()
|
||||
|
||||
# --------------------------------------------INIT AND OPEN HAMLIB
|
||||
# check how we want to control the radio
|
||||
# Check how we want to control the radio
|
||||
if static.HAMLIB_RADIOCONTROL == 'direct':
|
||||
import rig
|
||||
elif static.HAMLIB_RADIOCONTROL == 'rigctl':
|
||||
|
@ -198,7 +182,6 @@ class RF():
|
|||
else:
|
||||
import rigdummy as rig
|
||||
|
||||
|
||||
self.hamlib = rig.radio()
|
||||
self.hamlib.open_rig(devicename=static.HAMLIB_DEVICE_NAME, deviceport=static.HAMLIB_DEVICE_PORT, hamlib_ptt_type=static.HAMLIB_PTT_TYPE, serialspeed=static.HAMLIB_SERIAL_SPEED, pttport=static.HAMLIB_PTT_PORT, data_bits=static.HAMLIB_DATA_BITS, stop_bits=static.HAMLIB_STOP_BITS, handshake=static.HAMLIB_HANDSHAKE, rigctld_ip = static.HAMLIB_RGICTLD_IP, rigctld_port = static.HAMLIB_RGICTLD_PORT)
|
||||
|
||||
|
@ -233,55 +216,95 @@ class RF():
|
|||
worker_transmit.start()
|
||||
|
||||
# --------------------------------------------------------------------------------------------------------
|
||||
#def audio_callback(self, data_in48k, frame_count, time_info, status):
|
||||
def mkfifo_read_callback(self):
|
||||
while 1:
|
||||
time.sleep(0.01)
|
||||
# -----read
|
||||
data_in48k = bytes()
|
||||
with open(RXCHANNEL, 'rb') as fifo:
|
||||
for line in fifo:
|
||||
data_in48k += line
|
||||
|
||||
while len(data_in48k) >= 48:
|
||||
x = np.frombuffer(data_in48k[:48], dtype=np.int16)
|
||||
x = self.resampler.resample48_to_8(x)
|
||||
data_in48k = data_in48k[48:]
|
||||
|
||||
length_x = len(x)
|
||||
if not self.datac0_buffer.nbuffer + length_x > self.datac0_buffer.size:
|
||||
self.datac0_buffer.push(x)
|
||||
|
||||
if not self.datac1_buffer.nbuffer + length_x > self.datac1_buffer.size and RECEIVE_DATAC1:
|
||||
self.datac1_buffer.push(x)
|
||||
|
||||
if not self.datac3_buffer.nbuffer + length_x > self.datac3_buffer.size and RECEIVE_DATAC3:
|
||||
self.datac3_buffer.push(x)
|
||||
|
||||
def mkfifo_write_callback(self):
|
||||
while 1:
|
||||
time.sleep(0.01)
|
||||
|
||||
# -----write
|
||||
if len(self.modoutqueue) <= 0 or self.mod_out_locked:
|
||||
#data_out48k = np.zeros(self.AUDIO_FRAMES_PER_BUFFER_RX, dtype=np.int16)
|
||||
pass
|
||||
|
||||
else:
|
||||
data_out48k = self.modoutqueue.popleft()
|
||||
#print(len(data_out48k))
|
||||
|
||||
fifo_write = open(TXCHANNEL, 'wb')
|
||||
fifo_write.write(data_out48k)
|
||||
fifo_write.flush()
|
||||
|
||||
# --------------------------------------------------------------------
|
||||
def callback(self, data_in48k, outdata, frames, time, status):
|
||||
"""
|
||||
|
||||
Args:
|
||||
data_in48k:
|
||||
frame_count:
|
||||
time_info:
|
||||
data_in48k: Incoming data received
|
||||
outdata: Container for the data returned
|
||||
frames: Number of frames
|
||||
time:
|
||||
status:
|
||||
|
||||
Returns:
|
||||
|
||||
Nothing
|
||||
"""
|
||||
|
||||
x = np.frombuffer(data_in48k, dtype=np.int16)
|
||||
x = self.resampler.resample48_to_8(x)
|
||||
|
||||
length_x = len(x)
|
||||
|
||||
# avoid decoding when transmitting to reduce CPU
|
||||
# Avoid decoding when transmitting to reduce CPU
|
||||
if not static.TRANSMITTING:
|
||||
# avoid buffer overflow by filling only if buffer not full
|
||||
length_x = len(x)
|
||||
# Avoid buffer overflow by filling only if buffer not full
|
||||
if not self.datac0_buffer.nbuffer + length_x > self.datac0_buffer.size:
|
||||
self.datac0_buffer.push(x)
|
||||
else:
|
||||
static.BUFFER_OVERFLOW_COUNTER[0] += 1
|
||||
|
||||
# avoid buffer overflow by filling only if buffer not full and selected datachannel mode
|
||||
# Avoid buffer overflow by filling only if buffer not full and selected datachannel mode
|
||||
if not self.datac1_buffer.nbuffer + length_x > self.datac1_buffer.size:
|
||||
if RECEIVE_DATAC1:
|
||||
self.datac1_buffer.push(x)
|
||||
else:
|
||||
static.BUFFER_OVERFLOW_COUNTER[1] += 1
|
||||
|
||||
# avoid buffer overflow by filling only if buffer not full and selected datachannel mode
|
||||
# Avoid buffer overflow by filling only if buffer not full and selected datachannel mode
|
||||
if not self.datac3_buffer.nbuffer + length_x > self.datac3_buffer.size:
|
||||
if RECEIVE_DATAC3:
|
||||
self.datac3_buffer.push(x)
|
||||
else:
|
||||
static.BUFFER_OVERFLOW_COUNTER[2] += 1
|
||||
|
||||
# avoid buffer overflow by filling only if buffer not full and selected datachannel mode
|
||||
# Avoid buffer overflow by filling only if buffer not full and selected datachannel mode
|
||||
if not self.fsk_ldpc_buffer_0.nbuffer + length_x > self.fsk_ldpc_buffer_0.size:
|
||||
if static.ENABLE_FSK:
|
||||
self.fsk_ldpc_buffer_0.push(x)
|
||||
else:
|
||||
static.BUFFER_OVERFLOW_COUNTER[3] += 1
|
||||
|
||||
# avoid buffer overflow by filling only if buffer not full and selected datachannel mode
|
||||
# Avoid buffer overflow by filling only if buffer not full and selected datachannel mode
|
||||
if not self.fsk_ldpc_buffer_1.nbuffer + length_x > self.fsk_ldpc_buffer_1.size:
|
||||
if RECEIVE_FSK_LDPC_1 and static.ENABLE_FSK:
|
||||
self.fsk_ldpc_buffer_1.push(x)
|
||||
|
@ -292,21 +315,19 @@ class RF():
|
|||
# if not self.modoutqueue or self.mod_out_locked:
|
||||
data_out48k = np.zeros(frames, dtype=np.int16)
|
||||
self.fft_data = x
|
||||
|
||||
else:
|
||||
data_out48k = self.modoutqueue.popleft()
|
||||
self.fft_data = data_out48k
|
||||
|
||||
try:
|
||||
outdata[:] = data_out48k[:frames]
|
||||
except Exception as e:
|
||||
print(e)
|
||||
except IndexError as e:
|
||||
structlog.get_logger("structlog").debug(f"[MDM] callback: IndexError: {e}")
|
||||
|
||||
# return (data_out48k, audio.pyaudio.paContinue)
|
||||
|
||||
# --------------------------------------------------------------------------------------------------------
|
||||
|
||||
def transmit(self, mode, repeats, repeat_delay, frames):
|
||||
# --------------------------------------------------------------------
|
||||
def transmit(self, mode, repeats: int, repeat_delay: int, frames: bytearray):
|
||||
"""
|
||||
|
||||
Args:
|
||||
|
@ -318,112 +339,99 @@ class RF():
|
|||
Returns:
|
||||
|
||||
"""
|
||||
structlog.get_logger("structlog").debug("[MDM] transmit", mode=mode)
|
||||
static.TRANSMITTING = True
|
||||
# toggle ptt early to save some time and send ptt state via socket
|
||||
# Toggle ptt early to save some time and send ptt state via socket
|
||||
static.PTT_STATE = self.hamlib.set_ptt(True)
|
||||
jsondata = {"ptt":"True"}
|
||||
data_out = json.dumps(jsondata)
|
||||
sock.SOCKET_QUEUE.put(data_out)
|
||||
|
||||
|
||||
# open codec2 instance
|
||||
# Open codec2 instance
|
||||
self.MODE = mode
|
||||
if self.MODE == 'FSK_LDPC_0' or self.MODE == 200:
|
||||
freedv = cast(codec2.api.freedv_open_advanced(codec2.api.FREEDV_MODE_FSK_LDPC, ctypes.byref(codec2.api.FREEDV_MODE_FSK_LDPC_0_ADV)), c_void_p)
|
||||
elif self.MODE == 'FSK_LDPC_1' or self.MODE == 201:
|
||||
freedv = cast(codec2.api.freedv_open_advanced(codec2.api.FREEDV_MODE_FSK_LDPC, ctypes.byref(codec2.api.FREEDV_MODE_FSK_LDPC_1_ADV)), c_void_p)
|
||||
freedv = open_codec2_instance(self.MODE)
|
||||
|
||||
else:
|
||||
freedv = cast(codec2.api.freedv_open(self.MODE), c_void_p)
|
||||
|
||||
|
||||
# get number of bytes per frame for mode
|
||||
# Get number of bytes per frame for mode
|
||||
bytes_per_frame = int(codec2.api.freedv_get_bits_per_modem_frame(freedv) / 8)
|
||||
payload_bytes_per_frame = bytes_per_frame - 2
|
||||
# init buffer for data
|
||||
|
||||
# Init buffer for data
|
||||
n_tx_modem_samples = codec2.api.freedv_get_n_tx_modem_samples(freedv)
|
||||
mod_out = create_string_buffer(n_tx_modem_samples * 2)
|
||||
mod_out = ctypes.create_string_buffer(n_tx_modem_samples * 2)
|
||||
|
||||
# init buffer for preample
|
||||
# Init buffer for preample
|
||||
n_tx_preamble_modem_samples = codec2.api.freedv_get_n_tx_preamble_modem_samples(freedv)
|
||||
mod_out_preamble = create_string_buffer(n_tx_preamble_modem_samples * 2)
|
||||
mod_out_preamble = ctypes.create_string_buffer(n_tx_preamble_modem_samples * 2)
|
||||
|
||||
# init buffer for postamble
|
||||
# Init buffer for postamble
|
||||
n_tx_postamble_modem_samples = codec2.api.freedv_get_n_tx_postamble_modem_samples(freedv)
|
||||
mod_out_postamble = create_string_buffer(n_tx_postamble_modem_samples * 2)
|
||||
mod_out_postamble = ctypes.create_string_buffer(n_tx_postamble_modem_samples * 2)
|
||||
|
||||
# add empty data to handle ptt toggle time
|
||||
data_delay_mseconds = 0 #miliseconds
|
||||
# Add empty data to handle ptt toggle time
|
||||
data_delay_mseconds = 0 # milliseconds
|
||||
data_delay = int(self.MODEM_SAMPLE_RATE * (data_delay_mseconds / 1000))
|
||||
mod_out_silence = create_string_buffer(data_delay*2)
|
||||
mod_out_silence = ctypes.create_string_buffer(data_delay * 2)
|
||||
txbuffer = bytes(mod_out_silence)
|
||||
|
||||
structlog.get_logger("structlog").debug("TRANSMIT", mode=self.MODE, payload=payload_bytes_per_frame)
|
||||
structlog.get_logger("structlog").debug("[MDM] TRANSMIT", mode=self.MODE, payload=payload_bytes_per_frame)
|
||||
|
||||
for i in range(0,repeats):
|
||||
for _ in range(repeats):
|
||||
# codec2 fsk preamble may be broken - at least it sounds like that so we are disabling it for testing
|
||||
if not self.MODE == 'FSK_LDPC_0' or self.MODE == 200 or self.MODE == 'FSK_LDPC_1' or self.MODE == 201:
|
||||
# write preamble to txbuffer
|
||||
if self.MODE not in ['FSK_LDPC_0', 'FSK_LDPC_1', 200, 201]:
|
||||
# Write preamble to txbuffer
|
||||
codec2.api.freedv_rawdatapreambletx(freedv, mod_out_preamble)
|
||||
txbuffer += bytes(mod_out_preamble)
|
||||
# create modulaton for n frames in list
|
||||
for n in range(0,len(frames)):
|
||||
# create buffer for data
|
||||
buffer = bytearray(payload_bytes_per_frame) # use this if CRC16 checksum is required ( DATA1-3)
|
||||
buffer[:len(frames[n])] = frames[n] # set buffersize to length of data which will be send
|
||||
|
||||
# create crc for data frame - we are using the crc function shipped with codec2 to avoid
|
||||
# crc algorithm incompatibilities
|
||||
crc = ctypes.c_ushort(codec2.api.freedv_gen_crc16(bytes(buffer), payload_bytes_per_frame)) # generate CRC16
|
||||
crc = crc.value.to_bytes(2, byteorder='big') # convert crc to 2 byte hex string
|
||||
buffer += crc # append crc16 to buffer
|
||||
# Create modulaton for n frames in list
|
||||
for n in range(len(frames)):
|
||||
# Create buffer for data
|
||||
buffer = bytearray(payload_bytes_per_frame) # Use this if CRC16 checksum is required ( DATA1-3)
|
||||
buffer[:len(frames[n])] = frames[n] # Set buffersize to length of data which will be send
|
||||
|
||||
# Create crc for data frame - we are using the crc function shipped with codec2 to avoid
|
||||
# CRC algorithm incompatibilities
|
||||
crc = ctypes.c_ushort(codec2.api.freedv_gen_crc16(bytes(buffer), payload_bytes_per_frame)) # Generate CRC16
|
||||
crc = crc.value.to_bytes(2, byteorder='big') # Convert crc to 2 byte hex string
|
||||
buffer += crc # Append crc16 to buffer
|
||||
|
||||
data = (ctypes.c_ubyte * bytes_per_frame).from_buffer_copy(buffer)
|
||||
codec2.api.freedv_rawdatatx(freedv,mod_out,data) # modulate DATA and save it into mod_out pointer
|
||||
txbuffer += bytes(mod_out)
|
||||
|
||||
|
||||
# codec2 fsk preamble may be broken - at least it sounds like that so we are disabling it for testing
|
||||
if not self.MODE == 'FSK_LDPC_0' or self.MODE == 200 or self.MODE == 'FSK_LDPC_1' or self.MODE == 201:
|
||||
# write preamble to txbuffer
|
||||
# codec2 fsk postamble may be broken - at least it sounds like that so we are disabling it for testing
|
||||
if self.MODE not in ['FSK_LDPC_0', 'FSK_LDPC_1', 200, 201]:
|
||||
# Write postamble to txbuffer
|
||||
codec2.api.freedv_rawdatapostambletx(freedv, mod_out_postamble)
|
||||
# Append postamble to txbuffer
|
||||
txbuffer += bytes(mod_out_postamble)
|
||||
# append postamble to txbuffer
|
||||
# add delay to end of frames
|
||||
|
||||
# Add delay to end of frames
|
||||
samples_delay = int(self.MODEM_SAMPLE_RATE * (repeat_delay / 1000))
|
||||
mod_out_silence = create_string_buffer(samples_delay*2)
|
||||
mod_out_silence = ctypes.create_string_buffer(samples_delay * 2)
|
||||
txbuffer += bytes(mod_out_silence)
|
||||
|
||||
# resample up to 48k (resampler works on np.int16)
|
||||
# Re-sample back up to 48k (resampler works on np.int16)
|
||||
x = np.frombuffer(txbuffer, dtype=np.int16)
|
||||
x = set_audio_volume(x, static.TX_AUDIO_LEVEL)
|
||||
|
||||
txbuffer_48k = self.resampler.resample8_to_48(x)
|
||||
|
||||
# explicitly lock our usage of mod_out_queue if needed
|
||||
# deaktivated for testing purposes
|
||||
# Explicitly lock our usage of mod_out_queue if needed
|
||||
# Deaktivated for testing purposes
|
||||
self.mod_out_locked = False
|
||||
|
||||
|
||||
# -------------------------------
|
||||
|
||||
|
||||
|
||||
|
||||
chunk_length = self.AUDIO_FRAMES_PER_BUFFER_TX #4800
|
||||
chunk = [txbuffer_48k[i:i+chunk_length] for i in range(0, len(txbuffer_48k), chunk_length)]
|
||||
for c in chunk:
|
||||
|
||||
if len(c) < chunk_length:
|
||||
delta = chunk_length - len(c)
|
||||
delta_zeros = np.zeros(delta, dtype=np.int16)
|
||||
c = np.append(c, delta_zeros)
|
||||
#structlog.get_logger("structlog").debug("[MDM] mod out shorter than audio buffer", delta=delta)
|
||||
|
||||
#structlog.get_logger("structlog").debug("[TNC] mod out shorter than audio buffer", delta=delta)
|
||||
self.modoutqueue.append(c)
|
||||
|
||||
|
||||
|
||||
# Release our mod_out_lock so we can use the queue
|
||||
self.mod_out_locked = False
|
||||
|
||||
|
@ -432,12 +440,12 @@ class RF():
|
|||
|
||||
static.PTT_STATE = self.hamlib.set_ptt(False)
|
||||
|
||||
# push ptt state to socket stream
|
||||
# Push ptt state to socket stream
|
||||
jsondata = {"ptt":"False"}
|
||||
data_out = json.dumps(jsondata)
|
||||
sock.SOCKET_QUEUE.put(data_out)
|
||||
|
||||
# after processing we want to set the locking state back to true to be prepared for next transmission
|
||||
# After processing, set the locking state back to true to be prepared for next transmission
|
||||
self.mod_out_locked = True
|
||||
|
||||
self.c_lib.freedv_close(freedv)
|
||||
|
@ -520,19 +528,16 @@ class RF():
|
|||
#self.get_scatter(self.fsk_ldpc_freedv_1)
|
||||
self.calculate_snr(self.fsk_ldpc_freedv_1)
|
||||
|
||||
|
||||
|
||||
# worker for FIFO queue for processing received frames
|
||||
def worker_transmit(self):
|
||||
""" """
|
||||
while True:
|
||||
data = self.modem_transmit_queue.get()
|
||||
|
||||
structlog.get_logger("structlog").debug("[MDM] worker_transmit", mode=data[0])
|
||||
self.transmit(mode=data[0], repeats=data[1], repeat_delay=data[2], frames=data[3])
|
||||
#self.modem_transmit_queue.task_done()
|
||||
|
||||
|
||||
|
||||
# worker for FIFO queue for processing received frames
|
||||
def worker_received(self):
|
||||
""" """
|
||||
|
@ -544,7 +549,6 @@ class RF():
|
|||
data_handler.DATA_QUEUE_RECEIVED.put([data[0], data[1], data[2]])
|
||||
self.modem_received_queue.task_done()
|
||||
|
||||
|
||||
def get_frequency_offset(self, freedv):
|
||||
"""
|
||||
|
||||
|
@ -554,14 +558,13 @@ class RF():
|
|||
Returns:
|
||||
|
||||
"""
|
||||
modemStats = MODEMSTATS()
|
||||
modemStats = codec2.MODEMSTATS()
|
||||
self.c_lib.freedv_get_modem_extended_stats.restype = None
|
||||
self.c_lib.freedv_get_modem_extended_stats(freedv, ctypes.byref(modemStats))
|
||||
offset = round(modemStats.foff) * (-1)
|
||||
static.FREQ_OFFSET = offset
|
||||
return offset
|
||||
|
||||
|
||||
def get_scatter(self, freedv):
|
||||
"""
|
||||
|
||||
|
@ -571,7 +574,9 @@ class RF():
|
|||
Returns:
|
||||
|
||||
"""
|
||||
if static.ENABLE_SCATTER:
|
||||
if not static.ENABLE_SCATTER:
|
||||
return
|
||||
|
||||
modemStats = codec2.MODEMSTATS()
|
||||
self.c_lib.freedv_get_modem_extended_stats.restype = None
|
||||
self.c_lib.freedv_get_modem_extended_stats(freedv, ctypes.byref(modemStats))
|
||||
|
@ -596,7 +601,6 @@ class RF():
|
|||
scatterdata_small = scatterdata[::10]
|
||||
static.SCATTER = scatterdata_small
|
||||
|
||||
|
||||
def calculate_snr(self, freedv):
|
||||
"""
|
||||
|
||||
|
@ -606,20 +610,21 @@ class RF():
|
|||
Returns:
|
||||
|
||||
"""
|
||||
|
||||
try:
|
||||
modem_stats_snr = c_float()
|
||||
modem_stats_sync = c_int()
|
||||
modem_stats_snr = ctypes.c_float()
|
||||
modem_stats_sync = ctypes.c_int()
|
||||
|
||||
self.c_lib.freedv_get_modem_stats(freedv, byref(modem_stats_sync), byref(modem_stats_snr))
|
||||
self.c_lib.freedv_get_modem_stats(freedv, ctypes.byref(modem_stats_sync), ctypes.byref(modem_stats_snr))
|
||||
modem_stats_snr = modem_stats_snr.value
|
||||
modem_stats_sync = modem_stats_sync.value
|
||||
|
||||
snr = round(modem_stats_snr, 1)
|
||||
print(snr)
|
||||
structlog.get_logger("structlog").info("[MDM] calculate_snr: ", snr=snr)
|
||||
# print(snr)
|
||||
static.SNR = np.clip(snr, 0, 255) # limit to max value of 255
|
||||
return static.SNR
|
||||
except:
|
||||
except Exception as e:
|
||||
structlog.get_logger("structlog").error(f"[MDM] calculate_snr: Exception: {e}")
|
||||
static.SNR = 0
|
||||
return static.SNR
|
||||
|
||||
|
@ -633,7 +638,6 @@ class RF():
|
|||
static.HAMLIB_MODE = self.hamlib.get_mode()
|
||||
static.HAMLIB_BANDWITH = self.hamlib.get_bandwith()
|
||||
|
||||
|
||||
def calculate_fft(self):
|
||||
""" """
|
||||
# channel_busy_delay counter
|
||||
|
@ -645,11 +649,9 @@ class RF():
|
|||
# WE NEED TO OPTIMIZE THIS!
|
||||
|
||||
if len(self.fft_data) >= 128:
|
||||
|
||||
# https://gist.github.com/ZWMiller/53232427efc5088007cab6feee7c6e4c
|
||||
# Fast Fourier Transform, 10*log10(abs) is to scale it to dB
|
||||
# and make sure it's not imaginary
|
||||
|
||||
try:
|
||||
fftarray = np.fft.rfft(self.fft_data)
|
||||
|
||||
|
@ -660,32 +662,25 @@ class RF():
|
|||
# get average of dfft
|
||||
avg = np.mean(dfft)
|
||||
|
||||
|
||||
# detect signals which are higher than the average + 10 ( +10 smoothes the output )
|
||||
# data higher than the average must be a signal. Therefore we are setting it to 100 so it will be highlighted
|
||||
# have to do this when we are not transmittig so our own sending data will not affect this too much
|
||||
# Detect signals which are higher than the average + 10 ( +10 smoothes the output )
|
||||
# Data higher than the average must be a signal. Therefore we are setting it to 100 so it will be highlighted
|
||||
# Have to do this when we are not transmitting so our own sending data will not affect this too much
|
||||
if not static.TRANSMITTING:
|
||||
dfft[dfft>avg+10] = 100
|
||||
|
||||
# calculate audio max value
|
||||
# Calculate audio max value
|
||||
# static.AUDIO_RMS = np.amax(self.fft_data)
|
||||
|
||||
|
||||
# check for signals higher than average by checking for "100"
|
||||
# if we have a signal, increment our channel_busy delay counter so we have a smoother state toggle
|
||||
|
||||
# Check for signals higher than average by checking for "100"
|
||||
# If we have a signal, increment our channel_busy delay counter so we have a smoother state toggle
|
||||
if np.sum(dfft[dfft > avg + 10]) >= 300 and not static.TRANSMITTING:
|
||||
static.CHANNEL_BUSY = True
|
||||
channel_busy_delay += 5
|
||||
# limit delay counter to a maximun of 30. The higher this value, the linger we will wait until releasing state
|
||||
if channel_busy_delay > 50:
|
||||
channel_busy_delay = 50
|
||||
# Limit delay counter to a maximun of 30. The higher this value, the linger we will wait until releasing state
|
||||
channel_busy_delay = min(channel_busy_delay + 5, 50)
|
||||
else:
|
||||
# decrement channel busy counter if no signal has been detected.
|
||||
channel_busy_delay -= 1
|
||||
if channel_busy_delay < 0:
|
||||
channel_busy_delay = 0
|
||||
# if our channel busy counter reached 0, we toggle state to False
|
||||
# Decrement channel busy counter if no signal has been detected.
|
||||
channel_busy_delay = max(channel_busy_delay - 1, 0)
|
||||
# If our channel busy counter reached 0, toggle state to False
|
||||
if channel_busy_delay == 0:
|
||||
static.CHANNEL_BUSY = False
|
||||
|
||||
|
@ -693,16 +688,12 @@ class RF():
|
|||
dfft = np.around(dfft, 0)
|
||||
dfftlist = dfft.tolist()
|
||||
|
||||
static.FFT = dfftlist[0:320] #320 --> bandwith 3000
|
||||
|
||||
|
||||
except:
|
||||
|
||||
structlog.get_logger("structlog").debug("[TNC] Setting fft=0")
|
||||
static.FFT = dfftlist[:320] #320 --> bandwidth 3000
|
||||
except Exception as e:
|
||||
structlog.get_logger("structlog").error(f"[MDM] calculate_fft: Exception: {e}")
|
||||
structlog.get_logger("structlog").debug("[MDM] Setting fft=0")
|
||||
# else 0
|
||||
static.FFT = [0]
|
||||
else:
|
||||
pass
|
||||
|
||||
def set_frames_per_burst(self, n_frames_per_burst):
|
||||
"""
|
||||
|
@ -717,6 +708,17 @@ class RF():
|
|||
codec2.api.freedv_set_frames_per_burst(self.datac3_freedv, n_frames_per_burst)
|
||||
codec2.api.freedv_set_frames_per_burst(self.fsk_ldpc_freedv_0, n_frames_per_burst)
|
||||
|
||||
def open_codec2_instance(mode):
|
||||
""" Return a codec2 instance """
|
||||
if mode in ['FSK_LDPC_0', 200]:
|
||||
return ctypes.cast(codec2.api.freedv_open_advanced(codec2.api.FREEDV_MODE_FSK_LDPC,
|
||||
ctypes.byref(codec2.api.FREEDV_MODE_FSK_LDPC_0_ADV)), ctypes.c_void_p)
|
||||
|
||||
if mode in ['FSK_LDPC_1', 201]:
|
||||
return ctypes.cast(codec2.api.freedv_open_advanced(codec2.api.FREEDV_MODE_FSK_LDPC,
|
||||
ctypes.byref(codec2.api.FREEDV_MODE_FSK_LDPC_1_ADV)), ctypes.c_void_p)
|
||||
|
||||
return ctypes.cast(codec2.api.freedv_open(mode), ctypes.c_void_p)
|
||||
|
||||
|
||||
def get_bytes_per_frame(mode):
|
||||
|
@ -729,12 +731,7 @@ def get_bytes_per_frame(mode):
|
|||
Returns:
|
||||
|
||||
"""
|
||||
if mode == 200:
|
||||
freedv = cast(codec2.api.freedv_open_advanced(codec2.api.FREEDV_MODE_FSK_LDPC, ctypes.byref(codec2.api.FREEDV_MODE_FSK_LDPC_0_ADV)), c_void_p)
|
||||
elif mode == 201:
|
||||
freedv = cast(codec2.api.freedv_open_advanced(codec2.api.FREEDV_MODE_FSK_LDPC, ctypes.byref(codec2.api.FREEDV_MODE_FSK_LDPC_1_ADV)), c_void_p)
|
||||
else:
|
||||
freedv = cast(codec2.api.freedv_open(mode), c_void_p)
|
||||
freedv = open_codec2_instance(mode)
|
||||
|
||||
# get number of bytes per frame for mode
|
||||
return int(codec2.api.freedv_get_bits_per_modem_frame(freedv) / 8)
|
||||
|
@ -743,5 +740,3 @@ def get_bytes_per_frame(mode):
|
|||
def set_audio_volume(datalist, volume):
|
||||
data = np.fromstring(datalist, np.int16) * (volume / 100.)
|
||||
return data.astype(np.int16)
|
||||
|
||||
|
||||
|
|
21
tnc/rig.py
21
tnc/rig.py
|
@ -7,18 +7,15 @@ import atexit
|
|||
import subprocess
|
||||
import os
|
||||
|
||||
|
||||
# set global hamlib version
|
||||
hamlib_version = 0
|
||||
|
||||
# append local search path
|
||||
# check if we are running in a pyinstaller environment
|
||||
try:
|
||||
app_path = sys._MEIPASS
|
||||
except:
|
||||
app_path = os.path.abspath(".")
|
||||
sys.path.append(app_path)
|
||||
|
||||
if hasattr(sys, "_MEIPASS"):
|
||||
sys.path.append(getattr(sys, "_MEIPASS"))
|
||||
else:
|
||||
sys.path.append(os.path.abspath("."))
|
||||
|
||||
# try importing hamlib
|
||||
try:
|
||||
|
@ -76,7 +73,6 @@ except Exception as e:
|
|||
class radio:
|
||||
""" """
|
||||
def __init__(self):
|
||||
|
||||
self.devicename = ''
|
||||
self.devicenumber = ''
|
||||
self.deviceport = ''
|
||||
|
@ -106,7 +102,6 @@ class radio:
|
|||
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
|
||||
|
@ -116,7 +111,6 @@ class radio:
|
|||
self.stop_bits = str(stop_bits)
|
||||
self.handshake = str(handshake)
|
||||
|
||||
|
||||
# try to init hamlib
|
||||
try:
|
||||
Hamlib.rig_set_debug(Hamlib.RIG_DEBUG_NONE)
|
||||
|
@ -128,7 +122,6 @@ class radio:
|
|||
structlog.get_logger("structlog").error("[RIG] Hamlib: rig not supported...")
|
||||
self.devicenumber = 0
|
||||
|
||||
|
||||
self.my_rig = Hamlib.Rig(self.devicenumber)
|
||||
self.my_rig.set_conf("rig_pathname", self.deviceport)
|
||||
self.my_rig.set_conf("retry", "5")
|
||||
|
@ -138,9 +131,6 @@ 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':
|
||||
self.hamlib_ptt_type = Hamlib.RIG_PTT_RIG
|
||||
self.my_rig.set_conf("ptt_type", 'RIG')
|
||||
|
@ -149,7 +139,6 @@ class radio:
|
|||
self.hamlib_ptt_type = Hamlib.RIG_PORT_USB
|
||||
self.my_rig.set_conf("ptt_type", 'USB')
|
||||
|
||||
|
||||
elif self.hamlib_ptt_type == 'DTR-H':
|
||||
self.hamlib_ptt_type = Hamlib.RIG_PTT_SERIAL_DTR
|
||||
self.my_rig.set_conf("dtr_state", "HIGH")
|
||||
|
@ -182,7 +171,6 @@ class radio:
|
|||
|
||||
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.my_rig.open()
|
||||
atexit.register(self.my_rig.close)
|
||||
|
||||
|
@ -199,7 +187,6 @@ class radio:
|
|||
except:
|
||||
structlog.get_logger("structlog").info("[RIG] Hamlib device opened", status='SUCCESS')
|
||||
|
||||
|
||||
# set ptt to false if ptt is stuck for some reason
|
||||
self.set_ptt(False)
|
||||
|
||||
|
|
|
@ -2,26 +2,27 @@
|
|||
# Intially created by Franco Spinelli, IW2DHW, 01/2022
|
||||
# Updated by DJ2LS
|
||||
#
|
||||
#
|
||||
# versione mia di rig.py per gestire Ft897D tramite rigctl e senza
|
||||
# fare alcun riferimento alla configurazione
|
||||
#
|
||||
# e' una pezza clamorosa ma serve per poter provare on-air il modem
|
||||
#
|
||||
import subprocess
|
||||
import structlog
|
||||
import time
|
||||
import sys
|
||||
import os
|
||||
import subprocess
|
||||
import sys
|
||||
import time
|
||||
|
||||
import structlog
|
||||
|
||||
# for rig_model -> rig_number only
|
||||
|
||||
# set global hamlib version
|
||||
hamlib_version = 0
|
||||
|
||||
|
||||
class radio:
|
||||
""" """
|
||||
def __init__(self):
|
||||
|
||||
self.devicename = ''
|
||||
self.devicenumber = ''
|
||||
self.deviceport = ''
|
||||
|
@ -51,7 +52,6 @@ class radio:
|
|||
Returns:
|
||||
|
||||
"""
|
||||
|
||||
self.devicename = devicename
|
||||
self.deviceport = deviceport
|
||||
self.serialspeed = str(serialspeed) # we need to ensure this is a str, otherwise set_conf functions are crashing
|
||||
|
@ -61,24 +61,22 @@ class radio:
|
|||
self.stop_bits = stop_bits
|
||||
self.handshake = handshake
|
||||
|
||||
|
||||
# check if we are running in a pyinstaller environment
|
||||
try:
|
||||
app_path = sys._MEIPASS
|
||||
except:
|
||||
app_path = os.path.abspath(".")
|
||||
sys.path.append(app_path)
|
||||
if hasattr(sys, "_MEIPASS"):
|
||||
sys.path.append(getattr(sys, "_MEIPASS"))
|
||||
else:
|
||||
sys.path.append(os.path.abspath("."))
|
||||
|
||||
# get devicenumber by looking for deviceobject in Hamlib module
|
||||
try:
|
||||
import Hamlib
|
||||
self.devicenumber = int(getattr(Hamlib, self.devicename))
|
||||
except:
|
||||
except Exception as e:
|
||||
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)
|
||||
structlog.get_logger("structlog").warning("[RIGCTL] Radio not found. Using DUMMY!", error=e)
|
||||
|
||||
# set deviceport to dummy port, if we selected dummy model
|
||||
if self.devicenumber == 1 or self.devicenumber == 6:
|
||||
|
@ -86,12 +84,9 @@ class radio:
|
|||
|
||||
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 == 'win32' or sys.platform == 'win64':
|
||||
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))
|
||||
|
||||
else:
|
||||
|
@ -116,7 +111,6 @@ class radio:
|
|||
except:
|
||||
return False
|
||||
|
||||
|
||||
def get_mode(self):
|
||||
""" """
|
||||
#(hamlib_mode, bandwith) = self.my_rig.get_mode()
|
||||
|
@ -126,7 +120,6 @@ class radio:
|
|||
except:
|
||||
return False
|
||||
|
||||
|
||||
def get_bandwith(self):
|
||||
""" """
|
||||
#(hamlib_mode, bandwith) = self.my_rig.get_mode()
|
||||
|
|
|
@ -1,20 +1,24 @@
|
|||
#!/usr/bin/env python3
|
||||
import socket
|
||||
import structlog
|
||||
import log_handler
|
||||
import logging
|
||||
import time
|
||||
import static
|
||||
# class taken from darsidelemm
|
||||
# rigctl - https://github.com/darksidelemm/rotctld-web-gui/blob/master/rotatorgui.py#L35
|
||||
#
|
||||
# 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():
|
||||
"""rotctld (hamlib) communication class"""
|
||||
"""rigctld (hamlib) communication class"""
|
||||
# Note: This is a massive hack.
|
||||
|
||||
def __init__(self, hostname="localhost", port=4532, poll_rate=5, timeout=5):
|
||||
|
@ -48,11 +52,10 @@ class radio():
|
|||
self.hostname = rigctld_ip
|
||||
self.port = int(rigctld_port)
|
||||
|
||||
|
||||
if self.connect():
|
||||
logging.debug(f"Rigctl intialized")
|
||||
logging.debug("Rigctl intialized")
|
||||
return True
|
||||
else:
|
||||
|
||||
structlog.get_logger("structlog").error("[RIGCTLD] Can't connect to rigctld!", ip=self.hostname, port=self.port)
|
||||
return False
|
||||
|
||||
|
@ -75,7 +78,6 @@ class radio():
|
|||
self.sock.close()
|
||||
self.connected = False
|
||||
|
||||
|
||||
def send_command(self, command):
|
||||
"""Send a command to the connected rotctld instance,
|
||||
and return the return value.
|
||||
|
@ -104,7 +106,6 @@ class radio():
|
|||
time.sleep(0.5)
|
||||
self.connect()
|
||||
|
||||
|
||||
def get_mode(self):
|
||||
""" """
|
||||
try:
|
||||
|
@ -113,7 +114,8 @@ class radio():
|
|||
mode = data[0]
|
||||
return mode.decode("utf-8")
|
||||
except:
|
||||
0
|
||||
return 0
|
||||
|
||||
def get_bandwith(self):
|
||||
""" """
|
||||
try:
|
||||
|
|
|
@ -4,12 +4,12 @@ import structlog
|
|||
|
||||
hamlib_version = 0
|
||||
|
||||
|
||||
class radio:
|
||||
""" """
|
||||
def __init__(self):
|
||||
pass
|
||||
|
||||
|
||||
def open_rig(self, **kwargs):
|
||||
"""
|
||||
|
||||
|
@ -62,4 +62,3 @@ class radio:
|
|||
def close_rig(self):
|
||||
""" """
|
||||
return
|
||||
|
||||
|
|
163
tnc/sock.py
163
tnc/sock.py
|
@ -19,21 +19,25 @@ Created on Fri Dec 25 21:25:14 2020
|
|||
# "data" : "..."
|
||||
|
||||
"""
|
||||
import atexit
|
||||
import base64
|
||||
import logging
|
||||
import os
|
||||
import queue
|
||||
import socketserver
|
||||
import sys
|
||||
import threading
|
||||
import ujson as json
|
||||
import time
|
||||
import static
|
||||
|
||||
import psutil
|
||||
import structlog
|
||||
import ujson as json
|
||||
|
||||
import audio
|
||||
import data_handler
|
||||
import helpers
|
||||
import sys
|
||||
import os
|
||||
import logging, structlog, log_handler
|
||||
import queue
|
||||
import psutil
|
||||
import audio
|
||||
import base64
|
||||
import atexit
|
||||
import log_handler
|
||||
import static
|
||||
|
||||
SOCKET_QUEUE = queue.Queue()
|
||||
DAEMON_QUEUE = queue.Queue()
|
||||
|
@ -42,10 +46,6 @@ CONNECTED_CLIENTS = set()
|
|||
CLOSE_SIGNAL = False
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
class ThreadedTCPServer(socketserver.ThreadingMixIn, socketserver.TCPServer):
|
||||
"""
|
||||
the socket handler base class
|
||||
|
@ -53,23 +53,19 @@ class ThreadedTCPServer(socketserver.ThreadingMixIn, socketserver.TCPServer):
|
|||
pass
|
||||
|
||||
|
||||
|
||||
class ThreadedTCPRequestHandler(socketserver.StreamRequestHandler):
|
||||
""" """
|
||||
|
||||
connection_alive = False
|
||||
|
||||
def send_to_client(self):
|
||||
"""
|
||||
function called by socket handler
|
||||
send data to a network client if available
|
||||
|
||||
"""
|
||||
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
|
||||
|
||||
if self.server.server_address[1] == static.PORT and not static.TNCSTARTED:
|
||||
data = send_tnc_state()
|
||||
if data != tempdata:
|
||||
|
@ -82,7 +78,6 @@ class ThreadedTCPRequestHandler(socketserver.StreamRequestHandler):
|
|||
SOCKET_QUEUE.put(data)
|
||||
time.sleep(0.5)
|
||||
|
||||
|
||||
while not SOCKET_QUEUE.empty():
|
||||
data = SOCKET_QUEUE.get()
|
||||
sock_data = bytes(data, 'utf-8')
|
||||
|
@ -94,8 +89,8 @@ class ThreadedTCPRequestHandler(socketserver.StreamRequestHandler):
|
|||
try:
|
||||
client.send(sock_data)
|
||||
except Exception as e:
|
||||
print("connection lost...")
|
||||
print(e)
|
||||
# print("connection lost...")
|
||||
structlog.get_logger("structlog").info("[SCK] Connection lost", e=e)
|
||||
self.connection_alive = False
|
||||
|
||||
# we want to transmit scatter data only once to reduce network traffic
|
||||
|
@ -120,7 +115,6 @@ class ThreadedTCPRequestHandler(socketserver.StreamRequestHandler):
|
|||
#print("connection broken. Closing...")
|
||||
self.connection_alive = False
|
||||
|
||||
|
||||
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')
|
||||
|
@ -141,19 +135,16 @@ class ThreadedTCPRequestHandler(socketserver.StreamRequestHandler):
|
|||
# and which one can be processed during a running transmission
|
||||
time.sleep(3)
|
||||
|
||||
|
||||
# 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)
|
||||
self.connection_alive = False
|
||||
|
||||
|
||||
def handle(self):
|
||||
"""
|
||||
socket handler
|
||||
"""
|
||||
|
||||
CONNECTED_CLIENTS.add(self.request)
|
||||
|
||||
structlog.get_logger("structlog").debug("[SCK] Client connected", ip=self.client_address[0], port=self.client_address[1])
|
||||
|
@ -166,8 +157,6 @@ class ThreadedTCPRequestHandler(socketserver.StreamRequestHandler):
|
|||
while self.connection_alive and not CLOSE_SIGNAL:
|
||||
time.sleep(1)
|
||||
|
||||
|
||||
|
||||
def finish(self):
|
||||
""" """
|
||||
structlog.get_logger("structlog").warning("[SCK] Closing client socket", ip=self.client_address[0], port=self.client_address[1])
|
||||
|
@ -176,7 +165,6 @@ class ThreadedTCPRequestHandler(socketserver.StreamRequestHandler):
|
|||
except:
|
||||
structlog.get_logger("structlog").warning("[SCK] client connection already removed from client list", client=self.request)
|
||||
|
||||
|
||||
def process_tnc_commands(data):
|
||||
"""
|
||||
process tnc commands
|
||||
|
@ -189,7 +177,6 @@ def process_tnc_commands(data):
|
|||
"""
|
||||
# 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)
|
||||
|
@ -203,7 +190,6 @@ def process_tnc_commands(data):
|
|||
command_response("tx_audio_level", False)
|
||||
structlog.get_logger("structlog").warning("[SCK] command execution error", e=e, command=received_json)
|
||||
|
||||
|
||||
# TRANSMIT SINE WAVE -----------------------------------------------------
|
||||
if received_json["type"] == "set" and received_json["command"] == "send_test_frame":
|
||||
try:
|
||||
|
@ -222,6 +208,7 @@ def process_tnc_commands(data):
|
|||
except Exception as e:
|
||||
command_response("cqcqcq", False)
|
||||
structlog.get_logger("structlog").warning("[SCK] command execution error", e=e, command=received_json)
|
||||
|
||||
# START_BEACON -----------------------------------------------------
|
||||
if received_json["command"] == "start_beacon":
|
||||
try:
|
||||
|
@ -262,7 +249,6 @@ def process_tnc_commands(data):
|
|||
command_response("ping", False)
|
||||
structlog.get_logger("structlog").warning("[SCK] command execution error", e=e, command=received_json)
|
||||
|
||||
|
||||
# CONNECT ----------------------------------------------------------
|
||||
if received_json["type"] == 'arq' and received_json["command"] == "connect":
|
||||
static.BEACON_PAUSE = True
|
||||
|
@ -297,7 +283,6 @@ def process_tnc_commands(data):
|
|||
|
||||
# TRANSMIT RAW DATA -------------------------------------------
|
||||
if received_json["type"] == 'arq' and received_json["command"] == "send_raw":
|
||||
|
||||
static.BEACON_PAUSE = True
|
||||
try:
|
||||
if not static.ARQ_SESSION:
|
||||
|
@ -314,7 +299,6 @@ def process_tnc_commands(data):
|
|||
dxcallsign = static.DXCALLSIGN
|
||||
static.DXCALLSIGN_CRC = helpers.get_crc_24(static.DXCALLSIGN)
|
||||
|
||||
|
||||
mode = int(received_json["parameter"][0]["mode"])
|
||||
n_frames = int(received_json["parameter"][0]["n_frames"])
|
||||
base64data = received_json["parameter"][0]["data"]
|
||||
|
@ -335,15 +319,12 @@ def process_tnc_commands(data):
|
|||
binarydata = base64.b64decode(base64data)
|
||||
|
||||
data_handler.DATA_QUEUE_TRANSMIT.put(['ARQ_RAW', binarydata, mode, n_frames, arq_uuid, mycallsign])
|
||||
|
||||
else:
|
||||
raise TypeError
|
||||
except Exception as e:
|
||||
command_response("send_raw", False)
|
||||
structlog.get_logger("structlog").warning("[SCK] command execution error", e=e, command=received_json)
|
||||
|
||||
|
||||
|
||||
# STOP TRANSMISSION ----------------------------------------------------------
|
||||
if received_json["type"] == 'arq' and received_json["command"] == "stop_transmission":
|
||||
try:
|
||||
|
@ -357,7 +338,6 @@ def process_tnc_commands(data):
|
|||
command_response("stop_transmission", False)
|
||||
structlog.get_logger("structlog").warning("[SCK] command execution error", e=e, command=received_json)
|
||||
|
||||
|
||||
if received_json["type"] == 'get' and received_json["command"] == 'rx_buffer':
|
||||
try:
|
||||
output = {
|
||||
|
@ -365,7 +345,7 @@ def process_tnc_commands(data):
|
|||
"data-array": [],
|
||||
}
|
||||
|
||||
for i in range(0, len(static.RX_BUFFER)):
|
||||
for i in range(len(static.RX_BUFFER)):
|
||||
#print(static.RX_BUFFER[i][4])
|
||||
#rawdata = json.loads(static.RX_BUFFER[i][4])
|
||||
base64_data = static.RX_BUFFER[i][4]
|
||||
|
@ -380,7 +360,6 @@ def process_tnc_commands(data):
|
|||
command_response("rx_buffer", False)
|
||||
structlog.get_logger("structlog").warning("[SCK] command execution error", e=e, command=received_json)
|
||||
|
||||
|
||||
if received_json["type"] == 'set' and received_json["command"] == 'del_rx_buffer':
|
||||
try:
|
||||
static.RX_BUFFER = []
|
||||
|
@ -397,7 +376,6 @@ def send_tnc_state():
|
|||
"""
|
||||
send the tnc state to network
|
||||
"""
|
||||
|
||||
encoding = 'utf-8'
|
||||
|
||||
output = {
|
||||
|
@ -432,12 +410,17 @@ def send_tnc_state():
|
|||
}
|
||||
|
||||
# add heard stations to heard stations object
|
||||
for i in range(0, len(static.HEARD_STATIONS)):
|
||||
output["stations"].append({"dxcallsign": str(static.HEARD_STATIONS[i][0], 'utf-8'), "dxgrid": str(static.HEARD_STATIONS[i][1], 'utf-8'),"timestamp": static.HEARD_STATIONS[i][2], "datatype": static.HEARD_STATIONS[i][3], "snr": static.HEARD_STATIONS[i][4], "offset": static.HEARD_STATIONS[i][5], "frequency": static.HEARD_STATIONS[i][6]})
|
||||
|
||||
jsondata = json.dumps(output)
|
||||
return jsondata
|
||||
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]})
|
||||
|
||||
return json.dumps(output)
|
||||
|
||||
def process_daemon_commands(data):
|
||||
"""
|
||||
|
@ -469,7 +452,6 @@ def process_daemon_commands(data):
|
|||
command_response("mycallsign", False)
|
||||
structlog.get_logger("structlog").warning("[SCK] command execution error", e=e, command=received_json)
|
||||
|
||||
|
||||
if received_json["type"] == 'set' and received_json["command"] == 'mygrid':
|
||||
try:
|
||||
mygrid = received_json["parameter"]
|
||||
|
@ -484,9 +466,7 @@ def process_daemon_commands(data):
|
|||
command_response("mygrid", False)
|
||||
structlog.get_logger("structlog").warning("[SCK] command execution error", e=e, command=received_json)
|
||||
|
||||
|
||||
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"])
|
||||
|
@ -512,35 +492,34 @@ def process_daemon_commands(data):
|
|||
tx_audio_level = str(received_json["parameter"][0]["tx_audio_level"])
|
||||
respond_to_cq = str(received_json["parameter"][0]["respond_to_cq"])
|
||||
|
||||
|
||||
# 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])
|
||||
|
||||
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)
|
||||
|
||||
|
@ -549,7 +528,6 @@ def process_daemon_commands(data):
|
|||
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':
|
||||
|
||||
try:
|
||||
devicename = str(received_json["parameter"][0]["devicename"])
|
||||
deviceport = str(received_json["parameter"][0]["deviceport"])
|
||||
|
@ -563,18 +541,18 @@ 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:
|
||||
|
@ -599,7 +577,7 @@ def send_daemon_state():
|
|||
send the daemon state to network
|
||||
"""
|
||||
try:
|
||||
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])}"
|
||||
|
||||
output = {
|
||||
'command': 'daemon_state',
|
||||
|
@ -619,7 +597,6 @@ def send_daemon_state():
|
|||
else:
|
||||
output["daemon_state"].append({"status": "stopped"})
|
||||
|
||||
|
||||
jsondata = json.dumps(output)
|
||||
|
||||
return jsondata
|
||||
|
@ -628,11 +605,7 @@ def send_daemon_state():
|
|||
return None
|
||||
|
||||
def command_response(command, status):
|
||||
if status:
|
||||
status = "OK"
|
||||
else:
|
||||
status = "Failed"
|
||||
|
||||
jsondata = {"command_response": command, "status" : status}
|
||||
s_status = "OK" if status else "Failed"
|
||||
jsondata = {"command_response": command, "status" : s_status}
|
||||
data_out = json.dumps(jsondata)
|
||||
SOCKET_QUEUE.put(data_out)
|
||||
|
|
|
@ -15,7 +15,6 @@ DAEMONPORT = 3001
|
|||
TNCSTARTED = False
|
||||
TNCPROCESS = 0
|
||||
|
||||
|
||||
# Operator Defaults
|
||||
MYCALLSIGN = b'AA0AA'
|
||||
MYCALLSIGN_CRC = b'A'
|
||||
|
@ -39,7 +38,6 @@ SOCKET_TIMEOUT = 1 # seconds
|
|||
SERIAL_DEVICES = []
|
||||
# ---------------------------------
|
||||
|
||||
|
||||
PTT_STATE = False
|
||||
TRANSMITTING = False
|
||||
|
||||
|
@ -96,7 +94,6 @@ ARQ_TRANSMISSION_PERCENT = 0
|
|||
ARQ_SPEED_LEVEL = 0
|
||||
TOTAL_BYTES = 0
|
||||
|
||||
|
||||
#CHANNEL_STATE = 'RECEIVING_SIGNALLING'
|
||||
TNC_STATE = 'IDLE'
|
||||
ARQ_STATE = False
|
||||
|
|
Loading…
Reference in a new issue