mirror of
https://github.com/DJ2LS/FreeDATA
synced 2024-05-14 08:04:33 +00:00
Merge pull request #98 from DJ2LS/ls-arq
ARQ and protocol optimization This PR isnt't completed yet, but the new ARQ protocol can be tested now. I wasn't able solving the problems with N>1 frames per burst because of additional other problems. I decided merging this PR so other topics can be solved. N>1 frames per burst is a future milestone which will need a lot of time then. However, the basics are done!
This commit is contained in:
commit
96b0f7cb3a
|
@ -128,6 +128,18 @@ add_test(NAME highsnr_virtual5_P_P_multi_callback_outside
|
||||||
./test_virtual4b.sh")
|
./test_virtual4b.sh")
|
||||||
set_tests_properties(highsnr_virtual5_P_P_multi_callback_outside PROPERTIES PASS_REGULAR_EXPRESSION "DATAC0: 2/4 DATAC1: 2/4 DATAC3: 2/4")
|
set_tests_properties(highsnr_virtual5_P_P_multi_callback_outside PROPERTIES PASS_REGULAR_EXPRESSION "DATAC0: 2/4 DATAC1: 2/4 DATAC3: 2/4")
|
||||||
|
|
||||||
|
# ARQ test short
|
||||||
|
|
||||||
|
add_test(NAME highsnr_ARQ_short
|
||||||
|
COMMAND sh -c "export LD_LIBRARY_PATH=${CODEC2_BUILD_DIR}/src;
|
||||||
|
PATH=$PATH:${CODEC2_BUILD_DIR}/src;
|
||||||
|
cd ${CMAKE_CURRENT_SOURCE_DIR}/test;
|
||||||
|
python3 test_arq_short.py")
|
||||||
|
|
||||||
|
set_tests_properties(highsnr_ARQ_short PROPERTIES PASS_REGULAR_EXPRESSION "ARQ | TX | DATA TRANSMITTED!")
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
|
|
BIN
documentation/FreeDATA-Frametypes.ods
Normal file
BIN
documentation/FreeDATA-Frametypes.ods
Normal file
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
|
@ -648,11 +648,11 @@ ipcRenderer.on('action-update-tnc-state', (event, arg) => {
|
||||||
}
|
}
|
||||||
|
|
||||||
// ARQ STATE
|
// ARQ STATE
|
||||||
if (arg.arq_state == 'DATA') {
|
if (arg.arq_state == 'True') {
|
||||||
document.getElementById("arq_state").className = "btn btn-warning";
|
document.getElementById("arq_state").className = "btn btn-warning";
|
||||||
document.getElementById("startTransmission").disabled = true
|
document.getElementById("startTransmission").disabled = true
|
||||||
document.getElementById("stopTransmission").disabled = false
|
document.getElementById("stopTransmission").disabled = false
|
||||||
} else if (arg.arq_state == 'IDLE') {
|
} else if (arg.arq_state == 'False') {
|
||||||
document.getElementById("arq_state").className = "btn btn-secondary";
|
document.getElementById("arq_state").className = "btn btn-secondary";
|
||||||
document.getElementById("startTransmission").disabled = false
|
document.getElementById("startTransmission").disabled = false
|
||||||
document.getElementById("stopTransmission").disabled = true
|
document.getElementById("stopTransmission").disabled = true
|
||||||
|
@ -701,6 +701,15 @@ ipcRenderer.on('action-update-tnc-state', (event, arg) => {
|
||||||
}
|
}
|
||||||
document.getElementById("bytes_per_min").innerHTML = arq_bytes_per_minute
|
document.getElementById("bytes_per_min").innerHTML = arq_bytes_per_minute
|
||||||
|
|
||||||
|
// SET BYTES PER MINUTE COMPRESSED
|
||||||
|
if (typeof(arg.arq_bytes_per_minute) == 'undefined') {
|
||||||
|
var arq_bytes_per_minute_compressed = 0
|
||||||
|
} else {
|
||||||
|
var arq_bytes_per_minute_compressed = Math.round(arg.arq_bytes_per_minute * arg.arq_compression_factor)
|
||||||
|
}
|
||||||
|
document.getElementById("bytes_per_min_compressed").innerHTML = arq_bytes_per_minute_compressed
|
||||||
|
|
||||||
|
|
||||||
// SET TOTAL BYTES
|
// SET TOTAL BYTES
|
||||||
if (typeof(arg.total_bytes) == 'undefined') {
|
if (typeof(arg.total_bytes) == 'undefined') {
|
||||||
var total_bytes = 0
|
var total_bytes = 0
|
||||||
|
|
|
@ -152,6 +152,7 @@ client.on('data', function(data) {
|
||||||
arq_rx_n_current_arq_frame: data['ARQ_RX_N_CURRENT_ARQ_FRAME'],
|
arq_rx_n_current_arq_frame: data['ARQ_RX_N_CURRENT_ARQ_FRAME'],
|
||||||
arq_n_arq_frames_per_data_frame: data['ARQ_N_ARQ_FRAMES_PER_DATA_FRAME'],
|
arq_n_arq_frames_per_data_frame: data['ARQ_N_ARQ_FRAMES_PER_DATA_FRAME'],
|
||||||
arq_bytes_per_minute: data['ARQ_BYTES_PER_MINUTE'],
|
arq_bytes_per_minute: data['ARQ_BYTES_PER_MINUTE'],
|
||||||
|
arq_compression_factor: data['ARQ_COMPRESSION_FACTOR'],
|
||||||
total_bytes: data['TOTAL_BYTES'],
|
total_bytes: data['TOTAL_BYTES'],
|
||||||
arq_transmission_percent: data['ARQ_TRANSMISSION_PERCENT'],
|
arq_transmission_percent: data['ARQ_TRANSMISSION_PERCENT'],
|
||||||
stations: data['STATIONS'],
|
stations: data['STATIONS'],
|
||||||
|
|
|
@ -897,7 +897,8 @@
|
||||||
<div class="input-group input-group-sm"> <span class="input-group-text" id="basic-addon1">Mode</span>
|
<div class="input-group input-group-sm"> <span class="input-group-text" id="basic-addon1">Mode</span>
|
||||||
<select class="form-select form-select-sm" aria-label=".form-select-sm" id="datamode">
|
<select class="form-select form-select-sm" aria-label=".form-select-sm" id="datamode">
|
||||||
<!--<option value="14">low SNR (DC0)</option>-->
|
<!--<option value="14">low SNR (DC0)</option>-->
|
||||||
<option selected value="10">HIGH SNR (DC1)</option>
|
<option selected value="255">AUTO</option>
|
||||||
|
<option value="10">HIGH SNR (DC1)</option>
|
||||||
<option value="12">MED SNR (DC3)</option>
|
<option value="12">MED SNR (DC3)</option>
|
||||||
</select>
|
</select>
|
||||||
</div>
|
</div>
|
||||||
|
@ -951,7 +952,7 @@
|
||||||
<!---------------------------------------------------------------------- FOOTER AREA ------------------------------------------------------------>
|
<!---------------------------------------------------------------------- FOOTER AREA ------------------------------------------------------------>
|
||||||
<nav class="navbar fixed-bottom navbar-light bg-light">
|
<nav class="navbar fixed-bottom navbar-light bg-light">
|
||||||
<div class="container-fluid">
|
<div class="container-fluid">
|
||||||
<div class="btn-toolbar" style="width:20%" role="toolbar">
|
<div class="btn-toolbar" role="toolbar">
|
||||||
<div class="btn-group btn-group-sm me-2" role="group">
|
<div class="btn-group btn-group-sm me-2" role="group">
|
||||||
<button class="btn btn-secondary" id="ptt_state" type="button" data-bs-placement="top" data-bs-toggle="tooltip" data-bs-html="true" title="PTT state:<strong class='text-success'>RECEIVING</strong> / <strong class='text-danger'>TRANSMITTING</strong>">
|
<button class="btn btn-secondary" id="ptt_state" type="button" data-bs-placement="top" data-bs-toggle="tooltip" data-bs-html="true" title="PTT state:<strong class='text-success'>RECEIVING</strong> / <strong class='text-danger'>TRANSMITTING</strong>">
|
||||||
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="currentColor" class="bi bi-broadcast-pin" viewBox="0 0 16 16">
|
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="currentColor" class="bi bi-broadcast-pin" viewBox="0 0 16 16">
|
||||||
|
@ -975,21 +976,31 @@
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
<div class="container-fluid" style="width:30%">
|
|
||||||
|
<div class="container-fluid p-0" style="width:15rem">
|
||||||
<div class="input-group input-group-sm">
|
<div class="input-group input-group-sm">
|
||||||
<!--<span class="input-group-text" id="basic-addon1"><strong>Freq</strong></span>--> <span class="input-group-text" id="frequency">---</span>
|
<!--<span class="input-group-text" id="basic-addon1"><strong>Freq</strong></span>--> <span class="input-group-text" id="frequency">---</span>
|
||||||
<!--<span class="input-group-text" id="basic-addon1"><strong>Mode</strong></span>--> <span class="input-group-text" id="mode">---</span>
|
<!--<span class="input-group-text" id="basic-addon1"><strong>Mode</strong></span>--> <span class="input-group-text" id="mode">---</span>
|
||||||
<!--<span class="input-group-text" id="basic-addon1"><strong>BW</strong></span>--> <span class="input-group-text" id="bandwith">---</span>
|
<!--<span class="input-group-text" id="basic-addon1"><strong>BW</strong></span>--> <span class="input-group-text" id="bandwith">---</span>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="container-fluid" style="width:30%">
|
|
||||||
|
<div class="container-fluid p-0" style="width:12rem">
|
||||||
<div class="input-group input-group-sm"> <span class="input-group-text" id="basic-addon1"><strong>B/min</strong></span>
|
<div class="input-group input-group-sm"> <span class="input-group-text" id="basic-addon1"><strong>B/min</strong></span>
|
||||||
<span class="input-group-text" id="bytes_per_min">---</span>
|
<span class="input-group-text" id="bytes_per_min" data-bs-placement="bottom" data-bs-toggle="tooltip" data-bs-html="false" title="raw data rate modem in bytes per minute">---</span>
|
||||||
<span class="input-group-text" id="basic-addon1"><strong>Total</strong></span>
|
<span class="input-group-text" id="bytes_per_min_compressed" data-bs-placement="bottom" data-bs-toggle="tooltip" data-bs-html="false" title="data rate including file compression in bytes per minute">---</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="container-fluid p-0" style="width:10rem">
|
||||||
|
|
||||||
|
<div class="input-group input-group-sm"> <span class="input-group-text" id="basic-addon1"><strong>kBytes</strong></span>
|
||||||
<span class="input-group-text" id="total_bytes">---</span>
|
<span class="input-group-text" id="total_bytes">---</span>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
<div class="container-fluid" style="width:20%">
|
<div class="container-fluid p-0" style="width:15rem">
|
||||||
|
|
||||||
<div class="progress" style="height: 30px;">
|
<div class="progress" style="height: 30px;">
|
||||||
<div class="progress-bar progress-bar-striped bg-primary" id="transmission_progress" role="progressbar" style="width: 0%" aria-valuenow="0" aria-valuemin="0" aria-valuemax="100"></div>
|
<div class="progress-bar progress-bar-striped bg-primary" id="transmission_progress" role="progressbar" style="width: 0%" aria-valuenow="0" aria-valuemin="0" aria-valuemax="100"></div>
|
||||||
<!--<p class="justify-content-center d-flex position-absolute w-100">PROGRESS</p>-->
|
<!--<p class="justify-content-center d-flex position-absolute w-100">PROGRESS</p>-->
|
||||||
|
|
|
@ -1,3 +1,34 @@
|
||||||
|
|
||||||
|
# Instructions
|
||||||
|
|
||||||
|
1. Install:
|
||||||
|
```
|
||||||
|
cd FreeDATA
|
||||||
|
mkdir build
|
||||||
|
cd build
|
||||||
|
cmake -DCODEC2_BUILD_DIR=$HOME/codec2/build_linux ..
|
||||||
|
```
|
||||||
|
2. List available tests:
|
||||||
|
```
|
||||||
|
ctest -N
|
||||||
|
Test project /home/david/FreeDATA/build
|
||||||
|
Test #1: 000_audio_tests
|
||||||
|
Test #2: 001_highsnr_stdio_audio
|
||||||
|
|
||||||
|
Total Tests: 2
|
||||||
|
```
|
||||||
|
3. Run tests:
|
||||||
|
```
|
||||||
|
ctest --output-on-failure
|
||||||
|
```
|
||||||
|
4. Run tests verbosely:
|
||||||
|
```
|
||||||
|
ctest -V
|
||||||
|
```
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
# 001_HIGHSNR_STDIO_AUDIO TEST SUITE
|
# 001_HIGHSNR_STDIO_AUDIO TEST SUITE
|
||||||
|
|
||||||
1. Install
|
1. Install
|
||||||
|
|
28
test/test_arq_short.py
Normal file
28
test/test_arq_short.py
Normal file
|
@ -0,0 +1,28 @@
|
||||||
|
#!/usr/bin/env python3
|
||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
"""
|
||||||
|
Created on Wed Dec 23 07:04:24 2020
|
||||||
|
|
||||||
|
@author: DJ2LS
|
||||||
|
"""
|
||||||
|
|
||||||
|
import sys
|
||||||
|
sys.path.insert(0,'..')
|
||||||
|
sys.path.insert(0,'../tnc')
|
||||||
|
import data_handler
|
||||||
|
import argparse
|
||||||
|
import codec2
|
||||||
|
|
||||||
|
parser = argparse.ArgumentParser(description='ARQ TEST')
|
||||||
|
parser.add_argument('--mode', dest="FREEDV_MODE", type=str, choices=['datac0', 'datac1', 'datac3'])
|
||||||
|
parser.add_argument('--framesperburst', dest="N_FRAMES_PER_BURST", default=1, type=int)
|
||||||
|
args = parser.parse_args()
|
||||||
|
|
||||||
|
|
||||||
|
bytes_out = b'{"dt":"f","fn":"zeit.txt","ft":"text\\/plain","d":"data:text\\/plain;base64,MyBtb2Rlcywgb2huZSBjbGFzcwowLjAwMDk2OTQ4MTE4MDk5MTg0MTcKCjIgbW9kZXMsIG9obmUgY2xhc3MKMC4wMDA5NjY1NDUxODkxMjI1Mzk0CgoxIG1vZGUsIG9obmUgY2xhc3MKMC4wMDA5NjY5NzY1NTU4Nzc4MjA5CgMyBtb2Rlcywgb2huZSBjbGFzcwowLjAwMDk2OTQ4MTE4MDk5MTg0MTcKCjIgbW9kZXMsIG9obmUgY2xhc3MKMC4wMDA5NjY1NDUxODkxMjI1Mzk0CgoxIG1vZGUsIG9obmUgY2xhc3MKMC4wMDA5NjY5NzY1NTU4Nzc4MjA5Cg=MyBtb2Rlcywgb2huZSBjbGFzcwowLjAwMDk2OTQ4MTE4MDk5MTg0MTcKCjIgbW9kZXMsIG9obmUgY2xhc3MKMC4wMDA5NjY1NDUxODkxMjI1Mzk0CgoxIG1vZGUsIG9obmUgY2xhc3MKMC4wMDA5NjY5NzY1NTU4Nzc4MjA5CgMyBtb2Rlcywgb2huZSBjbGFzcwowLjAwMDk2OTQ4MTE4MDk5MTg0MTcKCjIgbW9kZXMsIG9obmUgY2xhc3MKMC4wMDA5NjY1NDUxODkxMjI1Mzk0CgoxIG1vZGUsIG9obmUgY2xhc3MKMC4wMDA5NjY5NzY1NTU4Nzc4MjA5CgMyBtb2Rlcywgb2huZSBjbGFzcwowLjAwMDk2OTQ4MTE4MDk5MTg0MTcKCjIgbW9kZXMsIG9obmUgY2xhc3MKMC4wMDA5NjY1NDUxODkxMjI1Mzk0CgoxIG1vZGUsIG9obmUgY2xhc3MKMC4wMDA5NjY5NzY1NTU4Nzc4MjA5Cg=","crc":"123123123"}'
|
||||||
|
|
||||||
|
mode = codec2.freedv_get_mode(args.FREEDV_MODE)
|
||||||
|
n_frames_per_burst = args.N_FRAMES_PER_BURST
|
||||||
|
|
||||||
|
data_handler.TESTMODE = True
|
||||||
|
data_handler.open_dc_and_transmit(bytes_out, mode, n_frames_per_burst)
|
|
@ -1,22 +0,0 @@
|
||||||
#!/usr/bin/env python3
|
|
||||||
# -*- coding: utf-8 -*-
|
|
||||||
"""
|
|
||||||
Created on Wed Dec 23 07:04:24 2020
|
|
||||||
|
|
||||||
@author: DJ2LS
|
|
||||||
"""
|
|
||||||
|
|
||||||
import sys
|
|
||||||
sys.path.insert(0,'..')
|
|
||||||
sys.path.insert(0,'../tnc')
|
|
||||||
import data_handler
|
|
||||||
|
|
||||||
|
|
||||||
teststring = b'HELLO WORLD'
|
|
||||||
|
|
||||||
data_handler.arq_transmit(teststring, 10, 1)
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
File diff suppressed because it is too large
Load diff
96
tnc/modem.py
96
tnc/modem.py
|
@ -18,6 +18,7 @@ import numpy as np
|
||||||
import helpers
|
import helpers
|
||||||
import static
|
import static
|
||||||
import data_handler
|
import data_handler
|
||||||
|
|
||||||
import re
|
import re
|
||||||
import queue
|
import queue
|
||||||
import codec2
|
import codec2
|
||||||
|
@ -233,17 +234,17 @@ class RF():
|
||||||
mod_out_postamble = create_string_buffer(n_tx_postamble_modem_samples * 2)
|
mod_out_postamble = create_string_buffer(n_tx_postamble_modem_samples * 2)
|
||||||
|
|
||||||
# add empty data to handle ptt toggle time
|
# add empty data to handle ptt toggle time
|
||||||
data_delay_seconds = 250
|
data_delay_mseconds = 0 #miliseconds
|
||||||
data_delay = int(self.MODEM_SAMPLE_RATE*(data_delay_seconds/1000))
|
data_delay = int(self.MODEM_SAMPLE_RATE*(data_delay_mseconds/1000))
|
||||||
mod_out_silence = create_string_buffer(data_delay*2)
|
mod_out_silence = create_string_buffer(data_delay*2)
|
||||||
txbuffer = bytes(mod_out_silence)
|
txbuffer = bytes(mod_out_silence)
|
||||||
|
|
||||||
for i in range(1,repeats+1):
|
for i in range(1,repeats+1):
|
||||||
|
|
||||||
# write preamble to txbuffer
|
# write preamble to txbuffer
|
||||||
codec2.api.freedv_rawdatapreambletx(freedv, mod_out_preamble)
|
codec2.api.freedv_rawdatapreambletx(freedv, mod_out_preamble)
|
||||||
time.sleep(0.01)
|
time.sleep(0.05)
|
||||||
txbuffer += bytes(mod_out_preamble)
|
txbuffer += bytes(mod_out_preamble)
|
||||||
|
|
||||||
|
|
||||||
# create modulaton for n frames in list
|
# create modulaton for n frames in list
|
||||||
for n in range(0,len(frames)):
|
for n in range(0,len(frames)):
|
||||||
|
@ -260,19 +261,19 @@ class RF():
|
||||||
|
|
||||||
data = (ctypes.c_ubyte * bytes_per_frame).from_buffer_copy(buffer)
|
data = (ctypes.c_ubyte * bytes_per_frame).from_buffer_copy(buffer)
|
||||||
codec2.api.freedv_rawdatatx(freedv,mod_out,data) # modulate DATA and save it into mod_out pointer
|
codec2.api.freedv_rawdatatx(freedv,mod_out,data) # modulate DATA and save it into mod_out pointer
|
||||||
time.sleep(0.01)
|
time.sleep(0.05)
|
||||||
txbuffer += bytes(mod_out)
|
txbuffer += bytes(mod_out)
|
||||||
|
|
||||||
|
|
||||||
# append postamble to txbuffer
|
# append postamble to txbuffer
|
||||||
codec2.api.freedv_rawdatapostambletx(freedv, mod_out_postamble)
|
codec2.api.freedv_rawdatapostambletx(freedv, mod_out_postamble)
|
||||||
txbuffer += bytes(mod_out_postamble)
|
txbuffer += bytes(mod_out_postamble)
|
||||||
time.sleep(0.01)
|
time.sleep(0.05)
|
||||||
# add delay to end of frames
|
# add delay to end of frames
|
||||||
samples_delay = int(self.MODEM_SAMPLE_RATE*(repeat_delay/1000))
|
samples_delay = int(self.MODEM_SAMPLE_RATE*(repeat_delay/1000))
|
||||||
mod_out_silence = create_string_buffer(samples_delay*2)
|
mod_out_silence = create_string_buffer(samples_delay*2)
|
||||||
txbuffer += bytes(mod_out_silence)
|
txbuffer += bytes(mod_out_silence)
|
||||||
time.sleep(0.01)
|
#time.sleep(0.05)
|
||||||
|
|
||||||
# resample up to 48k (resampler works on np.int16)
|
# resample up to 48k (resampler works on np.int16)
|
||||||
x = np.frombuffer(txbuffer, dtype=np.int16)
|
x = np.frombuffer(txbuffer, dtype=np.int16)
|
||||||
|
@ -287,7 +288,9 @@ class RF():
|
||||||
# if data is shorter than the expcected audio frames per buffer we need to append 0
|
# if data is shorter than the expcected audio frames per buffer we need to append 0
|
||||||
# to prevent the callback from stucking/crashing
|
# to prevent the callback from stucking/crashing
|
||||||
if len(c) < self.AUDIO_FRAMES_PER_BUFFER_RX*2:
|
if len(c) < self.AUDIO_FRAMES_PER_BUFFER_RX*2:
|
||||||
c += bytes(self.AUDIO_FRAMES_PER_BUFFER_RX*2 - len(c))
|
delta = bytes(self.AUDIO_FRAMES_PER_BUFFER_RX*2 - len(c))
|
||||||
|
c += delta
|
||||||
|
structlog.get_logger("structlog").debug("[TNC] mod out shorter than audio buffer", delta=len(delta))
|
||||||
self.modoutqueue.put(c)
|
self.modoutqueue.put(c)
|
||||||
|
|
||||||
# maybe we need to toggle PTT before craeting modulation because of queue processing
|
# maybe we need to toggle PTT before craeting modulation because of queue processing
|
||||||
|
@ -295,21 +298,21 @@ class RF():
|
||||||
while not self.modoutqueue.empty():
|
while not self.modoutqueue.empty():
|
||||||
pass
|
pass
|
||||||
static.PTT_STATE = self.hamlib.set_ptt(False)
|
static.PTT_STATE = self.hamlib.set_ptt(False)
|
||||||
|
|
||||||
|
|
||||||
self.c_lib.freedv_close(freedv)
|
self.c_lib.freedv_close(freedv)
|
||||||
return True
|
return True
|
||||||
|
|
||||||
def audio(self):
|
def audio(self):
|
||||||
try:
|
try:
|
||||||
print(f"starting pyaudio callback", file=sys.stderr)
|
structlog.get_logger("structlog").debug("[TNC] starting pyaudio callback")
|
||||||
self.audio_stream.start_stream()
|
self.audio_stream.start_stream()
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
print(f"pyAudio error: {e}", file=sys.stderr)
|
structlog.get_logger("structlog").error("[TNC] starting pyaudio callback failed", e=e)
|
||||||
|
|
||||||
|
|
||||||
while self.audio_stream.is_active():
|
while self.audio_stream.is_active():
|
||||||
while self.datac0_buffer.nbuffer >= self.datac0_nin:
|
while self.datac0_buffer.nbuffer >= self.datac0_nin:
|
||||||
|
|
||||||
# demodulate audio
|
# demodulate audio
|
||||||
nbytes = codec2.api.freedv_rawdatarx(self.datac0_freedv, self.datac0_bytes_out, self.datac0_buffer.buffer.ctypes)
|
nbytes = codec2.api.freedv_rawdatarx(self.datac0_freedv, self.datac0_bytes_out, self.datac0_buffer.buffer.ctypes)
|
||||||
self.datac0_buffer.pop(self.datac0_nin)
|
self.datac0_buffer.pop(self.datac0_nin)
|
||||||
|
@ -320,6 +323,7 @@ class RF():
|
||||||
self.calculate_snr(self.datac0_freedv)
|
self.calculate_snr(self.datac0_freedv)
|
||||||
|
|
||||||
while self.datac1_buffer.nbuffer >= self.datac1_nin:
|
while self.datac1_buffer.nbuffer >= self.datac1_nin:
|
||||||
|
|
||||||
# demodulate audio
|
# demodulate audio
|
||||||
nbytes = codec2.api.freedv_rawdatarx(self.datac1_freedv, self.datac1_bytes_out, self.datac1_buffer.buffer.ctypes)
|
nbytes = codec2.api.freedv_rawdatarx(self.datac1_freedv, self.datac1_bytes_out, self.datac1_buffer.buffer.ctypes)
|
||||||
self.datac1_buffer.pop(self.datac1_nin)
|
self.datac1_buffer.pop(self.datac1_nin)
|
||||||
|
@ -330,6 +334,7 @@ class RF():
|
||||||
self.calculate_snr(self.datac1_freedv)
|
self.calculate_snr(self.datac1_freedv)
|
||||||
|
|
||||||
while self.datac3_buffer.nbuffer >= self.datac3_nin:
|
while self.datac3_buffer.nbuffer >= self.datac3_nin:
|
||||||
|
|
||||||
# demodulate audio
|
# demodulate audio
|
||||||
nbytes = codec2.api.freedv_rawdatarx(self.datac3_freedv, self.datac3_bytes_out, self.datac3_buffer.buffer.ctypes)
|
nbytes = codec2.api.freedv_rawdatarx(self.datac3_freedv, self.datac3_bytes_out, self.datac3_buffer.buffer.ctypes)
|
||||||
self.datac3_buffer.pop(self.datac3_nin)
|
self.datac3_buffer.pop(self.datac3_nin)
|
||||||
|
@ -337,8 +342,9 @@ class RF():
|
||||||
if nbytes == self.datac3_bytes_per_frame:
|
if nbytes == self.datac3_bytes_per_frame:
|
||||||
self.dataqueue.put([self.datac3_bytes_out, self.datac3_freedv ,self.datac3_bytes_per_frame])
|
self.dataqueue.put([self.datac3_bytes_out, self.datac3_freedv ,self.datac3_bytes_per_frame])
|
||||||
self.get_scatter(self.datac3_freedv)
|
self.get_scatter(self.datac3_freedv)
|
||||||
self.calculate_snr(self.datac3_freedv)
|
self.calculate_snr(self.datac3_freedv)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
@ -346,8 +352,11 @@ class RF():
|
||||||
# worker for FIFO queue for processing received frames
|
# worker for FIFO queue for processing received frames
|
||||||
def worker(self):
|
def worker(self):
|
||||||
while True:
|
while True:
|
||||||
time.sleep(0.1)
|
time.sleep(0.01)
|
||||||
data = self.dataqueue.get()
|
data = self.dataqueue.get()
|
||||||
|
# data[0] = bytes_out
|
||||||
|
# data[1] = freedv session
|
||||||
|
# data[2] = bytes_per_frame
|
||||||
self.process_data(data[0], data[1], data[2])
|
self.process_data(data[0], data[1], data[2])
|
||||||
self.dataqueue.task_done()
|
self.dataqueue.task_done()
|
||||||
|
|
||||||
|
@ -359,7 +368,7 @@ class RF():
|
||||||
# we could also create an own function, which returns True.
|
# we could also create an own function, which returns True.
|
||||||
def process_data(self, bytes_out, freedv, bytes_per_frame):
|
def process_data(self, bytes_out, freedv, bytes_per_frame):
|
||||||
|
|
||||||
if bytes(bytes_out[1:2]) == static.MYCALLSIGN_CRC8 or bytes(bytes_out[6:7]) == static.MYCALLSIGN_CRC8 or bytes(bytes_out[1:2]) == b'\x01':
|
if bytes(bytes_out[1:2]) == static.MYCALLSIGN_CRC8 or bytes(bytes_out[3:4]) == static.MYCALLSIGN_CRC8 or bytes(bytes_out[1:2]) == b'\x01':
|
||||||
|
|
||||||
# CHECK IF FRAMETYPE IS BETWEEN 10 and 50 ------------------------
|
# CHECK IF FRAMETYPE IS BETWEEN 10 and 50 ------------------------
|
||||||
frametype = int.from_bytes(bytes(bytes_out[:1]), "big")
|
frametype = int.from_bytes(bytes(bytes_out[:1]), "big")
|
||||||
|
@ -371,39 +380,47 @@ class RF():
|
||||||
#print("Freq-Offset: " + str(frequency_offset))
|
#print("Freq-Offset: " + str(frequency_offset))
|
||||||
|
|
||||||
if 50 >= frametype >= 10:
|
if 50 >= frametype >= 10:
|
||||||
|
# get snr of received data
|
||||||
|
snr = self.calculate_snr(freedv)
|
||||||
|
structlog.get_logger("structlog").debug("[TNC] RX SNR", snr=snr)
|
||||||
# send payload data to arq checker without CRC16
|
# send payload data to arq checker without CRC16
|
||||||
data_handler.arq_data_received(bytes(bytes_out[:-2]), bytes_per_frame)
|
data_handler.arq_data_received(bytes(bytes_out[:-2]), bytes_per_frame, snr, freedv)
|
||||||
|
|
||||||
#print("static.ARQ_RX_BURST_BUFFER.count(None) " + str(static.ARQ_RX_BURST_BUFFER.count(None)))
|
# 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:
|
if static.RX_BURST_BUFFER.count(None) <= 1 or (frame+1) == n_frames_per_burst:
|
||||||
logging.debug("FULL BURST BUFFER ---> UNSYNC")
|
structlog.get_logger("structlog").debug(f"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)
|
||||||
|
|
||||||
|
|
||||||
# BURST ACK
|
# BURST ACK
|
||||||
elif frametype == 60:
|
elif frametype == 60:
|
||||||
logging.debug("ACK RECEIVED....")
|
structlog.get_logger("structlog").debug("ACK RECEIVED....")
|
||||||
data_handler.burst_ack_received()
|
|
||||||
|
data_handler.burst_ack_received(bytes_out[:-2])
|
||||||
|
|
||||||
# FRAME ACK
|
# FRAME ACK
|
||||||
elif frametype == 61:
|
elif frametype == 61:
|
||||||
logging.debug("FRAME ACK RECEIVED....")
|
structlog.get_logger("structlog").debug("FRAME ACK RECEIVED....")
|
||||||
data_handler.frame_ack_received()
|
data_handler.frame_ack_received()
|
||||||
|
|
||||||
# FRAME RPT
|
# FRAME RPT
|
||||||
elif frametype == 62:
|
elif frametype == 62:
|
||||||
logging.debug("REPEAT REQUEST RECEIVED....")
|
structlog.get_logger("structlog").debug("REPEAT REQUEST RECEIVED....")
|
||||||
data_handler.burst_rpt_received(bytes_out[:-2])
|
data_handler.burst_rpt_received(bytes_out[:-2])
|
||||||
|
|
||||||
|
# FRAME NACK
|
||||||
|
elif frametype == 63:
|
||||||
|
structlog.get_logger("structlog").debug("FRAME NOT ACK RECEIVED....")
|
||||||
|
data_handler.frame_nack_received(bytes_out[:-2])
|
||||||
|
|
||||||
# CQ FRAME
|
# CQ FRAME
|
||||||
elif frametype == 200:
|
elif frametype == 200:
|
||||||
logging.debug("CQ RECEIVED....")
|
structlog.get_logger("structlog").debug("CQ RECEIVED....")
|
||||||
data_handler.received_cq(bytes_out[:-2])
|
data_handler.received_cq(bytes_out[:-2])
|
||||||
|
|
||||||
# PING FRAME
|
# PING FRAME
|
||||||
elif frametype == 210:
|
elif frametype == 210:
|
||||||
logging.debug("PING RECEIVED....")
|
structlog.get_logger("structlog").debug("PING RECEIVED....")
|
||||||
frequency_offset = self.get_frequency_offset(freedv)
|
frequency_offset = self.get_frequency_offset(freedv)
|
||||||
#print("Freq-Offset: " + str(frequency_offset))
|
#print("Freq-Offset: " + str(frequency_offset))
|
||||||
data_handler.received_ping(bytes_out[:-2], frequency_offset)
|
data_handler.received_ping(bytes_out[:-2], frequency_offset)
|
||||||
|
@ -411,7 +428,7 @@ class RF():
|
||||||
|
|
||||||
# PING ACK
|
# PING ACK
|
||||||
elif frametype == 211:
|
elif frametype == 211:
|
||||||
logging.debug("PING ACK RECEIVED....")
|
structlog.get_logger("structlog").debug("PING ACK RECEIVED....")
|
||||||
# early detection of frequency offset
|
# early detection of frequency offset
|
||||||
#frequency_offset = int.from_bytes(bytes(bytes_out[9:11]), "big", signed=True)
|
#frequency_offset = int.from_bytes(bytes(bytes_out[9:11]), "big", signed=True)
|
||||||
#print("Freq-Offset: " + str(frequency_offset))
|
#print("Freq-Offset: " + str(frequency_offset))
|
||||||
|
@ -426,19 +443,21 @@ class RF():
|
||||||
|
|
||||||
# ARQ FILE TRANSFER RECEIVED!
|
# ARQ FILE TRANSFER RECEIVED!
|
||||||
elif frametype == 225:
|
elif frametype == 225:
|
||||||
logging.debug("ARQ arq_received_data_channel_opener")
|
structlog.get_logger("structlog").debug("ARQ arq_received_data_channel_opener")
|
||||||
data_handler.arq_received_data_channel_opener(bytes_out[:-2])
|
data_handler.arq_received_data_channel_opener(bytes_out[:-2])
|
||||||
|
|
||||||
# ARQ CHANNEL IS OPENED
|
# ARQ CHANNEL IS OPENED
|
||||||
elif frametype == 226:
|
elif frametype == 226:
|
||||||
logging.debug("ARQ arq_received_channel_is_open")
|
structlog.get_logger("structlog").debug("ARQ arq_received_channel_is_open")
|
||||||
data_handler.arq_received_channel_is_open(bytes_out[:-2])
|
data_handler.arq_received_channel_is_open(bytes_out[:-2])
|
||||||
|
|
||||||
# ARQ CONNECT ACK / KEEP ALIVE
|
# ARQ CONNECT ACK / KEEP ALIVE
|
||||||
|
# this is outdated and we may remove it
|
||||||
elif frametype == 230:
|
elif frametype == 230:
|
||||||
logging.debug("BEACON RECEIVED")
|
structlog.get_logger("structlog").debug("BEACON RECEIVED")
|
||||||
data_handler.received_beacon(bytes_out[:-2])
|
data_handler.received_beacon(bytes_out[:-2])
|
||||||
|
|
||||||
|
# TESTFRAMES
|
||||||
elif frametype == 255:
|
elif frametype == 255:
|
||||||
structlog.get_logger("structlog").debug("TESTFRAME RECEIVED", frame=bytes_out[:])
|
structlog.get_logger("structlog").debug("TESTFRAME RECEIVED", frame=bytes_out[:])
|
||||||
|
|
||||||
|
@ -448,6 +467,7 @@ class RF():
|
||||||
|
|
||||||
|
|
||||||
# DO UNSYNC AFTER LAST BURST by checking the frame nums against the total frames per burst
|
# DO UNSYNC AFTER LAST BURST by checking the frame nums against the total frames per burst
|
||||||
|
# this should be changed to a timeout based unsync
|
||||||
if frame == n_frames_per_burst:
|
if frame == n_frames_per_burst:
|
||||||
logging.info("LAST FRAME ---> UNSYNC")
|
logging.info("LAST FRAME ---> UNSYNC")
|
||||||
self.c_lib.freedv_set_sync(freedv, 0) # FORCE UNSYNC
|
self.c_lib.freedv_set_sync(freedv, 0) # FORCE UNSYNC
|
||||||
|
@ -501,11 +521,13 @@ class RF():
|
||||||
self.c_lib.freedv_get_modem_stats(freedv, byref(
|
self.c_lib.freedv_get_modem_stats(freedv, byref(
|
||||||
modem_stats_sync), byref(modem_stats_snr))
|
modem_stats_sync), byref(modem_stats_snr))
|
||||||
modem_stats_snr = modem_stats_snr.value
|
modem_stats_snr = modem_stats_snr.value
|
||||||
|
|
||||||
try:
|
try:
|
||||||
static.SNR = round(modem_stats_snr, 1)
|
static.SNR = round(modem_stats_snr, 1)
|
||||||
|
return static.SNR
|
||||||
except:
|
except:
|
||||||
static.SNR = 0
|
static.SNR = 0
|
||||||
|
return static.SNR
|
||||||
|
|
||||||
def update_rig_data(self):
|
def update_rig_data(self):
|
||||||
while True:
|
while True:
|
||||||
|
@ -546,3 +568,13 @@ class RF():
|
||||||
static.FFT = [0] * 320
|
static.FFT = [0] * 320
|
||||||
else:
|
else:
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
def get_bytes_per_frame(self, mode):
|
||||||
|
freedv = cast(codec2.api.freedv_open(mode), c_void_p)
|
||||||
|
|
||||||
|
# get number of bytes per frame for mode
|
||||||
|
return int(codec2.api.freedv_get_bits_per_modem_frame(freedv)/8)
|
||||||
|
|
||||||
|
def set_frames_per_burst(self, n_frames_per_burst):
|
||||||
|
codec2.api.freedv_set_frames_per_burst(self.datac1_freedv,n_frames_per_burst)
|
||||||
|
codec2.api.freedv_set_frames_per_burst(self.datac3_freedv,n_frames_per_burst)
|
||||||
|
|
|
@ -31,6 +31,7 @@ import time
|
||||||
|
|
||||||
import static
|
import static
|
||||||
import data_handler
|
import data_handler
|
||||||
|
|
||||||
import helpers
|
import helpers
|
||||||
|
|
||||||
import sys
|
import sys
|
||||||
|
@ -121,7 +122,7 @@ class ThreadedTCPRequestHandler(socketserver.BaseRequestHandler):
|
||||||
PING_THREAD = threading.Thread(target=data_handler.transmit_ping, args=[dxcallsign], name="PING")
|
PING_THREAD = threading.Thread(target=data_handler.transmit_ping, args=[dxcallsign], name="PING")
|
||||||
PING_THREAD.start()
|
PING_THREAD.start()
|
||||||
|
|
||||||
# and static.ARQ_READY_FOR_DATA == True: # and static.ARQ_STATE == 'CONNECTED' :
|
|
||||||
if received_json["type"] == 'ARQ' and received_json["command"] == "sendFile":
|
if received_json["type"] == 'ARQ' and received_json["command"] == "sendFile":
|
||||||
static.TNC_STATE = 'BUSY'
|
static.TNC_STATE = 'BUSY'
|
||||||
|
|
||||||
|
@ -191,7 +192,7 @@ class ThreadedTCPRequestHandler(socketserver.BaseRequestHandler):
|
||||||
print(" >>> STOPPING TRANSMISSION <<<")
|
print(" >>> STOPPING TRANSMISSION <<<")
|
||||||
structlog.get_logger("structlog").warning("[TNC] Stopping transmission!")
|
structlog.get_logger("structlog").warning("[TNC] Stopping transmission!")
|
||||||
static.TNC_STATE = 'IDLE'
|
static.TNC_STATE = 'IDLE'
|
||||||
static.ARQ_STATE = 'IDLE'
|
static.ARQ_STATE = False
|
||||||
|
|
||||||
|
|
||||||
# SETTINGS AND STATUS ---------------------------------------------
|
# SETTINGS AND STATUS ---------------------------------------------
|
||||||
|
@ -248,6 +249,7 @@ class ThreadedTCPRequestHandler(socketserver.BaseRequestHandler):
|
||||||
"RX_MSG_BUFFER_LENGTH": str(len(static.RX_MSG_BUFFER)),
|
"RX_MSG_BUFFER_LENGTH": str(len(static.RX_MSG_BUFFER)),
|
||||||
"ARQ_BYTES_PER_MINUTE": str(static.ARQ_BYTES_PER_MINUTE),
|
"ARQ_BYTES_PER_MINUTE": str(static.ARQ_BYTES_PER_MINUTE),
|
||||||
"ARQ_BYTES_PER_MINUTE_BURST": str(static.ARQ_BYTES_PER_MINUTE_BURST),
|
"ARQ_BYTES_PER_MINUTE_BURST": str(static.ARQ_BYTES_PER_MINUTE_BURST),
|
||||||
|
"ARQ_COMPRESSION_FACTOR": str(static.ARQ_COMPRESSION_FACTOR),
|
||||||
"ARQ_TRANSMISSION_PERCENT": str(static.ARQ_TRANSMISSION_PERCENT),
|
"ARQ_TRANSMISSION_PERCENT": str(static.ARQ_TRANSMISSION_PERCENT),
|
||||||
"TOTAL_BYTES": str(static.TOTAL_BYTES),
|
"TOTAL_BYTES": str(static.TOTAL_BYTES),
|
||||||
"INFO" : static.INFO,
|
"INFO" : static.INFO,
|
||||||
|
@ -322,8 +324,7 @@ class ThreadedTCPRequestHandler(socketserver.BaseRequestHandler):
|
||||||
print(exc_type, fname, exc_tb.tb_lineno)
|
print(exc_type, fname, exc_tb.tb_lineno)
|
||||||
print("############ END OF ERROR #######################")
|
print("############ END OF ERROR #######################")
|
||||||
|
|
||||||
print("reset of connection...")
|
structlog.get_logger("structlog").warning("[TNC] reset of tcp/ip connection...")
|
||||||
structlog.get_logger("structlog").warning("[TNC] Stopping transmission!")
|
|
||||||
#socketTimeout = 0
|
#socketTimeout = 0
|
||||||
|
|
||||||
structlog.get_logger("structlog").error("[TNC] Network error", e = sys.exc_info()[0])
|
structlog.get_logger("structlog").error("[TNC] Network error", e = sys.exc_info()[0])
|
||||||
|
|
|
@ -43,7 +43,7 @@ HAMLIB_SERIAL_SPEED = '9600'
|
||||||
HAMLIB_PTT_PORT = '/dev/ttyUSB0'
|
HAMLIB_PTT_PORT = '/dev/ttyUSB0'
|
||||||
HAMLIB_STOP_BITS = '1'
|
HAMLIB_STOP_BITS = '1'
|
||||||
HAMLIB_DATA_BITS = '8'
|
HAMLIB_DATA_BITS = '8'
|
||||||
HANLIB_HANDSHAKE = 'None'
|
HAMLIB_HANDSHAKE = 'None'
|
||||||
|
|
||||||
HAMLIB_FREQUENCY = 0
|
HAMLIB_FREQUENCY = 0
|
||||||
HAMLIB_MODE = ''
|
HAMLIB_MODE = ''
|
||||||
|
@ -71,12 +71,14 @@ ARQ_BYTES_PER_MINUTE_BURST = 0
|
||||||
ARQ_BYTES_PER_MINUTE = 0
|
ARQ_BYTES_PER_MINUTE = 0
|
||||||
ARQ_BITS_PER_SECOND_BURST = 0
|
ARQ_BITS_PER_SECOND_BURST = 0
|
||||||
ARQ_BITS_PER_SECOND = 0
|
ARQ_BITS_PER_SECOND = 0
|
||||||
|
ARQ_COMPRESSION_FACTOR = 0
|
||||||
ARQ_TRANSMISSION_PERCENT = 0
|
ARQ_TRANSMISSION_PERCENT = 0
|
||||||
TOTAL_BYTES = 0
|
TOTAL_BYTES = 0
|
||||||
|
|
||||||
|
|
||||||
#CHANNEL_STATE = 'RECEIVING_SIGNALLING'
|
#CHANNEL_STATE = 'RECEIVING_SIGNALLING'
|
||||||
TNC_STATE = 'IDLE'
|
TNC_STATE = 'IDLE'
|
||||||
ARQ_STATE = 'IDLE'
|
ARQ_STATE = False
|
||||||
|
|
||||||
# BEACON STATE
|
# BEACON STATE
|
||||||
BEACON_STATE = False
|
BEACON_STATE = False
|
||||||
|
@ -85,7 +87,7 @@ BEACON_STATE = False
|
||||||
RX_BUFFER = []
|
RX_BUFFER = []
|
||||||
RX_MSG_BUFFER = []
|
RX_MSG_BUFFER = []
|
||||||
RX_BURST_BUFFER = []
|
RX_BURST_BUFFER = []
|
||||||
RX_FRAME_BUFFER = []
|
RX_FRAME_BUFFER = b''
|
||||||
#RX_BUFFER_SIZE = 0
|
#RX_BUFFER_SIZE = 0
|
||||||
|
|
||||||
# ------- HEARD STATIOS BUFFER
|
# ------- HEARD STATIOS BUFFER
|
||||||
|
|
Loading…
Reference in a new issue