From b6face744bbe37623b8187a10e49afb6f670a58c Mon Sep 17 00:00:00 2001 From: DJ2LS <75909252+DJ2LS@users.noreply.github.com> Date: Mon, 23 May 2022 09:37:24 +0200 Subject: [PATCH] First run reducing number of problems --- tnc/audio.py | 28 +++--- tnc/codec2.py | 102 ++++++++++--------- tnc/daemon.py | 21 ++-- tnc/data_handler.py | 234 +++++++++++++++++++++----------------------- tnc/helpers.py | 123 ++++++++++++----------- 5 files changed, 259 insertions(+), 249 deletions(-) diff --git a/tnc/audio.py b/tnc/audio.py index 23c438dd..8bc66894 100644 --- a/tnc/audio.py +++ b/tnc/audio.py @@ -1,22 +1,20 @@ 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 also uses a process data manager """ - # we need to run this on windows for multiprocessing support + # we need to run this on Windows for multiprocessing support # multiprocessing.freeze_support() - #multiprocessing.get_context('spawn') + # multiprocessing.get_context('spawn') # we need to reset and initialize sounddevice before running the multiprocessing part. # If we are not doing this at this early point, not all devices will be displayed @@ -26,41 +24,41 @@ def get_audio_devices(): with multiprocessing.Manager() as manager: proxy_input_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.start() p.join() return list(proxy_input_devices), list(proxy_output_devices) + def fetch_audio_devices(input_devices, output_devices): """ get audio devices from portaudio Args: input_devices: proxy variable for input devices - output_devices: proxy variable for outout devices + output_devices: proxy variable for output devices Returns: """ devices = sd.query_devices(device=None, kind=None) for index, device in enumerate(devices): - #for i in range(0, p.get_device_count()): - # we need to do a try exception, beacuse for windows theres no audio device range + # we need to do a try exception, because for windows there's no audio device range try: name = device["name"] - maxOutputChannels = device["max_output_channels"] - maxInputChannels = device["max_input_channels"] + max_output_channels = device["max_output_channels"] + max_input_channels = device["max_input_channels"] except Exception as e: print(e) - maxInputChannels = 0 - maxOutputChannels = 0 + max_input_channels = 0 + max_output_channels = 0 name = '' - if maxInputChannels > 0: + if max_input_channels > 0: input_devices.append({"id": index, "name": name}) - if maxOutputChannels > 0: + if max_output_channels > 0: output_devices.append({"id": index, "name": name}) diff --git a/tnc/codec2.py b/tnc/codec2.py index adb9f025..ad1be938 100644 --- a/tnc/codec2.py +++ b/tnc/codec2.py @@ -22,11 +22,11 @@ class FREEDV_MODE(Enum): """ fsk_ldpc_0 = 200 fsk_ldpc_1 = 201 - fsk_ldpc = 9 - datac0 = 14 - datac1 = 10 - datac3 = 12 - allmodes = 255 + fsk_ldpc = 9 + datac0 = 14 + datac1 = 10 + datac3 = 12 + allmodes = 255 # 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 + # Function for returning the mode name 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 + # Check if we are running in a pyinstaller environment if hasattr(sys, "_MEIPASS"): sys.path.append(getattr(sys, "_MEIPASS")) @@ -62,12 +64,12 @@ else: structlog.get_logger("structlog").info("[C2 ] Searching for libcodec2...") if sys.platform == 'linux': - files = glob.glob(r'**/*libcodec2*',recursive=True) + files = glob.glob(r'**/*libcodec2*', recursive=True) files.append('libcodec2.so') 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']: - files = glob.glob(r'**\*libcodec2*.dll',recursive=True) + files = glob.glob(r'**\*libcodec2*.dll', recursive=True) else: files = [] @@ -87,8 +89,8 @@ if api is None or 'api' not in locals(): # ctypes function init -#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.restype = ctypes.c_int +# 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.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.restype = ctypes.c_int -api.FREEDV_FS_8000 = 8000 -api.FREEDV_MODE_DATAC1 = 10 -api.FREEDV_MODE_DATAC3 = 12 -api.FREEDV_MODE_DATAC0 = 14 +api.FREEDV_FS_8000 = 8000 +api.FREEDV_MODE_DATAC1 = 10 +api.FREEDV_MODE_DATAC3 = 12 +api.FREEDV_MODE_DATAC0 = 14 api.FREEDV_MODE_FSK_LDPC = 9 + # -------------------------------- FSK LDPC MODE SETTINGS # Advanced structure for fsk modes @@ -159,6 +162,7 @@ class ADVANCED(ctypes.Structure): ("codename", ctypes.c_char_p), ] + ''' adv.interleave_frames = 0 # max amplitude 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.Rs = 100 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.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.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.codename = 'H_128_256_5'.encode('utf-8') # code word # --------------- 4 H_256_512_4, 7 bytes 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.Rs = 100 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.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_NC_MAX = 50 + 1 -MODEM_STATS_NR_MAX = 160 -MODEM_STATS_ET_MAX = 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_NC_MAX = 50 + 1 +MODEM_STATS_NR_MAX = 160 +MODEM_STATS_ET_MAX = 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 class MODEMSTATS(ctypes.Structure): @@ -215,7 +220,7 @@ class MODEMSTATS(ctypes.Structure): _fields_ = [ ("Nc", ctypes.c_int), ("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), ("sync", ctypes.c_int), ("foff", ctypes.c_float), @@ -225,17 +230,18 @@ class MODEMSTATS(ctypes.Structure): ("pre", ctypes.c_int), ("post", ctypes.c_int), ("uw_fails", ctypes.c_int), - ("neyetr", ctypes.c_int), # How many eye traces are plotted - ("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 + ("neyetr", ctypes.c_int), # How many eye traces are plotted + ("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 ("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 -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_TRIAL_SYNC = 0x1 # demodulator has trial sync +api.FREEDV_RX_SYNC = 0x2 # demodulator has sync +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.rx_sync_flags_to_text = [ "----", @@ -255,6 +261,7 @@ api.rx_sync_flags_to_text = [ "EBS-", "EBST"] + # Audio buffer --------------------------------------------------------- class audio_buffer: """ @@ -262,6 +269,7 @@ class audio_buffer: made by David Rowe, VK5DGR """ + # 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): @@ -271,7 +279,7 @@ class audio_buffer: self.nbuffer = 0 self.mutex = Lock() - def push(self,samples): + def push(self, samples): """ Push new data to buffer @@ -283,12 +291,12 @@ class audio_buffer: """ self.mutex.acquire() # Add samples at the end of the buffer - assert self.nbuffer+len(samples) <= self.size - self.buffer[self.nbuffer:self.nbuffer+len(samples)] = samples + 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): + def pop(self, size): """ get data from buffer in size of NIN Args: @@ -300,18 +308,20 @@ class audio_buffer: self.mutex.acquire() # Remove samples from the start of the buffer 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 self.mutex.release() + # 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_OS_48 = 6 # oversampling rate +api.FDMDV_OS_TAPS_48K = 48 # number of OS filter taps at 48kHz +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_48_to_8_short.argtype = [ctypes.c_void_p, ctypes.c_void_p, ctypes.c_int] + class resampler: """ Re-sampler class @@ -340,7 +350,7 @@ class resampler: assert len(in48) % api.FDMDV_OS_48 == 0 # 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:] = in48 @@ -368,14 +378,14 @@ class resampler: assert in8.dtype == np.int16 # 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:] = in8 # In C: pin8=&in8_mem[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)); + 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 self.filter_mem8 = in8_mem[:self.MEM8] diff --git a/tnc/daemon.py b/tnc/daemon.py index d1729887..efc30f5e 100755 --- a/tnc/daemon.py +++ b/tnc/daemon.py @@ -15,8 +15,6 @@ import argparse import atexit import multiprocessing import os -import queue -import re import signal import socketserver import subprocess @@ -25,13 +23,11 @@ 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 @@ -51,9 +47,11 @@ def signal_handler(sig, frame): sock.CLOSE_SIGNAL = True sys.exit(0) + signal.signal(signal.SIGINT, signal_handler) -class DAEMON(): + +class DAEMON: """ Daemon class @@ -100,7 +98,7 @@ class DAEMON(): crc_hwid = crc_hwid.to_bytes(2, byteorder='big') crc_hwid = crc_hwid.hex() description = f"{desc} [{crc_hwid}]" - serial_devices.append({"port": str(port), "description": str(description) }) + serial_devices.append({"port": str(port), "description": str(description)}) static.SERIAL_DEVICES = serial_devices time.sleep(1) @@ -215,8 +213,9 @@ class DAEMON(): options.append('--tuning_range_fmax') options.append(data[20]) + # overriding FSK mode - #if data[21] == 'True': + # if data[21] == 'True': # options.append('--fsk') options.append('--tx-audio-level') @@ -304,7 +303,7 @@ class DAEMON(): 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 + # hamlib_version = rig.hamlib_version hamlib.set_ptt(True) pttstate = hamlib.get_ptt() @@ -327,10 +326,10 @@ class DAEMON(): except Exception as 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 + # we need to run this on Windows for multiprocessing support multiprocessing.freeze_support() # --------------------------------------------GET PARAMETER INPUTS @@ -354,7 +353,7 @@ if __name__ == '__main__': os.makedirs(logging_path) log_handler.setup_logging(logging_path) except Exception as e: - structlog.get_logger("structlog").error("[DMN] logger init error", exception=e) + 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) diff --git a/tnc/data_handler.py b/tnc/data_handler.py index 2c2c1594..74a9578e 100644 --- a/tnc/data_handler.py +++ b/tnc/data_handler.py @@ -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=import-outside-toplevel -import asyncio import base64 -import logging import queue import sys import threading @@ -25,7 +23,6 @@ import ujson as json import codec2 import helpers -import log_handler import modem import sock import static @@ -39,7 +36,7 @@ DATA_QUEUE_RECEIVED = queue.Queue() class DATA(): """ Terminal Node Controller for FreeDATA """ 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_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.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 = 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.rpt_request_received = False # if we received an request for repeater frames - self.rpt_request_buffer = [] # requested frames, saved in a list - 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_eof = b'EOF' # 2 bytes for the EOF End of File indicator in a data frame + self.burst_ack_snr = 0 # SNR from received ack frames + 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.rpt_request_received = False # if we received an request for repeater frames + self.rpt_request_buffer = [] # requested frames, saved in a list + 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_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.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.datachannel_timeout = False @@ -76,19 +73,19 @@ class DATA(): self.mode_list_low_bw = [14, 12] 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.time_list_high_bw = [3, 7, 8, 30] # list for time to wait for correspinding mode in seconds + 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 # mode list for selecting between low bandwith ( 500Hz ) and normal modes with higher bandwith 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.time_list = self.time_list_low_bw # list for time to wait for correspinding mode in seconds + 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 else: - 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.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.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 self.is_IRS = False @@ -101,21 +98,21 @@ class DATA(): 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_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() # 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() - 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() 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() def worker_transmit(self): @@ -204,7 +201,7 @@ class DATA(): if 50 >= frametype >= 10: # 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 snr = static.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) # 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}") # self.c_lib.freedv_set_sync(freedv, 0) @@ -339,8 +336,8 @@ class DATA(): def send_burst_ack_frame(self, snr): """ Build and send ACK frame for burst DATA frame """ - ack_frame = bytearray(14) - ack_frame[:1] = bytes([60]) + ack_frame = bytearray(14) + ack_frame[:1] = bytes([60]) ack_frame[1:4] = static.DXCALLSIGN_CRC ack_frame[4:7] = static.MYCALLSIGN_CRC ack_frame[7:8] = bytes([int(snr)]) @@ -351,8 +348,8 @@ class DATA(): def send_data_ack_frame(self, snr): """ Build and send ACK frame for received DATA frame """ - ack_frame = bytearray(14) - ack_frame[:1] = bytes([61]) + ack_frame = bytearray(14) + ack_frame[:1] = bytes([61]) ack_frame[1:4] = static.DXCALLSIGN_CRC ack_frame[4:7] = static.MYCALLSIGN_CRC 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) 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] # 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 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. # then create a repeat frame - rpt_frame = bytearray(14) - rpt_frame[:1] = bytes([62]) - rpt_frame[1:4] = static.DXCALLSIGN_CRC - rpt_frame[4:7] = static.MYCALLSIGN_CRC + rpt_frame = bytearray(14) + rpt_frame[:1] = bytes([62]) + rpt_frame[1:4] = static.DXCALLSIGN_CRC + rpt_frame[4:7] = static.MYCALLSIGN_CRC rpt_frame[7:13] = 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): """ Build and send NACK frame for received DATA frame """ - nack_frame = bytearray(14) - nack_frame[:1] = bytes([63]) + nack_frame = bytearray(14) + nack_frame[:1] = bytes([63]) nack_frame[1:4] = static.DXCALLSIGN_CRC nack_frame[4:7] = static.MYCALLSIGN_CRC nack_frame[7:8] = bytes([int(snr)]) @@ -397,8 +394,8 @@ class DATA(): def send_burst_nack_frame_watchdog(self, snr=0): """ Build and send NACK frame for watchdog timeout """ - nack_frame = bytearray(14) - nack_frame[:1] = bytes([64]) + nack_frame = bytearray(14) + nack_frame[:1] = bytes([64]) nack_frame[1:4] = static.DXCALLSIGN_CRC nack_frame[4:7] = static.MYCALLSIGN_CRC nack_frame[7:8] = bytes([int(snr)]) @@ -409,10 +406,10 @@ class DATA(): def send_disconnect_frame(self): """ Build and send a disconnect frame """ - disconnection_frame = bytearray(14) - disconnection_frame[:1] = bytes([223]) - disconnection_frame[1:4] = static.DXCALLSIGN_CRC - disconnection_frame[4:7] = static.MYCALLSIGN_CRC + disconnection_frame = bytearray(14) + disconnection_frame[:1] = bytes([223]) + disconnection_frame[1:4] = static.DXCALLSIGN_CRC + disconnection_frame[4:7] = static.MYCALLSIGN_CRC disconnection_frame[7:13] = helpers.callsign_to_bytes(self.mycallsign) 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 # 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) @@ -477,7 +474,7 @@ class DATA(): # the temp burst buffer is needed for checking, if we already recevied data temp_burst_buffer = b'' 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) # 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. # 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 # search_area --> area where we want to search 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 += temp_burst_buffer 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: static.RX_FRAME_BUFFER += temp_burst_buffer structlog.get_logger("structlog").debug("[TNC] ARQ | RX | appending data to buffer") @@ -558,10 +555,10 @@ class DATA(): if bof_position >=0: 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 - 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 = int.from_bytes(payload[8:9], "big") # 4:8 4bytes + compression_factor = np.clip(compression_factor, 0, 255) # limit to max value of 255 static.ARQ_COMPRESSION_FACTOR = compression_factor / 10 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 payload = static.RX_FRAME_BUFFER[bof_position+len(self.data_frame_bof):eof_position] # 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 - 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 # 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} json_data_out = json.dumps(jsondata) structlog.get_logger("structlog").debug("[TNC] arq_data_received:", jsondata=jsondata) - # print(jsondata) sock.SOCKET_QUEUE.put(json_data_out) 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 static.ARQ_SPEED_LEVEL = self.speed_level - TX_N_SENT_BYTES = 0 # already sent bytes per data frame - 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_FRAMES_PER_BURST = n_frames_per_burst # amount of n frames per burst + TX_N_SENT_BYTES = 0 # already sent bytes per data frame + 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_FRAMES_PER_BURST = n_frames_per_burst # amount of n frames per burst TX_BUFFER = [] # our buffer for appending new data # TIMEOUTS - BURST_ACK_TIMEOUT_SECONDS = 3.0 # timeout for burst acknowledges - DATA_FRAME_ACK_TIMEOUT_SECONDS = 3.0 # timeout for data frame acknowledges - RPT_ACK_TIMEOUT_SECONDS = 3.0 # timeout for rpt frame acknowledges + BURST_ACK_TIMEOUT_SECONDS = 3.0 # timeout for burst acknowledges + DATA_FRAME_ACK_TIMEOUT_SECONDS = 3.0 # timeout for data 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 - #static.TOTAL_BYTES = round(len(data_out) / 1024, 2) + # static.TOTAL_BYTES = round(len(data_out) / 1024, 2) static.TOTAL_BYTES = len(data_out) frame_total_size = len(data_out).to_bytes(4, byteorder='big') static.INFO.append("ARQ;TRANSMITTING") @@ -710,10 +706,10 @@ class DATA(): structlog.get_logger("structlog").debug("[TNC] FIXED MODE:", mode=data_mode) else: # 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 # 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: self.speed_level -= 1 @@ -721,7 +717,7 @@ class DATA(): self.speed_level = 0 ''' - #if self.tx_n_retry_of_burst <= 1: + # if self.tx_n_retry_of_burst <= 1: # self.speed_level += 1 # if 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! # TX_N_FRAMES_PER_BURST = 1 is working - arqheader = bytearray() - arqheader[:1] = bytes([10]) #bytes([10 + i]) + arqheader = bytearray() + arqheader[:1] = bytes([10]) # bytes([10 + i]) arqheader[1:2] = bytes([TX_N_FRAMES_PER_BURST]) arqheader[2:5] = static.DXCALLSIGN_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: 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 (self.burst_ack or self.burst_nack or self.rpt_request_received or self.data_frame_ack_received)): @@ -800,22 +796,22 @@ class DATA(): if self.burst_ack: self.burst_ack = False # reset ack state self.tx_n_retry_of_burst = 0 # reset retries - break #break retry loop + break # break retry loop if self.burst_nack: - self.burst_nack = False #reset nack state + self.burst_nack = False # reset nack state # not yet implemented if self.rpt_request_received: pass if self.data_frame_ack_received: - break #break retry loop + break # break retry loop # we need this part for leaving the repeat loop # static.ARQ_STATE == 'DATA' --> when stopping transmission manually if not static.ARQ_STATE: - #print("not ready for data...leaving loop....") + # print("not ready for data...leaving loop....") break 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) sock.SOCKET_QUEUE.put(json_data_out) - #GOING TO NEXT ITERATION + # GOING TO NEXT ITERATION if self.data_frame_ack_received: static.INFO.append("ARQ;TRANSMITTING;SUCCESS") @@ -870,8 +866,8 @@ class DATA(): """ # increase speed level if we received a burst ack - #self.speed_level += 1 - #if self.speed_level >= len(self.mode_list)-1: + # self.speed_level += 1 + # if 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 @@ -900,8 +896,8 @@ class DATA(): """ # increase speed level if we received a burst ack - #self.speed_level += 1 - #if self.speed_level >= len(self.mode_list)-1: + # self.speed_level += 1 + # if 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 @@ -980,7 +976,7 @@ class DATA(): 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 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 static.ARQ_SESSION_STATE = 'connecting' - connection_frame = bytearray(14) - connection_frame[:1] = bytes([221]) - connection_frame[1:4] = static.DXCALLSIGN_CRC - connection_frame[4:7] = static.MYCALLSIGN_CRC + connection_frame = bytearray(14) + connection_frame[:1] = bytes([221]) + connection_frame[1:4] = static.DXCALLSIGN_CRC + connection_frame[4:7] = static.MYCALLSIGN_CRC connection_frame[7:13] = helpers.callsign_to_bytes(self.mycallsign) while not static.ARQ_SESSION: @@ -1028,7 +1024,6 @@ class DATA(): time.sleep(0.01) # break if data channel is opened if static.ARQ_SESSION: - # eventuell einfach nur return true um die nächste break ebene zu vermeiden? return True # if static.ARQ_SESSION: # break @@ -1205,11 +1200,11 @@ class DATA(): structlog.get_logger("structlog").debug("[TNC] Requesting manual mode --> not yet implemented ") frametype = bytes([mode]) - connection_frame = bytearray(14) - connection_frame[:1] = frametype - connection_frame[1:4] = static.DXCALLSIGN_CRC - connection_frame[4:7] = static.MYCALLSIGN_CRC - connection_frame[7:13] = helpers.callsign_to_bytes(mycallsign) + connection_frame = bytearray(14) + connection_frame[:1] = frametype + connection_frame[1:4] = static.DXCALLSIGN_CRC + connection_frame[4:7] = static.MYCALLSIGN_CRC + connection_frame[7:13] = helpers.callsign_to_bytes(mycallsign) connection_frame[13:14] = bytes([n_frames_per_burst]) while not static.ARQ_STATE: @@ -1233,7 +1228,6 @@ class DATA(): if attempt == self.data_channel_max_retries: static.INFO.append("DATACHANNEL;FAILED") 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} json_data_out = json.dumps(jsondata) 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") 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: self.received_low_bandwith_mode = False self.mode_list = self.mode_list_high_bw @@ -1310,11 +1304,11 @@ class DATA(): frametype = bytes([226]) structlog.get_logger("structlog").debug("[TNC] Responding with high bandwidth mode") - connection_frame = bytearray(14) - connection_frame[:1] = frametype - connection_frame[1:4] = static.DXCALLSIGN_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 = bytearray(14) + connection_frame[:1] = frametype + connection_frame[1:4] = static.DXCALLSIGN_CRC + connection_frame[4:7] = static.MYCALLSIGN_CRC + connection_frame[13:14] = bytes([static.ARQ_PROTOCOL_VERSION]) # crc8 of version for checking protocol version self.enqueue_frame_for_tx(connection_frame) @@ -1380,10 +1374,10 @@ class DATA(): static.INFO.append("PING;SENDING") structlog.get_logger("structlog").info("[TNC] PING REQ [" + str(self.mycallsign, 'utf-8') + "] >>> [" + str(static.DXCALLSIGN, 'utf-8') + "]" ) - ping_frame = bytearray(14) - ping_frame[:1] = bytes([210]) - ping_frame[1:4] = static.DXCALLSIGN_CRC - ping_frame[4:7] = static.MYCALLSIGN_CRC + ping_frame = bytearray(14) + ping_frame[:1] = bytes([210]) + ping_frame[1:4] = static.DXCALLSIGN_CRC + ping_frame[4:7] = static.MYCALLSIGN_CRC ping_frame[7:13] = helpers.callsign_to_bytes(self.mycallsign) 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 ) - ping_frame = bytearray(14) - ping_frame[:1] = bytes([211]) + ping_frame = bytearray(14) + ping_frame[:1] = bytes([211]) ping_frame[1:4] = static.DXCALLSIGN_CRC ping_frame[4:7] = static.MYCALLSIGN_CRC ping_frame[7:13] = static.MYGRID @@ -1458,11 +1452,11 @@ class DATA(): Force a stop of the running transmission """ structlog.get_logger("structlog").warning("[TNC] Stopping transmission!") - stop_frame = bytearray(14) - stop_frame[:1] = bytes([249]) + stop_frame = bytearray(14) + stop_frame[:1] = bytes([249]) stop_frame[1:4] = static.DXCALLSIGN_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) @@ -1486,7 +1480,7 @@ class DATA(): """ Controlling funktion for running a beacon Args: - interval:int: + self: Returns: @@ -1551,9 +1545,9 @@ class DATA(): structlog.get_logger("structlog").info("[TNC] CQ CQ CQ") static.INFO.append("CQ;SENDING") - cq_frame = bytearray(14) - cq_frame[:1] = bytes([200]) - cq_frame[1:7] = helpers.callsign_to_bytes(self.mycallsign) + cq_frame = bytearray(14) + cq_frame[:1] = bytes([200]) + cq_frame[1:7] = helpers.callsign_to_bytes(self.mycallsign) cq_frame[7:11] = helpers.encode_grid(static.MYGRID.decode("utf-8")) structlog.get_logger("structlog").info("[TNC] ENABLE FSK", state=static.ENABLE_FSK) @@ -1602,9 +1596,9 @@ class DATA(): static.INFO.append("QRV;SENDING") structlog.get_logger("structlog").info("[TNC] Sending QRV!") - qrv_frame = bytearray(14) - qrv_frame[:1] = bytes([201]) - qrv_frame[1:7] = helpers.callsign_to_bytes(self.mycallsign) + qrv_frame = bytearray(14) + qrv_frame[:1] = bytes([201]) + qrv_frame[1:7] = helpers.callsign_to_bytes(self.mycallsign) qrv_frame[7:11] = helpers.encode_grid(static.MYGRID.decode("utf-8")) 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: """ - Calcualte Transferrate for transmission + Calculate transfer rate for transmission Args: tx_start_of_transmission:float: sentbytes:int: @@ -1701,8 +1695,8 @@ class DATA(): transmissiontime = time.time() - tx_start_of_transmission if sentbytes > 0: - 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_BITS_PER_SECOND = int((sentbytes * 8) / transmissiontime) # Bits per Second + static.ARQ_BYTES_PER_MINUTE = int((sentbytes) / (transmissiontime / 60)) # Bytes per Minute else: static.ARQ_BITS_PER_SECOND = 0 @@ -1739,7 +1733,7 @@ class DATA(): # reset modem receiving state to reduce cpu load modem.RECEIVE_DATAC1 = False modem.RECEIVE_DATAC3 = False - #modem.RECEIVE_FSK_LDPC_0 = False + # modem.RECEIVE_FSK_LDPC_0 = False modem.RECEIVE_FSK_LDPC_1 = False # reset buffer overflow counter @@ -1842,8 +1836,8 @@ class DATA(): self.burst_nack_counter += 1 if self.burst_nack_counter >= 2: self.speed_level -= 1 - #print(self.burst_nack_counter) - #print(self.speed_level) + # print(self.burst_nack_counter) + # print(self.speed_level) static.ARQ_SPEED_LEVEL = self.speed_level self.burst_nack_counter = 0 if self.speed_level <= 0: @@ -1873,8 +1867,8 @@ class DATA(): time.sleep(0.01) if self.data_channel_last_received + self.transmission_timeout > time.time(): time.sleep(0.01) - #print(self.data_channel_last_received + self.transmission_timeout - time.time()) - #pass + # print(self.data_channel_last_received + self.transmission_timeout - time.time()) + # pass else: self.data_channel_last_received = 0 structlog.get_logger("structlog").info("[TNC] DATA [" + str(self.mycallsign, 'utf-8') + "]<>[" + str(static.DXCALLSIGN, 'utf-8') + "]") diff --git a/tnc/helpers.py b/tnc/helpers.py index 00378f7d..57697f8a 100644 --- a/tnc/helpers.py +++ b/tnc/helpers.py @@ -27,6 +27,7 @@ def wait(seconds: float) -> bool: time.sleep(0.01) return True + def get_crc_8(data) -> bytes: """Author: DJ2LS @@ -45,6 +46,7 @@ def get_crc_8(data) -> bytes: crc_data = crc_data.to_bytes(1, byteorder='big') return crc_data + def get_crc_16(data) -> bytes: """Author: DJ2LS @@ -63,6 +65,7 @@ def get_crc_16(data) -> bytes: crc_data = crc_data.to_bytes(2, byteorder='big') return crc_data + def get_crc_24(data) -> bytes: """Author: DJ2LS @@ -84,6 +87,7 @@ def get_crc_24(data) -> bytes: crc_data = crc_data.to_bytes(3, byteorder='big') return crc_data + def get_crc_32(data: bytes) -> bytes: """Author: DJ2LS @@ -102,6 +106,7 @@ def get_crc_32(data: bytes) -> bytes: crc_data = crc_data.to_bytes(4, byteorder='big') return crc_data + 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())] # static.HEARD_STATIONS[idx] = item + def callsign_to_bytes(callsign) -> bytes: """ @@ -146,22 +152,22 @@ def callsign_to_bytes(callsign) -> bytes: """ # http://www.aprs.org/aprs11/SSIDs.txt - #-0 Your primary station usually fixed and message capable - #-1 generic additional station, digi, mobile, wx, etc - #-2 generic additional station, digi, mobile, wx, etc - #-3 generic additional station, digi, mobile, wx, etc - #-4 generic additional station, digi, mobile, wx, etc - #-5 Other networks (Dstar, Iphones, Androids, Blackberry's etc) - #-6 Special activity, Satellite ops, camping or 6 meters, etc - #-7 walkie talkies, HT's or other human portable - #-8 boats, sailboats, RV's or second main mobile - #-9 Primary Mobile (usually message capable) - #-10 internet, Igates, echolink, winlink, AVRS, APRN, etc - #-11 balloons, aircraft, spacecraft, etc - #-12 APRStt, DTMF, RFID, devices, one-way trackers*, etc - #-13 Weather stations - #-14 Truckers or generally full time drivers - #-15 generic additional station, digi, mobile, wx, etc + # -0 Your primary station usually fixed and message capable + # -1 generic additional station, digi, mobile, wx, etc + # -2 generic additional station, digi, mobile, wx, etc + # -3 generic additional station, digi, mobile, wx, etc + # -4 generic additional station, digi, mobile, wx, etc + # -5 Other networks (Dstar, Iphones, Androids, Blackberry's etc) + # -6 Special activity, Satellite ops, camping or 6 meters, etc + # -7 walkie talkies, HT's or other human portable + # -8 boats, sailboats, RV's or second main mobile + # -9 Primary Mobile (usually message capable) + # -10 internet, Igates, echolink, winlink, AVRS, APRN, etc + # -11 balloons, aircraft, spacecraft, etc + # -12 APRStt, DTMF, RFID, devices, one-way trackers*, etc + # -13 Weather stations + # -14 Truckers or generally full time drivers + # -15 generic additional station, digi, mobile, wx, etc # Try converting to bytestring if possible type string try: @@ -178,16 +184,16 @@ def callsign_to_bytes(callsign) -> bytes: 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) - #bytestring[:len(callsign)] = callsign - #bytestring[7:8] = bytes([ssid]) + # callsign = callsign[0] + # bytestring = bytearray(8) + # bytestring[:len(callsign)] = callsign + # bytestring[7:8] = bytes([ssid]) # ---- callsign with encoding always 6 bytes long callsign = callsign[0].decode("utf-8") ssid = bytes([ssid]).decode("utf-8") return encode_call(callsign + ssid) - #return bytes(bytestring) + # return bytes(bytestring) def bytes_to_callsign(bytestring: bytes) -> bytes: """ @@ -200,22 +206,22 @@ def bytes_to_callsign(bytestring: bytes) -> bytes: bytes """ # http://www.aprs.org/aprs11/SSIDs.txt - #-0 Your primary station usually fixed and message capable - #-1 generic additional station, digi, mobile, wx, etc - #-2 generic additional station, digi, mobile, wx, etc - #-3 generic additional station, digi, mobile, wx, etc - #-4 generic additional station, digi, mobile, wx, etc - #-5 Other networks (Dstar, Iphones, Androids, Blackberry's etc) - #-6 Special activity, Satellite ops, camping or 6 meters, etc - #-7 walkie talkies, HT's or other human portable - #-8 boats, sailboats, RV's or second main mobile - #-9 Primary Mobile (usually message capable) - #-10 internet, Igates, echolink, winlink, AVRS, APRN, etc - #-11 balloons, aircraft, spacecraft, etc - #-12 APRStt, DTMF, RFID, devices, one-way trackers*, etc - #-13 Weather stations - #-14 Truckers or generally full time drivers - #-15 generic additional station, digi, mobile, wx, etc + # -0 Your primary station usually fixed and message capable + # -1 generic additional station, digi, mobile, wx, etc + # -2 generic additional station, digi, mobile, wx, etc + # -3 generic additional station, digi, mobile, wx, etc + # -4 generic additional station, digi, mobile, wx, etc + # -5 Other networks (Dstar, Iphones, Androids, Blackberry's etc) + # -6 Special activity, Satellite ops, camping or 6 meters, etc + # -7 walkie talkies, HT's or other human portable + # -8 boats, sailboats, RV's or second main mobile + # -9 Primary Mobile (usually message capable) + # -10 internet, Igates, echolink, winlink, AVRS, APRN, etc + # -11 balloons, aircraft, spacecraft, etc + # -12 APRStt, DTMF, RFID, devices, one-way trackers*, etc + # -13 Weather stations + # -14 Truckers or generally full time drivers + # -15 generic additional station, digi, mobile, wx, etc # we need to do this step to reduce the needed paypload by the callsign ( stripping "-" out of the callsign ) ''' @@ -235,6 +241,7 @@ def bytes_to_callsign(bytestring: bytes) -> bytes: ssid = ord(bytes(decoded[-1], "utf-8")) return bytes(f"{callsign}-{ssid}", "utf-8") + 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 @@ -270,9 +277,10 @@ def check_callsign(callsign:bytes, crc_to_check:bytes): return [False, ""] + def encode_grid(grid): """ - @auther: DB1UJ + @author: DB1UJ Args: grid:string: maidenhead QTH locater [a-r][a-r][0-9][0-9][a-x][a-x] Returns: @@ -282,30 +290,30 @@ def encode_grid(grid): grid = grid.upper() # upper case to be save - int_first = ord(grid[0]) - 65 # -65 offset for 'A' become zero, utf8 table - int_sec = ord(grid[1]) - 65 # -65 offset for 'A' become zero, utf8 table + int_first = ord(grid[0]) - 65 # -65 offset for 'A' become zero, utf8 table + int_sec = ord(grid[1]) - 65 # -65 offset for 'A' become zero, utf8 table int_val = (int_first * 18) + int_sec # encode for modulo devision, 2 numbers in 1 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 <<= 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 <<= 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 return out_code_word.to_bytes(length=4, byteorder='big') def decode_grid(b_code_word:bytes): """ - @auther: DB1UJ + @author: DB1UJ Args: b_code_word:bytes: 4 bytes with 26 bit valid data LSB Returns: @@ -334,7 +342,7 @@ def decode_grid(b_code_word:bytes): def encode_call(call): """ - @auther: DB1UJ + @author: DB1UJ Args: 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 for x in call: - int_val = ord(x) - 48 # -48 reduce bits, begin with first number utf8 table - out_code_word <<= 6 # shift left 6 bit, making space for a new char - out_code_word |= (int_val & 0b111111) # bit OR adds the new char, masked with AND 0b111111 - out_code_word >>= 6 # clean last char - out_code_word <<= 6 # make clean space - out_code_word |= (ord(call[-1]) & 0b111111) # add the SSID uncoded only 0 - 63 + int_val = ord(x) - 48 # -48 reduce bits, begin with first number utf8 table + out_code_word <<= 6 # shift left 6 bit, making space for a new char + out_code_word |= (int_val & 0b111111) # bit OR adds the new char, masked with AND 0b111111 + out_code_word >>= 6 # clean last char + out_code_word <<= 6 # make clean space + out_code_word |= (ord(call[-1]) & 0b111111) # add the SSID uncoded only 0 - 63 return out_code_word.to_bytes(length=6, byteorder='big') + def decode_call(b_code_word:bytes): """ - @auther: DB1UJ + @author: DB1UJ Args: 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 """ 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() while code_word != 0: call = chr((code_word & 0b111111)+48) + call code_word >>= 6 - call = call[:-1] + ssid # remove the last char from call and replace with SSID + call = call[:-1] + ssid # remove the last char from call and replace with SSID return call