mirror of
https://github.com/DJ2LS/FreeDATA
synced 2024-05-14 08:04:33 +00:00
Merge branch 'main' into qm-guiabout
This commit is contained in:
commit
16ddbfc60a
2
.github/workflows/ctest.yml
vendored
2
.github/workflows/ctest.yml
vendored
|
@ -21,7 +21,7 @@ jobs:
|
|||
- python-version: "3.9"
|
||||
- python-version: "3.10"
|
||||
- python-version: "3.11"
|
||||
- python-version: "3.12-dev"
|
||||
#- python-version: "3.12-dev"
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
|
|
|
@ -7,10 +7,7 @@ for c in colormaps:
|
|||
cmap_name = c
|
||||
cmap = plt.get_cmap(cmap_name)
|
||||
|
||||
colors = []
|
||||
for i in range(256):
|
||||
colors.append([int(round(255 * x)) for x in cmap(i)[:3]])
|
||||
|
||||
colors = [[int(round(255 * x)) for x in cmap(i)[:3]] for i in range(256)]
|
||||
print(f'var {c} = {colors}')
|
||||
|
||||
print(f'var colormaps = [{", ".join(colormaps)}];')
|
||||
|
|
30
test/ping.py
30
test/ping.py
|
@ -85,11 +85,11 @@ def receive():
|
|||
bytes_out = ctypes.c_ubyte * bytes_per_frame # bytes_per_frame
|
||||
bytes_out = bytes_out() # get pointer from bytes_out
|
||||
|
||||
total_n_bytes = 0
|
||||
rx_total_frames = 0
|
||||
rx_frames = 0
|
||||
rx_bursts = 0
|
||||
receive = True
|
||||
total_n_bytes = 0
|
||||
while receive:
|
||||
time.sleep(0.01)
|
||||
|
||||
|
@ -97,7 +97,7 @@ def receive():
|
|||
nin_converted = int(nin * (AUDIO_SAMPLE_RATE_RX / MODEM_SAMPLE_RATE))
|
||||
if DEBUGGING_MODE:
|
||||
print("-----------------------------")
|
||||
print("NIN: " + str(nin) + " [ " + str(nin_converted) + " ]")
|
||||
print(f"NIN: {str(nin)} [ {nin_converted} ]")
|
||||
|
||||
data_in = stream_rx.read(nin_converted, exception_on_overflow=False)
|
||||
data_in = data_in.rstrip(b"\x00")
|
||||
|
@ -110,7 +110,7 @@ def receive():
|
|||
nbytes = c_lib.freedv_rawdatarx(freedv, bytes_out, data_in) # demodulate audio
|
||||
total_n_bytes = total_n_bytes + nbytes
|
||||
if DEBUGGING_MODE:
|
||||
print("SYNC: " + str(c_lib.freedv_get_rx_status(freedv)))
|
||||
print(f"SYNC: {str(c_lib.freedv_get_rx_status(freedv))}")
|
||||
|
||||
if nbytes == bytes_per_frame:
|
||||
rx_total_frames = rx_total_frames + 1
|
||||
|
@ -127,15 +127,7 @@ def receive():
|
|||
n_total_frame = bytes_out[3]
|
||||
|
||||
print(
|
||||
"RX | PONG | BURST ["
|
||||
+ str(burst)
|
||||
+ "/"
|
||||
+ str(n_total_burst)
|
||||
+ "] FRAME ["
|
||||
+ str(frame)
|
||||
+ "/"
|
||||
+ str(n_total_frame)
|
||||
+ "]"
|
||||
f"RX | PONG | BURST [{str(burst)}/{str(n_total_burst)}] FRAME [{str(frame)}/{str(n_total_frame)}]"
|
||||
)
|
||||
print("-----------------------------------------------------------------")
|
||||
c_lib.freedv_set_sync(freedv, 0)
|
||||
|
@ -151,7 +143,6 @@ RECEIVE.start()
|
|||
c_lib.freedv_open.restype = ctypes.POINTER(ctypes.c_ubyte)
|
||||
freedv = c_lib.freedv_open(FREEDV_TX_MODE)
|
||||
bytes_per_frame = int(c_lib.freedv_get_bits_per_modem_frame(freedv) / 8)
|
||||
payload_per_frame = bytes_per_frame - 2
|
||||
n_nom_modem_samples = c_lib.freedv_get_n_nom_modem_samples(freedv)
|
||||
n_tx_modem_samples = c_lib.freedv_get_n_tx_modem_samples(
|
||||
freedv
|
||||
|
@ -165,9 +156,10 @@ mod_out_preamble = ctypes.c_short * (
|
|||
mod_out_preamble = mod_out_preamble()
|
||||
|
||||
|
||||
print("BURSTS: " + str(N_BURSTS) + " FRAMES: " + str(N_FRAMES_PER_BURST))
|
||||
print(f"BURSTS: {str(N_BURSTS)} FRAMES: {str(N_FRAMES_PER_BURST)}")
|
||||
print("-----------------------------------------------------------------")
|
||||
|
||||
payload_per_frame = bytes_per_frame - 2
|
||||
for i in range(N_BURSTS):
|
||||
|
||||
c_lib.freedv_rawdatapreambletx(freedv, mod_out_preamble)
|
||||
|
@ -204,15 +196,7 @@ for i in range(N_BURSTS):
|
|||
txbuffer += bytes(mod_out)
|
||||
|
||||
print(
|
||||
"TX | PING | BURST ["
|
||||
+ str(i + 1)
|
||||
+ "/"
|
||||
+ str(N_BURSTS)
|
||||
+ "] FRAME ["
|
||||
+ str(n + 1)
|
||||
+ "/"
|
||||
+ str(N_FRAMES_PER_BURST)
|
||||
+ "]"
|
||||
f"TX | PING | BURST [{str(i + 1)}/{str(N_BURSTS)}] FRAME [{str(n + 1)}/{str(N_FRAMES_PER_BURST)}]"
|
||||
)
|
||||
stream_tx.write(bytes(txbuffer))
|
||||
ACK_TIMEOUT = time.time() + 3
|
||||
|
|
24
test/pong.py
24
test/pong.py
|
@ -81,7 +81,7 @@ c_lib.freedv_open.restype = ctypes.POINTER(ctypes.c_ubyte)
|
|||
def send_pong(burst,n_total_burst,frame,n_total_frame):
|
||||
|
||||
data_out = bytearray()
|
||||
data_out[0:1] = bytes([burst])
|
||||
data_out[:1] = bytes([burst])
|
||||
data_out[1:2] = bytes([n_total_burst])
|
||||
data_out[2:3] = bytes([frame])
|
||||
data_out[4:5] = bytes([n_total_frame])
|
||||
|
@ -139,7 +139,7 @@ while receive:
|
|||
nin_converted = int(nin*(AUDIO_SAMPLE_RATE_RX/MODEM_SAMPLE_RATE))
|
||||
if DEBUGGING_MODE:
|
||||
print("-----------------------------")
|
||||
print("NIN: " + str(nin) + " [ " + str(nin_converted) + " ]")
|
||||
print(f"NIN: {str(nin)} [ {nin_converted} ]")
|
||||
|
||||
data_in = stream_rx.read(nin_converted, exception_on_overflow = False)
|
||||
data_in = data_in.rstrip(b'\x00')
|
||||
|
@ -148,17 +148,19 @@ while receive:
|
|||
nbytes = c_lib.freedv_rawdatarx(freedv, bytes_out, data_in) # demodulate audio
|
||||
|
||||
if DEBUGGING_MODE:
|
||||
print("SYNC: " + str(c_lib.freedv_get_rx_status(freedv)))
|
||||
print(f"SYNC: {str(c_lib.freedv_get_rx_status(freedv))}")
|
||||
|
||||
if nbytes == bytes_per_frame:
|
||||
|
||||
burst = bytes_out[0]
|
||||
n_total_burst = bytes_out[1]
|
||||
frame = bytes_out[2]
|
||||
n_total_frame = bytes_out[3]
|
||||
print("RX | BURST [" + str(burst) + "/" + str(n_total_burst) + "] FRAME [" + str(frame) + "/" + str(n_total_frame) + "] >>> SENDING PONG")
|
||||
burst = bytes_out[0]
|
||||
n_total_burst = bytes_out[1]
|
||||
frame = bytes_out[2]
|
||||
n_total_frame = bytes_out[3]
|
||||
print(
|
||||
f"RX | BURST [{str(burst)}/{str(n_total_burst)}] FRAME [{str(frame)}/{str(n_total_frame)}] >>> SENDING PONG"
|
||||
)
|
||||
|
||||
TRANSMIT_PONG = threading.Thread(target=send_pong, args=[burst,n_total_burst,frame,n_total_frame], name="SEND PONG")
|
||||
TRANSMIT_PONG.start()
|
||||
TRANSMIT_PONG = threading.Thread(target=send_pong, args=[burst,n_total_burst,frame,n_total_frame], name="SEND PONG")
|
||||
TRANSMIT_PONG.start()
|
||||
|
||||
c_lib.freedv_set_sync(freedv,0)
|
||||
c_lib.freedv_set_sync(freedv,0)
|
||||
|
|
|
@ -77,10 +77,12 @@ class Test:
|
|||
self.p = pyaudio.PyAudio()
|
||||
# auto search for loopback devices
|
||||
if self.AUDIO_INPUT_DEVICE == -2:
|
||||
loopback_list = []
|
||||
for dev in range(self.p.get_device_count()):
|
||||
if "Loopback: PCM" in self.p.get_device_info_by_index(dev)["name"]:
|
||||
loopback_list.append(dev)
|
||||
loopback_list = [
|
||||
dev
|
||||
for dev in range(self.p.get_device_count())
|
||||
if "Loopback: PCM"
|
||||
in self.p.get_device_info_by_index(dev)["name"]
|
||||
]
|
||||
if len(loopback_list) >= 2:
|
||||
# 0 = RX 1 = TX
|
||||
self.AUDIO_INPUT_DEVICE = loopback_list[0]
|
||||
|
@ -287,7 +289,7 @@ class Test:
|
|||
|
||||
def run_audio(self):
|
||||
try:
|
||||
print(f"starting pyaudio callback", file=sys.stderr)
|
||||
print("starting pyaudio callback", file=sys.stderr)
|
||||
self.stream_rx.start_stream()
|
||||
except Exception as e:
|
||||
print(f"pyAudio error: {e}", file=sys.stderr)
|
||||
|
|
|
@ -219,7 +219,7 @@ class Test:
|
|||
|
||||
def run_audio(self):
|
||||
try:
|
||||
print(f"starting pyaudio callback", file=sys.stderr)
|
||||
print("starting pyaudio callback", file=sys.stderr)
|
||||
self.stream_rx.start_stream()
|
||||
except Exception as e:
|
||||
print(f"pyAudio error: {e}", file=sys.stderr)
|
||||
|
|
|
@ -138,7 +138,7 @@ class Test:
|
|||
|
||||
def run_audio(self):
|
||||
try:
|
||||
print(f"starting pyaudio callback", file=sys.stderr)
|
||||
print("starting pyaudio callback", file=sys.stderr)
|
||||
self.stream_tx.start_stream()
|
||||
except Exception as e:
|
||||
print(f"pyAudio error: {e}", file=sys.stderr)
|
||||
|
|
|
@ -81,10 +81,12 @@ class Test:
|
|||
self.p = pyaudio.PyAudio()
|
||||
# auto search for loopback devices
|
||||
if self.AUDIO_INPUT_DEVICE == -2:
|
||||
loopback_list = []
|
||||
for dev in range(self.p.get_device_count()):
|
||||
if "Loopback: PCM" in self.p.get_device_info_by_index(dev)["name"]:
|
||||
loopback_list.append(dev)
|
||||
loopback_list = [
|
||||
dev
|
||||
for dev in range(self.p.get_device_count())
|
||||
if "Loopback: PCM"
|
||||
in self.p.get_device_info_by_index(dev)["name"]
|
||||
]
|
||||
if len(loopback_list) >= 2:
|
||||
self.AUDIO_INPUT_DEVICE = loopback_list[0] # 0 = RX 1 = TX
|
||||
print(f"loopback_list rx: {loopback_list}", file=sys.stderr)
|
||||
|
@ -185,7 +187,7 @@ class Test:
|
|||
|
||||
def run_audio(self):
|
||||
try:
|
||||
print(f"starting pyaudio callback", file=sys.stderr)
|
||||
print("starting pyaudio callback", file=sys.stderr)
|
||||
self.stream_rx.start_stream()
|
||||
except Exception as e:
|
||||
print(f"pyAudio error: {e}", file=sys.stderr)
|
||||
|
|
|
@ -81,10 +81,12 @@ class Test:
|
|||
self.p = pyaudio.PyAudio()
|
||||
# auto search for loopback devices
|
||||
if self.AUDIO_INPUT_DEVICE == -2:
|
||||
loopback_list = []
|
||||
for dev in range(self.p.get_device_count()):
|
||||
if "Loopback: PCM" in self.p.get_device_info_by_index(dev)["name"]:
|
||||
loopback_list.append(dev)
|
||||
loopback_list = [
|
||||
dev
|
||||
for dev in range(self.p.get_device_count())
|
||||
if "Loopback: PCM"
|
||||
in self.p.get_device_info_by_index(dev)["name"]
|
||||
]
|
||||
if len(loopback_list) >= 2:
|
||||
self.AUDIO_INPUT_DEVICE = loopback_list[0] # 0 = RX 1 = TX
|
||||
print(f"loopback_list rx: {loopback_list}", file=sys.stderr)
|
||||
|
@ -147,7 +149,7 @@ class Test:
|
|||
|
||||
def run_audio(self):
|
||||
try:
|
||||
print(f"starting pyaudio callback", file=sys.stderr)
|
||||
print("starting pyaudio callback", file=sys.stderr)
|
||||
self.stream_rx.start_stream()
|
||||
except Exception as e:
|
||||
print(f"pyAudio error: {e}", file=sys.stderr)
|
||||
|
|
|
@ -87,10 +87,12 @@ class Test:
|
|||
self.p = pyaudio.PyAudio()
|
||||
# auto search for loopback devices
|
||||
if self.AUDIO_OUTPUT_DEVICE == -2:
|
||||
loopback_list = []
|
||||
for dev in range( self.p.get_device_count()):
|
||||
if "Loopback: PCM" in self.p.get_device_info_by_index(dev)["name"]:
|
||||
loopback_list.append(dev)
|
||||
loopback_list = [
|
||||
dev
|
||||
for dev in range(self.p.get_device_count())
|
||||
if "Loopback: PCM"
|
||||
in self.p.get_device_info_by_index(dev)["name"]
|
||||
]
|
||||
if len(loopback_list) >= 2:
|
||||
self.AUDIO_OUTPUT_DEVICE = loopback_list[0] # 0 = RX 1 = TX
|
||||
print(f"loopback_list rx: {loopback_list}", file=sys.stderr)
|
||||
|
@ -148,7 +150,7 @@ class Test:
|
|||
|
||||
def run_audio(self):
|
||||
try:
|
||||
print(f"starting pyaudio callback", file=sys.stderr)
|
||||
print("starting pyaudio callback", file=sys.stderr)
|
||||
self.stream_tx.start_stream()
|
||||
except Exception as e:
|
||||
print(f"pyAudio error: {e}", file=sys.stderr)
|
||||
|
|
|
@ -39,6 +39,10 @@ def t_setup(
|
|||
):
|
||||
# Disable data_handler testmode - This is required to test a conversation.
|
||||
data_handler.TESTMODE = False
|
||||
|
||||
# Enable socket testmode for overriding socket class
|
||||
sock.TESTMODE = True
|
||||
|
||||
modem.RXCHANNEL = tmp_path / rx_channel
|
||||
modem.TESTMODE = True
|
||||
modem.TXCHANNEL = tmp_path / tx_channel
|
||||
|
@ -147,9 +151,9 @@ def t_datac0_1(
|
|||
log.debug("t_datac0_1: STOP test, setting TNC state")
|
||||
static.TNC_STATE = "BUSY"
|
||||
static.ARQ_STATE = True
|
||||
sock.process_tnc_commands(json.dumps(data, indent=None))
|
||||
sock.ThreadedTCPRequestHandler.process_tnc_commands(None,json.dumps(data, indent=None))
|
||||
time.sleep(0.5)
|
||||
sock.process_tnc_commands(json.dumps(data, indent=None))
|
||||
sock.ThreadedTCPRequestHandler.process_tnc_commands(None,json.dumps(data, indent=None))
|
||||
|
||||
# Assure the test completes.
|
||||
timeout = time.time() + timeout_duration
|
||||
|
@ -167,7 +171,7 @@ def t_datac0_1(
|
|||
# override ARQ SESSION STATE for allowing disconnect command
|
||||
static.ARQ_SESSION_STATE = "connected"
|
||||
data = {"type": "arq", "command": "disconnect", "dxcallsign": dxcall}
|
||||
sock.process_tnc_commands(json.dumps(data, indent=None))
|
||||
sock.ThreadedTCPRequestHandler.process_tnc_commands(None,json.dumps(data, indent=None))
|
||||
time.sleep(0.5)
|
||||
|
||||
# Allow enough time for this side to process the disconnect frame.
|
||||
|
@ -260,8 +264,8 @@ def t_datac0_2(
|
|||
|
||||
if "cq" in data:
|
||||
t_data = {"type": "arq", "command": "stop_transmission"}
|
||||
sock.process_tnc_commands(json.dumps(t_data, indent=None))
|
||||
sock.process_tnc_commands(json.dumps(t_data, indent=None))
|
||||
sock.ThreadedTCPRequestHandler.process_tnc_commands(None,json.dumps(t_data, indent=None))
|
||||
sock.ThreadedTCPRequestHandler.process_tnc_commands(None,json.dumps(t_data, indent=None))
|
||||
|
||||
# Assure the test completes.
|
||||
timeout = time.time() + timeout_duration
|
||||
|
|
|
@ -33,6 +33,10 @@ def t_setup(
|
|||
):
|
||||
# Disable data_handler testmode - This is required to test a conversation.
|
||||
data_handler.TESTMODE = False
|
||||
|
||||
# Enable socket testmode for overriding socket class
|
||||
sock.TESTMODE = True
|
||||
|
||||
modem.RXCHANNEL = tmp_path / rx_channel
|
||||
modem.TESTMODE = True
|
||||
modem.TXCHANNEL = tmp_path / tx_channel
|
||||
|
@ -148,8 +152,8 @@ def t_datac0_1(
|
|||
static.DXCALLSIGN_CRC = helpers.get_crc_24(static.DXCALLSIGN)
|
||||
static.TNC_STATE = "BUSY"
|
||||
static.ARQ_STATE = True
|
||||
sock.process_tnc_commands(json.dumps(data, indent=None))
|
||||
sock.process_tnc_commands(json.dumps(data, indent=None))
|
||||
sock.ThreadedTCPRequestHandler.process_tnc_commands(None,json.dumps(data, indent=None))
|
||||
sock.ThreadedTCPRequestHandler.process_tnc_commands(None,json.dumps(data, indent=None))
|
||||
|
||||
# Assure the test completes.
|
||||
timeout = time.time() + timeout_duration
|
||||
|
@ -173,7 +177,7 @@ def t_datac0_1(
|
|||
# override ARQ SESSION STATE for allowing disconnect command
|
||||
static.ARQ_SESSION_STATE = "connected"
|
||||
data = {"type": "arq", "command": "disconnect", "dxcallsign": dxcall}
|
||||
sock.process_tnc_commands(json.dumps(data, indent=None))
|
||||
sock.ThreadedTCPRequestHandler.process_tnc_commands(None,json.dumps(data, indent=None))
|
||||
time.sleep(0.5)
|
||||
|
||||
# Allow enough time for this side to process the disconnect frame.
|
||||
|
@ -266,8 +270,8 @@ def t_datac0_2(
|
|||
|
||||
if "cq" in data:
|
||||
t_data = {"type": "arq", "command": "stop_transmission"}
|
||||
sock.process_tnc_commands(json.dumps(t_data, indent=None))
|
||||
sock.process_tnc_commands(json.dumps(t_data, indent=None))
|
||||
sock.ThreadedTCPRequestHandler.process_tnc_commands(None,json.dumps(t_data, indent=None))
|
||||
sock.ThreadedTCPRequestHandler.process_tnc_commands(None,json.dumps(t_data, indent=None))
|
||||
|
||||
# Assure the test completes.
|
||||
timeout = time.time() + timeout_duration
|
||||
|
|
|
@ -26,8 +26,6 @@ from tnc import codec2
|
|||
|
||||
|
||||
def test_mm_tx():
|
||||
# AUDIO PARAMETERS
|
||||
AUDIO_FRAMES_PER_BUFFER = 2400
|
||||
MODEM_SAMPLE_RATE = codec2.api.FREEDV_FS_8000
|
||||
AUDIO_SAMPLE_RATE_TX = 48000
|
||||
assert (AUDIO_SAMPLE_RATE_TX % MODEM_SAMPLE_RATE) == 0
|
||||
|
@ -72,6 +70,8 @@ def test_mm_tx():
|
|||
else:
|
||||
sys.exit()
|
||||
|
||||
# AUDIO PARAMETERS
|
||||
AUDIO_FRAMES_PER_BUFFER = 2400
|
||||
# pyaudio init
|
||||
stream_tx = p_audio.open(
|
||||
format=pyaudio.paInt16,
|
||||
|
|
154
test/util_rx.py
154
test/util_rx.py
|
@ -121,99 +121,93 @@ def util_rx():
|
|||
time_start = 0
|
||||
time_end = 0
|
||||
|
||||
# Copy received 48 kHz to a file. Listen to this file with:
|
||||
# aplay -r 48000 -f S16_LE rx48.raw
|
||||
# Corruption of this file is a good way to detect audio card issues
|
||||
frx = open("rx48.raw", mode="wb")
|
||||
with open("rx48.raw", mode="wb") as frx:
|
||||
# initial number of samples we need
|
||||
nin = codec2.api.freedv_nin(freedv)
|
||||
while receive and time.time() < timeout:
|
||||
if AUDIO_INPUT_DEVICE != -1:
|
||||
try:
|
||||
# data_in48k = stream_rx.read(AUDIO_FRAMES_PER_BUFFER, exception_on_overflow = True)
|
||||
data_in48k, overflowed = stream_rx.read(AUDIO_FRAMES_PER_BUFFER) # type: ignore
|
||||
except OSError as err:
|
||||
print(err, file=sys.stderr)
|
||||
# if str(err).find("Input overflowed") != -1:
|
||||
# nread_exceptions += 1
|
||||
# if str(err).find("Stream closed") != -1:
|
||||
# print("Ending...")
|
||||
# receive = False
|
||||
else:
|
||||
data_in48k = sys.stdin.buffer.read(AUDIO_FRAMES_PER_BUFFER * 2)
|
||||
|
||||
# initial number of samples we need
|
||||
nin = codec2.api.freedv_nin(freedv)
|
||||
while receive and time.time() < timeout:
|
||||
if AUDIO_INPUT_DEVICE != -1:
|
||||
try:
|
||||
# data_in48k = stream_rx.read(AUDIO_FRAMES_PER_BUFFER, exception_on_overflow = True)
|
||||
data_in48k, overflowed = stream_rx.read(AUDIO_FRAMES_PER_BUFFER) # type: ignore
|
||||
except OSError as err:
|
||||
print(err, file=sys.stderr)
|
||||
# if str(err).find("Input overflowed") != -1:
|
||||
# nread_exceptions += 1
|
||||
# if str(err).find("Stream closed") != -1:
|
||||
# print("Ending...")
|
||||
# receive = False
|
||||
else:
|
||||
data_in48k = sys.stdin.buffer.read(AUDIO_FRAMES_PER_BUFFER * 2)
|
||||
# insert samples in buffer
|
||||
x = np.frombuffer(data_in48k, dtype=np.int16) # type: ignore
|
||||
# print(x)
|
||||
# x = data_in48k
|
||||
x.tofile(frx)
|
||||
if len(x) != AUDIO_FRAMES_PER_BUFFER:
|
||||
receive = False
|
||||
x = resampler.resample48_to_8(x)
|
||||
audio_buffer.push(x)
|
||||
|
||||
# insert samples in buffer
|
||||
x = np.frombuffer(data_in48k, dtype=np.int16) # type: ignore
|
||||
# print(x)
|
||||
# x = data_in48k
|
||||
x.tofile(frx)
|
||||
if len(x) != AUDIO_FRAMES_PER_BUFFER:
|
||||
receive = False
|
||||
x = resampler.resample48_to_8(x)
|
||||
audio_buffer.push(x)
|
||||
|
||||
# when we have enough samples call FreeDV Rx
|
||||
while audio_buffer.nbuffer >= nin:
|
||||
# start time measurement
|
||||
time_start = time.time()
|
||||
# demodulate audio
|
||||
nbytes = codec2.api.freedv_rawdatarx(
|
||||
freedv, bytes_out, audio_buffer.buffer.ctypes
|
||||
)
|
||||
time_end = time.time()
|
||||
|
||||
audio_buffer.pop(nin)
|
||||
|
||||
# call me on every loop!
|
||||
nin = codec2.api.freedv_nin(freedv)
|
||||
|
||||
rx_status = codec2.api.freedv_get_rx_status(freedv)
|
||||
if rx_status & codec2.api.FREEDV_RX_BIT_ERRORS:
|
||||
rx_errors = rx_errors + 1
|
||||
if DEBUGGING_MODE:
|
||||
rx_status = codec2.api.rx_sync_flags_to_text[rx_status] # type: ignore
|
||||
time_needed = time_end - time_start
|
||||
|
||||
print(
|
||||
f"nin: {nin:5d} rx_status: {rx_status:4s} "
|
||||
f"naudio_buffer: {audio_buffer.nbuffer:4d} time: {time_needed:4f}",
|
||||
file=sys.stderr,
|
||||
# when we have enough samples call FreeDV Rx
|
||||
while audio_buffer.nbuffer >= nin:
|
||||
# start time measurement
|
||||
time_start = time.time()
|
||||
# demodulate audio
|
||||
nbytes = codec2.api.freedv_rawdatarx(
|
||||
freedv, bytes_out, audio_buffer.buffer.ctypes
|
||||
)
|
||||
time_end = time.time()
|
||||
|
||||
if nbytes:
|
||||
total_n_bytes += nbytes
|
||||
audio_buffer.pop(nin)
|
||||
|
||||
if nbytes == bytes_per_frame:
|
||||
rx_total_frames += 1
|
||||
rx_frames += 1
|
||||
# call me on every loop!
|
||||
nin = codec2.api.freedv_nin(freedv)
|
||||
|
||||
if rx_frames == N_FRAMES_PER_BURST:
|
||||
rx_frames = 0
|
||||
rx_bursts += 1
|
||||
rx_status = codec2.api.freedv_get_rx_status(freedv)
|
||||
if rx_status & codec2.api.FREEDV_RX_BIT_ERRORS:
|
||||
rx_errors = rx_errors + 1
|
||||
if DEBUGGING_MODE:
|
||||
rx_status = codec2.api.rx_sync_flags_to_text[rx_status] # type: ignore
|
||||
time_needed = time_end - time_start
|
||||
|
||||
if rx_bursts == N_BURSTS:
|
||||
receive = False
|
||||
print(
|
||||
f"nin: {nin:5d} rx_status: {rx_status:4s} "
|
||||
f"naudio_buffer: {audio_buffer.nbuffer:4d} time: {time_needed:4f}",
|
||||
file=sys.stderr,
|
||||
)
|
||||
|
||||
if time.time() >= timeout:
|
||||
print("TIMEOUT REACHED")
|
||||
if nbytes:
|
||||
total_n_bytes += nbytes
|
||||
|
||||
time.sleep(0.01)
|
||||
if nbytes == bytes_per_frame:
|
||||
rx_total_frames += 1
|
||||
rx_frames += 1
|
||||
|
||||
if nread_exceptions:
|
||||
if rx_frames == N_FRAMES_PER_BURST:
|
||||
rx_frames = 0
|
||||
rx_bursts += 1
|
||||
|
||||
if rx_bursts == N_BURSTS:
|
||||
receive = False
|
||||
|
||||
if time.time() >= timeout:
|
||||
print("TIMEOUT REACHED")
|
||||
|
||||
time.sleep(0.01)
|
||||
|
||||
if nread_exceptions:
|
||||
print(
|
||||
f"nread_exceptions {nread_exceptions:d} - receive audio lost! "
|
||||
"Consider increasing Pyaudio frames_per_buffer...",
|
||||
file=sys.stderr,
|
||||
)
|
||||
print(
|
||||
f"nread_exceptions {nread_exceptions:d} - receive audio lost! "
|
||||
"Consider increasing Pyaudio frames_per_buffer...",
|
||||
f"RECEIVED BURSTS: {rx_bursts} "
|
||||
f"RECEIVED FRAMES: {rx_total_frames} "
|
||||
f"RX_ERRORS: {rx_errors}",
|
||||
file=sys.stderr,
|
||||
)
|
||||
print(
|
||||
f"RECEIVED BURSTS: {rx_bursts} "
|
||||
f"RECEIVED FRAMES: {rx_total_frames} "
|
||||
f"RX_ERRORS: {rx_errors}",
|
||||
file=sys.stderr,
|
||||
)
|
||||
frx.close()
|
||||
|
||||
# and at last check if we had an opened audio instance and close it
|
||||
if AUDIO_INPUT_DEVICE != -1:
|
||||
sd._terminate()
|
||||
|
|
|
@ -124,17 +124,17 @@ def t_arq_iss(*args):
|
|||
|
||||
time.sleep(0.5)
|
||||
|
||||
sock.process_tnc_commands(json.dumps(data, indent=None))
|
||||
sock.process_tnc_commands(json.dumps(data, indent=None))
|
||||
sock.process_tnc_commands(json.dumps(data, indent=None))
|
||||
sock.ThreadedTCPRequestHandler.process_tnc_commands(None,json.dumps(data, indent=None))
|
||||
sock.ThreadedTCPRequestHandler.process_tnc_commands(None,json.dumps(data, indent=None))
|
||||
sock.ThreadedTCPRequestHandler.process_tnc_commands(None,json.dumps(data, indent=None))
|
||||
|
||||
time.sleep(1.5)
|
||||
|
||||
data = {"type": "arq", "command": "stop_transmission"}
|
||||
sock.process_tnc_commands(json.dumps(data, indent=None))
|
||||
sock.ThreadedTCPRequestHandler.process_tnc_commands(None,json.dumps(data, indent=None))
|
||||
|
||||
time.sleep(0.5)
|
||||
sock.process_tnc_commands(json.dumps(data, indent=None))
|
||||
sock.ThreadedTCPRequestHandler.process_tnc_commands(None,json.dumps(data, indent=None))
|
||||
|
||||
# Set timeout
|
||||
timeout = time.time() + 15
|
||||
|
|
370
tnc/daemon.py
370
tnc/daemon.py
|
@ -135,221 +135,179 @@ class DAEMON:
|
|||
# data[2] mygrid
|
||||
# data[3] rx_audio
|
||||
# data[4] tx_audio
|
||||
# REMOVED - data[5] devicename
|
||||
# REMOVED - data[6] deviceport
|
||||
# REMOVED - data[7] serialspeed
|
||||
# REMOVED - data[8] pttprotocol
|
||||
# REMOVED - data[9] pttport
|
||||
# REMOVED - data[10] data_bits
|
||||
# REMOVED - data[11] stop_bits
|
||||
# REMOVED - data[12] handshake
|
||||
# data[13] radiocontrol
|
||||
# data[14] rigctld_ip
|
||||
# data[15] rigctld_port
|
||||
# data[16] send_scatter
|
||||
# data[17] send_fft
|
||||
# data[18] low_bandwidth_mode
|
||||
# data[19] tuning_range_fmin
|
||||
# data[20] tuning_range_fmax
|
||||
# data[21] enable FSK
|
||||
# data[22] tx-audio-level
|
||||
# data[23] respond_to_cq
|
||||
# data[24] rx_buffer_size
|
||||
# data[25] explorer
|
||||
# data[26] ssid_list
|
||||
# data[27] auto_tune
|
||||
# data[28] stats
|
||||
# TODO: We need to remove 5-12 and adjust the list number for other paramters
|
||||
# This is only a dirty fix
|
||||
# data[5] radiocontrol
|
||||
# data[6] rigctld_ip
|
||||
# data[7] rigctld_port
|
||||
# data[8] send_scatter
|
||||
# data[9] send_fft
|
||||
# data[10] low_bandwidth_mode
|
||||
# data[11] tuning_range_fmin
|
||||
# data[12] tuning_range_fmax
|
||||
# data[13] enable FSK
|
||||
# data[14] tx-audio-level
|
||||
# data[15] respond_to_cq
|
||||
# data[16] rx_buffer_size
|
||||
# data[17] explorer
|
||||
# data[18] ssid_list
|
||||
# data[19] auto_tune
|
||||
# data[20] stats
|
||||
|
||||
if data[0] == "STARTTNC":
|
||||
self.log.warning("[DMN] Starting TNC", rig=data[5], port=data[6])
|
||||
self.start_tnc(data)
|
||||
|
||||
# list of parameters, necessary for running subprocess command as a list
|
||||
options = []
|
||||
|
||||
options.append("--port")
|
||||
options.append(str(static.DAEMONPORT - 1))
|
||||
# create an additional list entry for parameters not covered by gui
|
||||
data[50] = int(static.DAEMONPORT - 1)
|
||||
|
||||
options.append("--mycall")
|
||||
options.append(data[1])
|
||||
|
||||
options.append("--mygrid")
|
||||
options.append(data[2])
|
||||
|
||||
options.append("--rx")
|
||||
options.append(data[3])
|
||||
|
||||
options.append("--tx")
|
||||
options.append(data[4])
|
||||
|
||||
# if radiocontrol != disabled
|
||||
# this should hopefully avoid a ton of problems if we are just running in
|
||||
# disabled mode
|
||||
|
||||
if data[5] != "disabled":
|
||||
|
||||
options.append("--radiocontrol")
|
||||
options.append(data[5])
|
||||
|
||||
if data[5] == "rigctld":
|
||||
options.append("--rigctld_ip")
|
||||
options.append(data[6])
|
||||
|
||||
options.append("--rigctld_port")
|
||||
options.append(data[7])
|
||||
|
||||
if data[8] == "True":
|
||||
options.append("--scatter")
|
||||
|
||||
if data[9] == "True":
|
||||
options.append("--fft")
|
||||
|
||||
if data[10] == "True":
|
||||
options.append("--500hz")
|
||||
|
||||
options.append("--tuning_range_fmin")
|
||||
options.append(data[11])
|
||||
|
||||
options.append("--tuning_range_fmax")
|
||||
options.append(data[12])
|
||||
|
||||
# overriding FSK mode
|
||||
# if data[13] == "True":
|
||||
# options.append("--fsk")
|
||||
|
||||
options.append("--tx-audio-level")
|
||||
options.append(data[14])
|
||||
|
||||
if data[15] == "True":
|
||||
options.append("--qrv")
|
||||
|
||||
options.append("--rx-buffer-size")
|
||||
options.append(data[16])
|
||||
|
||||
if data[17] == "True":
|
||||
options.append("--explorer")
|
||||
|
||||
options.append("--ssid")
|
||||
for i in data[18]:
|
||||
options.append(str(i))
|
||||
|
||||
if data[19] == "True":
|
||||
options.append("--tune")
|
||||
|
||||
if data[20] == "True":
|
||||
options.append("--stats")
|
||||
|
||||
# safe data to config file
|
||||
config.write_entire_config(data)
|
||||
|
||||
|
||||
|
||||
|
||||
# Try running tnc from binary, else run from source
|
||||
# This helps running the tnc in a developer environment
|
||||
try:
|
||||
command = []
|
||||
|
||||
if (getattr(sys, 'frozen', False) or hasattr(sys, "_MEIPASS")) and sys.platform in ["darwin"]:
|
||||
# If the application is run as a bundle, the PyInstaller bootloader
|
||||
# extends the sys module by a flag frozen=True and sets the app
|
||||
# path into variable _MEIPASS'.
|
||||
application_path = sys._MEIPASS
|
||||
command.append(application_path + '/freedata-tnc')
|
||||
|
||||
else:
|
||||
|
||||
if sys.platform in ["linux", "darwin"]:
|
||||
command.append("./freedata-tnc")
|
||||
elif sys.platform in ["win32", "win64"]:
|
||||
command.append("freedata-tnc.exe")
|
||||
|
||||
command += options
|
||||
proc = subprocess.Popen(command)
|
||||
|
||||
atexit.register(proc.kill)
|
||||
|
||||
self.log.info("[DMN] TNC started", path="binary")
|
||||
except FileNotFoundError as err1:
|
||||
self.log.info("[DMN] worker: ", e=err1)
|
||||
command = []
|
||||
|
||||
if sys.platform in ["linux", "darwin"]:
|
||||
command.append("python3")
|
||||
elif sys.platform in ["win32", "win64"]:
|
||||
command.append("python")
|
||||
|
||||
command.append("main.py")
|
||||
command += options
|
||||
print(command)
|
||||
proc = subprocess.Popen(command)
|
||||
atexit.register(proc.kill)
|
||||
|
||||
self.log.info("[DMN] TNC started", path="source")
|
||||
|
||||
static.TNCPROCESS = proc
|
||||
static.TNCSTARTED = True
|
||||
"""
|
||||
# WE HAVE THIS PART in SOCKET
|
||||
if data[0] == "STOPTNC":
|
||||
static.TNCPROCESS.kill()
|
||||
self.log.warning("[DMN] Stopping TNC")
|
||||
#os.kill(static.TNCPROCESS, signal.SIGKILL)
|
||||
static.TNCSTARTED = False
|
||||
"""
|
||||
|
||||
# data[9] radiocontrol
|
||||
# data[10] rigctld_ip
|
||||
# data[11] rigctld_port
|
||||
if data[0] == "TEST_HAMLIB":
|
||||
|
||||
radiocontrol = data[1]
|
||||
rigctld_ip = data[2]
|
||||
rigctld_port = data[3]
|
||||
|
||||
# check how we want to control the radio
|
||||
if radiocontrol == "direct":
|
||||
print("direct hamlib support deprecated - not usable anymore")
|
||||
sys.exit(1)
|
||||
elif radiocontrol == "rigctl":
|
||||
print("rigctl support deprecated - not usable anymore")
|
||||
sys.exit(1)
|
||||
elif radiocontrol == "rigctld":
|
||||
import rigctld as rig
|
||||
else:
|
||||
import rigdummy as rig
|
||||
|
||||
hamlib = rig.radio()
|
||||
hamlib.open_rig(
|
||||
rigctld_ip=rigctld_ip,
|
||||
rigctld_port=rigctld_port,
|
||||
)
|
||||
|
||||
# hamlib_version = rig.hamlib_version
|
||||
|
||||
hamlib.set_ptt(True)
|
||||
pttstate = hamlib.get_ptt()
|
||||
|
||||
if pttstate:
|
||||
self.log.info("[DMN] Hamlib PTT", status="SUCCESS")
|
||||
response = {"command": "test_hamlib", "result": "SUCCESS"}
|
||||
else:
|
||||
self.log.warning("[DMN] Hamlib PTT", status="NO SUCCESS")
|
||||
response = {"command": "test_hamlib", "result": "NOSUCCESS"}
|
||||
|
||||
hamlib.set_ptt(False)
|
||||
hamlib.close_rig()
|
||||
|
||||
jsondata = json.dumps(response)
|
||||
sock.SOCKET_QUEUE.put(jsondata)
|
||||
# data[9] radiocontrol
|
||||
# data[10] rigctld_ip
|
||||
# data[11] rigctld_port
|
||||
self.test_hamlib_ptt(data)
|
||||
|
||||
except Exception as err1:
|
||||
self.log.error("[DMN] worker: Exception: ", e=err1)
|
||||
|
||||
def test_hamlib_ptt(self, data):
|
||||
radiocontrol = data[1]
|
||||
rigctld_ip = data[2]
|
||||
rigctld_port = data[3]
|
||||
|
||||
# check how we want to control the radio
|
||||
if radiocontrol == "direct":
|
||||
print("direct hamlib support deprecated - not usable anymore")
|
||||
sys.exit(1)
|
||||
elif radiocontrol == "rigctl":
|
||||
print("rigctl support deprecated - not usable anymore")
|
||||
sys.exit(1)
|
||||
elif radiocontrol == "rigctld":
|
||||
import rigctld as rig
|
||||
else:
|
||||
import rigdummy as rig
|
||||
|
||||
hamlib = rig.radio()
|
||||
hamlib.open_rig(
|
||||
rigctld_ip=rigctld_ip,
|
||||
rigctld_port=rigctld_port,
|
||||
)
|
||||
|
||||
# hamlib_version = rig.hamlib_version
|
||||
|
||||
hamlib.set_ptt(True)
|
||||
if hamlib.get_ptt():
|
||||
self.log.info("[DMN] Hamlib PTT", status="SUCCESS")
|
||||
response = {"command": "test_hamlib", "result": "SUCCESS"}
|
||||
else:
|
||||
self.log.warning("[DMN] Hamlib PTT", status="NO SUCCESS")
|
||||
response = {"command": "test_hamlib", "result": "NOSUCCESS"}
|
||||
|
||||
hamlib.set_ptt(False)
|
||||
hamlib.close_rig()
|
||||
|
||||
jsondata = json.dumps(response)
|
||||
sock.SOCKET_QUEUE.put(jsondata)
|
||||
|
||||
def start_tnc(self, data):
|
||||
self.log.warning("[DMN] Starting TNC", rig=data[5], port=data[6])
|
||||
|
||||
# list of parameters, necessary for running subprocess command as a list
|
||||
options = ["--port", str(static.DAEMONPORT - 1)]
|
||||
|
||||
# create an additional list entry for parameters not covered by gui
|
||||
data[50] = int(static.DAEMONPORT - 1)
|
||||
|
||||
options.append("--mycall")
|
||||
options.extend((data[1], "--mygrid"))
|
||||
options.extend((data[2], "--rx"))
|
||||
options.extend((data[3], "--tx"))
|
||||
options.append(data[4])
|
||||
|
||||
# if radiocontrol != disabled
|
||||
# this should hopefully avoid a ton of problems if we are just running in
|
||||
# disabled mode
|
||||
|
||||
if data[5] != "disabled":
|
||||
|
||||
options.append("--radiocontrol")
|
||||
options.append(data[5])
|
||||
|
||||
if data[5] == "rigctld":
|
||||
options.append("--rigctld_ip")
|
||||
options.extend((data[6], "--rigctld_port"))
|
||||
options.append(data[7])
|
||||
|
||||
if data[8] == "True":
|
||||
options.append("--scatter")
|
||||
|
||||
if data[9] == "True":
|
||||
options.append("--fft")
|
||||
|
||||
if data[10] == "True":
|
||||
options.append("--500hz")
|
||||
|
||||
options.append("--tuning_range_fmin")
|
||||
options.extend((data[11], "--tuning_range_fmax"))
|
||||
options.extend((data[12], "--tx-audio-level"))
|
||||
options.append(data[14])
|
||||
|
||||
if data[15] == "True":
|
||||
options.append("--qrv")
|
||||
|
||||
options.append("--rx-buffer-size")
|
||||
options.append(data[16])
|
||||
|
||||
if data[17] == "True":
|
||||
options.append("--explorer")
|
||||
|
||||
options.append("--ssid")
|
||||
options.extend(str(i) for i in data[18])
|
||||
if data[19] == "True":
|
||||
options.append("--tune")
|
||||
|
||||
if data[20] == "True":
|
||||
options.append("--stats")
|
||||
|
||||
# safe data to config file
|
||||
config.write_entire_config(data)
|
||||
|
||||
# Try running tnc from binary, else run from source
|
||||
# This helps running the tnc in a developer environment
|
||||
try:
|
||||
command = []
|
||||
|
||||
if (getattr(sys, 'frozen', False) or hasattr(sys, "_MEIPASS")) and sys.platform in ["darwin"]:
|
||||
# If the application is run as a bundle, the PyInstaller bootloader
|
||||
# extends the sys module by a flag frozen=True and sets the app
|
||||
# path into variable _MEIPASS'.
|
||||
application_path = sys._MEIPASS
|
||||
command.append(f'{application_path}/freedata-tnc')
|
||||
|
||||
elif sys.platform in ["linux", "darwin"]:
|
||||
command.append("./freedata-tnc")
|
||||
elif sys.platform in ["win32", "win64"]:
|
||||
command.append("freedata-tnc.exe")
|
||||
|
||||
command += options
|
||||
proc = subprocess.Popen(command)
|
||||
|
||||
atexit.register(proc.kill)
|
||||
|
||||
self.log.info("[DMN] TNC started", path="binary")
|
||||
except FileNotFoundError as err1:
|
||||
self.log.info("[DMN] worker: ", e=err1)
|
||||
command = []
|
||||
|
||||
if sys.platform in ["linux", "darwin"]:
|
||||
command.append("python3")
|
||||
elif sys.platform in ["win32", "win64"]:
|
||||
command.append("python")
|
||||
|
||||
command.append("main.py")
|
||||
command += options
|
||||
print(command)
|
||||
proc = subprocess.Popen(command)
|
||||
atexit.register(proc.kill)
|
||||
|
||||
self.log.info("[DMN] TNC started", path="source")
|
||||
|
||||
static.TNCPROCESS = proc
|
||||
static.TNCSTARTED = True
|
||||
if __name__ == "__main__":
|
||||
mainlog = structlog.get_logger(__file__)
|
||||
# we need to run this on Windows for multiprocessing support
|
||||
|
|
|
@ -249,7 +249,10 @@ class DATA:
|
|||
#
|
||||
# send transmission queued information once
|
||||
if static.ARQ_STATE or static.IS_CODEC2_TRAFFIC:
|
||||
self.log.debug(f"[TNC] TX DISPATCHER - waiting with processing command ", arq_state=static.ARQ_STATE)
|
||||
self.log.debug(
|
||||
"[TNC] TX DISPATCHER - waiting with processing command ",
|
||||
arq_state=static.ARQ_STATE,
|
||||
)
|
||||
|
||||
self.send_data_to_socket_queue(
|
||||
freedata="tnc-message",
|
||||
|
@ -635,15 +638,6 @@ class DATA:
|
|||
# is intended for this station.
|
||||
data_in = bytes(data_in)
|
||||
|
||||
# TODO: this seems not to work anymore
|
||||
# get received crc for different mycall ssids
|
||||
# check if callsign ssid override
|
||||
# _, mycallsign = helpers.check_callsign(
|
||||
# self.mycallsign, data_in[2:5]
|
||||
# )
|
||||
# attempt fixing this
|
||||
mycallsign = self.mycallsign
|
||||
|
||||
# only process data if we are in ARQ and BUSY state else return to quit
|
||||
if not static.ARQ_STATE and static.TNC_STATE not in ["BUSY"]:
|
||||
self.log.warning("[TNC] wrong tnc state - dropping data", arq_state=static.ARQ_STATE, tnc_state=static.TNC_STATE)
|
||||
|
@ -701,10 +695,10 @@ class DATA:
|
|||
# catch possible modem error which leads into false byteorder
|
||||
# modem possibly decodes too late - data then is pushed to buffer
|
||||
# which leads into wrong byteorder
|
||||
# Lets put this in try/except so we are not crashing tnc as its hihgly experimental
|
||||
# Lets put this in try/except so we are not crashing tnc as its highly experimental
|
||||
# This might only work for datac1 and datac3
|
||||
try:
|
||||
#area_of_interest = (modem.get_bytes_per_frame(self.mode_list[speed_level] - 1) -3) * 2
|
||||
# area_of_interest = (modem.get_bytes_per_frame(self.mode_list[speed_level] - 1) -3) * 2
|
||||
if static.RX_FRAME_BUFFER.endswith(temp_burst_buffer[:246]) and len(temp_burst_buffer) >= 246:
|
||||
self.log.warning(
|
||||
"[TNC] ARQ | RX | wrong byteorder received - dropping data"
|
||||
|
@ -716,7 +710,6 @@ class DATA:
|
|||
"[TNC] ARQ | RX | wrong byteorder check failed", e=e
|
||||
)
|
||||
|
||||
|
||||
# if frame buffer ends not with the current frame, we are going to append new data
|
||||
# if data already exists, we received the frame correctly,
|
||||
# but the ACK frame didn't receive its destination (ISS)
|
||||
|
@ -734,10 +727,9 @@ class DATA:
|
|||
# temp_burst_buffer --> new data
|
||||
# search_area --> area where we want to search
|
||||
|
||||
|
||||
#data_mode = self.mode_list[self.speed_level]
|
||||
#payload_per_frame = modem.get_bytes_per_frame(data_mode) - 2
|
||||
#search_area = payload_per_frame - 3 # (3 bytes arq frame header)
|
||||
# data_mode = self.mode_list[self.speed_level]
|
||||
# payload_per_frame = modem.get_bytes_per_frame(data_mode) - 2
|
||||
# search_area = payload_per_frame - 3 # (3 bytes arq frame header)
|
||||
search_area = 510 - 3 # (3 bytes arq frame header)
|
||||
|
||||
search_position = len(static.RX_FRAME_BUFFER) - search_area
|
||||
|
@ -771,28 +763,11 @@ class DATA:
|
|||
and data_in.find(self.data_frame_eof) < 0
|
||||
):
|
||||
|
||||
self.frame_received_counter += 1
|
||||
# try increasing speed level only if we had two successful decodes
|
||||
if self.frame_received_counter >= 2:
|
||||
self.frame_received_counter = 0
|
||||
|
||||
# make sure new speed level isn't higher than available modes
|
||||
new_speed_level = min(self.speed_level + 1, len(self.mode_list) - 1)
|
||||
# check if actual snr is higher than minimum snr for next mode
|
||||
if static.SNR >= self.snr_list[new_speed_level]:
|
||||
self.speed_level = new_speed_level
|
||||
else:
|
||||
self.log.info("[TNC] ARQ | increasing speed level not possible because of SNR limit",
|
||||
given_snr=static.SNR,
|
||||
needed_snr=self.snr_list[new_speed_level]
|
||||
)
|
||||
static.ARQ_SPEED_LEVEL = self.speed_level
|
||||
|
||||
# Update modes we are listening to
|
||||
self.set_listening_modes(False, True, self.mode_list[self.speed_level])
|
||||
self.arq_calculate_speed_level(snr)
|
||||
|
||||
# Create and send ACK frame
|
||||
self.log.info("[TNC] ARQ | RX | SENDING ACK", finished=static.ARQ_SECONDS_UNTIL_FINISH, bytesperminute=static.ARQ_BYTES_PER_MINUTE)
|
||||
self.log.info("[TNC] ARQ | RX | SENDING ACK", finished=static.ARQ_SECONDS_UNTIL_FINISH,
|
||||
bytesperminute=static.ARQ_BYTES_PER_MINUTE)
|
||||
self.send_burst_ack_frame(snr)
|
||||
|
||||
# Reset n retries per burst counter
|
||||
|
@ -817,7 +792,7 @@ class DATA:
|
|||
finished=static.ARQ_SECONDS_UNTIL_FINISH,
|
||||
irs=helpers.bool_to_string(self.is_IRS)
|
||||
)
|
||||
|
||||
|
||||
elif rx_n_frame_of_burst == rx_n_frames_per_burst - 1:
|
||||
# We have "Nones" in our rx buffer,
|
||||
# Check if we received last frame of burst - this is an indicator for missed frames.
|
||||
|
@ -834,7 +809,6 @@ class DATA:
|
|||
self.rx_start_of_transmission, len(static.RX_FRAME_BUFFER)
|
||||
)
|
||||
|
||||
# Should never reach this point
|
||||
else:
|
||||
self.log.error(
|
||||
"[TNC] data_handler: Should not reach this point...",
|
||||
|
@ -851,19 +825,7 @@ class DATA:
|
|||
# get total bytes per transmission information as soon we received a frame with a BOF
|
||||
|
||||
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
|
||||
static.TOTAL_BYTES = frame_length
|
||||
compression_factor = int.from_bytes(payload[8:9], "big") # 4:8 4bytes
|
||||
# limit to max value of 255
|
||||
compression_factor = np.clip(compression_factor, 0, 255)
|
||||
static.ARQ_COMPRESSION_FACTOR = compression_factor / 10
|
||||
self.calculate_transfer_rate_rx(
|
||||
self.rx_start_of_transmission, len(static.RX_FRAME_BUFFER)
|
||||
)
|
||||
|
||||
self.arq_extract_statistics_from_data_frame(bof_position, eof_position)
|
||||
if (
|
||||
bof_position >= 0
|
||||
and eof_position > 0
|
||||
|
@ -893,131 +855,7 @@ class DATA:
|
|||
|
||||
# Check if data_frame_crc is equal with received crc
|
||||
if data_frame_crc == data_frame_crc_received:
|
||||
|
||||
# transmittion duration
|
||||
duration = time.time() - self.rx_start_of_transmission
|
||||
self.calculate_transfer_rate_rx(
|
||||
self.rx_start_of_transmission, len(static.RX_FRAME_BUFFER)
|
||||
)
|
||||
self.log.info("[TNC] ARQ | RX | DATA FRAME SUCCESSFULLY RECEIVED", nacks=self.frame_nack_counter,bytesperminute=static.ARQ_BYTES_PER_MINUTE, total_bytes=static.TOTAL_BYTES, duration=duration)
|
||||
|
||||
# Decompress the data frame
|
||||
data_frame_decompressed = lzma.decompress(data_frame)
|
||||
static.ARQ_COMPRESSION_FACTOR = len(data_frame_decompressed) / len(
|
||||
data_frame
|
||||
)
|
||||
data_frame = data_frame_decompressed
|
||||
|
||||
self.transmission_uuid = str(uuid.uuid4())
|
||||
timestamp = int(time.time())
|
||||
|
||||
# Re-code data_frame in base64, UTF-8 for JSON UI communication.
|
||||
base64_data = base64.b64encode(data_frame).decode("UTF-8")
|
||||
|
||||
# check if RX_BUFFER isn't full
|
||||
if not RX_BUFFER.full():
|
||||
# make sure we have always the correct buffer size
|
||||
RX_BUFFER.maxsize = int(static.RX_BUFFER_SIZE)
|
||||
else:
|
||||
# if full, free space by getting an item
|
||||
self.log.info(
|
||||
"[TNC] ARQ | RX | RX_BUFFER FULL - dropping old data",
|
||||
buffer_size=RX_BUFFER.qsize(),
|
||||
maxsize=int(static.RX_BUFFER_SIZE)
|
||||
)
|
||||
RX_BUFFER.get()
|
||||
|
||||
# add item to RX_BUFFER
|
||||
self.log.info(
|
||||
"[TNC] ARQ | RX | saving data to rx buffer",
|
||||
buffer_size=RX_BUFFER.qsize() + 1,
|
||||
maxsize=RX_BUFFER.maxsize
|
||||
)
|
||||
try:
|
||||
RX_BUFFER.put(
|
||||
[
|
||||
self.transmission_uuid,
|
||||
timestamp,
|
||||
static.DXCALLSIGN,
|
||||
static.DXGRID,
|
||||
base64_data,
|
||||
]
|
||||
)
|
||||
except Exception as e:
|
||||
# File "/usr/lib/python3.7/queue.py", line 133, in put
|
||||
# if self.maxsize > 0
|
||||
# TypeError: '>' not supported between instances of 'str' and 'int'
|
||||
#
|
||||
# Occurs on Raspberry Pi and Python 3.7
|
||||
self.log.error(
|
||||
"[TNC] ARQ | RX | error occurred when saving data!",
|
||||
e=e,
|
||||
uuid=self.transmission_uuid,
|
||||
timestamp=timestamp,
|
||||
dxcall=static.DXCALLSIGN,
|
||||
dxgrid=static.DXGRID,
|
||||
data=base64_data
|
||||
)
|
||||
|
||||
if static.ARQ_SAVE_TO_FOLDER:
|
||||
try:
|
||||
self.save_data_to_folder(
|
||||
self.transmission_uuid,
|
||||
timestamp,
|
||||
mycallsign,
|
||||
static.DXCALLSIGN,
|
||||
static.DXGRID,
|
||||
data_frame
|
||||
)
|
||||
except Exception as e:
|
||||
self.log.error(
|
||||
"[TNC] ARQ | RX | can't save file to folder",
|
||||
e=e,
|
||||
uuid=self.transmission_uuid,
|
||||
timestamp=timestamp,
|
||||
dxcall=static.DXCALLSIGN,
|
||||
dxgrid=static.DXGRID,
|
||||
data=base64_data
|
||||
)
|
||||
|
||||
self.send_data_to_socket_queue(
|
||||
freedata="tnc-message",
|
||||
arq="transmission",
|
||||
status="received",
|
||||
uuid=self.transmission_uuid,
|
||||
timestamp=timestamp,
|
||||
mycallsign=str(mycallsign, "UTF-8"),
|
||||
dxcallsign=str(static.DXCALLSIGN, "UTF-8"),
|
||||
dxgrid=str(static.DXGRID, "UTF-8"),
|
||||
data=base64_data,
|
||||
irs=helpers.bool_to_string(self.is_IRS)
|
||||
)
|
||||
|
||||
if static.ENABLE_STATS:
|
||||
duration = time.time() - self.rx_start_of_transmission
|
||||
self.stats.push(frame_nack_counter=self.frame_nack_counter, status="received", duration=duration)
|
||||
|
||||
self.log.info(
|
||||
"[TNC] ARQ | RX | SENDING DATA FRAME ACK",
|
||||
snr=snr,
|
||||
crc=data_frame_crc.hex(),
|
||||
)
|
||||
|
||||
self.send_data_ack_frame(snr)
|
||||
# Update statistics AFTER the frame ACK is sent
|
||||
self.calculate_transfer_rate_rx(
|
||||
self.rx_start_of_transmission, len(static.RX_FRAME_BUFFER)
|
||||
)
|
||||
|
||||
self.log.info(
|
||||
"[TNC] | RX | DATACHANNEL ["
|
||||
+ str(self.mycallsign, "UTF-8")
|
||||
+ "]<< >>["
|
||||
+ str(static.DXCALLSIGN, "UTF-8")
|
||||
+ "]",
|
||||
snr=snr,
|
||||
)
|
||||
|
||||
self.arq_process_received_data_frame(data_frame, snr)
|
||||
else:
|
||||
self.send_data_to_socket_queue(
|
||||
freedata="tnc-message",
|
||||
|
@ -1055,6 +893,168 @@ class DATA:
|
|||
# Finally cleanup our buffers and states,
|
||||
self.arq_cleanup()
|
||||
|
||||
def arq_extract_statistics_from_data_frame(self, bof_position, 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
|
||||
static.TOTAL_BYTES = frame_length
|
||||
compression_factor = int.from_bytes(payload[8:9], "big") # 4:8 4bytes
|
||||
# limit to max value of 255
|
||||
compression_factor = np.clip(compression_factor, 0, 255)
|
||||
static.ARQ_COMPRESSION_FACTOR = compression_factor / 10
|
||||
self.calculate_transfer_rate_rx(
|
||||
self.rx_start_of_transmission, len(static.RX_FRAME_BUFFER)
|
||||
)
|
||||
|
||||
def arq_calculate_speed_level(self, snr):
|
||||
self.frame_received_counter += 1
|
||||
# try increasing speed level only if we had two successful decodes
|
||||
if self.frame_received_counter >= 2:
|
||||
self.frame_received_counter = 0
|
||||
|
||||
# make sure new speed level isn't higher than available modes
|
||||
new_speed_level = min(self.speed_level + 1, len(self.mode_list) - 1)
|
||||
# check if actual snr is higher than minimum snr for next mode
|
||||
if static.SNR >= self.snr_list[new_speed_level]:
|
||||
self.speed_level = new_speed_level
|
||||
else:
|
||||
self.log.info("[TNC] ARQ | increasing speed level not possible because of SNR limit",
|
||||
given_snr=static.SNR,
|
||||
needed_snr=self.snr_list[new_speed_level]
|
||||
)
|
||||
static.ARQ_SPEED_LEVEL = self.speed_level
|
||||
|
||||
# Update modes we are listening to
|
||||
self.set_listening_modes(False, True, self.mode_list[self.speed_level])
|
||||
|
||||
def arq_process_received_data_frame(self, data_frame, snr):
|
||||
"""
|
||||
|
||||
|
||||
"""
|
||||
# transmittion duration
|
||||
duration = time.time() - self.rx_start_of_transmission
|
||||
self.calculate_transfer_rate_rx(
|
||||
self.rx_start_of_transmission, len(static.RX_FRAME_BUFFER)
|
||||
)
|
||||
self.log.info("[TNC] ARQ | RX | DATA FRAME SUCCESSFULLY RECEIVED", nacks=self.frame_nack_counter,
|
||||
bytesperminute=static.ARQ_BYTES_PER_MINUTE, total_bytes=static.TOTAL_BYTES, duration=duration)
|
||||
|
||||
# Decompress the data frame
|
||||
data_frame_decompressed = lzma.decompress(data_frame)
|
||||
static.ARQ_COMPRESSION_FACTOR = len(data_frame_decompressed) / len(
|
||||
data_frame
|
||||
)
|
||||
data_frame = data_frame_decompressed
|
||||
|
||||
self.transmission_uuid = str(uuid.uuid4())
|
||||
timestamp = int(time.time())
|
||||
|
||||
# Re-code data_frame in base64, UTF-8 for JSON UI communication.
|
||||
base64_data = base64.b64encode(data_frame).decode("UTF-8")
|
||||
|
||||
# check if RX_BUFFER isn't full
|
||||
if not RX_BUFFER.full():
|
||||
# make sure we have always the correct buffer size
|
||||
RX_BUFFER.maxsize = int(static.RX_BUFFER_SIZE)
|
||||
else:
|
||||
# if full, free space by getting an item
|
||||
self.log.info(
|
||||
"[TNC] ARQ | RX | RX_BUFFER FULL - dropping old data",
|
||||
buffer_size=RX_BUFFER.qsize(),
|
||||
maxsize=int(static.RX_BUFFER_SIZE)
|
||||
)
|
||||
RX_BUFFER.get()
|
||||
|
||||
# add item to RX_BUFFER
|
||||
self.log.info(
|
||||
"[TNC] ARQ | RX | saving data to rx buffer",
|
||||
buffer_size=RX_BUFFER.qsize() + 1,
|
||||
maxsize=RX_BUFFER.maxsize
|
||||
)
|
||||
try:
|
||||
RX_BUFFER.put(
|
||||
[
|
||||
self.transmission_uuid,
|
||||
timestamp,
|
||||
static.DXCALLSIGN,
|
||||
static.DXGRID,
|
||||
base64_data,
|
||||
]
|
||||
)
|
||||
except Exception as e:
|
||||
# File "/usr/lib/python3.7/queue.py", line 133, in put
|
||||
# if self.maxsize > 0
|
||||
# TypeError: '>' not supported between instances of 'str' and 'int'
|
||||
#
|
||||
# Occurs on Raspberry Pi and Python 3.7
|
||||
self.log.error(
|
||||
"[TNC] ARQ | RX | error occurred when saving data!",
|
||||
e=e,
|
||||
uuid=self.transmission_uuid,
|
||||
timestamp=timestamp,
|
||||
dxcall=static.DXCALLSIGN,
|
||||
dxgrid=static.DXGRID,
|
||||
data=base64_data
|
||||
)
|
||||
|
||||
if static.ARQ_SAVE_TO_FOLDER:
|
||||
try:
|
||||
self.save_data_to_folder(
|
||||
self.transmission_uuid,
|
||||
timestamp,
|
||||
self.mycallsign,
|
||||
static.DXCALLSIGN,
|
||||
static.DXGRID,
|
||||
data_frame
|
||||
)
|
||||
except Exception as e:
|
||||
self.log.error(
|
||||
"[TNC] ARQ | RX | can't save file to folder",
|
||||
e=e,
|
||||
uuid=self.transmission_uuid,
|
||||
timestamp=timestamp,
|
||||
dxcall=static.DXCALLSIGN,
|
||||
dxgrid=static.DXGRID,
|
||||
data=base64_data
|
||||
)
|
||||
|
||||
self.send_data_to_socket_queue(
|
||||
freedata="tnc-message",
|
||||
arq="transmission",
|
||||
status="received",
|
||||
uuid=self.transmission_uuid,
|
||||
timestamp=timestamp,
|
||||
mycallsign=str(self.mycallsign, "UTF-8"),
|
||||
dxcallsign=str(static.DXCALLSIGN, "UTF-8"),
|
||||
dxgrid=str(static.DXGRID, "UTF-8"),
|
||||
data=base64_data,
|
||||
irs=helpers.bool_to_string(self.is_IRS)
|
||||
)
|
||||
|
||||
if static.ENABLE_STATS:
|
||||
duration = time.time() - self.rx_start_of_transmission
|
||||
self.stats.push(frame_nack_counter=self.frame_nack_counter, status="received", duration=duration)
|
||||
|
||||
self.log.info(
|
||||
"[TNC] ARQ | RX | SENDING DATA FRAME ACK")
|
||||
|
||||
self.send_data_ack_frame(snr)
|
||||
# Update statistics AFTER the frame ACK is sent
|
||||
self.calculate_transfer_rate_rx(
|
||||
self.rx_start_of_transmission, len(static.RX_FRAME_BUFFER)
|
||||
)
|
||||
|
||||
self.log.info(
|
||||
"[TNC] | RX | DATACHANNEL ["
|
||||
+ str(self.mycallsign, "UTF-8")
|
||||
+ "]<< >>["
|
||||
+ str(static.DXCALLSIGN, "UTF-8")
|
||||
+ "]",
|
||||
snr=snr,
|
||||
)
|
||||
|
||||
def arq_transmit(self, data_out: bytes, mode: int, n_frames_per_burst: int):
|
||||
"""
|
||||
Transmit ARQ frame
|
||||
|
@ -1065,8 +1065,6 @@ class DATA:
|
|||
n_frames_per_burst:int:
|
||||
|
||||
"""
|
||||
self.arq_file_transfer = True
|
||||
|
||||
# set signalling modes we want to listen to
|
||||
# we are in an ongoing arq transmission, so we don't need sig0 actually
|
||||
modem.RECEIVE_SIG0 = False
|
||||
|
@ -1075,9 +1073,9 @@ class DATA:
|
|||
self.tx_n_retry_of_burst = 0 # retries we already sent data
|
||||
# Maximum number of retries to send before declaring a frame is lost
|
||||
|
||||
# save len of data_out to TOTAL_BYTES for our statistics --> kBytes
|
||||
# static.TOTAL_BYTES = round(len(data_out) / 1024, 2)
|
||||
# save len of data_out to TOTAL_BYTES for our statistics
|
||||
static.TOTAL_BYTES = len(data_out)
|
||||
self.arq_file_transfer = True
|
||||
frame_total_size = len(data_out).to_bytes(4, byteorder="big")
|
||||
|
||||
# Compress data frame
|
||||
|
@ -1132,21 +1130,6 @@ class DATA:
|
|||
while not self.data_frame_ack_received and static.ARQ_STATE:
|
||||
# we have self.tx_n_max_retries_per_burst attempts for sending a burst
|
||||
for self.tx_n_retry_of_burst in range(self.tx_n_max_retries_per_burst):
|
||||
# data_mode = mode
|
||||
# self.log.debug("[TNC] FIXED MODE:", mode=FREEDV_MODE(data_mode).name)
|
||||
|
||||
# we are doing a modulo check of transmission retries of the actual burst
|
||||
# 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 intent is to optimize speed by adapting to the current RF conditions.
|
||||
# if not self.tx_n_retry_of_burst % 2 and self.tx_n_retry_of_burst > 0:
|
||||
# self.speed_level = max(self.speed_level - 1, 0)
|
||||
|
||||
# if self.tx_n_retry_of_burst <= 1:
|
||||
# self.speed_level += 1
|
||||
# self.speed_level = max(self.speed_level + 1, len(self.mode_list) - 1)
|
||||
|
||||
# Bound speed level to:
|
||||
# - minimum of either the speed or the length of mode list - 1
|
||||
# - maximum of either the speed or zero
|
||||
|
@ -1166,9 +1149,6 @@ class DATA:
|
|||
# Payload information
|
||||
payload_per_frame = modem.get_bytes_per_frame(data_mode) - 2
|
||||
|
||||
# Tempbuffer list for storing our data frames
|
||||
tempbuffer = []
|
||||
|
||||
# Append data frames with n_frames_per_burst to tempbuffer
|
||||
# TODO: this part needs a complete rewrite!
|
||||
# n_frames_per_burst = 1 is working
|
||||
|
@ -1194,9 +1174,7 @@ class DATA:
|
|||
)
|
||||
frame = arqheader + extended_data_out
|
||||
|
||||
# Append frame to tempbuffer for transmission
|
||||
tempbuffer.append(frame)
|
||||
|
||||
tempbuffer = [frame]
|
||||
self.log.debug("[TNC] tempbuffer:", tempbuffer=tempbuffer)
|
||||
self.log.info(
|
||||
"[TNC] ARQ | TX | FRAMES",
|
||||
|
@ -1209,18 +1187,12 @@ class DATA:
|
|||
self.enqueue_frame_for_tx([t_buf_item], c2_mode=data_mode)
|
||||
|
||||
# After transmission finished, wait for an ACK or RPT frame
|
||||
# burstacktimeout = time.time() + self.burst_ack_timeout_seconds + 100
|
||||
# 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):
|
||||
# threading.Event().wait(0.01)
|
||||
|
||||
# burstacktimeout = time.time() + self.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
|
||||
while (
|
||||
static.ARQ_STATE
|
||||
and not self.burst_ack
|
||||
and not self.burst_nack
|
||||
and not self.rpt_request_received
|
||||
and not self.data_frame_ack_received
|
||||
):
|
||||
threading.Event().wait(0.01)
|
||||
|
||||
|
@ -1236,20 +1208,15 @@ class DATA:
|
|||
if self.burst_nack:
|
||||
self.burst_nack = False # reset nack state
|
||||
|
||||
# not yet implemented
|
||||
if self.rpt_request_received:
|
||||
pass
|
||||
|
||||
if self.data_frame_ack_received:
|
||||
self.log.debug(
|
||||
"[TNC] arq_transmit: Received FRAME ACK. Sending next chunk."
|
||||
"[TNC] arq_transmit: Received FRAME ACK. Braking 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....")
|
||||
self.log.debug(
|
||||
"[TNC] arq_transmit: ARQ State changed to FALSE. Breaking retry loop."
|
||||
)
|
||||
|
@ -1265,7 +1232,6 @@ class DATA:
|
|||
maxretries=self.tx_n_max_retries_per_burst,
|
||||
overflows=static.BUFFER_OVERFLOW_COUNTER,
|
||||
)
|
||||
# End of FOR loop
|
||||
|
||||
# update buffer position
|
||||
bufferposition = bufferposition_end
|
||||
|
@ -1296,66 +1262,77 @@ class DATA:
|
|||
if self.data_frame_ack_received and bufferposition > len(data_out):
|
||||
self.log.debug("[TNC] arq_tx: Last fragment sent and acknowledged.")
|
||||
break
|
||||
# GOING TO NEXT ITERATION
|
||||
# GOING TO NEXT ITERATION
|
||||
|
||||
if self.data_frame_ack_received:
|
||||
# we need to wait until sending "transmitted" state
|
||||
# gui database is too slow for handling this within 0.001 seconds
|
||||
# so let's sleep a little
|
||||
threading.Event().wait(0.2)
|
||||
self.send_data_to_socket_queue(
|
||||
freedata="tnc-message",
|
||||
arq="transmission",
|
||||
status="transmitted",
|
||||
uuid=self.transmission_uuid,
|
||||
percent=static.ARQ_TRANSMISSION_PERCENT,
|
||||
bytesperminute=static.ARQ_BYTES_PER_MINUTE,
|
||||
compression=static.ARQ_COMPRESSION_FACTOR,
|
||||
finished=static.ARQ_SECONDS_UNTIL_FINISH,
|
||||
mycallsign=str(self.mycallsign, 'UTF-8'),
|
||||
dxcallsign=str(self.dxcallsign, 'UTF-8'),
|
||||
irs=helpers.bool_to_string(self.is_IRS)
|
||||
)
|
||||
|
||||
self.log.info(
|
||||
"[TNC] ARQ | TX | DATA TRANSMITTED!",
|
||||
BytesPerMinute=static.ARQ_BYTES_PER_MINUTE,
|
||||
total_bytes=static.TOTAL_BYTES,
|
||||
BitsPerSecond=static.ARQ_BITS_PER_SECOND,
|
||||
overflows=static.BUFFER_OVERFLOW_COUNTER,
|
||||
|
||||
)
|
||||
|
||||
# finally do an arq cleanup
|
||||
self.arq_cleanup()
|
||||
|
||||
self.arq_transmit_success()
|
||||
else:
|
||||
self.send_data_to_socket_queue(
|
||||
freedata="tnc-message",
|
||||
arq="transmission",
|
||||
status="failed",
|
||||
uuid=self.transmission_uuid,
|
||||
percent=static.ARQ_TRANSMISSION_PERCENT,
|
||||
bytesperminute=static.ARQ_BYTES_PER_MINUTE,
|
||||
compression=static.ARQ_COMPRESSION_FACTOR,
|
||||
mycallsign=str(self.mycallsign, 'UTF-8'),
|
||||
dxcallsign=str(self.dxcallsign, 'UTF-8'),
|
||||
irs=helpers.bool_to_string(self.is_IRS)
|
||||
)
|
||||
|
||||
self.log.info(
|
||||
"[TNC] ARQ | TX | TRANSMISSION FAILED OR TIME OUT!",
|
||||
overflows=static.BUFFER_OVERFLOW_COUNTER,
|
||||
)
|
||||
|
||||
|
||||
self.stop_transmission()
|
||||
self.arq_transmit_failed()
|
||||
|
||||
if TESTMODE:
|
||||
# Quit after transmission
|
||||
self.log.debug("[TNC] TESTMODE: arq_transmit exiting.")
|
||||
sys.exit(0)
|
||||
|
||||
def arq_transmit_success(self):
|
||||
"""
|
||||
will be called if we successfully transmitted all of queued data
|
||||
|
||||
"""
|
||||
# we need to wait until sending "transmitted" state
|
||||
# gui database is too slow for handling this within 0.001 seconds
|
||||
# so let's sleep a little
|
||||
threading.Event().wait(0.2)
|
||||
self.send_data_to_socket_queue(
|
||||
freedata="tnc-message",
|
||||
arq="transmission",
|
||||
status="transmitted",
|
||||
uuid=self.transmission_uuid,
|
||||
percent=static.ARQ_TRANSMISSION_PERCENT,
|
||||
bytesperminute=static.ARQ_BYTES_PER_MINUTE,
|
||||
compression=static.ARQ_COMPRESSION_FACTOR,
|
||||
finished=static.ARQ_SECONDS_UNTIL_FINISH,
|
||||
mycallsign=str(self.mycallsign, 'UTF-8'),
|
||||
dxcallsign=str(self.dxcallsign, 'UTF-8'),
|
||||
irs=helpers.bool_to_string(self.is_IRS)
|
||||
)
|
||||
|
||||
self.log.info(
|
||||
"[TNC] ARQ | TX | DATA TRANSMITTED!",
|
||||
BytesPerMinute=static.ARQ_BYTES_PER_MINUTE,
|
||||
total_bytes=static.TOTAL_BYTES,
|
||||
BitsPerSecond=static.ARQ_BITS_PER_SECOND,
|
||||
overflows=static.BUFFER_OVERFLOW_COUNTER,
|
||||
|
||||
)
|
||||
|
||||
# finally do an arq cleanup
|
||||
self.arq_cleanup()
|
||||
|
||||
def arq_transmit_failed(self):
|
||||
"""
|
||||
will be called if we not successfully transmitted all of queued data
|
||||
"""
|
||||
self.send_data_to_socket_queue(
|
||||
freedata="tnc-message",
|
||||
arq="transmission",
|
||||
status="failed",
|
||||
uuid=self.transmission_uuid,
|
||||
percent=static.ARQ_TRANSMISSION_PERCENT,
|
||||
bytesperminute=static.ARQ_BYTES_PER_MINUTE,
|
||||
compression=static.ARQ_COMPRESSION_FACTOR,
|
||||
mycallsign=str(self.mycallsign, 'UTF-8'),
|
||||
dxcallsign=str(self.dxcallsign, 'UTF-8'),
|
||||
irs=helpers.bool_to_string(self.is_IRS)
|
||||
)
|
||||
|
||||
self.log.info(
|
||||
"[TNC] ARQ | TX | TRANSMISSION FAILED OR TIME OUT!",
|
||||
overflows=static.BUFFER_OVERFLOW_COUNTER,
|
||||
)
|
||||
|
||||
self.stop_transmission()
|
||||
|
||||
def burst_ack_nack_received(self, data_in: bytes) -> None:
|
||||
"""
|
||||
Received an ACK/NACK for a transmitted frame, keep track and
|
||||
|
@ -1390,6 +1367,8 @@ class DATA:
|
|||
self.burst_nack_counter = 0
|
||||
# Reset n retries per burst counter
|
||||
self.n_retries_per_burst = 0
|
||||
|
||||
self.burst_ack_snr = helpers.snr_from_bytes(data_in[2:3])
|
||||
else:
|
||||
# Decrease speed level if we received a burst nack
|
||||
# self.speed_level = max(self.speed_level - 1, 0)
|
||||
|
@ -1397,7 +1376,7 @@ class DATA:
|
|||
self.burst_nack = True
|
||||
# Increment burst nack counter
|
||||
self.burst_nack_counter += 1
|
||||
desc = "nack"
|
||||
self.burst_ack_snr = 'NaN'
|
||||
|
||||
# Update data_channel timestamp
|
||||
self.data_channel_last_received = int(time.time())
|
||||
|
@ -1408,12 +1387,6 @@ class DATA:
|
|||
self.speed_level = int.from_bytes(bytes(data_in[3:4]), "big")
|
||||
static.ARQ_SPEED_LEVEL = self.speed_level
|
||||
|
||||
#self.log.debug(
|
||||
# f"[TNC] burst_{desc}_received:",
|
||||
# speed_level=self.speed_level,
|
||||
# c2_mode=FREEDV_MODE(self.mode_list[self.speed_level]).name,
|
||||
#)
|
||||
|
||||
def frame_ack_received(
|
||||
self, data_in: bytes # pylint: disable=unused-argument
|
||||
) -> None:
|
||||
|
@ -1491,28 +1464,28 @@ class DATA:
|
|||
|
||||
"""
|
||||
# Only process data if we are in ARQ and BUSY state
|
||||
if static.ARQ_STATE and static.TNC_STATE == "BUSY":
|
||||
static.DXGRID = b'------'
|
||||
helpers.add_to_heard_stations(
|
||||
static.DXCALLSIGN,
|
||||
static.DXGRID,
|
||||
"DATA-CHANNEL",
|
||||
static.SNR,
|
||||
static.FREQ_OFFSET,
|
||||
static.HAMLIB_FREQUENCY,
|
||||
)
|
||||
if not static.ARQ_STATE or static.TNC_STATE != "BUSY":
|
||||
return
|
||||
static.DXGRID = b'------'
|
||||
helpers.add_to_heard_stations(
|
||||
static.DXCALLSIGN,
|
||||
static.DXGRID,
|
||||
"DATA-CHANNEL",
|
||||
static.SNR,
|
||||
static.FREQ_OFFSET,
|
||||
static.HAMLIB_FREQUENCY,
|
||||
)
|
||||
|
||||
self.rpt_request_received = True
|
||||
# Update data_channel timestamp
|
||||
self.data_channel_last_received = int(time.time())
|
||||
self.rpt_request_buffer = []
|
||||
self.rpt_request_received = True
|
||||
# Update data_channel timestamp
|
||||
self.data_channel_last_received = int(time.time())
|
||||
self.rpt_request_buffer = []
|
||||
|
||||
missing_area = bytes(data_in[3:12]) # 1:9
|
||||
missing_area = bytes(data_in[3:12]) # 1:9
|
||||
|
||||
for i in range(0, 6, 2):
|
||||
if not missing_area[i: i + 2].endswith(b"\x00\x00"):
|
||||
missing = missing_area[i: i + 2]
|
||||
self.rpt_request_buffer.insert(0, missing)
|
||||
for i in range(0, 6, 2):
|
||||
if not missing_area[i: i + 2].endswith(b"\x00\x00"):
|
||||
self.rpt_request_buffer.insert(0, missing_area[i: i + 2])
|
||||
|
||||
############################################################################################################
|
||||
# ARQ SESSION HANDLER
|
||||
|
@ -1800,7 +1773,6 @@ class DATA:
|
|||
data_in:bytes:
|
||||
|
||||
"""
|
||||
print(static.ARQ_SESSION_STATE)
|
||||
# We've arrived here from process_data which already checked that the frame
|
||||
# is intended for this station.
|
||||
# Close the session if the CRC matches the remote station in static.
|
||||
|
@ -2166,7 +2138,7 @@ class DATA:
|
|||
self.mode_list = self.mode_list_high_bw
|
||||
self.time_list = self.time_list_high_bw
|
||||
self.snr_list = self.snr_list_high_bw
|
||||
elif frametype == FR_TYPE.ARQ_DC_OPEN_W.value and static.LOW_BANDWIDTH_MODE:
|
||||
elif frametype == FR_TYPE.ARQ_DC_OPEN_W.value:
|
||||
# ISS(w) <-> IRS(n)
|
||||
constellation = "ISS(w) <-> IRS(n)"
|
||||
self.received_LOW_BANDWIDTH_MODE = False
|
||||
|
@ -2180,7 +2152,7 @@ class DATA:
|
|||
self.mode_list = self.mode_list_low_bw
|
||||
self.time_list = self.time_list_low_bw
|
||||
self.snr_list = self.snr_list_low_bw
|
||||
elif frametype == FR_TYPE.ARQ_DC_OPEN_N.value and static.LOW_BANDWIDTH_MODE:
|
||||
elif frametype == FR_TYPE.ARQ_DC_OPEN_N.value:
|
||||
# ISS(n) <-> IRS(n)
|
||||
constellation = "ISS(n) <-> IRS(n)"
|
||||
self.received_LOW_BANDWIDTH_MODE = True
|
||||
|
@ -2195,7 +2167,7 @@ class DATA:
|
|||
self.snr_list = self.snr_list_low_bw
|
||||
|
||||
# get mode which fits to given SNR
|
||||
# initially set speed_level 0 in case of really bad SNR and no matching mode
|
||||
# initially set speed_level 0 in case of bad SNR and no matching mode
|
||||
self.speed_level = 0
|
||||
for i in range(len(self.mode_list)):
|
||||
if static.SNR >= self.snr_list[i]:
|
||||
|
@ -2236,7 +2208,7 @@ class DATA:
|
|||
|
||||
# Reset data_channel/burst timestamps
|
||||
self.data_channel_last_received = int(time.time())
|
||||
self.burst_last_received = int(time.time() + 6) # we might need some more time so lets increase this
|
||||
self.burst_last_received = int(time.time() + 6) # we might need some more time so lets increase this
|
||||
|
||||
# Set ARQ State AFTER resetting timeouts
|
||||
# this avoids timeouts starting too early
|
||||
|
@ -2257,8 +2229,6 @@ class DATA:
|
|||
connection_frame[:1] = frametype
|
||||
connection_frame[1:2] = self.session_id
|
||||
connection_frame[8:9] = bytes([self.speed_level])
|
||||
|
||||
# For checking protocol version on the receiving side
|
||||
connection_frame[13:14] = bytes([static.ARQ_PROTOCOL_VERSION])
|
||||
|
||||
self.enqueue_frame_for_tx([connection_frame], c2_mode=FREEDV_MODE.datac0.value, copies=1, repeat_delay=0)
|
||||
|
@ -2287,7 +2257,7 @@ class DATA:
|
|||
|
||||
# Reset data_channel/burst timestamps once again for avoiding running into timeout
|
||||
self.data_channel_last_received = int(time.time())
|
||||
self.burst_last_received = int(time.time() + 6) # we might need some more time so lets increase this
|
||||
self.burst_last_received = int(time.time() + 6) # we might need some more time so lets increase this
|
||||
|
||||
def arq_received_channel_is_open(self, data_in: bytes) -> None:
|
||||
"""
|
||||
|
@ -2402,8 +2372,8 @@ class DATA:
|
|||
ping_frame[4:7] = helpers.get_crc_24(mycallsign)
|
||||
ping_frame[7:13] = helpers.callsign_to_bytes(mycallsign)
|
||||
|
||||
self.log.info("[TNC] ENABLE FSK", state=static.ENABLE_FSK)
|
||||
if static.ENABLE_FSK:
|
||||
self.log.info("[TNC] ENABLE FSK", state=static.ENABLE_FSK)
|
||||
self.enqueue_frame_for_tx([ping_frame], c2_mode=FREEDV_MODE.fsk_ldpc_0.value)
|
||||
else:
|
||||
self.enqueue_frame_for_tx([ping_frame], c2_mode=FREEDV_MODE.datac0.value)
|
||||
|
@ -2458,17 +2428,25 @@ class DATA:
|
|||
snr=str(static.SNR),
|
||||
)
|
||||
if static.RESPOND_TO_CALL:
|
||||
ping_frame = bytearray(self.length_sig0_frame)
|
||||
ping_frame[:1] = bytes([FR_TYPE.PING_ACK.value])
|
||||
ping_frame[1:4] = static.DXCALLSIGN_CRC
|
||||
ping_frame[4:7] = static.MYCALLSIGN_CRC
|
||||
ping_frame[7:11] = helpers.encode_grid(static.MYGRID.decode("UTF-8"))
|
||||
ping_frame[13:14] = helpers.snr_to_bytes(static.SNR)
|
||||
self.transmit_ping_ack()
|
||||
|
||||
if static.ENABLE_FSK:
|
||||
self.enqueue_frame_for_tx([ping_frame], c2_mode=FREEDV_MODE.fsk_ldpc_0.value)
|
||||
else:
|
||||
self.enqueue_frame_for_tx([ping_frame], c2_mode=FREEDV_MODE.datac0.value)
|
||||
def transmit_ping_ack(self):
|
||||
"""
|
||||
|
||||
transmit a ping ack frame
|
||||
called by def received_ping
|
||||
"""
|
||||
ping_frame = bytearray(self.length_sig0_frame)
|
||||
ping_frame[:1] = bytes([FR_TYPE.PING_ACK.value])
|
||||
ping_frame[1:4] = static.DXCALLSIGN_CRC
|
||||
ping_frame[4:7] = static.MYCALLSIGN_CRC
|
||||
ping_frame[7:11] = helpers.encode_grid(static.MYGRID.decode("UTF-8"))
|
||||
ping_frame[13:14] = helpers.snr_to_bytes(static.SNR)
|
||||
|
||||
if static.ENABLE_FSK:
|
||||
self.enqueue_frame_for_tx([ping_frame], c2_mode=FREEDV_MODE.fsk_ldpc_0.value)
|
||||
else:
|
||||
self.enqueue_frame_for_tx([ping_frame], c2_mode=FREEDV_MODE.datac0.value)
|
||||
|
||||
def received_ping_ack(self, data_in: bytes) -> None:
|
||||
"""
|
||||
|
@ -2611,9 +2589,9 @@ class DATA:
|
|||
beacon_frame[:1] = bytes([FR_TYPE.BEACON.value])
|
||||
beacon_frame[1:7] = helpers.callsign_to_bytes(self.mycallsign)
|
||||
beacon_frame[7:11] = helpers.encode_grid(static.MYGRID.decode("UTF-8"))
|
||||
self.log.info("[TNC] ENABLE FSK", state=static.ENABLE_FSK)
|
||||
|
||||
if static.ENABLE_FSK:
|
||||
self.log.info("[TNC] ENABLE FSK", state=static.ENABLE_FSK)
|
||||
self.enqueue_frame_for_tx(
|
||||
[beacon_frame],
|
||||
c2_mode=FREEDV_MODE.fsk_ldpc_0.value,
|
||||
|
@ -2691,10 +2669,10 @@ class DATA:
|
|||
cq_frame[1:7] = helpers.callsign_to_bytes(self.mycallsign)
|
||||
cq_frame[7:11] = helpers.encode_grid(static.MYGRID.decode("UTF-8"))
|
||||
|
||||
self.log.info("[TNC] ENABLE FSK", state=static.ENABLE_FSK)
|
||||
self.log.debug("[TNC] CQ Frame:", data=[cq_frame])
|
||||
|
||||
if static.ENABLE_FSK:
|
||||
self.log.info("[TNC] ENABLE FSK", state=static.ENABLE_FSK)
|
||||
self.enqueue_frame_for_tx([cq_frame], c2_mode=FREEDV_MODE.fsk_ldpc_0.value)
|
||||
else:
|
||||
self.enqueue_frame_for_tx([cq_frame], c2_mode=FREEDV_MODE.datac0.value, copies=1, repeat_delay=0)
|
||||
|
@ -2766,7 +2744,6 @@ class DATA:
|
|||
qrv_frame[7:11] = helpers.encode_grid(static.MYGRID.decode("UTF-8"))
|
||||
qrv_frame[11:12] = helpers.snr_to_bytes(static.SNR)
|
||||
|
||||
|
||||
if static.ENABLE_FSK:
|
||||
self.log.info("[TNC] ENABLE FSK", state=static.ENABLE_FSK)
|
||||
self.enqueue_frame_for_tx([qrv_frame], c2_mode=FREEDV_MODE.fsk_ldpc_0.value)
|
||||
|
@ -3012,6 +2989,7 @@ class DATA:
|
|||
self.data_frame_ack_received = state
|
||||
|
||||
def set_listening_modes(self, enable_sig0: bool, enable_sig1: bool, mode: int) -> None:
|
||||
# sourcery skip: extract-duplicate-method
|
||||
"""
|
||||
Function for setting the data modes we are listening to for saving cpu power
|
||||
|
||||
|
@ -3087,20 +3065,14 @@ class DATA:
|
|||
print(time.time() - (self.burst_last_received + self.time_list[self.speed_level]))
|
||||
|
||||
print("-----------------------")
|
||||
if modem_error_state:
|
||||
self.log.warning(
|
||||
"[TNC] Decoding Error",
|
||||
attempt=self.n_retries_per_burst,
|
||||
max_attempts=self.rx_n_max_retries_per_burst,
|
||||
speed_level=self.speed_level,
|
||||
)
|
||||
else:
|
||||
self.log.warning(
|
||||
"[TNC] Burst timeout",
|
||||
attempt=self.n_retries_per_burst,
|
||||
max_attempts=self.rx_n_max_retries_per_burst,
|
||||
speed_level=self.speed_level,
|
||||
)
|
||||
|
||||
self.log.warning(
|
||||
"[TNC] Burst decoding error or timeout",
|
||||
attempt=self.n_retries_per_burst,
|
||||
max_attempts=self.rx_n_max_retries_per_burst,
|
||||
speed_level=self.speed_level,
|
||||
modem_error_state=modem_error_state
|
||||
)
|
||||
|
||||
# reset self.burst_last_received
|
||||
self.burst_last_received = time.time() + self.time_list[self.speed_level]
|
||||
|
|
21
tnc/modem.py
21
tnc/modem.py
|
@ -537,17 +537,22 @@ class RF:
|
|||
x = np.frombuffer(txbuffer, dtype=np.int16)
|
||||
if static.AUDIO_AUTO_TUNE:
|
||||
if static.HAMLIB_ALC == 0.0:
|
||||
static.TX_AUDIO_LEVEL = static.TX_AUDIO_LEVEL + 30
|
||||
elif 0.0 < static.HAMLIB_ALC <= 0.8:
|
||||
print("0.001 > static.HAMLIB_ALC <= 0.8")
|
||||
static.TX_AUDIO_LEVEL = static.TX_AUDIO_LEVEL + 20
|
||||
self.log.debug("[MDM] AUDIO TUNE", audio_level=str(static.TX_AUDIO_LEVEL), alc_level=str(static.HAMLIB_ALC))
|
||||
elif 0.8 < static.HAMLIB_ALC < 0.99:
|
||||
print("0.8 > static.HAMLIB_ALC <= 0.99")
|
||||
elif 0.0 < static.HAMLIB_ALC <= 0.1:
|
||||
print("0.0 < static.HAMLIB_ALC <= 0.1")
|
||||
static.TX_AUDIO_LEVEL = static.TX_AUDIO_LEVEL + 2
|
||||
self.log.debug("[MDM] AUDIO TUNE", audio_level=str(static.TX_AUDIO_LEVEL), alc_level=str(static.HAMLIB_ALC))
|
||||
elif 1.0 <= static.HAMLIB_ALC:
|
||||
static.TX_AUDIO_LEVEL = static.TX_AUDIO_LEVEL - 2
|
||||
elif 0.1 < static.HAMLIB_ALC < 0.2:
|
||||
print("0.1 < static.HAMLIB_ALC < 0.2")
|
||||
static.TX_AUDIO_LEVEL = static.TX_AUDIO_LEVEL
|
||||
self.log.debug("[MDM] AUDIO TUNE", audio_level=str(static.TX_AUDIO_LEVEL), alc_level=str(static.HAMLIB_ALC))
|
||||
elif 0.2 < static.HAMLIB_ALC < 0.99:
|
||||
print("0.2 < static.HAMLIB_ALC < 0.99")
|
||||
static.TX_AUDIO_LEVEL = static.TX_AUDIO_LEVEL - 20
|
||||
self.log.debug("[MDM] AUDIO TUNE", audio_level=str(static.TX_AUDIO_LEVEL), alc_level=str(static.HAMLIB_ALC))
|
||||
elif 1.0 >=static.HAMLIB_ALC:
|
||||
print("1.0 >= static.HAMLIB_ALC")
|
||||
static.TX_AUDIO_LEVEL = static.TX_AUDIO_LEVEL - 40
|
||||
self.log.debug("[MDM] AUDIO TUNE", audio_level=str(static.TX_AUDIO_LEVEL), alc_level=str(static.HAMLIB_ALC))
|
||||
else:
|
||||
self.log.debug("[MDM] AUDIO TUNE", audio_level=str(static.TX_AUDIO_LEVEL), alc_level=str(static.HAMLIB_ALC))
|
||||
|
|
1255
tnc/sock.py
1255
tnc/sock.py
File diff suppressed because it is too large
Load diff
10
tnc/stats.py
10
tnc/stats.py
|
@ -22,16 +22,10 @@ class stats():
|
|||
self.explorer_url = "https://api.freedata.app/stats.php"
|
||||
|
||||
def push(self, frame_nack_counter, status, duration):
|
||||
if status in ["crc_error"]:
|
||||
crcerror = True
|
||||
else:
|
||||
crcerror = False
|
||||
|
||||
crcerror = status in ["crc_error"]
|
||||
# get avg snr
|
||||
try:
|
||||
snr_raw = []
|
||||
for item in static.SPEED_LIST:
|
||||
snr_raw.append(item["snr"])
|
||||
snr_raw = [item["snr"] for item in static.SPEED_LIST]
|
||||
avg_snr = round(sum(snr_raw) / len(snr_raw), 2 )
|
||||
except Exception:
|
||||
avg_snr = 0
|
||||
|
|
|
@ -6,6 +6,7 @@ Created on Fri Dec 11 21:53:35 2020
|
|||
@author: parallels
|
||||
"""
|
||||
|
||||
|
||||
import socket
|
||||
import sys
|
||||
import argparse
|
||||
|
@ -13,7 +14,7 @@ import time
|
|||
|
||||
# --------------------------------------------GET PARAMETER INPUTS
|
||||
parser = argparse.ArgumentParser(description='Simons TEST TNC')
|
||||
parser.add_argument('--port', dest="socket_port", default=3000, help="Set the port, the socket is listening on.", type=int)
|
||||
parser.add_argument('--port', dest="socket_port", default=3000, help="Set the port, the socket is listening on.", type=int)
|
||||
parser.add_argument('--data', dest="data", default=False, help="data", type=str)
|
||||
|
||||
args = parser.parse_args()
|
||||
|
@ -29,6 +30,6 @@ while True:
|
|||
sock.connect((ip, port))
|
||||
sock.sendall(bytes(message, 'utf-8') + b'\n')
|
||||
response = str(sock.recv(1024), 'utf-8')
|
||||
print("CMD: {}".format(response))
|
||||
print(f"CMD: {response}")
|
||||
False
|
||||
break
|
||||
|
|
Loading…
Reference in a new issue