First run reducing number of problems

This commit is contained in:
DJ2LS 2022-05-23 09:37:24 +02:00
parent 9f8f17b633
commit b6face744b
5 changed files with 259 additions and 249 deletions

View file

@ -1,22 +1,20 @@
import atexit import atexit
import json
import multiprocessing import multiprocessing
import sys
import sounddevice as sd import sounddevice as sd
atexit.register(sd._terminate) atexit.register(sd._terminate)
def get_audio_devices(): def get_audio_devices():
""" """
return list of input and output audio devices in own process to avoid crashes of portaudio on raspberry pi return list of input and output audio devices in own process to avoid crashes of portaudio on raspberry pi
also uses a process data manager also uses a process data manager
""" """
# we need to run this on windows for multiprocessing support # we need to run this on Windows for multiprocessing support
# multiprocessing.freeze_support() # multiprocessing.freeze_support()
#multiprocessing.get_context('spawn') # multiprocessing.get_context('spawn')
# we need to reset and initialize sounddevice before running the multiprocessing part. # we need to reset and initialize sounddevice before running the multiprocessing part.
# If we are not doing this at this early point, not all devices will be displayed # If we are not doing this at this early point, not all devices will be displayed
@ -26,41 +24,41 @@ def get_audio_devices():
with multiprocessing.Manager() as manager: with multiprocessing.Manager() as manager:
proxy_input_devices = manager.list() proxy_input_devices = manager.list()
proxy_output_devices = manager.list() proxy_output_devices = manager.list()
#print(multiprocessing.get_start_method()) # print(multiprocessing.get_start_method())
p = multiprocessing.Process(target=fetch_audio_devices, args=(proxy_input_devices, proxy_output_devices)) p = multiprocessing.Process(target=fetch_audio_devices, args=(proxy_input_devices, proxy_output_devices))
p.start() p.start()
p.join() p.join()
return list(proxy_input_devices), list(proxy_output_devices) return list(proxy_input_devices), list(proxy_output_devices)
def fetch_audio_devices(input_devices, output_devices): def fetch_audio_devices(input_devices, output_devices):
""" """
get audio devices from portaudio get audio devices from portaudio
Args: Args:
input_devices: proxy variable for input devices input_devices: proxy variable for input devices
output_devices: proxy variable for outout devices output_devices: proxy variable for output devices
Returns: Returns:
""" """
devices = sd.query_devices(device=None, kind=None) devices = sd.query_devices(device=None, kind=None)
for index, device in enumerate(devices): for index, device in enumerate(devices):
#for i in range(0, p.get_device_count()): # we need to do a try exception, because for windows there's no audio device range
# we need to do a try exception, beacuse for windows theres no audio device range
try: try:
name = device["name"] name = device["name"]
maxOutputChannels = device["max_output_channels"] max_output_channels = device["max_output_channels"]
maxInputChannels = device["max_input_channels"] max_input_channels = device["max_input_channels"]
except Exception as e: except Exception as e:
print(e) print(e)
maxInputChannels = 0 max_input_channels = 0
maxOutputChannels = 0 max_output_channels = 0
name = '' name = ''
if maxInputChannels > 0: if max_input_channels > 0:
input_devices.append({"id": index, "name": name}) input_devices.append({"id": index, "name": name})
if maxOutputChannels > 0: if max_output_channels > 0:
output_devices.append({"id": index, "name": name}) output_devices.append({"id": index, "name": name})

View file

@ -22,11 +22,11 @@ class FREEDV_MODE(Enum):
""" """
fsk_ldpc_0 = 200 fsk_ldpc_0 = 200
fsk_ldpc_1 = 201 fsk_ldpc_1 = 201
fsk_ldpc = 9 fsk_ldpc = 9
datac0 = 14 datac0 = 14
datac1 = 10 datac1 = 10
datac3 = 12 datac3 = 12
allmodes = 255 allmodes = 255
# Function for returning the mode value # Function for returning the mode value
@ -42,6 +42,7 @@ def freedv_get_mode_value_by_name(mode: str) -> int:
""" """
return FREEDV_MODE[mode].value return FREEDV_MODE[mode].value
# Function for returning the mode name # Function for returning the mode name
def freedv_get_mode_name_by_value(mode: int) -> str: def freedv_get_mode_name_by_value(mode: int) -> str:
""" """
@ -54,6 +55,7 @@ def freedv_get_mode_name_by_value(mode: int) -> str:
""" """
return FREEDV_MODE(mode).name return FREEDV_MODE(mode).name
# Check if we are running in a pyinstaller environment # Check if we are running in a pyinstaller environment
if hasattr(sys, "_MEIPASS"): if hasattr(sys, "_MEIPASS"):
sys.path.append(getattr(sys, "_MEIPASS")) sys.path.append(getattr(sys, "_MEIPASS"))
@ -62,12 +64,12 @@ else:
structlog.get_logger("structlog").info("[C2 ] Searching for libcodec2...") structlog.get_logger("structlog").info("[C2 ] Searching for libcodec2...")
if sys.platform == 'linux': if sys.platform == 'linux':
files = glob.glob(r'**/*libcodec2*',recursive=True) files = glob.glob(r'**/*libcodec2*', recursive=True)
files.append('libcodec2.so') files.append('libcodec2.so')
elif sys.platform == 'darwin': elif sys.platform == 'darwin':
files = glob.glob(r'**/*libcodec2*.dylib',recursive=True) files = glob.glob(r'**/*libcodec2*.dylib', recursive=True)
elif sys.platform in ['win32', 'win64']: elif sys.platform in ['win32', 'win64']:
files = glob.glob(r'**\*libcodec2*.dll',recursive=True) files = glob.glob(r'**\*libcodec2*.dll', recursive=True)
else: else:
files = [] files = []
@ -87,8 +89,8 @@ if api is None or 'api' not in locals():
# ctypes function init # ctypes function init
#api.freedv_set_tuning_range.restype = ctypes.c_int # 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_set_tuning_range.argype = [ctypes.c_void_p, ctypes.c_float, ctypes.c_float]
api.freedv_open.argype = [ctypes.c_int] api.freedv_open.argype = [ctypes.c_int]
api.freedv_open.restype = ctypes.c_void_p api.freedv_open.restype = ctypes.c_void_p
@ -138,12 +140,13 @@ api.freedv_get_n_tx_modem_samples.restype = ctypes.c_int
api.freedv_get_n_max_modem_samples.argtype = [ctypes.c_void_p] 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_get_n_max_modem_samples.restype = ctypes.c_int
api.FREEDV_FS_8000 = 8000 api.FREEDV_FS_8000 = 8000
api.FREEDV_MODE_DATAC1 = 10 api.FREEDV_MODE_DATAC1 = 10
api.FREEDV_MODE_DATAC3 = 12 api.FREEDV_MODE_DATAC3 = 12
api.FREEDV_MODE_DATAC0 = 14 api.FREEDV_MODE_DATAC0 = 14
api.FREEDV_MODE_FSK_LDPC = 9 api.FREEDV_MODE_FSK_LDPC = 9
# -------------------------------- FSK LDPC MODE SETTINGS # -------------------------------- FSK LDPC MODE SETTINGS
# Advanced structure for fsk modes # Advanced structure for fsk modes
@ -159,6 +162,7 @@ class ADVANCED(ctypes.Structure):
("codename", ctypes.c_char_p), ("codename", ctypes.c_char_p),
] ]
''' '''
adv.interleave_frames = 0 # max amplitude adv.interleave_frames = 0 # max amplitude
adv.M = 2 # number of fsk tones 2/4 adv.M = 2 # number of fsk tones 2/4
@ -186,9 +190,9 @@ api.FREEDV_MODE_FSK_LDPC_0_ADV.interleave_frames = 0
api.FREEDV_MODE_FSK_LDPC_0_ADV.M = 4 api.FREEDV_MODE_FSK_LDPC_0_ADV.M = 4
api.FREEDV_MODE_FSK_LDPC_0_ADV.Rs = 100 api.FREEDV_MODE_FSK_LDPC_0_ADV.Rs = 100
api.FREEDV_MODE_FSK_LDPC_0_ADV.Fs = 8000 api.FREEDV_MODE_FSK_LDPC_0_ADV.Fs = 8000
api.FREEDV_MODE_FSK_LDPC_0_ADV.first_tone = 1400 # 1150 4fsk, 1500 2fsk api.FREEDV_MODE_FSK_LDPC_0_ADV.first_tone = 1400 # 1150 4fsk, 1500 2fsk
api.FREEDV_MODE_FSK_LDPC_0_ADV.tone_spacing = 120 #200 api.FREEDV_MODE_FSK_LDPC_0_ADV.tone_spacing = 120 # 200
api.FREEDV_MODE_FSK_LDPC_0_ADV.codename = 'H_128_256_5'.encode('utf-8') # code word api.FREEDV_MODE_FSK_LDPC_0_ADV.codename = 'H_128_256_5'.encode('utf-8') # code word
# --------------- 4 H_256_512_4, 7 bytes # --------------- 4 H_256_512_4, 7 bytes
api.FREEDV_MODE_FSK_LDPC_1_ADV = ADVANCED() api.FREEDV_MODE_FSK_LDPC_1_ADV = ADVANCED()
@ -196,18 +200,19 @@ api.FREEDV_MODE_FSK_LDPC_1_ADV.interleave_frames = 0
api.FREEDV_MODE_FSK_LDPC_1_ADV.M = 4 api.FREEDV_MODE_FSK_LDPC_1_ADV.M = 4
api.FREEDV_MODE_FSK_LDPC_1_ADV.Rs = 100 api.FREEDV_MODE_FSK_LDPC_1_ADV.Rs = 100
api.FREEDV_MODE_FSK_LDPC_1_ADV.Fs = 8000 api.FREEDV_MODE_FSK_LDPC_1_ADV.Fs = 8000
api.FREEDV_MODE_FSK_LDPC_1_ADV.first_tone = 1250 # 1250 4fsk, 1500 2fsk api.FREEDV_MODE_FSK_LDPC_1_ADV.first_tone = 1250 # 1250 4fsk, 1500 2fsk
api.FREEDV_MODE_FSK_LDPC_1_ADV.tone_spacing = 200 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 api.FREEDV_MODE_FSK_LDPC_1_ADV.codename = 'H_256_512_4'.encode('utf-8') # code word
# ------- MODEM STATS STRUCTURES # ------- MODEM STATS STRUCTURES
MODEM_STATS_NC_MAX = 50 + 1 MODEM_STATS_NC_MAX = 50 + 1
MODEM_STATS_NR_MAX = 160 MODEM_STATS_NR_MAX = 160
MODEM_STATS_ET_MAX = 8 MODEM_STATS_ET_MAX = 8
MODEM_STATS_EYE_IND_MAX = 160 MODEM_STATS_EYE_IND_MAX = 160
MODEM_STATS_NSPEC = 512 MODEM_STATS_NSPEC = 512
MODEM_STATS_MAX_F_HZ = 4000 MODEM_STATS_MAX_F_HZ = 4000
MODEM_STATS_MAX_F_EST = 4 MODEM_STATS_MAX_F_EST = 4
# Modem stats structure # Modem stats structure
class MODEMSTATS(ctypes.Structure): class MODEMSTATS(ctypes.Structure):
@ -215,7 +220,7 @@ class MODEMSTATS(ctypes.Structure):
_fields_ = [ _fields_ = [
("Nc", ctypes.c_int), ("Nc", ctypes.c_int),
("snr_est", ctypes.c_float), ("snr_est", ctypes.c_float),
("rx_symbols", (ctypes.c_float * MODEM_STATS_NR_MAX)*MODEM_STATS_NC_MAX), ("rx_symbols", (ctypes.c_float * MODEM_STATS_NR_MAX) * MODEM_STATS_NC_MAX),
("nr", ctypes.c_int), ("nr", ctypes.c_int),
("sync", ctypes.c_int), ("sync", ctypes.c_int),
("foff", ctypes.c_float), ("foff", ctypes.c_float),
@ -225,17 +230,18 @@ class MODEMSTATS(ctypes.Structure):
("pre", ctypes.c_int), ("pre", ctypes.c_int),
("post", ctypes.c_int), ("post", ctypes.c_int),
("uw_fails", ctypes.c_int), ("uw_fails", ctypes.c_int),
("neyetr", ctypes.c_int), # How many eye traces are plotted ("neyetr", ctypes.c_int), # How many eye traces are plotted
("neyesamp", ctypes.c_int), # How many samples in the eye diagram ("neyesamp", ctypes.c_int), # How many samples in the eye diagram
("f_est", (ctypes.c_float * MODEM_STATS_MAX_F_EST)), # How many samples in the eye diagram ("f_est", (ctypes.c_float * MODEM_STATS_MAX_F_EST)), # How many samples in the eye diagram
("fft_buf", (ctypes.c_float * MODEM_STATS_NSPEC * 2)), ("fft_buf", (ctypes.c_float * MODEM_STATS_NSPEC * 2)),
] ]
# Return code flags for freedv_get_rx_status() function # Return code flags for freedv_get_rx_status() function
api.FREEDV_RX_TRIAL_SYNC = 0x1 # demodulator has trial sync api.FREEDV_RX_TRIAL_SYNC = 0x1 # demodulator has trial sync
api.FREEDV_RX_SYNC = 0x2 # demodulator has sync api.FREEDV_RX_SYNC = 0x2 # demodulator has sync
api.FREEDV_RX_BITS = 0x4 # data bits have been returned api.FREEDV_RX_BITS = 0x4 # data bits have been returned
api.FREEDV_RX_BIT_ERRORS = 0x8 # FEC may not have corrected all bit errors (not all parity checks OK) api.FREEDV_RX_BIT_ERRORS = 0x8 # FEC may not have corrected all bit errors (not all parity checks OK)
api.rx_sync_flags_to_text = [ api.rx_sync_flags_to_text = [
"----", "----",
@ -255,6 +261,7 @@ api.rx_sync_flags_to_text = [
"EBS-", "EBS-",
"EBST"] "EBST"]
# Audio buffer --------------------------------------------------------- # Audio buffer ---------------------------------------------------------
class audio_buffer: class audio_buffer:
""" """
@ -262,6 +269,7 @@ class audio_buffer:
made by David Rowe, VK5DGR 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 # self.nbuffer is the current number of samples in the buffer
def __init__(self, size): def __init__(self, size):
@ -271,7 +279,7 @@ class audio_buffer:
self.nbuffer = 0 self.nbuffer = 0
self.mutex = Lock() self.mutex = Lock()
def push(self,samples): def push(self, samples):
""" """
Push new data to buffer Push new data to buffer
@ -283,12 +291,12 @@ class audio_buffer:
""" """
self.mutex.acquire() 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 assert self.nbuffer + len(samples) <= self.size
self.buffer[self.nbuffer:self.nbuffer+len(samples)] = samples self.buffer[self.nbuffer:self.nbuffer + len(samples)] = samples
self.nbuffer += len(samples) self.nbuffer += len(samples)
self.mutex.release() self.mutex.release()
def pop(self,size): def pop(self, size):
""" """
get data from buffer in size of NIN get data from buffer in size of NIN
Args: Args:
@ -300,18 +308,20 @@ class audio_buffer:
self.mutex.acquire() self.mutex.acquire()
# Remove samples from the start of the buffer # Remove samples from the start of the buffer
self.nbuffer -= size self.nbuffer -= size
self.buffer[:self.nbuffer] = self.buffer[size:size+self.nbuffer] self.buffer[:self.nbuffer] = self.buffer[size:size + self.nbuffer]
assert self.nbuffer >= 0 assert self.nbuffer >= 0
self.mutex.release() self.mutex.release()
# Resampler --------------------------------------------------------- # Resampler ---------------------------------------------------------
api.FDMDV_OS_48 = int(6) # oversampling rate api.FDMDV_OS_48 = 6 # oversampling rate
api.FDMDV_OS_TAPS_48K = int(48) # number of OS filter taps at 48kHz api.FDMDV_OS_TAPS_48K = 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_OS_TAPS_48_8K = api.FDMDV_OS_TAPS_48K // api.FDMDV_OS_48 # number of OS filter taps at 8kHz
api.fdmdv_8_to_48_short.argtype = [ctypes.c_void_p, ctypes.c_void_p, ctypes.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] api.fdmdv_48_to_8_short.argtype = [ctypes.c_void_p, ctypes.c_void_p, ctypes.c_int]
class resampler: class resampler:
""" """
Re-sampler class Re-sampler class
@ -340,7 +350,7 @@ class resampler:
assert len(in48) % api.FDMDV_OS_48 == 0 assert len(in48) % api.FDMDV_OS_48 == 0
# Concatenate filter memory and input samples # Concatenate filter memory and input samples
in48_mem = np.zeros(self.MEM48+len(in48), dtype=np.int16) in48_mem = np.zeros(self.MEM48 + len(in48), dtype=np.int16)
in48_mem[:self.MEM48] = self.filter_mem48 in48_mem[:self.MEM48] = self.filter_mem48
in48_mem[self.MEM48:] = in48 in48_mem[self.MEM48:] = in48
@ -368,14 +378,14 @@ class resampler:
assert in8.dtype == np.int16 assert in8.dtype == np.int16
# Concatenate filter memory and input samples # Concatenate filter memory and input samples
in8_mem = np.zeros(self.MEM8+len(in8), dtype=np.int16) in8_mem = np.zeros(self.MEM8 + len(in8), dtype=np.int16)
in8_mem[:self.MEM8] = self.filter_mem8 in8_mem[:self.MEM8] = self.filter_mem8
in8_mem[self.MEM8:] = in8 in8_mem[self.MEM8:] = in8
# In C: pin8=&in8_mem[MEM8] # In C: pin8=&in8_mem[MEM8]
pin8 = ctypes.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) out48 = np.zeros(api.FDMDV_OS_48 * len(in8), dtype=np.int16)
api.fdmdv_8_to_48_short(out48.ctypes, pin8, len(in8)); 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] self.filter_mem8 = in8_mem[:self.MEM8]

View file

@ -15,8 +15,6 @@ import argparse
import atexit import atexit
import multiprocessing import multiprocessing
import os import os
import queue
import re
import signal import signal
import socketserver import socketserver
import subprocess import subprocess
@ -25,13 +23,11 @@ import threading
import time import time
import crcengine import crcengine
import psutil
import serial.tools.list_ports import serial.tools.list_ports
import structlog import structlog
import ujson as json import ujson as json
import audio import audio
import helpers
import log_handler import log_handler
import sock import sock
import static import static
@ -51,9 +47,11 @@ def signal_handler(sig, frame):
sock.CLOSE_SIGNAL = True sock.CLOSE_SIGNAL = True
sys.exit(0) sys.exit(0)
signal.signal(signal.SIGINT, signal_handler) signal.signal(signal.SIGINT, signal_handler)
class DAEMON():
class DAEMON:
""" """
Daemon class Daemon class
@ -100,7 +98,7 @@ class DAEMON():
crc_hwid = crc_hwid.to_bytes(2, byteorder='big') crc_hwid = crc_hwid.to_bytes(2, byteorder='big')
crc_hwid = crc_hwid.hex() crc_hwid = crc_hwid.hex()
description = f"{desc} [{crc_hwid}]" description = f"{desc} [{crc_hwid}]"
serial_devices.append({"port": str(port), "description": str(description) }) serial_devices.append({"port": str(port), "description": str(description)})
static.SERIAL_DEVICES = serial_devices static.SERIAL_DEVICES = serial_devices
time.sleep(1) time.sleep(1)
@ -215,8 +213,9 @@ class DAEMON():
options.append('--tuning_range_fmax') options.append('--tuning_range_fmax')
options.append(data[20]) options.append(data[20])
# overriding FSK mode # overriding FSK mode
#if data[21] == 'True': # if data[21] == 'True':
# options.append('--fsk') # options.append('--fsk')
options.append('--tx-audio-level') options.append('--tx-audio-level')
@ -304,7 +303,7 @@ class DAEMON():
serialspeed=serialspeed, pttport=pttport, data_bits=data_bits, stop_bits=stop_bits, serialspeed=serialspeed, pttport=pttport, data_bits=data_bits, stop_bits=stop_bits,
handshake=handshake, rigctld_ip=rigctld_ip, rigctld_port = rigctld_port) handshake=handshake, rigctld_ip=rigctld_ip, rigctld_port = rigctld_port)
hamlib_version = rig.hamlib_version # hamlib_version = rig.hamlib_version
hamlib.set_ptt(True) hamlib.set_ptt(True)
pttstate = hamlib.get_ptt() pttstate = hamlib.get_ptt()
@ -327,10 +326,10 @@ class DAEMON():
except Exception as e: except Exception as e:
structlog.get_logger("structlog").error("[DMN] worker: Exception: ", e=e) structlog.get_logger("structlog").error("[DMN] worker: Exception: ", e=e)
# print(e)
if __name__ == '__main__': if __name__ == '__main__':
# we need to run this on windows for multiprocessing support # we need to run this on Windows for multiprocessing support
multiprocessing.freeze_support() multiprocessing.freeze_support()
# --------------------------------------------GET PARAMETER INPUTS # --------------------------------------------GET PARAMETER INPUTS
@ -354,7 +353,7 @@ if __name__ == '__main__':
os.makedirs(logging_path) os.makedirs(logging_path)
log_handler.setup_logging(logging_path) log_handler.setup_logging(logging_path)
except Exception as e: except Exception as e:
structlog.get_logger("structlog").error("[DMN] logger init error", exception=e) structlog.get_logger("structlog").error("[DMN] logger init error", exception=e)
try: try:
structlog.get_logger("structlog").info("[DMN] Starting TCP/IP socket", port=static.DAEMONPORT) structlog.get_logger("structlog").info("[DMN] Starting TCP/IP socket", port=static.DAEMONPORT)

View file

@ -8,9 +8,7 @@ Created on Sun Dec 27 20:43:40 2020
# pylint: disable=invalid-name, line-too-long, c-extension-no-member # pylint: disable=invalid-name, line-too-long, c-extension-no-member
# pylint: disable=import-outside-toplevel # pylint: disable=import-outside-toplevel
import asyncio
import base64 import base64
import logging
import queue import queue
import sys import sys
import threading import threading
@ -25,7 +23,6 @@ import ujson as json
import codec2 import codec2
import helpers import helpers
import log_handler
import modem import modem
import sock import sock
import static import static
@ -39,7 +36,7 @@ DATA_QUEUE_RECEIVED = queue.Queue()
class DATA(): class DATA():
""" Terminal Node Controller for FreeDATA """ """ Terminal Node Controller for FreeDATA """
def __init__(self): def __init__(self):
self.mycallsign = static.MYCALLSIGN # initial callsign. Will be overwritten later self.mycallsign = static.MYCALLSIGN # initial callsign. Will be overwritten later
self.data_queue_transmit = DATA_QUEUE_TRANSMIT self.data_queue_transmit = DATA_QUEUE_TRANSMIT
self.data_queue_received = DATA_QUEUE_RECEIVED self.data_queue_received = DATA_QUEUE_RECEIVED
@ -56,19 +53,19 @@ class DATA():
self.received_mycall_crc = b'' # Received my callsign crc if we received a crc for another ssid self.received_mycall_crc = b'' # Received my callsign crc if we received a crc for another ssid
self.data_channel_last_received = 0.0 # time of last "live sign" of a frame self.data_channel_last_received = 0.0 # time of last "live sign" of a frame
self.burst_ack_snr = 0 # SNR from received ack frames self.burst_ack_snr = 0 # SNR from received ack frames
self.burst_ack = False # if we received an acknowledge frame for a burst self.burst_ack = False # if we received an acknowledge frame for a burst
self.data_frame_ack_received = False # if we received an acknowledge frame for a data frame self.data_frame_ack_received = False # if we received an acknowledge frame for a data frame
self.rpt_request_received = False # if we received an request for repeater frames self.rpt_request_received = False # if we received an request for repeater frames
self.rpt_request_buffer = [] # requested frames, saved in a list self.rpt_request_buffer = [] # requested frames, saved in a list
self.rx_start_of_transmission = 0 # time of transmission start self.rx_start_of_transmission = 0 # time of transmission start
self.data_frame_bof = b'BOF' # 2 bytes for the BOF End of File indicator in a data frame self.data_frame_bof = b'BOF' # 2 bytes for the BOF End of File indicator in a data frame
self.data_frame_eof = b'EOF' # 2 bytes for the EOF End of File indicator in a data frame self.data_frame_eof = b'EOF' # 2 bytes for the EOF End of File indicator in a data frame
self.rx_n_max_retries_per_burst = 50 self.rx_n_max_retries_per_burst = 50
self.n_retries_per_burst = 0 self.n_retries_per_burst = 0
self.received_low_bandwith_mode = False # indicator if we recevied a low bandwith mode channel opener self.received_low_bandwith_mode = False # indicator if we recevied a low bandwith mode channel opener
self.data_channel_max_retries = 5 self.data_channel_max_retries = 5
self.datachannel_timeout = False self.datachannel_timeout = False
@ -76,19 +73,19 @@ class DATA():
self.mode_list_low_bw = [14, 12] self.mode_list_low_bw = [14, 12]
self.time_list_low_bw = [3, 7] self.time_list_low_bw = [3, 7]
self.mode_list_high_bw = [14, 12, 10] #201 = FSK mode list of available modes, each mode will be used 2times per speed level self.mode_list_high_bw = [14, 12, 10] # 201 = FSK mode list of available modes, each mode will be used 2times per speed level
self.time_list_high_bw = [3, 7, 8, 30] # list for time to wait for correspinding mode in seconds self.time_list_high_bw = [3, 7, 8, 30] # list for time to wait for correspinding mode in seconds
# mode list for selecting between low bandwith ( 500Hz ) and normal modes with higher bandwith # mode list for selecting between low bandwith ( 500Hz ) and normal modes with higher bandwith
if static.LOW_BANDWITH_MODE: if static.LOW_BANDWITH_MODE:
self.mode_list = self.mode_list_low_bw # mode list of available modes, each mode will be used 2times per speed level self.mode_list = self.mode_list_low_bw # mode list of available modes, each mode will be used 2times per speed level
self.time_list = self.time_list_low_bw # list for time to wait for correspinding mode in seconds self.time_list = self.time_list_low_bw # list for time to wait for correspinding mode in seconds
else: else:
self.mode_list = self.mode_list_high_bw # mode list of available modes, each mode will be used 2times per speed level self.mode_list = self.mode_list_high_bw # mode list of available modes, each mode will be used 2times per speed level
self.time_list = self.time_list_high_bw # list for time to wait for correspinding mode in seconds self.time_list = self.time_list_high_bw # list for time to wait for correspinding mode in seconds
self.speed_level = len(self.mode_list) - 1 # speed level for selecting mode self.speed_level = len(self.mode_list) - 1 # speed level for selecting mode
static.ARQ_SPEED_LEVEL = self.speed_level static.ARQ_SPEED_LEVEL = self.speed_level
self.is_IRS = False self.is_IRS = False
@ -101,21 +98,21 @@ class DATA():
self.transmission_timeout = 360 # transmission timeout in seconds self.transmission_timeout = 360 # transmission timeout in seconds
worker_thread_transmit = threading.Thread(target=self.worker_transmit, name="worker thread transmit",daemon=True) worker_thread_transmit = threading.Thread(target=self.worker_transmit, name="worker thread transmit", daemon=True)
worker_thread_transmit.start() worker_thread_transmit.start()
worker_thread_receive = threading.Thread(target=self.worker_receive, name="worker thread receive",daemon=True) worker_thread_receive = threading.Thread(target=self.worker_receive, name="worker thread receive", daemon=True)
worker_thread_receive.start() worker_thread_receive.start()
# START THE THREAD FOR THE TIMEOUT WATCHDOG # START THE THREAD FOR THE TIMEOUT WATCHDOG
watchdog_thread = threading.Thread(target=self.watchdog, name="watchdog",daemon=True) watchdog_thread = threading.Thread(target=self.watchdog, name="watchdog", daemon=True)
watchdog_thread.start() watchdog_thread.start()
arq_session_thread = threading.Thread(target=self.heartbeat, name="watchdog",daemon=True) arq_session_thread = threading.Thread(target=self.heartbeat, name="watchdog", daemon=True)
arq_session_thread.start() arq_session_thread.start()
self.beacon_interval = 0 self.beacon_interval = 0
self.beacon_thread = threading.Thread(target=self.run_beacon, name="watchdog",daemon=True) self.beacon_thread = threading.Thread(target=self.run_beacon, name="watchdog", daemon=True)
self.beacon_thread.start() self.beacon_thread.start()
def worker_transmit(self): def worker_transmit(self):
@ -204,7 +201,7 @@ class DATA():
if 50 >= frametype >= 10: if 50 >= frametype >= 10:
# get snr of received data # get snr of received data
#snr = self.calculate_snr(freedv) # snr = self.calculate_snr(freedv)
# we need to find a way of fixing this because after moving to class system this doesn't work anymore # we need to find a way of fixing this because after moving to class system this doesn't work anymore
snr = static.SNR snr = static.SNR
structlog.get_logger("structlog").debug("[TNC] RX SNR", snr=snr) structlog.get_logger("structlog").debug("[TNC] RX SNR", snr=snr)
@ -212,7 +209,7 @@ class DATA():
self.arq_data_received(bytes(bytes_out[:-2]), bytes_per_frame, snr, freedv) self.arq_data_received(bytes(bytes_out[:-2]), bytes_per_frame, snr, freedv)
# if we received the last frame of a burst or the last remaining rpt frame, do a modem unsync # if we received the last frame of a burst or the last remaining rpt frame, do a modem unsync
#if static.RX_BURST_BUFFER.count(None) <= 1 or (frame+1) == n_frames_per_burst: # if static.RX_BURST_BUFFER.count(None) <= 1 or (frame+1) == n_frames_per_burst:
# structlog.get_logger("structlog").debug(f"[TNC] LAST FRAME OF BURST --> UNSYNC {frame+1}/{n_frames_per_burst}") # structlog.get_logger("structlog").debug(f"[TNC] LAST FRAME OF BURST --> UNSYNC {frame+1}/{n_frames_per_burst}")
# self.c_lib.freedv_set_sync(freedv, 0) # self.c_lib.freedv_set_sync(freedv, 0)
@ -339,8 +336,8 @@ class DATA():
def send_burst_ack_frame(self, snr): def send_burst_ack_frame(self, snr):
""" Build and send ACK frame for burst DATA frame """ """ Build and send ACK frame for burst DATA frame """
ack_frame = bytearray(14) ack_frame = bytearray(14)
ack_frame[:1] = bytes([60]) ack_frame[:1] = bytes([60])
ack_frame[1:4] = static.DXCALLSIGN_CRC ack_frame[1:4] = static.DXCALLSIGN_CRC
ack_frame[4:7] = static.MYCALLSIGN_CRC ack_frame[4:7] = static.MYCALLSIGN_CRC
ack_frame[7:8] = bytes([int(snr)]) ack_frame[7:8] = bytes([int(snr)])
@ -351,8 +348,8 @@ class DATA():
def send_data_ack_frame(self, snr): def send_data_ack_frame(self, snr):
""" Build and send ACK frame for received DATA frame """ """ Build and send ACK frame for received DATA frame """
ack_frame = bytearray(14) ack_frame = bytearray(14)
ack_frame[:1] = bytes([61]) ack_frame[:1] = bytes([61])
ack_frame[1:4] = static.DXCALLSIGN_CRC ack_frame[1:4] = static.DXCALLSIGN_CRC
ack_frame[4:7] = static.MYCALLSIGN_CRC ack_frame[4:7] = static.MYCALLSIGN_CRC
ack_frame[7:8] = bytes([int(snr)]) ack_frame[7:8] = bytes([int(snr)])
@ -362,21 +359,21 @@ class DATA():
self.enqueue_frame_for_tx(ack_frame, copies=3, repeat_delay=100) self.enqueue_frame_for_tx(ack_frame, copies=3, repeat_delay=100)
def send_retransmit_request_frame(self, freedv): def send_retransmit_request_frame(self, freedv):
# check where a None is in our burst buffer and do frame+1, beacuse lists start at 0 # check where a None is in our burst buffer and do frame+1, because lists start at 0
missing_frames = [frame + 1 for frame, element in enumerate(static.RX_BURST_BUFFER) if element is None] missing_frames = [frame + 1 for frame, element in enumerate(static.RX_BURST_BUFFER) if element is None]
# set n frames per burst to modem # set n frames per burst to modem
# this is an idea so its not getting lost.... # this is an idea, so it's not getting lost....
# we need to work on this # we need to work on this
codec2.api.freedv_set_frames_per_burst(freedv,len(missing_frames)) codec2.api.freedv_set_frames_per_burst(freedv,len(missing_frames))
# TODO: Trim `missing_frames` bytesarray to [7:13] (6) frames, if it's larger. # TODO: Trim `missing_frames` bytesarray to [7:13] (6) frames, if it's larger.
# then create a repeat frame # then create a repeat frame
rpt_frame = bytearray(14) rpt_frame = bytearray(14)
rpt_frame[:1] = bytes([62]) rpt_frame[:1] = bytes([62])
rpt_frame[1:4] = static.DXCALLSIGN_CRC rpt_frame[1:4] = static.DXCALLSIGN_CRC
rpt_frame[4:7] = static.MYCALLSIGN_CRC rpt_frame[4:7] = static.MYCALLSIGN_CRC
rpt_frame[7:13] = missing_frames rpt_frame[7:13] = missing_frames
structlog.get_logger("structlog").info("[TNC] ARQ | RX | Requesting", frames=missing_frames) structlog.get_logger("structlog").info("[TNC] ARQ | RX | Requesting", frames=missing_frames)
@ -385,8 +382,8 @@ class DATA():
def send_burst_nack_frame(self, snr=0): def send_burst_nack_frame(self, snr=0):
""" Build and send NACK frame for received DATA frame """ """ Build and send NACK frame for received DATA frame """
nack_frame = bytearray(14) nack_frame = bytearray(14)
nack_frame[:1] = bytes([63]) nack_frame[:1] = bytes([63])
nack_frame[1:4] = static.DXCALLSIGN_CRC nack_frame[1:4] = static.DXCALLSIGN_CRC
nack_frame[4:7] = static.MYCALLSIGN_CRC nack_frame[4:7] = static.MYCALLSIGN_CRC
nack_frame[7:8] = bytes([int(snr)]) nack_frame[7:8] = bytes([int(snr)])
@ -397,8 +394,8 @@ class DATA():
def send_burst_nack_frame_watchdog(self, snr=0): def send_burst_nack_frame_watchdog(self, snr=0):
""" Build and send NACK frame for watchdog timeout """ """ Build and send NACK frame for watchdog timeout """
nack_frame = bytearray(14) nack_frame = bytearray(14)
nack_frame[:1] = bytes([64]) nack_frame[:1] = bytes([64])
nack_frame[1:4] = static.DXCALLSIGN_CRC nack_frame[1:4] = static.DXCALLSIGN_CRC
nack_frame[4:7] = static.MYCALLSIGN_CRC nack_frame[4:7] = static.MYCALLSIGN_CRC
nack_frame[7:8] = bytes([int(snr)]) nack_frame[7:8] = bytes([int(snr)])
@ -409,10 +406,10 @@ class DATA():
def send_disconnect_frame(self): def send_disconnect_frame(self):
""" Build and send a disconnect frame """ """ Build and send a disconnect frame """
disconnection_frame = bytearray(14) disconnection_frame = bytearray(14)
disconnection_frame[:1] = bytes([223]) disconnection_frame[:1] = bytes([223])
disconnection_frame[1:4] = static.DXCALLSIGN_CRC disconnection_frame[1:4] = static.DXCALLSIGN_CRC
disconnection_frame[4:7] = static.MYCALLSIGN_CRC disconnection_frame[4:7] = static.MYCALLSIGN_CRC
disconnection_frame[7:13] = helpers.callsign_to_bytes(self.mycallsign) disconnection_frame[7:13] = helpers.callsign_to_bytes(self.mycallsign)
self.enqueue_frame_for_tx(disconnection_frame, copies=5, repeat_delay=250) self.enqueue_frame_for_tx(disconnection_frame, copies=5, repeat_delay=250)
@ -464,7 +461,7 @@ class DATA():
static.RX_BURST_BUFFER = [None] * RX_N_FRAMES_PER_BURST static.RX_BURST_BUFFER = [None] * RX_N_FRAMES_PER_BURST
# Append data to rx burst buffer # Append data to rx burst buffer
static.RX_BURST_BUFFER[RX_N_FRAME_OF_BURST] = data_in[8:] # [frame_type][n_frames_per_burst][CRC24][CRC24] static.RX_BURST_BUFFER[RX_N_FRAME_OF_BURST] = data_in[8:] # [frame_type][n_frames_per_burst][CRC24][CRC24]
structlog.get_logger("structlog").debug("[TNC] static.RX_BURST_BUFFER", buffer=static.RX_BURST_BUFFER) structlog.get_logger("structlog").debug("[TNC] static.RX_BURST_BUFFER", buffer=static.RX_BURST_BUFFER)
@ -477,7 +474,7 @@ class DATA():
# the temp burst buffer is needed for checking, if we already recevied data # the temp burst buffer is needed for checking, if we already recevied data
temp_burst_buffer = b'' temp_burst_buffer = b''
for value in static.RX_BURST_BUFFER: for value in static.RX_BURST_BUFFER:
#static.RX_FRAME_BUFFER += static.RX_BURST_BUFFER[i] # static.RX_FRAME_BUFFER += static.RX_BURST_BUFFER[i]
temp_burst_buffer += bytes(value) temp_burst_buffer += bytes(value)
# if frame buffer ends not with the current frame, we are going to append new data # if frame buffer ends not with the current frame, we are going to append new data
@ -490,7 +487,7 @@ class DATA():
# Here we are going to search for our data in the last received bytes. # Here we are going to search for our data in the last received bytes.
# This reduces the chance we will lose the entire frame in the case of signalling frame loss # This reduces the chance we will lose the entire frame in the case of signalling frame loss
# static.RX_FRAME_BUFFER --> exisitng data # static.RX_FRAME_BUFFER --> existing data
# temp_burst_buffer --> new data # temp_burst_buffer --> new data
# search_area --> area where we want to search # search_area --> area where we want to search
search_area = 510 search_area = 510
@ -504,7 +501,7 @@ class DATA():
static.RX_FRAME_BUFFER = static.RX_FRAME_BUFFER[:search_position + get_position] static.RX_FRAME_BUFFER = static.RX_FRAME_BUFFER[:search_position + get_position]
static.RX_FRAME_BUFFER += temp_burst_buffer static.RX_FRAME_BUFFER += temp_burst_buffer
structlog.get_logger("structlog").warning("[TNC] ARQ | RX | replacing existing buffer data", area=search_area, pos=get_position) structlog.get_logger("structlog").warning("[TNC] ARQ | RX | replacing existing buffer data", area=search_area, pos=get_position)
# if we dont find data n this range, we really have new data and going to replace it # if we don't find data n this range, we really have new data and going to replace it
else: else:
static.RX_FRAME_BUFFER += temp_burst_buffer static.RX_FRAME_BUFFER += temp_burst_buffer
structlog.get_logger("structlog").debug("[TNC] ARQ | RX | appending data to buffer") structlog.get_logger("structlog").debug("[TNC] ARQ | RX | appending data to buffer")
@ -558,10 +555,10 @@ class DATA():
if bof_position >=0: if bof_position >=0:
payload = static.RX_FRAME_BUFFER[bof_position+len(self.data_frame_bof):eof_position] payload = static.RX_FRAME_BUFFER[bof_position+len(self.data_frame_bof):eof_position]
frame_length = int.from_bytes(payload[4:8], "big") #4:8 4bytes frame_length = int.from_bytes(payload[4:8], "big") # 4:8 4bytes
static.TOTAL_BYTES = frame_length static.TOTAL_BYTES = frame_length
compression_factor = int.from_bytes(payload[8:9], "big") #4:8 4bytes compression_factor = int.from_bytes(payload[8:9], "big") # 4:8 4bytes
compression_factor = np.clip(compression_factor, 0, 255) #limit to max value of 255 compression_factor = np.clip(compression_factor, 0, 255) # limit to max value of 255
static.ARQ_COMPRESSION_FACTOR = compression_factor / 10 static.ARQ_COMPRESSION_FACTOR = compression_factor / 10
self.calculate_transfer_rate_rx(self.rx_start_of_transmission, len(static.RX_FRAME_BUFFER)) self.calculate_transfer_rate_rx(self.rx_start_of_transmission, len(static.RX_FRAME_BUFFER))
@ -574,9 +571,9 @@ class DATA():
# Extract raw data from buffer # Extract raw data from buffer
payload = static.RX_FRAME_BUFFER[bof_position+len(self.data_frame_bof):eof_position] payload = static.RX_FRAME_BUFFER[bof_position+len(self.data_frame_bof):eof_position]
# Get the data frame crc # Get the data frame crc
data_frame_crc = payload[:4] #0:4 4bytes data_frame_crc = payload[:4] # 0:4 4bytes
# Get the data frame length # Get the data frame length
frame_length = int.from_bytes(payload[4:8], "big") #4:8 4bytes frame_length = int.from_bytes(payload[4:8], "big") # 4:8 4bytes
static.TOTAL_BYTES = frame_length static.TOTAL_BYTES = frame_length
# 8:9 = compression factor # 8:9 = compression factor
@ -609,7 +606,6 @@ class DATA():
jsondata = {"arq":"received", "uuid" : uniqueid, "timestamp": timestamp, "mycallsign" : str(mycallsign, 'utf-8'), "dxcallsign": str(static.DXCALLSIGN, 'utf-8'), "dxgrid": str(static.DXGRID, 'utf-8'), "data": base64_data} jsondata = {"arq":"received", "uuid" : uniqueid, "timestamp": timestamp, "mycallsign" : str(mycallsign, 'utf-8'), "dxcallsign": str(static.DXCALLSIGN, 'utf-8'), "dxgrid": str(static.DXGRID, 'utf-8'), "data": base64_data}
json_data_out = json.dumps(jsondata) json_data_out = json.dumps(jsondata)
structlog.get_logger("structlog").debug("[TNC] arq_data_received:", jsondata=jsondata) structlog.get_logger("structlog").debug("[TNC] arq_data_received:", jsondata=jsondata)
# print(jsondata)
sock.SOCKET_QUEUE.put(json_data_out) sock.SOCKET_QUEUE.put(json_data_out)
static.INFO.append("ARQ;RECEIVING;SUCCESS") static.INFO.append("ARQ;RECEIVING;SUCCESS")
@ -652,19 +648,19 @@ class DATA():
self.speed_level = len(self.mode_list) - 1 # speed level for selecting mode self.speed_level = len(self.mode_list) - 1 # speed level for selecting mode
static.ARQ_SPEED_LEVEL = self.speed_level static.ARQ_SPEED_LEVEL = self.speed_level
TX_N_SENT_BYTES = 0 # already sent bytes per data frame TX_N_SENT_BYTES = 0 # already sent bytes per data frame
self.tx_n_retry_of_burst = 0 # retries we already sent data self.tx_n_retry_of_burst = 0 # retries we already sent data
TX_N_MAX_RETRIES_PER_BURST = 50 # max amount of retries we sent before frame is lost TX_N_MAX_RETRIES_PER_BURST = 50 # max amount of retries we sent before frame is lost
TX_N_FRAMES_PER_BURST = n_frames_per_burst # amount of n frames per burst TX_N_FRAMES_PER_BURST = n_frames_per_burst # amount of n frames per burst
TX_BUFFER = [] # our buffer for appending new data TX_BUFFER = [] # our buffer for appending new data
# TIMEOUTS # TIMEOUTS
BURST_ACK_TIMEOUT_SECONDS = 3.0 # timeout for burst acknowledges BURST_ACK_TIMEOUT_SECONDS = 3.0 # timeout for burst acknowledges
DATA_FRAME_ACK_TIMEOUT_SECONDS = 3.0 # timeout for data frame acknowledges DATA_FRAME_ACK_TIMEOUT_SECONDS = 3.0 # timeout for data frame acknowledges
RPT_ACK_TIMEOUT_SECONDS = 3.0 # timeout for rpt frame acknowledges RPT_ACK_TIMEOUT_SECONDS = 3.0 # timeout for rpt frame acknowledges
# save len of data_out to TOTAL_BYTES for our statistics --> kBytes # save len of data_out to TOTAL_BYTES for our statistics --> kBytes
#static.TOTAL_BYTES = round(len(data_out) / 1024, 2) # static.TOTAL_BYTES = round(len(data_out) / 1024, 2)
static.TOTAL_BYTES = len(data_out) static.TOTAL_BYTES = len(data_out)
frame_total_size = len(data_out).to_bytes(4, byteorder='big') frame_total_size = len(data_out).to_bytes(4, byteorder='big')
static.INFO.append("ARQ;TRANSMITTING") static.INFO.append("ARQ;TRANSMITTING")
@ -710,10 +706,10 @@ class DATA():
structlog.get_logger("structlog").debug("[TNC] FIXED MODE:", mode=data_mode) structlog.get_logger("structlog").debug("[TNC] FIXED MODE:", mode=data_mode)
else: else:
# we are doing a modulo check of transmission retries of the actual burst # we are doing a modulo check of transmission retries of the actual burst
# every 2nd retry which failes, decreases speedlevel by 1. # every 2nd retry which fails, decreases speedlevel by 1.
# as soon as we received an ACK for the current burst, speed_level will increase # as soon as we received an ACK for the current burst, speed_level will increase
# by 1. # by 1.
# the can be optimised by checking the optimal speed level for the current conditions # They can be optimised by checking the optimal speed level for the current conditions
''' '''
if not self.tx_n_retry_of_burst % 2 and self.tx_n_retry_of_burst > 0: if not self.tx_n_retry_of_burst % 2 and self.tx_n_retry_of_burst > 0:
self.speed_level -= 1 self.speed_level -= 1
@ -721,7 +717,7 @@ class DATA():
self.speed_level = 0 self.speed_level = 0
''' '''
#if self.tx_n_retry_of_burst <= 1: # if self.tx_n_retry_of_burst <= 1:
# self.speed_level += 1 # self.speed_level += 1
# if self.speed_level >= len(self.mode_list)-1: # if self.speed_level >= len(self.mode_list)-1:
# self.speed_level = len(self.mode_list)-1 # self.speed_level = len(self.mode_list)-1
@ -746,8 +742,8 @@ class DATA():
# TODO: this part needs a complete rewrite! # TODO: this part needs a complete rewrite!
# TX_N_FRAMES_PER_BURST = 1 is working # TX_N_FRAMES_PER_BURST = 1 is working
arqheader = bytearray() arqheader = bytearray()
arqheader[:1] = bytes([10]) #bytes([10 + i]) arqheader[:1] = bytes([10]) # bytes([10 + i])
arqheader[1:2] = bytes([TX_N_FRAMES_PER_BURST]) arqheader[1:2] = bytes([TX_N_FRAMES_PER_BURST])
arqheader[2:5] = static.DXCALLSIGN_CRC arqheader[2:5] = static.DXCALLSIGN_CRC
arqheader[5:8] = static.MYCALLSIGN_CRC arqheader[5:8] = static.MYCALLSIGN_CRC
@ -790,7 +786,7 @@ class DATA():
while not self.burst_ack and not self.burst_nack and not self.rpt_request_received and not self.data_frame_ack_received and time.time() < burstacktimeout and static.ARQ_STATE: while not self.burst_ack and not self.burst_nack and not self.rpt_request_received and not self.data_frame_ack_received and time.time() < burstacktimeout and static.ARQ_STATE:
time.sleep(0.01) time.sleep(0.01)
''' '''
#burstacktimeout = time.time() + BURST_ACK_TIMEOUT_SECONDS + 100 # burstacktimeout = time.time() + BURST_ACK_TIMEOUT_SECONDS + 100
while (static.ARQ_STATE and not while (static.ARQ_STATE and not
(self.burst_ack or self.burst_nack or (self.burst_ack or self.burst_nack or
self.rpt_request_received or self.data_frame_ack_received)): self.rpt_request_received or self.data_frame_ack_received)):
@ -800,22 +796,22 @@ class DATA():
if self.burst_ack: if self.burst_ack:
self.burst_ack = False # reset ack state self.burst_ack = False # reset ack state
self.tx_n_retry_of_burst = 0 # reset retries self.tx_n_retry_of_burst = 0 # reset retries
break #break retry loop break # break retry loop
if self.burst_nack: if self.burst_nack:
self.burst_nack = False #reset nack state self.burst_nack = False # reset nack state
# not yet implemented # not yet implemented
if self.rpt_request_received: if self.rpt_request_received:
pass pass
if self.data_frame_ack_received: if self.data_frame_ack_received:
break #break retry loop break # break retry loop
# we need this part for leaving the repeat loop # we need this part for leaving the repeat loop
# static.ARQ_STATE == 'DATA' --> when stopping transmission manually # static.ARQ_STATE == 'DATA' --> when stopping transmission manually
if not static.ARQ_STATE: if not static.ARQ_STATE:
#print("not ready for data...leaving loop....") # print("not ready for data...leaving loop....")
break break
self.calculate_transfer_rate_tx(tx_start_of_transmission, bufferposition_end, len(data_out)) self.calculate_transfer_rate_tx(tx_start_of_transmission, bufferposition_end, len(data_out))
@ -832,7 +828,7 @@ class DATA():
json_data_out = json.dumps(jsondata) json_data_out = json.dumps(jsondata)
sock.SOCKET_QUEUE.put(json_data_out) sock.SOCKET_QUEUE.put(json_data_out)
#GOING TO NEXT ITERATION # GOING TO NEXT ITERATION
if self.data_frame_ack_received: if self.data_frame_ack_received:
static.INFO.append("ARQ;TRANSMITTING;SUCCESS") static.INFO.append("ARQ;TRANSMITTING;SUCCESS")
@ -870,8 +866,8 @@ class DATA():
""" """
# increase speed level if we received a burst ack # increase speed level if we received a burst ack
#self.speed_level += 1 # self.speed_level += 1
#if self.speed_level >= len(self.mode_list)-1: # if self.speed_level >= len(self.mode_list)-1:
# self.speed_level = len(self.mode_list)-1 # self.speed_level = len(self.mode_list)-1
# only process data if we are in ARQ and BUSY state # only process data if we are in ARQ and BUSY state
@ -900,8 +896,8 @@ class DATA():
""" """
# increase speed level if we received a burst ack # increase speed level if we received a burst ack
#self.speed_level += 1 # self.speed_level += 1
#if self.speed_level >= len(self.mode_list)-1: # if self.speed_level >= len(self.mode_list)-1:
# self.speed_level = len(self.mode_list)-1 # self.speed_level = len(self.mode_list)-1
# only process data if we are in ARQ and BUSY state # only process data if we are in ARQ and BUSY state
@ -980,7 +976,7 @@ class DATA():
Returns: Returns:
""" """
# das hier müssen wir checken. Sollte vielleicht in INIT!!! # TODO: we need to check this, maybe placing it to class init
self.datachannel_timeout = False self.datachannel_timeout = False
structlog.get_logger("structlog").info("[TNC] SESSION [" + str(self.mycallsign, 'utf-8') + "]>> <<[" + str(static.DXCALLSIGN, 'utf-8') + "]", state=static.ARQ_SESSION_STATE) structlog.get_logger("structlog").info("[TNC] SESSION [" + str(self.mycallsign, 'utf-8') + "]>> <<[" + str(static.DXCALLSIGN, 'utf-8') + "]", state=static.ARQ_SESSION_STATE)
@ -1010,10 +1006,10 @@ class DATA():
self.IS_ARQ_SESSION_MASTER = True self.IS_ARQ_SESSION_MASTER = True
static.ARQ_SESSION_STATE = 'connecting' static.ARQ_SESSION_STATE = 'connecting'
connection_frame = bytearray(14) connection_frame = bytearray(14)
connection_frame[:1] = bytes([221]) connection_frame[:1] = bytes([221])
connection_frame[1:4] = static.DXCALLSIGN_CRC connection_frame[1:4] = static.DXCALLSIGN_CRC
connection_frame[4:7] = static.MYCALLSIGN_CRC connection_frame[4:7] = static.MYCALLSIGN_CRC
connection_frame[7:13] = helpers.callsign_to_bytes(self.mycallsign) connection_frame[7:13] = helpers.callsign_to_bytes(self.mycallsign)
while not static.ARQ_SESSION: while not static.ARQ_SESSION:
@ -1028,7 +1024,6 @@ class DATA():
time.sleep(0.01) time.sleep(0.01)
# break if data channel is opened # break if data channel is opened
if static.ARQ_SESSION: if static.ARQ_SESSION:
# eventuell einfach nur return true um die nächste break ebene zu vermeiden?
return True return True
# if static.ARQ_SESSION: # if static.ARQ_SESSION:
# break # break
@ -1205,11 +1200,11 @@ class DATA():
structlog.get_logger("structlog").debug("[TNC] Requesting manual mode --> not yet implemented ") structlog.get_logger("structlog").debug("[TNC] Requesting manual mode --> not yet implemented ")
frametype = bytes([mode]) frametype = bytes([mode])
connection_frame = bytearray(14) connection_frame = bytearray(14)
connection_frame[:1] = frametype connection_frame[:1] = frametype
connection_frame[1:4] = static.DXCALLSIGN_CRC connection_frame[1:4] = static.DXCALLSIGN_CRC
connection_frame[4:7] = static.MYCALLSIGN_CRC connection_frame[4:7] = static.MYCALLSIGN_CRC
connection_frame[7:13] = helpers.callsign_to_bytes(mycallsign) connection_frame[7:13] = helpers.callsign_to_bytes(mycallsign)
connection_frame[13:14] = bytes([n_frames_per_burst]) connection_frame[13:14] = bytes([n_frames_per_burst])
while not static.ARQ_STATE: while not static.ARQ_STATE:
@ -1233,7 +1228,6 @@ class DATA():
if attempt == self.data_channel_max_retries: if attempt == self.data_channel_max_retries:
static.INFO.append("DATACHANNEL;FAILED") static.INFO.append("DATACHANNEL;FAILED")
structlog.get_logger("structlog").debug("[TNC] arq_open_data_channel:", transmission_uuid=self.transmission_uuid) structlog.get_logger("structlog").debug("[TNC] arq_open_data_channel:", transmission_uuid=self.transmission_uuid)
# print(self.transmission_uuid)
jsondata = {"arq":"transmission", "status" :"failed", "uuid" : self.transmission_uuid, "percent" : static.ARQ_TRANSMISSION_PERCENT, "bytesperminute" : static.ARQ_BYTES_PER_MINUTE} jsondata = {"arq":"transmission", "status" :"failed", "uuid" : self.transmission_uuid, "percent" : static.ARQ_TRANSMISSION_PERCENT, "bytesperminute" : static.ARQ_BYTES_PER_MINUTE}
json_data_out = json.dumps(jsondata) json_data_out = json.dumps(jsondata)
sock.SOCKET_QUEUE.put(json_data_out) sock.SOCKET_QUEUE.put(json_data_out)
@ -1266,7 +1260,7 @@ class DATA():
n_frames_per_burst = int.from_bytes(bytes(data_in[13:14]), "big") n_frames_per_burst = int.from_bytes(bytes(data_in[13:14]), "big")
frametype = int.from_bytes(bytes(data_in[:1]), "big") frametype = int.from_bytes(bytes(data_in[:1]), "big")
#check if we received low bandwith mode # check if we received low bandwith mode
if frametype == 225: if frametype == 225:
self.received_low_bandwith_mode = False self.received_low_bandwith_mode = False
self.mode_list = self.mode_list_high_bw self.mode_list = self.mode_list_high_bw
@ -1310,11 +1304,11 @@ class DATA():
frametype = bytes([226]) frametype = bytes([226])
structlog.get_logger("structlog").debug("[TNC] Responding with high bandwidth mode") structlog.get_logger("structlog").debug("[TNC] Responding with high bandwidth mode")
connection_frame = bytearray(14) connection_frame = bytearray(14)
connection_frame[:1] = frametype connection_frame[:1] = frametype
connection_frame[1:4] = static.DXCALLSIGN_CRC connection_frame[1:4] = static.DXCALLSIGN_CRC
connection_frame[4:7] = static.MYCALLSIGN_CRC connection_frame[4:7] = static.MYCALLSIGN_CRC
connection_frame[13:14] = bytes([static.ARQ_PROTOCOL_VERSION]) #crc8 of version for checking protocol version connection_frame[13:14] = bytes([static.ARQ_PROTOCOL_VERSION]) # crc8 of version for checking protocol version
self.enqueue_frame_for_tx(connection_frame) self.enqueue_frame_for_tx(connection_frame)
@ -1380,10 +1374,10 @@ class DATA():
static.INFO.append("PING;SENDING") static.INFO.append("PING;SENDING")
structlog.get_logger("structlog").info("[TNC] PING REQ [" + str(self.mycallsign, 'utf-8') + "] >>> [" + str(static.DXCALLSIGN, 'utf-8') + "]" ) structlog.get_logger("structlog").info("[TNC] PING REQ [" + str(self.mycallsign, 'utf-8') + "] >>> [" + str(static.DXCALLSIGN, 'utf-8') + "]" )
ping_frame = bytearray(14) ping_frame = bytearray(14)
ping_frame[:1] = bytes([210]) ping_frame[:1] = bytes([210])
ping_frame[1:4] = static.DXCALLSIGN_CRC ping_frame[1:4] = static.DXCALLSIGN_CRC
ping_frame[4:7] = static.MYCALLSIGN_CRC ping_frame[4:7] = static.MYCALLSIGN_CRC
ping_frame[7:13] = helpers.callsign_to_bytes(self.mycallsign) ping_frame[7:13] = helpers.callsign_to_bytes(self.mycallsign)
structlog.get_logger("structlog").info("[TNC] ENABLE FSK", state=static.ENABLE_FSK) structlog.get_logger("structlog").info("[TNC] ENABLE FSK", state=static.ENABLE_FSK)
@ -1418,8 +1412,8 @@ class DATA():
structlog.get_logger("structlog").info("[TNC] PING REQ [" + str(mycallsign, 'utf-8') + "] <<< [" + str(static.DXCALLSIGN, 'utf-8') + "]", snr=static.SNR ) structlog.get_logger("structlog").info("[TNC] PING REQ [" + str(mycallsign, 'utf-8') + "] <<< [" + str(static.DXCALLSIGN, 'utf-8') + "]", snr=static.SNR )
ping_frame = bytearray(14) ping_frame = bytearray(14)
ping_frame[:1] = bytes([211]) ping_frame[:1] = bytes([211])
ping_frame[1:4] = static.DXCALLSIGN_CRC ping_frame[1:4] = static.DXCALLSIGN_CRC
ping_frame[4:7] = static.MYCALLSIGN_CRC ping_frame[4:7] = static.MYCALLSIGN_CRC
ping_frame[7:13] = static.MYGRID ping_frame[7:13] = static.MYGRID
@ -1458,11 +1452,11 @@ class DATA():
Force a stop of the running transmission Force a stop of the running transmission
""" """
structlog.get_logger("structlog").warning("[TNC] Stopping transmission!") structlog.get_logger("structlog").warning("[TNC] Stopping transmission!")
stop_frame = bytearray(14) stop_frame = bytearray(14)
stop_frame[:1] = bytes([249]) stop_frame[:1] = bytes([249])
stop_frame[1:4] = static.DXCALLSIGN_CRC stop_frame[1:4] = static.DXCALLSIGN_CRC
stop_frame[4:7] = static.MYCALLSIGN_CRC stop_frame[4:7] = static.MYCALLSIGN_CRC
stop_frame[7:13] = helpers.callsign_to_bytes(self.mycallsign) stop_frame[7:13] = helpers.callsign_to_bytes(self.mycallsign)
self.enqueue_frame_for_tx(stop_frame, copies=2, repeat_delay=250) self.enqueue_frame_for_tx(stop_frame, copies=2, repeat_delay=250)
@ -1486,7 +1480,7 @@ class DATA():
""" """
Controlling funktion for running a beacon Controlling funktion for running a beacon
Args: Args:
interval:int: self:
Returns: Returns:
@ -1551,9 +1545,9 @@ class DATA():
structlog.get_logger("structlog").info("[TNC] CQ CQ CQ") structlog.get_logger("structlog").info("[TNC] CQ CQ CQ")
static.INFO.append("CQ;SENDING") static.INFO.append("CQ;SENDING")
cq_frame = bytearray(14) cq_frame = bytearray(14)
cq_frame[:1] = bytes([200]) cq_frame[:1] = bytes([200])
cq_frame[1:7] = helpers.callsign_to_bytes(self.mycallsign) cq_frame[1:7] = helpers.callsign_to_bytes(self.mycallsign)
cq_frame[7:11] = helpers.encode_grid(static.MYGRID.decode("utf-8")) cq_frame[7:11] = helpers.encode_grid(static.MYGRID.decode("utf-8"))
structlog.get_logger("structlog").info("[TNC] ENABLE FSK", state=static.ENABLE_FSK) structlog.get_logger("structlog").info("[TNC] ENABLE FSK", state=static.ENABLE_FSK)
@ -1602,9 +1596,9 @@ class DATA():
static.INFO.append("QRV;SENDING") static.INFO.append("QRV;SENDING")
structlog.get_logger("structlog").info("[TNC] Sending QRV!") structlog.get_logger("structlog").info("[TNC] Sending QRV!")
qrv_frame = bytearray(14) qrv_frame = bytearray(14)
qrv_frame[:1] = bytes([201]) qrv_frame[:1] = bytes([201])
qrv_frame[1:7] = helpers.callsign_to_bytes(self.mycallsign) qrv_frame[1:7] = helpers.callsign_to_bytes(self.mycallsign)
qrv_frame[7:11] = helpers.encode_grid(static.MYGRID.decode("utf-8")) qrv_frame[7:11] = helpers.encode_grid(static.MYGRID.decode("utf-8"))
structlog.get_logger("structlog").info("[TNC] ENABLE FSK", state=static.ENABLE_FSK) structlog.get_logger("structlog").info("[TNC] ENABLE FSK", state=static.ENABLE_FSK)
@ -1686,7 +1680,7 @@ class DATA():
def calculate_transfer_rate_tx(self, tx_start_of_transmission:float, sentbytes:int, tx_buffer_length:int) -> list: def calculate_transfer_rate_tx(self, tx_start_of_transmission:float, sentbytes:int, tx_buffer_length:int) -> list:
""" """
Calcualte Transferrate for transmission Calculate transfer rate for transmission
Args: Args:
tx_start_of_transmission:float: tx_start_of_transmission:float:
sentbytes:int: sentbytes:int:
@ -1701,8 +1695,8 @@ class DATA():
transmissiontime = time.time() - tx_start_of_transmission transmissiontime = time.time() - tx_start_of_transmission
if sentbytes > 0: if sentbytes > 0:
static.ARQ_BITS_PER_SECOND = int((sentbytes * 8) / transmissiontime) # Bits per Second static.ARQ_BITS_PER_SECOND = int((sentbytes * 8) / transmissiontime) # Bits per Second
static.ARQ_BYTES_PER_MINUTE = int((sentbytes) / (transmissiontime / 60)) # Bytes per Minute static.ARQ_BYTES_PER_MINUTE = int((sentbytes) / (transmissiontime / 60)) # Bytes per Minute
else: else:
static.ARQ_BITS_PER_SECOND = 0 static.ARQ_BITS_PER_SECOND = 0
@ -1739,7 +1733,7 @@ class DATA():
# reset modem receiving state to reduce cpu load # reset modem receiving state to reduce cpu load
modem.RECEIVE_DATAC1 = False modem.RECEIVE_DATAC1 = False
modem.RECEIVE_DATAC3 = False modem.RECEIVE_DATAC3 = False
#modem.RECEIVE_FSK_LDPC_0 = False # modem.RECEIVE_FSK_LDPC_0 = False
modem.RECEIVE_FSK_LDPC_1 = False modem.RECEIVE_FSK_LDPC_1 = False
# reset buffer overflow counter # reset buffer overflow counter
@ -1842,8 +1836,8 @@ class DATA():
self.burst_nack_counter += 1 self.burst_nack_counter += 1
if self.burst_nack_counter >= 2: if self.burst_nack_counter >= 2:
self.speed_level -= 1 self.speed_level -= 1
#print(self.burst_nack_counter) # print(self.burst_nack_counter)
#print(self.speed_level) # print(self.speed_level)
static.ARQ_SPEED_LEVEL = self.speed_level static.ARQ_SPEED_LEVEL = self.speed_level
self.burst_nack_counter = 0 self.burst_nack_counter = 0
if self.speed_level <= 0: if self.speed_level <= 0:
@ -1873,8 +1867,8 @@ class DATA():
time.sleep(0.01) time.sleep(0.01)
if self.data_channel_last_received + self.transmission_timeout > time.time(): if self.data_channel_last_received + self.transmission_timeout > time.time():
time.sleep(0.01) time.sleep(0.01)
#print(self.data_channel_last_received + self.transmission_timeout - time.time()) # print(self.data_channel_last_received + self.transmission_timeout - time.time())
#pass # pass
else: else:
self.data_channel_last_received = 0 self.data_channel_last_received = 0
structlog.get_logger("structlog").info("[TNC] DATA [" + str(self.mycallsign, 'utf-8') + "]<<T>>[" + str(static.DXCALLSIGN, 'utf-8') + "]") structlog.get_logger("structlog").info("[TNC] DATA [" + str(self.mycallsign, 'utf-8') + "]<<T>>[" + str(static.DXCALLSIGN, 'utf-8') + "]")

View file

@ -27,6 +27,7 @@ def wait(seconds: float) -> bool:
time.sleep(0.01) time.sleep(0.01)
return True return True
def get_crc_8(data) -> bytes: def get_crc_8(data) -> bytes:
"""Author: DJ2LS """Author: DJ2LS
@ -45,6 +46,7 @@ def get_crc_8(data) -> bytes:
crc_data = crc_data.to_bytes(1, byteorder='big') crc_data = crc_data.to_bytes(1, byteorder='big')
return crc_data return crc_data
def get_crc_16(data) -> bytes: def get_crc_16(data) -> bytes:
"""Author: DJ2LS """Author: DJ2LS
@ -63,6 +65,7 @@ def get_crc_16(data) -> bytes:
crc_data = crc_data.to_bytes(2, byteorder='big') crc_data = crc_data.to_bytes(2, byteorder='big')
return crc_data return crc_data
def get_crc_24(data) -> bytes: def get_crc_24(data) -> bytes:
"""Author: DJ2LS """Author: DJ2LS
@ -84,6 +87,7 @@ def get_crc_24(data) -> bytes:
crc_data = crc_data.to_bytes(3, byteorder='big') crc_data = crc_data.to_bytes(3, byteorder='big')
return crc_data return crc_data
def get_crc_32(data: bytes) -> bytes: def get_crc_32(data: bytes) -> bytes:
"""Author: DJ2LS """Author: DJ2LS
@ -102,6 +106,7 @@ def get_crc_32(data: bytes) -> bytes:
crc_data = crc_data.to_bytes(4, byteorder='big') crc_data = crc_data.to_bytes(4, byteorder='big')
return crc_data return crc_data
def add_to_heard_stations(dxcallsign, dxgrid, datatype, snr, offset, frequency): def add_to_heard_stations(dxcallsign, dxgrid, datatype, snr, offset, frequency):
""" """
@ -136,6 +141,7 @@ def add_to_heard_stations(dxcallsign, dxgrid, datatype, snr, offset, frequency):
# item = [dxcallsign, int(time.time())] # item = [dxcallsign, int(time.time())]
# static.HEARD_STATIONS[idx] = item # static.HEARD_STATIONS[idx] = item
def callsign_to_bytes(callsign) -> bytes: def callsign_to_bytes(callsign) -> bytes:
""" """
@ -146,22 +152,22 @@ def callsign_to_bytes(callsign) -> bytes:
""" """
# http://www.aprs.org/aprs11/SSIDs.txt # http://www.aprs.org/aprs11/SSIDs.txt
#-0 Your primary station usually fixed and message capable # -0 Your primary station usually fixed and message capable
#-1 generic additional station, digi, mobile, wx, etc # -1 generic additional station, digi, mobile, wx, etc
#-2 generic additional station, digi, mobile, wx, etc # -2 generic additional station, digi, mobile, wx, etc
#-3 generic additional station, digi, mobile, wx, etc # -3 generic additional station, digi, mobile, wx, etc
#-4 generic additional station, digi, mobile, wx, etc # -4 generic additional station, digi, mobile, wx, etc
#-5 Other networks (Dstar, Iphones, Androids, Blackberry's etc) # -5 Other networks (Dstar, Iphones, Androids, Blackberry's etc)
#-6 Special activity, Satellite ops, camping or 6 meters, etc # -6 Special activity, Satellite ops, camping or 6 meters, etc
#-7 walkie talkies, HT's or other human portable # -7 walkie talkies, HT's or other human portable
#-8 boats, sailboats, RV's or second main mobile # -8 boats, sailboats, RV's or second main mobile
#-9 Primary Mobile (usually message capable) # -9 Primary Mobile (usually message capable)
#-10 internet, Igates, echolink, winlink, AVRS, APRN, etc # -10 internet, Igates, echolink, winlink, AVRS, APRN, etc
#-11 balloons, aircraft, spacecraft, etc # -11 balloons, aircraft, spacecraft, etc
#-12 APRStt, DTMF, RFID, devices, one-way trackers*, etc # -12 APRStt, DTMF, RFID, devices, one-way trackers*, etc
#-13 Weather stations # -13 Weather stations
#-14 Truckers or generally full time drivers # -14 Truckers or generally full time drivers
#-15 generic additional station, digi, mobile, wx, etc # -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: try:
@ -178,16 +184,16 @@ def callsign_to_bytes(callsign) -> bytes:
except IndexError as e: except IndexError as e:
structlog.get_logger("structlog").debug("[HLP] callsign_to_bytes: Error callsign SSID to integer:", e=e) structlog.get_logger("structlog").debug("[HLP] callsign_to_bytes: Error callsign SSID to integer:", e=e)
#callsign = callsign[0] # callsign = callsign[0]
#bytestring = bytearray(8) # bytestring = bytearray(8)
#bytestring[:len(callsign)] = callsign # bytestring[:len(callsign)] = callsign
#bytestring[7:8] = bytes([ssid]) # bytestring[7:8] = bytes([ssid])
# ---- callsign with encoding always 6 bytes long # ---- callsign with encoding always 6 bytes long
callsign = callsign[0].decode("utf-8") callsign = callsign[0].decode("utf-8")
ssid = bytes([ssid]).decode("utf-8") ssid = bytes([ssid]).decode("utf-8")
return encode_call(callsign + ssid) return encode_call(callsign + ssid)
#return bytes(bytestring) # return bytes(bytestring)
def bytes_to_callsign(bytestring: bytes) -> bytes: def bytes_to_callsign(bytestring: bytes) -> bytes:
""" """
@ -200,22 +206,22 @@ def bytes_to_callsign(bytestring: bytes) -> bytes:
bytes bytes
""" """
# http://www.aprs.org/aprs11/SSIDs.txt # http://www.aprs.org/aprs11/SSIDs.txt
#-0 Your primary station usually fixed and message capable # -0 Your primary station usually fixed and message capable
#-1 generic additional station, digi, mobile, wx, etc # -1 generic additional station, digi, mobile, wx, etc
#-2 generic additional station, digi, mobile, wx, etc # -2 generic additional station, digi, mobile, wx, etc
#-3 generic additional station, digi, mobile, wx, etc # -3 generic additional station, digi, mobile, wx, etc
#-4 generic additional station, digi, mobile, wx, etc # -4 generic additional station, digi, mobile, wx, etc
#-5 Other networks (Dstar, Iphones, Androids, Blackberry's etc) # -5 Other networks (Dstar, Iphones, Androids, Blackberry's etc)
#-6 Special activity, Satellite ops, camping or 6 meters, etc # -6 Special activity, Satellite ops, camping or 6 meters, etc
#-7 walkie talkies, HT's or other human portable # -7 walkie talkies, HT's or other human portable
#-8 boats, sailboats, RV's or second main mobile # -8 boats, sailboats, RV's or second main mobile
#-9 Primary Mobile (usually message capable) # -9 Primary Mobile (usually message capable)
#-10 internet, Igates, echolink, winlink, AVRS, APRN, etc # -10 internet, Igates, echolink, winlink, AVRS, APRN, etc
#-11 balloons, aircraft, spacecraft, etc # -11 balloons, aircraft, spacecraft, etc
#-12 APRStt, DTMF, RFID, devices, one-way trackers*, etc # -12 APRStt, DTMF, RFID, devices, one-way trackers*, etc
#-13 Weather stations # -13 Weather stations
#-14 Truckers or generally full time drivers # -14 Truckers or generally full time drivers
#-15 generic additional station, digi, mobile, wx, etc # -15 generic additional station, digi, mobile, wx, etc
# we need to do this step to reduce the needed paypload by the callsign ( stripping "-" out of the callsign ) # we need to do this step to reduce the needed paypload by the callsign ( stripping "-" out of the callsign )
''' '''
@ -235,6 +241,7 @@ def bytes_to_callsign(bytestring: bytes) -> bytes:
ssid = ord(bytes(decoded[-1], "utf-8")) ssid = ord(bytes(decoded[-1], "utf-8"))
return bytes(f"{callsign}-{ssid}", "utf-8") return bytes(f"{callsign}-{ssid}", "utf-8")
def check_callsign(callsign:bytes, crc_to_check:bytes): def check_callsign(callsign:bytes, crc_to_check:bytes):
""" """
Funktion to check a crc against a callsign to calculate the ssid by generating crc until we got it Funktion to check a crc against a callsign to calculate the ssid by generating crc until we got it
@ -270,9 +277,10 @@ def check_callsign(callsign:bytes, crc_to_check:bytes):
return [False, ""] return [False, ""]
def encode_grid(grid): def encode_grid(grid):
""" """
@auther: DB1UJ @author: DB1UJ
Args: Args:
grid:string: maidenhead QTH locater [a-r][a-r][0-9][0-9][a-x][a-x] grid:string: maidenhead QTH locater [a-r][a-r][0-9][0-9][a-x][a-x]
Returns: Returns:
@ -282,30 +290,30 @@ def encode_grid(grid):
grid = grid.upper() # upper case to be save grid = grid.upper() # upper case to be save
int_first = ord(grid[0]) - 65 # -65 offset for 'A' become zero, utf8 table int_first = ord(grid[0]) - 65 # -65 offset for 'A' become zero, utf8 table
int_sec = ord(grid[1]) - 65 # -65 offset for 'A' become zero, utf8 table int_sec = ord(grid[1]) - 65 # -65 offset for 'A' become zero, utf8 table
int_val = (int_first * 18) + int_sec # encode for modulo devision, 2 numbers in 1 int_val = (int_first * 18) + int_sec # encode for modulo devision, 2 numbers in 1
out_code_word = (int_val & 0b111111111) # only 9 bit LSB A - R * A - R is needed out_code_word = (int_val & 0b111111111) # only 9 bit LSB A - R * A - R is needed
out_code_word <<= 9 # shift 9 bit left having space next bits, letter A-R * A-R out_code_word <<= 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 int_val = int(grid[2:4]) # number string to number int, highest value 99
out_code_word |= (int_val & 0b1111111) # using bit OR to add new value out_code_word |= (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 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 int_val = ord(grid[4]) - 65 # -65 offset for 'A' become zero, utf8 table
out_code_word |= (int_val & 0b11111) # using bit OR to add new value out_code_word |= (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 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 int_val = ord(grid[5]) - 65 # -65 offset for 'A' become zero, utf8 table
out_code_word |= (int_val & 0b11111) # using bit OR to add new value out_code_word |= (int_val & 0b11111) # using bit OR to add new value
return out_code_word.to_bytes(length=4, byteorder='big') return out_code_word.to_bytes(length=4, byteorder='big')
def decode_grid(b_code_word:bytes): def decode_grid(b_code_word:bytes):
""" """
@auther: DB1UJ @author: DB1UJ
Args: Args:
b_code_word:bytes: 4 bytes with 26 bit valid data LSB b_code_word:bytes: 4 bytes with 26 bit valid data LSB
Returns: Returns:
@ -334,7 +342,7 @@ def decode_grid(b_code_word:bytes):
def encode_call(call): def encode_call(call):
""" """
@auther: DB1UJ @author: DB1UJ
Args: Args:
call:string: ham radio call sign [A-Z,0-9], last char SSID 0-63 call:string: ham radio call sign [A-Z,0-9], last char SSID 0-63
@ -346,18 +354,19 @@ def encode_call(call):
call = call.upper() # upper case to be save call = call.upper() # upper case to be save
for x in call: for x in call:
int_val = ord(x) - 48 # -48 reduce bits, begin with first number utf8 table int_val = ord(x) - 48 # -48 reduce bits, begin with first number utf8 table
out_code_word <<= 6 # shift left 6 bit, making space for a new char out_code_word <<= 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 |= (int_val & 0b111111) # bit OR adds the new char, masked with AND 0b111111
out_code_word >>= 6 # clean last char out_code_word >>= 6 # clean last char
out_code_word <<= 6 # make clean space out_code_word <<= 6 # make clean space
out_code_word |= (ord(call[-1]) & 0b111111) # add the SSID uncoded only 0 - 63 out_code_word |= (ord(call[-1]) & 0b111111) # add the SSID uncoded only 0 - 63
return out_code_word.to_bytes(length=6, byteorder='big') return out_code_word.to_bytes(length=6, byteorder='big')
def decode_call(b_code_word:bytes): def decode_call(b_code_word:bytes):
""" """
@auther: DB1UJ @author: DB1UJ
Args: Args:
b_code_word:bytes: 6 bytes with 6 bits/sign valid data char signs LSB b_code_word:bytes: 6 bytes with 6 bits/sign valid data char signs LSB
@ -365,13 +374,13 @@ def decode_call(b_code_word:bytes):
call:str: upper case ham radio call sign [A-Z,0-9] + binary SSID call:str: upper case ham radio call sign [A-Z,0-9] + binary SSID
""" """
code_word = int.from_bytes(b_code_word, byteorder='big', signed=False) code_word = int.from_bytes(b_code_word, byteorder='big', signed=False)
ssid = chr(code_word & 0b111111) # save the uncoded binary SSID ssid = chr(code_word & 0b111111) # save the uncoded binary SSID
call = str() call = str()
while code_word != 0: while code_word != 0:
call = chr((code_word & 0b111111)+48) + call call = chr((code_word & 0b111111)+48) + call
code_word >>= 6 code_word >>= 6
call = call[:-1] + ssid # remove the last char from call and replace with SSID call = call[:-1] + ssid # remove the last char from call and replace with SSID
return call return call