mirror of
https://github.com/DJ2LS/FreeDATA
synced 2024-05-14 08:04:33 +00:00
First run reducing number of problems
This commit is contained in:
parent
9f8f17b633
commit
b6face744b
28
tnc/audio.py
28
tnc/audio.py
|
@ -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})
|
||||||
|
|
102
tnc/codec2.py
102
tnc/codec2.py
|
@ -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]
|
||||||
|
|
|
@ -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)
|
||||||
|
|
|
@ -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') + "]")
|
||||||
|
|
123
tnc/helpers.py
123
tnc/helpers.py
|
@ -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
|
||||||
|
|
Loading…
Reference in a new issue