mirror of
https://github.com/DJ2LS/FreeDATA
synced 2024-05-14 08:04:33 +00:00
Merge pull request #259 from DJ2LS/ls-arq
WIP: Move to session id instead of callsign crc check
This commit is contained in:
commit
aaea0d1509
10
.github/workflows/ctest.yml
vendored
10
.github/workflows/ctest.yml
vendored
|
@ -13,12 +13,18 @@ jobs:
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v3
|
- uses: actions/checkout@v3
|
||||||
|
|
||||||
|
- name: Set up Python 3.9
|
||||||
|
uses: actions/setup-python@v4
|
||||||
|
with:
|
||||||
|
python-version: 3.9
|
||||||
|
|
||||||
|
|
||||||
- name: Install packages
|
- name: Install packages
|
||||||
shell: bash
|
shell: bash
|
||||||
run: |
|
run: |
|
||||||
sudo apt-get update
|
sudo apt-get update
|
||||||
sudo apt-get install octave octave-common octave-signal sox python3 python3-pip portaudio19-dev python3-pyaudio
|
sudo apt-get install octave octave-common octave-signal sox portaudio19-dev python3-pyaudio
|
||||||
pip3 install psutil crcengine ujson pyserial numpy structlog sounddevice
|
pip3 install psutil crcengine ujson pyserial numpy structlog sounddevice pyaudio
|
||||||
pip3 install pytest pytest-rerunfailures
|
pip3 install pytest pytest-rerunfailures
|
||||||
|
|
||||||
- name: Build codec2
|
- name: Build codec2
|
||||||
|
|
|
@ -1458,11 +1458,10 @@ ipcRenderer.on('action-update-tnc-state', (event, arg) => {
|
||||||
document.getElementById("beaconInterval").disabled = false;
|
document.getElementById("beaconInterval").disabled = false;
|
||||||
}
|
}
|
||||||
// RMS
|
// RMS
|
||||||
/*
|
var rms_level = (arg.rms_level / 32767) * 100
|
||||||
var rms_level = Math.round((arg.rms_level/60) * 100)
|
|
||||||
document.getElementById("rms_level").setAttribute("aria-valuenow", rms_level);
|
document.getElementById("rms_level").setAttribute("aria-valuenow", rms_level);
|
||||||
document.getElementById("rms_level").setAttribute("style", "width:" + rms_level + "%;");
|
document.getElementById("rms_level").setAttribute("style", "width:" + rms_level + "%;");
|
||||||
*/
|
|
||||||
|
|
||||||
// SET FREQUENCY
|
// SET FREQUENCY
|
||||||
document.getElementById("frequency").innerHTML = arg.frequency;
|
document.getElementById("frequency").innerHTML = arg.frequency;
|
||||||
|
|
|
@ -746,11 +746,11 @@
|
||||||
<div class="card-body p-2">
|
<div class="card-body p-2">
|
||||||
<div class="progress mb-0" style="height: 15px;">
|
<div class="progress mb-0" style="height: 15px;">
|
||||||
<div class="progress-bar progress-bar-striped bg-primary" id="rms_level" 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="rms_level" 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">RX AUDIO LEVEL - not implemented yet</p>
|
<p class="justify-content-center d-flex position-absolute w-100">RX AUDIO LEVEL</p>
|
||||||
</div>
|
</div>
|
||||||
<div class="progress mb-0" style="height: 5px;">
|
<div class="progress mb-0" style="height: 5px;">
|
||||||
<div class="progress-bar progress-bar-striped bg-warning" role="progressbar" style="width: 10%" aria-valuenow="10" aria-valuemin="0" aria-valuemax="100"></div>
|
<div class="progress-bar progress-bar-striped bg-warning" role="progressbar" style="width: 1%" aria-valuenow="1" aria-valuemin="0" aria-valuemax="100"></div>
|
||||||
<div class="progress-bar bg-success" role="progressbar" style="width: 80%" aria-valuenow="80" aria-valuemin="0" aria-valuemax="100"></div>
|
<div class="progress-bar bg-success" role="progressbar" style="width: 89%" aria-valuenow="89" aria-valuemin="0" aria-valuemax="100"></div>
|
||||||
<div class="progress-bar progress-bar-striped bg-danger" role="progressbar" style="width: 10%" aria-valuenow="10" aria-valuemin="0" aria-valuemax="100"></div>
|
<div class="progress-bar progress-bar-striped bg-danger" role="progressbar" style="width: 10%" aria-valuenow="10" aria-valuemin="0" aria-valuemax="100"></div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -15,6 +15,7 @@ Uses util_datac0.py in separate process to perform the data transfer.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
import multiprocessing
|
import multiprocessing
|
||||||
|
from random import randbytes
|
||||||
import sys
|
import sys
|
||||||
import time
|
import time
|
||||||
|
|
||||||
|
@ -62,16 +63,23 @@ def t_create_frame(frame_type: int, mycall: str, dxcall: str) -> bytearray:
|
||||||
dxcallsign = helpers.bytes_to_callsign(dxcallsign_bytes)
|
dxcallsign = helpers.bytes_to_callsign(dxcallsign_bytes)
|
||||||
dxcallsign_crc = helpers.get_crc_24(dxcallsign)
|
dxcallsign_crc = helpers.get_crc_24(dxcallsign)
|
||||||
|
|
||||||
|
# frame = bytearray(14)
|
||||||
|
# frame[:1] = bytes([frame_type])
|
||||||
|
# frame[1:4] = dxcallsign_crc
|
||||||
|
# frame[4:7] = mycallsign_crc
|
||||||
|
# frame[7:13] = mycallsign_bytes
|
||||||
|
session_id = randbytes(1)
|
||||||
frame = bytearray(14)
|
frame = bytearray(14)
|
||||||
frame[:1] = bytes([frame_type])
|
frame[:1] = bytes([frame_type])
|
||||||
frame[1:4] = dxcallsign_crc
|
frame[1:2] = session_id
|
||||||
frame[4:7] = mycallsign_crc
|
frame[2:5] = dxcallsign_crc
|
||||||
frame[7:13] = mycallsign_bytes
|
frame[5:8] = mycallsign_crc
|
||||||
|
frame[8:14] = mycallsign_bytes
|
||||||
|
|
||||||
return frame
|
return frame
|
||||||
|
|
||||||
|
|
||||||
def t_create_session_close(mycall: str, dxcall: str) -> bytearray:
|
def t_create_session_close_old(mycall: str, dxcall: str) -> bytearray:
|
||||||
"""
|
"""
|
||||||
Generate the session_close frame.
|
Generate the session_close frame.
|
||||||
|
|
||||||
|
@ -85,6 +93,23 @@ def t_create_session_close(mycall: str, dxcall: str) -> bytearray:
|
||||||
return t_create_frame(223, mycall, dxcall)
|
return t_create_frame(223, mycall, dxcall)
|
||||||
|
|
||||||
|
|
||||||
|
def t_create_session_close(session_id: bytes) -> bytearray:
|
||||||
|
"""
|
||||||
|
Generate the session_close frame.
|
||||||
|
|
||||||
|
:param session_id: Session to close
|
||||||
|
:type mycall: int
|
||||||
|
:return: Bytearray of the requested frame
|
||||||
|
:rtype: bytearray
|
||||||
|
"""
|
||||||
|
# return t_create_frame(223, mycall, dxcall)
|
||||||
|
frame = bytearray(14)
|
||||||
|
frame[:1] = bytes([223])
|
||||||
|
frame[1:2] = session_id
|
||||||
|
|
||||||
|
return frame
|
||||||
|
|
||||||
|
|
||||||
def t_create_start_session(mycall: str, dxcall: str) -> bytearray:
|
def t_create_start_session(mycall: str, dxcall: str) -> bytearray:
|
||||||
"""
|
"""
|
||||||
Generate the create_session frame.
|
Generate the create_session frame.
|
||||||
|
@ -150,18 +175,24 @@ def t_foreign_disconnect(mycall: str, dxcall: str):
|
||||||
assert static.ARQ_SESSION_STATE == "connecting"
|
assert static.ARQ_SESSION_STATE == "connecting"
|
||||||
|
|
||||||
# Set up a frame from a non-associated station.
|
# Set up a frame from a non-associated station.
|
||||||
foreigncall_bytes = helpers.callsign_to_bytes("ZZ0ZZ-0")
|
# foreigncall_bytes = helpers.callsign_to_bytes("ZZ0ZZ-0")
|
||||||
foreigncall = helpers.bytes_to_callsign(foreigncall_bytes)
|
# foreigncall = helpers.bytes_to_callsign(foreigncall_bytes)
|
||||||
|
|
||||||
close_frame = t_create_session_close("ZZ0ZZ-0", "ZZ0ZZ-0")
|
# close_frame = t_create_session_close_old("ZZ0ZZ-0", "ZZ0ZZ-0")
|
||||||
|
open_session = create_frame[1:2]
|
||||||
|
wrong_session = randbytes(1)
|
||||||
|
while wrong_session == open_session:
|
||||||
|
wrong_session = randbytes(1)
|
||||||
|
close_frame = t_create_session_close(wrong_session)
|
||||||
print_frame(close_frame)
|
print_frame(close_frame)
|
||||||
assert (
|
|
||||||
helpers.check_callsign(static.DXCALLSIGN, bytes(close_frame[4:7]))[0] is False
|
|
||||||
), f"{helpers.get_crc_24(static.DXCALLSIGN)} == {bytes(close_frame[4:7])} but should be not equal."
|
|
||||||
|
|
||||||
assert (
|
# assert (
|
||||||
helpers.check_callsign(foreigncall, bytes(close_frame[4:7]))[0] is True
|
# helpers.check_callsign(static.DXCALLSIGN, bytes(close_frame[4:7]))[0] is False
|
||||||
), f"{helpers.get_crc_24(foreigncall)} != {bytes(close_frame[4:7])} but should be equal."
|
# ), f"{helpers.get_crc_24(static.DXCALLSIGN)} == {bytes(close_frame[4:7])} but should be not equal."
|
||||||
|
|
||||||
|
# assert (
|
||||||
|
# helpers.check_callsign(foreigncall, bytes(close_frame[4:7]))[0] is True
|
||||||
|
# ), f"{helpers.get_crc_24(foreigncall)} != {bytes(close_frame[4:7])} but should be equal."
|
||||||
|
|
||||||
# Send the non-associated session close frame to the TNC
|
# Send the non-associated session close frame to the TNC
|
||||||
tnc.received_session_close(close_frame)
|
tnc.received_session_close(close_frame)
|
||||||
|
@ -221,7 +252,9 @@ def t_valid_disconnect(mycall: str, dxcall: str):
|
||||||
assert static.ARQ_SESSION_STATE == "connecting"
|
assert static.ARQ_SESSION_STATE == "connecting"
|
||||||
|
|
||||||
# Create packet to be 'received' by this station.
|
# Create packet to be 'received' by this station.
|
||||||
close_frame = t_create_session_close(mycall=dxcall, dxcall=mycall)
|
# close_frame = t_create_session_close_old(mycall=dxcall, dxcall=mycall)
|
||||||
|
open_session = create_frame[1:2]
|
||||||
|
close_frame = t_create_session_close(open_session)
|
||||||
print_frame(close_frame)
|
print_frame(close_frame)
|
||||||
tnc.received_session_close(close_frame)
|
tnc.received_session_close(close_frame)
|
||||||
|
|
||||||
|
@ -241,7 +274,7 @@ def t_valid_disconnect(mycall: str, dxcall: str):
|
||||||
|
|
||||||
@pytest.mark.parametrize("mycall", ["AA1AA-2", "DE2DE-0", "E4AWQ-4"])
|
@pytest.mark.parametrize("mycall", ["AA1AA-2", "DE2DE-0", "E4AWQ-4"])
|
||||||
@pytest.mark.parametrize("dxcall", ["AA9AA-1", "DE2ED-0", "F6QWE-3"])
|
@pytest.mark.parametrize("dxcall", ["AA9AA-1", "DE2ED-0", "F6QWE-3"])
|
||||||
@pytest.mark.flaky(reruns=2)
|
# @pytest.mark.flaky(reruns=2)
|
||||||
def test_foreign_disconnect(mycall: str, dxcall: str):
|
def test_foreign_disconnect(mycall: str, dxcall: str):
|
||||||
proc = multiprocessing.Process(target=t_foreign_disconnect, args=(mycall, dxcall))
|
proc = multiprocessing.Process(target=t_foreign_disconnect, args=(mycall, dxcall))
|
||||||
# print("Starting threads.")
|
# print("Starting threads.")
|
||||||
|
|
|
@ -42,8 +42,8 @@ def get_audio_devices():
|
||||||
proc.start()
|
proc.start()
|
||||||
proc.join()
|
proc.join()
|
||||||
|
|
||||||
#log.debug("[AUD] get_audio_devices: input_devices:", list=f"{proxy_input_devices}")
|
log.debug("[AUD] get_audio_devices: input_devices:", list=f"{proxy_input_devices}")
|
||||||
#log.debug("[AUD] get_audio_devices: output_devices:", list=f"{proxy_output_devices}")
|
log.debug("[AUD] get_audio_devices: output_devices:", list=f"{proxy_output_devices}")
|
||||||
return list(proxy_input_devices), list(proxy_output_devices)
|
return list(proxy_input_devices), list(proxy_output_devices)
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -23,7 +23,8 @@ class FREEDV_MODE(Enum):
|
||||||
"""
|
"""
|
||||||
Enumeration for codec2 modes and names
|
Enumeration for codec2 modes and names
|
||||||
"""
|
"""
|
||||||
|
sig0 = 14
|
||||||
|
sig1 = 14
|
||||||
datac0 = 14
|
datac0 = 14
|
||||||
datac1 = 10
|
datac1 = 10
|
||||||
datac3 = 12
|
datac3 = 12
|
||||||
|
@ -103,6 +104,9 @@ api.freedv_open_advanced.restype = ctypes.c_void_p
|
||||||
api.freedv_get_bits_per_modem_frame.argtype = [ctypes.c_void_p] # type: ignore
|
api.freedv_get_bits_per_modem_frame.argtype = [ctypes.c_void_p] # type: ignore
|
||||||
api.freedv_get_bits_per_modem_frame.restype = ctypes.c_int
|
api.freedv_get_bits_per_modem_frame.restype = ctypes.c_int
|
||||||
|
|
||||||
|
api.freedv_get_modem_extended_stats.argtype = [ctypes.c_void_p, ctypes.c_void_p]
|
||||||
|
api.freedv_get_modem_extended_stats.restype = ctypes.c_int
|
||||||
|
|
||||||
api.freedv_nin.argtype = [ctypes.c_void_p] # type: ignore
|
api.freedv_nin.argtype = [ctypes.c_void_p] # type: ignore
|
||||||
api.freedv_nin.restype = ctypes.c_int
|
api.freedv_nin.restype = ctypes.c_int
|
||||||
|
|
||||||
|
@ -208,8 +212,8 @@ api.FREEDV_MODE_FSK_LDPC_1_ADV.tone_spacing = 200
|
||||||
api.FREEDV_MODE_FSK_LDPC_1_ADV.codename = "H_256_512_4".encode("utf-8") # code word
|
api.FREEDV_MODE_FSK_LDPC_1_ADV.codename = "H_256_512_4".encode("utf-8") # code word
|
||||||
|
|
||||||
# ------- MODEM STATS STRUCTURES
|
# ------- MODEM STATS STRUCTURES
|
||||||
MODEM_STATS_NC_MAX = 50 + 1
|
MODEM_STATS_NC_MAX = 50 + 1 * 2
|
||||||
MODEM_STATS_NR_MAX = 160
|
MODEM_STATS_NR_MAX = 160 * 2
|
||||||
MODEM_STATS_ET_MAX = 8
|
MODEM_STATS_ET_MAX = 8
|
||||||
MODEM_STATS_EYE_IND_MAX = 160
|
MODEM_STATS_EYE_IND_MAX = 160
|
||||||
MODEM_STATS_NSPEC = 512
|
MODEM_STATS_NSPEC = 512
|
||||||
|
@ -233,10 +237,12 @@ class MODEMSTATS(ctypes.Structure):
|
||||||
("pre", ctypes.c_int),
|
("pre", ctypes.c_int),
|
||||||
("post", ctypes.c_int),
|
("post", ctypes.c_int),
|
||||||
("uw_fails", ctypes.c_int),
|
("uw_fails", ctypes.c_int),
|
||||||
|
("rx_eye", (ctypes.c_float * MODEM_STATS_ET_MAX) * MODEM_STATS_EYE_IND_MAX),
|
||||||
("neyetr", ctypes.c_int), # How many eye traces are plotted
|
("neyetr", ctypes.c_int), # How many eye traces are plotted
|
||||||
("neyesamp", ctypes.c_int), # How many samples in the eye diagram
|
("neyesamp", ctypes.c_int), # How many samples in the eye diagram
|
||||||
("f_est", (ctypes.c_float * MODEM_STATS_MAX_F_EST)),
|
("f_est", (ctypes.c_float * MODEM_STATS_MAX_F_EST)),
|
||||||
("fft_buf", (ctypes.c_float * MODEM_STATS_NSPEC * 2)),
|
("fft_buf", (ctypes.c_float * MODEM_STATS_NSPEC * 2)),
|
||||||
|
("fft_cfg", ctypes.c_void_p)
|
||||||
]
|
]
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -13,7 +13,7 @@ import threading
|
||||||
import time
|
import time
|
||||||
import uuid
|
import uuid
|
||||||
import zlib
|
import zlib
|
||||||
from random import randrange
|
from random import randrange, randbytes
|
||||||
|
|
||||||
import codec2
|
import codec2
|
||||||
import helpers
|
import helpers
|
||||||
|
@ -44,7 +44,11 @@ class DATA:
|
||||||
self.data_queue_received = DATA_QUEUE_RECEIVED
|
self.data_queue_received = DATA_QUEUE_RECEIVED
|
||||||
|
|
||||||
# length of signalling frame
|
# length of signalling frame
|
||||||
self.length_sig_frame = 14
|
self.length_sig0_frame = 14
|
||||||
|
self.length_sig1_frame = 14
|
||||||
|
|
||||||
|
# hold session id
|
||||||
|
self.session_id = bytes(1)
|
||||||
|
|
||||||
# ------- ARQ SESSION
|
# ------- ARQ SESSION
|
||||||
self.arq_file_transfer = False
|
self.arq_file_transfer = False
|
||||||
|
@ -53,6 +57,9 @@ class DATA:
|
||||||
self.arq_session_timeout = 30
|
self.arq_session_timeout = 30
|
||||||
self.session_connect_max_retries = 15
|
self.session_connect_max_retries = 15
|
||||||
|
|
||||||
|
# actual n retries of burst
|
||||||
|
self.tx_n_retry_of_burst = 0
|
||||||
|
|
||||||
self.transmission_uuid = ""
|
self.transmission_uuid = ""
|
||||||
|
|
||||||
self.data_channel_last_received = 0.0 # time of last "live sign" of a frame
|
self.data_channel_last_received = 0.0 # time of last "live sign" of a frame
|
||||||
|
@ -72,6 +79,7 @@ class DATA:
|
||||||
# 3 bytes for the EOF End of File indicator in a data frame
|
# 3 bytes for the EOF End of File indicator in a data frame
|
||||||
self.data_frame_eof = b"EOF"
|
self.data_frame_eof = b"EOF"
|
||||||
|
|
||||||
|
self.tx_n_max_retries_per_burst = 50
|
||||||
self.rx_n_max_retries_per_burst = 50
|
self.rx_n_max_retries_per_burst = 50
|
||||||
self.n_retries_per_burst = 0
|
self.n_retries_per_burst = 0
|
||||||
|
|
||||||
|
@ -128,7 +136,11 @@ class DATA:
|
||||||
self.rx_frame_bof_received = False
|
self.rx_frame_bof_received = False
|
||||||
self.rx_frame_eof_received = False
|
self.rx_frame_eof_received = False
|
||||||
|
|
||||||
self.transmission_timeout = 360 # transmission timeout in seconds
|
# TIMEOUTS
|
||||||
|
self.burst_ack_timeout_seconds = 3.0 # timeout for burst acknowledges
|
||||||
|
self.data_frame_ack_timeout_seconds = 3.0 # timeout for data frame acknowledges
|
||||||
|
self.rpt_ack_timeout_seconds = 3.0 # timeout for rpt frame acknowledges
|
||||||
|
self.transmission_timeout = 500 # transmission timeout in seconds
|
||||||
|
|
||||||
# Dictionary of functions and log messages used in process_data
|
# Dictionary of functions and log messages used in process_data
|
||||||
# instead of a long series of if-elif-else statements.
|
# instead of a long series of if-elif-else statements.
|
||||||
|
@ -278,23 +290,33 @@ class DATA:
|
||||||
# bytes_out[2:5] == transmission
|
# bytes_out[2:5] == transmission
|
||||||
# we could also create an own function, which returns True.
|
# we could also create an own function, which returns True.
|
||||||
frametype = int.from_bytes(bytes(bytes_out[:1]), "big")
|
frametype = int.from_bytes(bytes(bytes_out[:1]), "big")
|
||||||
|
|
||||||
|
# check for callsign CRC
|
||||||
_valid1, _ = helpers.check_callsign(self.mycallsign, bytes(bytes_out[1:4]))
|
_valid1, _ = helpers.check_callsign(self.mycallsign, bytes(bytes_out[1:4]))
|
||||||
_valid2, _ = helpers.check_callsign(self.mycallsign, bytes(bytes_out[2:5]))
|
_valid2, _ = helpers.check_callsign(self.mycallsign, bytes(bytes_out[2:5]))
|
||||||
|
|
||||||
|
# check for session ID
|
||||||
|
# signalling frames
|
||||||
|
_valid3 = helpers.check_session_id(self.session_id, bytes(bytes_out[1:2]))
|
||||||
|
# arq data frames
|
||||||
|
_valid4 = helpers.check_session_id(self.session_id, bytes(bytes_out[2:3]))
|
||||||
if (
|
if (
|
||||||
_valid1
|
_valid1
|
||||||
or _valid2
|
or _valid2
|
||||||
|
or _valid3
|
||||||
|
or _valid4
|
||||||
or frametype
|
or frametype
|
||||||
in [
|
in [
|
||||||
FR_TYPE.CQ.value,
|
FR_TYPE.CQ.value,
|
||||||
FR_TYPE.QRV.value,
|
FR_TYPE.QRV.value,
|
||||||
FR_TYPE.PING.value,
|
FR_TYPE.PING.value,
|
||||||
FR_TYPE.BEACON.value,
|
FR_TYPE.BEACON.value,
|
||||||
]
|
]
|
||||||
):
|
):
|
||||||
|
|
||||||
# CHECK IF FRAMETYPE IS BETWEEN 10 and 50 ------------------------
|
# CHECK IF FRAMETYPE IS BETWEEN 10 and 50 ------------------------
|
||||||
frame = frametype - 10
|
# frame = frametype - 10
|
||||||
n_frames_per_burst = int.from_bytes(bytes(bytes_out[1:2]), "big")
|
# n_frames_per_burst = int.from_bytes(bytes(bytes_out[1:2]), "big")
|
||||||
|
|
||||||
# Dispatch activity based on received frametype
|
# Dispatch activity based on received frametype
|
||||||
if frametype in self.rx_dispatcher:
|
if frametype in self.rx_dispatcher:
|
||||||
|
@ -339,7 +361,7 @@ class DATA:
|
||||||
|
|
||||||
def enqueue_frame_for_tx(
|
def enqueue_frame_for_tx(
|
||||||
self,
|
self,
|
||||||
frame_to_tx: bytearray,
|
frame_to_tx: list[bytearray],
|
||||||
c2_mode=FREEDV_MODE.datac0.value,
|
c2_mode=FREEDV_MODE.datac0.value,
|
||||||
copies=1,
|
copies=1,
|
||||||
repeat_delay=0,
|
repeat_delay=0,
|
||||||
|
@ -348,7 +370,7 @@ class DATA:
|
||||||
Send (transmit) supplied frame to TNC
|
Send (transmit) supplied frame to TNC
|
||||||
|
|
||||||
:param frame_to_tx: Frame data to send
|
:param frame_to_tx: Frame data to send
|
||||||
:type frame_to_tx: bytearray
|
:type frame_to_tx: list of bytearrays
|
||||||
:param c2_mode: Codec2 mode to use, defaults to 14 (datac0)
|
:param c2_mode: Codec2 mode to use, defaults to 14 (datac0)
|
||||||
:type c2_mode: int, optional
|
:type c2_mode: int, optional
|
||||||
:param copies: Number of frame copies to send, defaults to 1
|
:param copies: Number of frame copies to send, defaults to 1
|
||||||
|
@ -361,7 +383,7 @@ class DATA:
|
||||||
# Set the TRANSMITTING flag before adding an object to the transmit queue
|
# Set the TRANSMITTING flag before adding an object to the transmit queue
|
||||||
# TODO: This is not that nice, we could improve this somehow
|
# TODO: This is not that nice, we could improve this somehow
|
||||||
static.TRANSMITTING = True
|
static.TRANSMITTING = True
|
||||||
modem.MODEM_TRANSMIT_QUEUE.put([c2_mode, copies, repeat_delay, [frame_to_tx]])
|
modem.MODEM_TRANSMIT_QUEUE.put([c2_mode, copies, repeat_delay, frame_to_tx])
|
||||||
|
|
||||||
# Wait while transmitting
|
# Wait while transmitting
|
||||||
while static.TRANSMITTING:
|
while static.TRANSMITTING:
|
||||||
|
@ -390,32 +412,48 @@ class DATA:
|
||||||
json_data_out = json.dumps(jsondata)
|
json_data_out = json.dumps(jsondata)
|
||||||
sock.SOCKET_QUEUE.put(json_data_out)
|
sock.SOCKET_QUEUE.put(json_data_out)
|
||||||
|
|
||||||
def send_burst_ack_frame(self, snr) -> None:
|
def send_ident_frame(self, transmit) -> None:
|
||||||
"""Build and send ACK frame for burst DATA frame"""
|
"""Build and send IDENT frame """
|
||||||
ack_frame = bytearray(self.length_sig_frame)
|
ident_frame = bytearray(self.length_sig1_frame)
|
||||||
ack_frame[:1] = bytes([FR_TYPE.BURST_ACK.value])
|
ident_frame[:1] = bytes([FR_TYPE.IDENT.value])
|
||||||
ack_frame[1:4] = static.DXCALLSIGN_CRC
|
ident_frame[1:self.length_sig1_frame] = self.mycallsign
|
||||||
ack_frame[4:7] = static.MYCALLSIGN_CRC
|
|
||||||
ack_frame[7:8] = bytes([int(snr)])
|
|
||||||
ack_frame[8:9] = bytes([int(self.speed_level)])
|
|
||||||
|
|
||||||
# Transmit frame
|
# Transmit frame
|
||||||
self.enqueue_frame_for_tx(ack_frame)
|
if transmit:
|
||||||
|
self.enqueue_frame_for_tx([ident_frame], c2_mode=FREEDV_MODE.datac0.value)
|
||||||
|
else:
|
||||||
|
return ident_frame
|
||||||
|
|
||||||
|
def send_burst_ack_frame(self, snr) -> None:
|
||||||
|
"""Build and send ACK frame for burst DATA frame"""
|
||||||
|
ack_frame = bytearray(self.length_sig1_frame)
|
||||||
|
ack_frame[:1] = bytes([FR_TYPE.BURST_ACK.value])
|
||||||
|
# ack_frame[1:4] = static.DXCALLSIGN_CRC
|
||||||
|
# ack_frame[4:7] = static.MYCALLSIGN_CRC
|
||||||
|
ack_frame[1:2] = self.session_id
|
||||||
|
ack_frame[2:3] = bytes([int(snr)])
|
||||||
|
ack_frame[3:4] = bytes([int(self.speed_level)])
|
||||||
|
|
||||||
|
# Transmit frame
|
||||||
|
self.enqueue_frame_for_tx([ack_frame], c2_mode=FREEDV_MODE.sig1.value)
|
||||||
|
|
||||||
def send_data_ack_frame(self, snr) -> None:
|
def send_data_ack_frame(self, snr) -> None:
|
||||||
"""Build and send ACK frame for received DATA frame"""
|
"""Build and send ACK frame for received DATA frame"""
|
||||||
ack_frame = bytearray(self.length_sig_frame)
|
ack_frame = bytearray(self.length_sig1_frame)
|
||||||
ack_frame[:1] = bytes([FR_TYPE.FR_ACK.value])
|
ack_frame[:1] = bytes([FR_TYPE.FR_ACK.value])
|
||||||
ack_frame[1:4] = static.DXCALLSIGN_CRC
|
ack_frame[1:2] = self.session_id
|
||||||
ack_frame[4:7] = static.MYCALLSIGN_CRC
|
# ack_frame[1:4] = static.DXCALLSIGN_CRC
|
||||||
ack_frame[7:8] = bytes([int(snr)])
|
# ack_frame[4:7] = static.MYCALLSIGN_CRC
|
||||||
ack_frame[8:9] = bytes([int(self.speed_level)])
|
# ack_frame[7:8] = bytes([int(snr)])
|
||||||
|
# ack_frame[8:9] = bytes([int(self.speed_level)])
|
||||||
|
|
||||||
# Transmit frame
|
# Transmit frame
|
||||||
self.enqueue_frame_for_tx(ack_frame, copies=3, repeat_delay=100)
|
# TODO: Do we have to send , self.send_ident_frame(False) ?
|
||||||
|
# self.enqueue_frame_for_tx([ack_frame, self.send_ident_frame(False)], c2_mode=FREEDV_MODE.sig1.value, copies=3, repeat_delay=0)
|
||||||
|
self.enqueue_frame_for_tx([ack_frame], c2_mode=FREEDV_MODE.sig1.value, copies=3, repeat_delay=0)
|
||||||
|
|
||||||
def send_retransmit_request_frame(self, freedv) -> None:
|
def send_retransmit_request_frame(self, freedv) -> None:
|
||||||
# check where a None is in our burst buffer and do frame+1, beacuse lists start at 0
|
# check where a None is in our burst buffer and do frame+1, because lists start at 0
|
||||||
# FIXME: Check to see if there's a `frame - 1` in the receive portion. Remove both if there is.
|
# FIXME: Check to see if there's a `frame - 1` in the receive portion. Remove both if there is.
|
||||||
missing_frames = [
|
missing_frames = [
|
||||||
frame + 1
|
frame + 1
|
||||||
|
@ -429,51 +467,60 @@ class DATA:
|
||||||
codec2.api.freedv_set_frames_per_burst(freedv, len(missing_frames))
|
codec2.api.freedv_set_frames_per_burst(freedv, len(missing_frames))
|
||||||
|
|
||||||
# TODO: Trim `missing_frames` bytesarray to [7:13] (6) frames, if it's larger.
|
# TODO: Trim `missing_frames` bytesarray to [7:13] (6) frames, if it's larger.
|
||||||
|
# TODO: Instead of using int we could use a binary flag
|
||||||
# then create a repeat frame
|
# then create a repeat frame
|
||||||
rpt_frame = bytearray(self.length_sig_frame)
|
rpt_frame = bytearray(self.length_sig1_frame)
|
||||||
rpt_frame[:1] = bytes([FR_TYPE.FR_REPEAT.value])
|
rpt_frame[:1] = bytes([FR_TYPE.FR_REPEAT.value])
|
||||||
rpt_frame[1:4] = static.DXCALLSIGN_CRC
|
rpt_frame[1:2] = self.session_id
|
||||||
rpt_frame[4:7] = static.MYCALLSIGN_CRC
|
# rpt_frame[1:4] = static.DXCALLSIGN_CRC
|
||||||
rpt_frame[7:13] = missing_frames
|
# rpt_frame[4:7] = static.MYCALLSIGN_CRC
|
||||||
|
# rpt_frame[7:13] = missing_frames
|
||||||
|
|
||||||
self.log.info("[TNC] ARQ | RX | Requesting", frames=missing_frames)
|
self.log.info("[TNC] ARQ | RX | Requesting", frames=missing_frames)
|
||||||
# Transmit frame
|
# Transmit frame
|
||||||
self.enqueue_frame_for_tx(rpt_frame)
|
self.enqueue_frame_for_tx([rpt_frame], c2_mode=FREEDV_MODE.sig1.value, copies=1, repeat_delay=0)
|
||||||
|
|
||||||
def send_burst_nack_frame(self, snr: float = 0) -> None:
|
def send_burst_nack_frame(self, snr: float = 0) -> None:
|
||||||
"""Build and send NACK frame for received DATA frame"""
|
"""Build and send NACK frame for received DATA frame"""
|
||||||
nack_frame = bytearray(self.length_sig_frame)
|
nack_frame = bytearray(self.length_sig1_frame)
|
||||||
nack_frame[:1] = bytes([FR_TYPE.FR_NACK.value])
|
nack_frame[:1] = bytes([FR_TYPE.FR_NACK.value])
|
||||||
nack_frame[1:4] = static.DXCALLSIGN_CRC
|
nack_frame[1:2] = self.session_id
|
||||||
nack_frame[4:7] = static.MYCALLSIGN_CRC
|
# nack_frame[1:4] = static.DXCALLSIGN_CRC
|
||||||
nack_frame[7:8] = bytes([int(snr)])
|
# nack_frame[4:7] = static.MYCALLSIGN_CRC
|
||||||
nack_frame[8:9] = bytes([int(self.speed_level)])
|
nack_frame[2:3] = bytes([int(snr)])
|
||||||
|
nack_frame[3:4] = bytes([int(self.speed_level)])
|
||||||
|
|
||||||
# TRANSMIT NACK FRAME FOR BURST
|
# TRANSMIT NACK FRAME FOR BURST
|
||||||
self.enqueue_frame_for_tx(nack_frame)
|
# TODO: Do we have to send ident frame?
|
||||||
|
# self.enqueue_frame_for_tx([ack_frame, self.send_ident_frame(False)], c2_mode=FREEDV_MODE.sig1.value, copies=3, repeat_delay=0)
|
||||||
|
self.enqueue_frame_for_tx([nack_frame], c2_mode=FREEDV_MODE.sig1.value, copies=1, repeat_delay=0)
|
||||||
def send_burst_nack_frame_watchdog(self, snr: float = 0) -> None:
|
def send_burst_nack_frame_watchdog(self, snr: float = 0) -> None:
|
||||||
|
|
||||||
"""Build and send NACK frame for watchdog timeout"""
|
"""Build and send NACK frame for watchdog timeout"""
|
||||||
nack_frame = bytearray(self.length_sig_frame)
|
nack_frame = bytearray(self.length_sig1_frame)
|
||||||
nack_frame[:1] = bytes([FR_TYPE.BURST_NACK.value])
|
nack_frame[:1] = bytes([FR_TYPE.BURST_NACK.value])
|
||||||
nack_frame[1:4] = static.DXCALLSIGN_CRC
|
nack_frame[1:2] = self.session_id
|
||||||
nack_frame[4:7] = static.MYCALLSIGN_CRC
|
# nack_frame[1:4] = static.DXCALLSIGN_CRC
|
||||||
nack_frame[7:8] = bytes([int(snr)])
|
# nack_frame[4:7] = static.MYCALLSIGN_CRC
|
||||||
nack_frame[8:9] = bytes([int(self.speed_level)])
|
nack_frame[2:3] = bytes([int(snr)])
|
||||||
|
nack_frame[3:4] = bytes([int(self.speed_level)])
|
||||||
|
|
||||||
# TRANSMIT NACK FRAME FOR BURST
|
# TRANSMIT NACK FRAME FOR BURST
|
||||||
self.enqueue_frame_for_tx(nack_frame)
|
self.enqueue_frame_for_tx([nack_frame], c2_mode=FREEDV_MODE.sig1.value, copies=1, repeat_delay=0)
|
||||||
|
|
||||||
def send_disconnect_frame(self) -> None:
|
def send_disconnect_frame(self) -> None:
|
||||||
"""Build and send a disconnect frame"""
|
"""Build and send a disconnect frame"""
|
||||||
disconnection_frame = bytearray(self.length_sig_frame)
|
disconnection_frame = bytearray(self.length_sig1_frame)
|
||||||
disconnection_frame[:1] = bytes([FR_TYPE.ARQ_SESSION_CLOSE.value])
|
disconnection_frame[:1] = bytes([FR_TYPE.ARQ_SESSION_CLOSE.value])
|
||||||
disconnection_frame[1:4] = static.DXCALLSIGN_CRC
|
disconnection_frame[1:2] = self.session_id
|
||||||
disconnection_frame[4:7] = static.MYCALLSIGN_CRC
|
# disconnection_frame[1:4] = static.DXCALLSIGN_CRC
|
||||||
disconnection_frame[7:13] = helpers.callsign_to_bytes(self.mycallsign)
|
# disconnection_frame[4:7] = static.MYCALLSIGN_CRC
|
||||||
|
# TODO: Needed? disconnection_frame[7:13] = helpers.callsign_to_bytes(self.mycallsign)
|
||||||
self.enqueue_frame_for_tx(disconnection_frame, copies=5, repeat_delay=250)
|
# self.enqueue_frame_for_tx([disconnection_frame, self.send_ident_frame(False)], c2_mode=FREEDV_MODE.sig0.value, copies=5, repeat_delay=0)
|
||||||
|
# TODO: We need to add the ident frame feature with a seperate PR after publishing latest protocol
|
||||||
|
# TODO: We need to wait some time between last arq related signalling frame and ident frame
|
||||||
|
# TODO: Maybe about 500ms - 1500ms to avoid confusion and too much PTT toggles
|
||||||
|
self.enqueue_frame_for_tx([disconnection_frame], c2_mode=FREEDV_MODE.sig0.value, copies=5, repeat_delay=0)
|
||||||
|
|
||||||
def arq_data_received(
|
def arq_data_received(
|
||||||
self, data_in: bytes, bytes_per_frame: int, snr: float, freedv
|
self, data_in: bytes, bytes_per_frame: int, snr: float, freedv
|
||||||
|
@ -491,11 +538,14 @@ class DATA:
|
||||||
# is intended for this station.
|
# is intended for this station.
|
||||||
data_in = bytes(data_in)
|
data_in = bytes(data_in)
|
||||||
|
|
||||||
|
# TODO: this seems not to work anymore
|
||||||
# get received crc for different mycall ssids
|
# get received crc for different mycall ssids
|
||||||
# check if callsign ssid override
|
# check if callsign ssid override
|
||||||
_, mycallsign = helpers.check_callsign(
|
# _, mycallsign = helpers.check_callsign(
|
||||||
self.mycallsign, data_in[2:5]
|
# 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
|
# only process data if we are in ARQ and BUSY state else return to quit
|
||||||
if not static.ARQ_STATE and static.TNC_STATE != "BUSY":
|
if not static.ARQ_STATE and static.TNC_STATE != "BUSY":
|
||||||
|
@ -511,19 +561,20 @@ class DATA:
|
||||||
|
|
||||||
# Extract some important data from the frame
|
# Extract some important data from the frame
|
||||||
# Get sequence number of burst frame
|
# Get sequence number of burst frame
|
||||||
RX_N_FRAME_OF_BURST = int.from_bytes(bytes(data_in[:1]), "big") - 10
|
rx_n_frame_of_burst = int.from_bytes(bytes(data_in[:1]), "big") - 10
|
||||||
# Get number of bursts from received frame
|
# Get number of bursts from received frame
|
||||||
RX_N_FRAMES_PER_BURST = int.from_bytes(bytes(data_in[1:2]), "big")
|
rx_n_frames_per_burst = int.from_bytes(bytes(data_in[1:2]), "big")
|
||||||
|
|
||||||
# The RX burst buffer needs to have a fixed length filled with "None".
|
# The RX burst buffer needs to have a fixed length filled with "None".
|
||||||
# We need this later for counting the "Nones" to detect missing data.
|
# We need this later for counting the "Nones" to detect missing data.
|
||||||
# Check if burst buffer has expected length else create it
|
# Check if burst buffer has expected length else create it
|
||||||
if len(static.RX_BURST_BUFFER) != RX_N_FRAMES_PER_BURST:
|
if len(static.RX_BURST_BUFFER) != rx_n_frames_per_burst:
|
||||||
static.RX_BURST_BUFFER = [None] * RX_N_FRAMES_PER_BURST
|
static.RX_BURST_BUFFER = [None] * rx_n_frames_per_burst
|
||||||
|
|
||||||
# Append data to rx burst buffer
|
# Append data to rx burst buffer
|
||||||
# [frame_type][n_frames_per_burst][CRC24][CRC24]
|
# [frame_type][n_frames_per_burst][CRC24][CRC24]
|
||||||
static.RX_BURST_BUFFER[RX_N_FRAME_OF_BURST] = data_in[8:] # type: ignore
|
# static.RX_BURST_BUFFER[rx_n_frame_of_burst] = data_in[8:] # type: ignore
|
||||||
|
static.RX_BURST_BUFFER[rx_n_frame_of_burst] = data_in[3:] # type: ignore
|
||||||
|
|
||||||
self.log.debug("[TNC] static.RX_BURST_BUFFER", buffer=static.RX_BURST_BUFFER)
|
self.log.debug("[TNC] static.RX_BURST_BUFFER", buffer=static.RX_BURST_BUFFER)
|
||||||
|
|
||||||
|
@ -540,7 +591,7 @@ class DATA:
|
||||||
# This is the ideal case because we received all data
|
# This is the ideal case because we received all data
|
||||||
if None not in static.RX_BURST_BUFFER:
|
if None not in static.RX_BURST_BUFFER:
|
||||||
# then iterate through burst buffer and stick the burst together
|
# then iterate through burst buffer and stick the burst together
|
||||||
# the temp burst buffer is needed for checking, if we already recevied data
|
# the temp burst buffer is needed for checking, if we already received data
|
||||||
temp_burst_buffer = b""
|
temp_burst_buffer = b""
|
||||||
for value in static.RX_BURST_BUFFER:
|
for value in static.RX_BURST_BUFFER:
|
||||||
# static.RX_FRAME_BUFFER += static.RX_BURST_BUFFER[i]
|
# static.RX_FRAME_BUFFER += static.RX_BURST_BUFFER[i]
|
||||||
|
@ -603,9 +654,8 @@ class DATA:
|
||||||
)
|
)
|
||||||
static.ARQ_SPEED_LEVEL = self.speed_level
|
static.ARQ_SPEED_LEVEL = self.speed_level
|
||||||
|
|
||||||
|
|
||||||
# Update modes we are listening to
|
# Update modes we are listening to
|
||||||
self.set_listening_modes(self.mode_list[self.speed_level])
|
self.set_listening_modes(False, True, self.mode_list[self.speed_level])
|
||||||
|
|
||||||
# Create and send ACK frame
|
# Create and send ACK frame
|
||||||
self.log.info("[TNC] ARQ | RX | SENDING ACK")
|
self.log.info("[TNC] ARQ | RX | SENDING ACK")
|
||||||
|
@ -619,7 +669,7 @@ class DATA:
|
||||||
self.rx_start_of_transmission, len(static.RX_FRAME_BUFFER)
|
self.rx_start_of_transmission, len(static.RX_FRAME_BUFFER)
|
||||||
)
|
)
|
||||||
|
|
||||||
elif RX_N_FRAME_OF_BURST == RX_N_FRAMES_PER_BURST - 1:
|
elif rx_n_frame_of_burst == rx_n_frames_per_burst - 1:
|
||||||
# We have "Nones" in our rx buffer,
|
# We have "Nones" in our rx buffer,
|
||||||
# Check if we received last frame of burst - this is an indicator for missed frames.
|
# Check if we received last frame of burst - this is an indicator for missed frames.
|
||||||
# With this way of doing this, we always MUST receive the last
|
# With this way of doing this, we always MUST receive the last
|
||||||
|
@ -627,8 +677,8 @@ class DATA:
|
||||||
# TODO: See if a timeout on the send side with re-transmit last burst would help.
|
# TODO: See if a timeout on the send side with re-transmit last burst would help.
|
||||||
self.log.debug(
|
self.log.debug(
|
||||||
"[TNC] all frames in burst received:",
|
"[TNC] all frames in burst received:",
|
||||||
frame=RX_N_FRAME_OF_BURST,
|
frame=rx_n_frame_of_burst,
|
||||||
frames=RX_N_FRAMES_PER_BURST,
|
frames=rx_n_frames_per_burst,
|
||||||
)
|
)
|
||||||
self.send_retransmit_request_frame(freedv)
|
self.send_retransmit_request_frame(freedv)
|
||||||
self.calculate_transfer_rate_rx(
|
self.calculate_transfer_rate_rx(
|
||||||
|
@ -639,8 +689,8 @@ class DATA:
|
||||||
else:
|
else:
|
||||||
self.log.error(
|
self.log.error(
|
||||||
"[TNC] data_handler: Should not reach this point...",
|
"[TNC] data_handler: Should not reach this point...",
|
||||||
frame=RX_N_FRAME_OF_BURST,
|
frame=rx_n_frame_of_burst,
|
||||||
frames=RX_N_FRAMES_PER_BURST,
|
frames=rx_n_frames_per_burst,
|
||||||
)
|
)
|
||||||
|
|
||||||
# We have a BOF and EOF flag in our data. If we received both we received our frame.
|
# We have a BOF and EOF flag in our data. If we received both we received our frame.
|
||||||
|
@ -649,7 +699,7 @@ class DATA:
|
||||||
bof_position = static.RX_FRAME_BUFFER.find(self.data_frame_bof)
|
bof_position = static.RX_FRAME_BUFFER.find(self.data_frame_bof)
|
||||||
eof_position = static.RX_FRAME_BUFFER.find(self.data_frame_eof)
|
eof_position = static.RX_FRAME_BUFFER.find(self.data_frame_eof)
|
||||||
|
|
||||||
# get total bytes per transmission information as soon we recevied a frame with a BOF
|
# get total bytes per transmission information as soon we received a frame with a BOF
|
||||||
|
|
||||||
if bof_position >= 0:
|
if bof_position >= 0:
|
||||||
payload = static.RX_FRAME_BUFFER[
|
payload = static.RX_FRAME_BUFFER[
|
||||||
|
@ -755,6 +805,7 @@ class DATA:
|
||||||
snr=snr,
|
snr=snr,
|
||||||
crc=data_frame_crc.hex(),
|
crc=data_frame_crc.hex(),
|
||||||
)
|
)
|
||||||
|
|
||||||
self.send_data_ack_frame(snr)
|
self.send_data_ack_frame(snr)
|
||||||
# Update statistics AFTER the frame ACK is sent
|
# Update statistics AFTER the frame ACK is sent
|
||||||
self.calculate_transfer_rate_rx(
|
self.calculate_transfer_rate_rx(
|
||||||
|
@ -807,15 +858,13 @@ class DATA:
|
||||||
"""
|
"""
|
||||||
self.arq_file_transfer = True
|
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
|
||||||
|
modem.RECEIVE_SIG1 = True
|
||||||
|
|
||||||
self.tx_n_retry_of_burst = 0 # retries we already sent 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
|
# Maximum number of retries to send before declaring a frame is lost
|
||||||
TX_N_MAX_RETRIES_PER_BURST = 50
|
|
||||||
TX_N_FRAMES_PER_BURST = n_frames_per_burst # amount of n frames per burst
|
|
||||||
|
|
||||||
# TIMEOUTS
|
|
||||||
BURST_ACK_TIMEOUT_SECONDS = 3.0 # timeout for burst acknowledges
|
|
||||||
DATA_FRAME_ACK_TIMEOUT_SECONDS = 3.0 # timeout for data frame acknowledges
|
|
||||||
RPT_ACK_TIMEOUT_SECONDS = 3.0 # timeout for rpt frame acknowledges
|
|
||||||
|
|
||||||
# save len of data_out to TOTAL_BYTES for our statistics --> kBytes
|
# save len of data_out to TOTAL_BYTES for our statistics --> kBytes
|
||||||
# static.TOTAL_BYTES = round(len(data_out) / 1024, 2)
|
# static.TOTAL_BYTES = round(len(data_out) / 1024, 2)
|
||||||
|
@ -867,9 +916,9 @@ class DATA:
|
||||||
|
|
||||||
# Iterate through data_out buffer
|
# Iterate through data_out buffer
|
||||||
while not self.data_frame_ack_received and static.ARQ_STATE:
|
while not self.data_frame_ack_received and static.ARQ_STATE:
|
||||||
# we have TX_N_MAX_RETRIES_PER_BURST attempts for sending a burst
|
# we have self.tx_n_max_retries_per_burst attempts for sending a burst
|
||||||
for self.tx_n_retry_of_burst in range(TX_N_MAX_RETRIES_PER_BURST):
|
for self.tx_n_retry_of_burst in range(self.tx_n_max_retries_per_burst):
|
||||||
data_mode = mode
|
# data_mode = mode
|
||||||
# self.log.debug("[TNC] FIXED MODE:", mode=FREEDV_MODE(data_mode).name)
|
# 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
|
# we are doing a modulo check of transmission retries of the actual burst
|
||||||
|
@ -906,16 +955,17 @@ class DATA:
|
||||||
# Tempbuffer list for storing our data frames
|
# Tempbuffer list for storing our data frames
|
||||||
tempbuffer = []
|
tempbuffer = []
|
||||||
|
|
||||||
# Append data frames with TX_N_FRAMES_PER_BURST to tempbuffer
|
# Append data frames with n_frames_per_burst to tempbuffer
|
||||||
# TODO: this part needs a complete rewrite!
|
# TODO: this part needs a complete rewrite!
|
||||||
# TX_N_FRAMES_PER_BURST = 1 is working
|
# n_frames_per_burst = 1 is working
|
||||||
|
|
||||||
arqheader = bytearray()
|
arqheader = bytearray()
|
||||||
# arqheader[:1] = bytes([FR_TYPE.BURST_01.value + i])
|
# arqheader[:1] = bytes([FR_TYPE.BURST_01.value + i])
|
||||||
arqheader[:1] = bytes([FR_TYPE.BURST_01.value])
|
arqheader[:1] = bytes([FR_TYPE.BURST_01.value])
|
||||||
arqheader[1:2] = bytes([TX_N_FRAMES_PER_BURST])
|
arqheader[1:2] = bytes([n_frames_per_burst])
|
||||||
arqheader[2:5] = static.DXCALLSIGN_CRC
|
arqheader[2:3] = self.session_id
|
||||||
arqheader[5:8] = static.MYCALLSIGN_CRC
|
# arqheader[2:5] = static.DXCALLSIGN_CRC
|
||||||
|
# arqheader[5:8] = static.MYCALLSIGN_CRC
|
||||||
|
|
||||||
bufferposition_end = bufferposition + payload_per_frame - len(arqheader)
|
bufferposition_end = bufferposition + payload_per_frame - len(arqheader)
|
||||||
|
|
||||||
|
@ -939,21 +989,21 @@ class DATA:
|
||||||
self.log.info(
|
self.log.info(
|
||||||
"[TNC] ARQ | TX | FRAMES",
|
"[TNC] ARQ | TX | FRAMES",
|
||||||
mode=FREEDV_MODE(data_mode).name,
|
mode=FREEDV_MODE(data_mode).name,
|
||||||
fpb=TX_N_FRAMES_PER_BURST,
|
fpb=n_frames_per_burst,
|
||||||
retry=self.tx_n_retry_of_burst,
|
retry=self.tx_n_retry_of_burst,
|
||||||
)
|
)
|
||||||
|
|
||||||
for t_buf_item in tempbuffer:
|
for t_buf_item in tempbuffer:
|
||||||
self.enqueue_frame_for_tx(t_buf_item, c2_mode=data_mode)
|
self.enqueue_frame_for_tx([t_buf_item], c2_mode=data_mode)
|
||||||
|
|
||||||
# After transmission finished, wait for an ACK or RPT frame
|
# After transmission finished, wait for an ACK or RPT frame
|
||||||
# burstacktimeout = time.time() + BURST_ACK_TIMEOUT_SECONDS + 100
|
# burstacktimeout = time.time() + self.burst_ack_timeout_seconds + 100
|
||||||
# while (not self.burst_ack and not self.burst_nack and
|
# while (not self.burst_ack and not self.burst_nack and
|
||||||
# not self.rpt_request_received and not self.data_frame_ack_received and
|
# not self.rpt_request_received and not self.data_frame_ack_received and
|
||||||
# time.time() < burstacktimeout and static.ARQ_STATE):
|
# time.time() < burstacktimeout and static.ARQ_STATE):
|
||||||
# time.sleep(0.01)
|
# time.sleep(0.01)
|
||||||
|
|
||||||
# burstacktimeout = time.time() + BURST_ACK_TIMEOUT_SECONDS + 100
|
# burstacktimeout = time.time() + self.burst_ack_timeout_seconds + 100
|
||||||
while static.ARQ_STATE and not (
|
while static.ARQ_STATE and not (
|
||||||
self.burst_ack
|
self.burst_ack
|
||||||
or self.burst_nack
|
or self.burst_nack
|
||||||
|
@ -1000,7 +1050,7 @@ class DATA:
|
||||||
self.log.debug(
|
self.log.debug(
|
||||||
"[TNC] ATTEMPT:",
|
"[TNC] ATTEMPT:",
|
||||||
retry=self.tx_n_retry_of_burst,
|
retry=self.tx_n_retry_of_burst,
|
||||||
maxretries=TX_N_MAX_RETRIES_PER_BURST,
|
maxretries=self.tx_n_max_retries_per_burst,
|
||||||
overflows=static.BUFFER_OVERFLOW_COUNTER,
|
overflows=static.BUFFER_OVERFLOW_COUNTER,
|
||||||
)
|
)
|
||||||
# End of FOR loop
|
# End of FOR loop
|
||||||
|
@ -1022,7 +1072,7 @@ class DATA:
|
||||||
bytesperminute=static.ARQ_BYTES_PER_MINUTE,
|
bytesperminute=static.ARQ_BYTES_PER_MINUTE,
|
||||||
)
|
)
|
||||||
|
|
||||||
# Stay in the while loop until we receive a data_frame_ack. Otherwise
|
# Stay in the while loop until we receive a data_frame_ack. Otherwise,
|
||||||
# the loop exits after sending the last frame only once and doesn't
|
# the loop exits after sending the last frame only once and doesn't
|
||||||
# wait for an acknowledgement.
|
# wait for an acknowledgement.
|
||||||
if self.data_frame_ack_received and bufferposition > len(data_out):
|
if self.data_frame_ack_received and bufferposition > len(data_out):
|
||||||
|
@ -1076,7 +1126,7 @@ class DATA:
|
||||||
|
|
||||||
def burst_ack_nack_received(self, data_in: bytes) -> None:
|
def burst_ack_nack_received(self, data_in: bytes) -> None:
|
||||||
"""
|
"""
|
||||||
Received a ACK/NACK for a transmitted frame, keep track and
|
Received an ACK/NACK for a transmitted frame, keep track and
|
||||||
make adjustments to speed level if needed.
|
make adjustments to speed level if needed.
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
|
@ -1118,8 +1168,8 @@ class DATA:
|
||||||
|
|
||||||
# Update data_channel timestamp
|
# Update data_channel timestamp
|
||||||
self.data_channel_last_received = int(time.time())
|
self.data_channel_last_received = int(time.time())
|
||||||
self.burst_ack_snr = int.from_bytes(bytes(data_in[7:8]), "big")
|
self.burst_ack_snr = int.from_bytes(bytes(data_in[2:3]), "big")
|
||||||
self.speed_level = int.from_bytes(bytes(data_in[8:9]), "big")
|
self.speed_level = int.from_bytes(bytes(data_in[3:4]), "big")
|
||||||
static.ARQ_SPEED_LEVEL = self.speed_level
|
static.ARQ_SPEED_LEVEL = self.speed_level
|
||||||
|
|
||||||
self.log.debug(
|
self.log.debug(
|
||||||
|
@ -1272,11 +1322,16 @@ class DATA:
|
||||||
self.IS_ARQ_SESSION_MASTER = True
|
self.IS_ARQ_SESSION_MASTER = True
|
||||||
static.ARQ_SESSION_STATE = "connecting"
|
static.ARQ_SESSION_STATE = "connecting"
|
||||||
|
|
||||||
connection_frame = bytearray(self.length_sig_frame)
|
# create a random session id
|
||||||
|
self.session_id = randbytes(1)
|
||||||
|
print(self.session_id)
|
||||||
|
|
||||||
|
connection_frame = bytearray(self.length_sig0_frame)
|
||||||
connection_frame[:1] = bytes([FR_TYPE.ARQ_SESSION_OPEN.value])
|
connection_frame[:1] = bytes([FR_TYPE.ARQ_SESSION_OPEN.value])
|
||||||
connection_frame[1:4] = static.DXCALLSIGN_CRC
|
connection_frame[1:2] = self.session_id
|
||||||
connection_frame[4:7] = static.MYCALLSIGN_CRC
|
connection_frame[2:5] = static.DXCALLSIGN_CRC
|
||||||
connection_frame[7:13] = helpers.callsign_to_bytes(self.mycallsign)
|
connection_frame[5:8] = static.MYCALLSIGN_CRC
|
||||||
|
connection_frame[8:14] = helpers.callsign_to_bytes(self.mycallsign)
|
||||||
|
|
||||||
while not static.ARQ_SESSION:
|
while not static.ARQ_SESSION:
|
||||||
time.sleep(0.01)
|
time.sleep(0.01)
|
||||||
|
@ -1291,7 +1346,7 @@ class DATA:
|
||||||
state=static.ARQ_SESSION_STATE,
|
state=static.ARQ_SESSION_STATE,
|
||||||
)
|
)
|
||||||
|
|
||||||
self.enqueue_frame_for_tx(connection_frame)
|
self.enqueue_frame_for_tx([connection_frame], c2_mode=FREEDV_MODE.datac0.value, copies=1, repeat_delay=0)
|
||||||
|
|
||||||
# Wait for a time, looking to see if `static.ARQ_SESSION`
|
# Wait for a time, looking to see if `static.ARQ_SESSION`
|
||||||
# indicates we've received a positive response from the far station.
|
# indicates we've received a positive response from the far station.
|
||||||
|
@ -1325,8 +1380,9 @@ class DATA:
|
||||||
# Update arq_session timestamp
|
# Update arq_session timestamp
|
||||||
self.arq_session_last_received = int(time.time())
|
self.arq_session_last_received = int(time.time())
|
||||||
|
|
||||||
static.DXCALLSIGN_CRC = bytes(data_in[4:7])
|
self.session_id = bytes(data_in[1:2])
|
||||||
static.DXCALLSIGN = helpers.bytes_to_callsign(bytes(data_in[7:13]))
|
static.DXCALLSIGN_CRC = bytes(data_in[5:8])
|
||||||
|
static.DXCALLSIGN = helpers.bytes_to_callsign(bytes(data_in[8:14]))
|
||||||
|
|
||||||
helpers.add_to_heard_stations(
|
helpers.add_to_heard_stations(
|
||||||
static.DXCALLSIGN,
|
static.DXCALLSIGN,
|
||||||
|
@ -1377,9 +1433,11 @@ class DATA:
|
||||||
|
|
||||||
self.IS_ARQ_SESSION_MASTER = False
|
self.IS_ARQ_SESSION_MASTER = False
|
||||||
static.ARQ_SESSION = False
|
static.ARQ_SESSION = False
|
||||||
self.arq_cleanup()
|
|
||||||
|
|
||||||
|
# we need to send disconnect frame before doing arq cleanup
|
||||||
|
# we would lose our session id then
|
||||||
self.send_disconnect_frame()
|
self.send_disconnect_frame()
|
||||||
|
self.arq_cleanup()
|
||||||
|
|
||||||
def received_session_close(self, data_in: bytes):
|
def received_session_close(self, data_in: bytes):
|
||||||
"""
|
"""
|
||||||
|
@ -1394,7 +1452,8 @@ class DATA:
|
||||||
# is intended for this station.
|
# is intended for this station.
|
||||||
# Close the session if the CRC matches the remote station in static.
|
# Close the session if the CRC matches the remote station in static.
|
||||||
_valid_crc, _ = helpers.check_callsign(static.DXCALLSIGN, bytes(data_in[4:7]))
|
_valid_crc, _ = helpers.check_callsign(static.DXCALLSIGN, bytes(data_in[4:7]))
|
||||||
if _valid_crc:
|
_valid_session = helpers.check_session_id(self.session_id, bytes(data_in[1:2]))
|
||||||
|
if _valid_crc or _valid_session:
|
||||||
static.ARQ_SESSION_STATE = "disconnected"
|
static.ARQ_SESSION_STATE = "disconnected"
|
||||||
helpers.add_to_heard_stations(
|
helpers.add_to_heard_stations(
|
||||||
static.DXCALLSIGN,
|
static.DXCALLSIGN,
|
||||||
|
@ -1429,12 +1488,13 @@ class DATA:
|
||||||
# static.TNC_STATE = "BUSY"
|
# static.TNC_STATE = "BUSY"
|
||||||
# static.ARQ_SESSION_STATE = "connected"
|
# static.ARQ_SESSION_STATE = "connected"
|
||||||
|
|
||||||
connection_frame = bytearray(self.length_sig_frame)
|
connection_frame = bytearray(self.length_sig0_frame)
|
||||||
connection_frame[:1] = bytes([FR_TYPE.ARQ_SESSION_HB.value])
|
connection_frame[:1] = bytes([FR_TYPE.ARQ_SESSION_HB.value])
|
||||||
connection_frame[1:4] = static.DXCALLSIGN_CRC
|
connection_frame[1:2] = self.session_id
|
||||||
connection_frame[4:7] = static.MYCALLSIGN_CRC
|
# connection_frame[1:4] = static.DXCALLSIGN_CRC
|
||||||
|
# connection_frame[4:7] = static.MYCALLSIGN_CRC
|
||||||
|
|
||||||
self.enqueue_frame_for_tx(connection_frame)
|
self.enqueue_frame_for_tx([connection_frame], c2_mode=FREEDV_MODE.datac0.value, copies=1, repeat_delay=0)
|
||||||
|
|
||||||
def received_session_heartbeat(self, data_in: bytes) -> None:
|
def received_session_heartbeat(self, data_in: bytes) -> None:
|
||||||
"""
|
"""
|
||||||
|
@ -1443,9 +1503,10 @@ class DATA:
|
||||||
data_in:bytes:
|
data_in:bytes:
|
||||||
|
|
||||||
"""
|
"""
|
||||||
# Accept session data if the DXCALLSIGN_CRC matches the station in static.
|
# Accept session data if the DXCALLSIGN_CRC matches the station in static or session id.
|
||||||
_valid_crc, _ = helpers.check_callsign(static.DXCALLSIGN, bytes(data_in[4:7]))
|
_valid_crc, _ = helpers.check_callsign(static.DXCALLSIGN, bytes(data_in[4:7]))
|
||||||
if _valid_crc:
|
_valid_session = helpers.check_session_id(self.session_id, bytes(data_in[1:2]))
|
||||||
|
if _valid_crc or _valid_session:
|
||||||
self.log.debug("[TNC] Received session heartbeat")
|
self.log.debug("[TNC] Received session heartbeat")
|
||||||
helpers.add_to_heard_stations(
|
helpers.add_to_heard_stations(
|
||||||
static.DXCALLSIGN,
|
static.DXCALLSIGN,
|
||||||
|
@ -1463,8 +1524,19 @@ class DATA:
|
||||||
# Update the timeout timestamps
|
# Update the timeout timestamps
|
||||||
self.arq_session_last_received = int(time.time())
|
self.arq_session_last_received = int(time.time())
|
||||||
self.data_channel_last_received = int(time.time())
|
self.data_channel_last_received = int(time.time())
|
||||||
|
# transmit session heartbeat only
|
||||||
if not self.IS_ARQ_SESSION_MASTER and not self.arq_file_transfer:
|
# -> if not session master
|
||||||
|
# --> this will be triggered by heartbeat watchdog
|
||||||
|
# -> if not during a file transfer
|
||||||
|
# -> if ARQ_SESSION_STATE != disconnecting, disconnected, failed
|
||||||
|
# --> to avoid heartbeat toggle loops while disconnecting
|
||||||
|
if (
|
||||||
|
not self.IS_ARQ_SESSION_MASTER
|
||||||
|
and not self.arq_file_transfer
|
||||||
|
and static.ARQ_SESSION_STATE != 'disconnecting'
|
||||||
|
and static.ARQ_SESSION_STATE != 'disconnected'
|
||||||
|
and static.ARQ_SESSION_STATE != 'failed'
|
||||||
|
):
|
||||||
self.transmit_session_heartbeat()
|
self.transmit_session_heartbeat()
|
||||||
|
|
||||||
##########################################################################################################
|
##########################################################################################################
|
||||||
|
@ -1485,6 +1557,8 @@ class DATA:
|
||||||
data_out:bytes:
|
data_out:bytes:
|
||||||
mode:int:
|
mode:int:
|
||||||
n_frames_per_burst:int:
|
n_frames_per_burst:int:
|
||||||
|
transmission_uuid:str:
|
||||||
|
mycallsign:bytes:
|
||||||
|
|
||||||
Returns:
|
Returns:
|
||||||
True if the data session was opened and the data was sent
|
True if the data session was opened and the data was sent
|
||||||
|
@ -1499,12 +1573,13 @@ class DATA:
|
||||||
self.transmission_uuid = transmission_uuid
|
self.transmission_uuid = transmission_uuid
|
||||||
|
|
||||||
# wait a moment for the case, a heartbeat is already on the way back to us
|
# wait a moment for the case, a heartbeat is already on the way back to us
|
||||||
|
# this makes channel establishment more clean
|
||||||
if static.ARQ_SESSION:
|
if static.ARQ_SESSION:
|
||||||
time.sleep(0.5)
|
time.sleep(2)
|
||||||
|
|
||||||
self.datachannel_timeout = False
|
self.datachannel_timeout = False
|
||||||
|
|
||||||
# we need to compress data for gettin a compression factor.
|
# we need to compress data for getting a compression factor.
|
||||||
# so we are compressing twice. This is not that nice and maybe there is another way
|
# so we are compressing twice. This is not that nice and maybe there is another way
|
||||||
# for calculating transmission statistics
|
# for calculating transmission statistics
|
||||||
# static.ARQ_COMPRESSION_FACTOR = len(data_out) / len(zlib.compress(data_out))
|
# static.ARQ_COMPRESSION_FACTOR = len(data_out) / len(zlib.compress(data_out))
|
||||||
|
@ -1530,6 +1605,7 @@ class DATA:
|
||||||
Args:
|
Args:
|
||||||
mode:int:
|
mode:int:
|
||||||
n_frames_per_burst:int:
|
n_frames_per_burst:int:
|
||||||
|
mycallsign:bytes:
|
||||||
|
|
||||||
Returns:
|
Returns:
|
||||||
True if the data channel was opened successfully
|
True if the data channel was opened successfully
|
||||||
|
@ -1537,6 +1613,11 @@ class DATA:
|
||||||
"""
|
"""
|
||||||
self.is_IRS = False
|
self.is_IRS = False
|
||||||
|
|
||||||
|
# init a new random session id if we are not in an arq session
|
||||||
|
if not static.ARQ_SESSION:
|
||||||
|
self.session_id = randbytes(1)
|
||||||
|
print(self.session_id)
|
||||||
|
|
||||||
# Update data_channel timestamp
|
# Update data_channel timestamp
|
||||||
self.data_channel_last_received = int(time.time())
|
self.data_channel_last_received = int(time.time())
|
||||||
|
|
||||||
|
@ -1548,12 +1629,13 @@ class DATA:
|
||||||
frametype = bytes([FR_TYPE.ARQ_DC_OPEN_W.value])
|
frametype = bytes([FR_TYPE.ARQ_DC_OPEN_W.value])
|
||||||
self.log.debug("[TNC] Requesting high bandwidth mode")
|
self.log.debug("[TNC] Requesting high bandwidth mode")
|
||||||
|
|
||||||
connection_frame = bytearray(self.length_sig_frame)
|
connection_frame = bytearray(self.length_sig0_frame)
|
||||||
connection_frame[:1] = frametype
|
connection_frame[:1] = frametype
|
||||||
connection_frame[1:4] = static.DXCALLSIGN_CRC
|
connection_frame[1:4] = static.DXCALLSIGN_CRC
|
||||||
connection_frame[4:7] = static.MYCALLSIGN_CRC
|
connection_frame[4:7] = static.MYCALLSIGN_CRC
|
||||||
connection_frame[7:13] = helpers.callsign_to_bytes(mycallsign)
|
connection_frame[7:13] = helpers.callsign_to_bytes(mycallsign)
|
||||||
connection_frame[13:14] = bytes([n_frames_per_burst])
|
# connection_frame[13:14] = bytes([n_frames_per_burst])
|
||||||
|
connection_frame[13:14] = self.session_id
|
||||||
|
|
||||||
while not static.ARQ_STATE:
|
while not static.ARQ_STATE:
|
||||||
time.sleep(0.01)
|
time.sleep(0.01)
|
||||||
|
@ -1574,7 +1656,7 @@ class DATA:
|
||||||
attempt=f"{str(attempt + 1)}/{str(self.data_channel_max_retries)}",
|
attempt=f"{str(attempt + 1)}/{str(self.data_channel_max_retries)}",
|
||||||
)
|
)
|
||||||
|
|
||||||
self.enqueue_frame_for_tx(connection_frame)
|
self.enqueue_frame_for_tx([connection_frame], c2_mode=FREEDV_MODE.datac0.value, copies=1, repeat_delay=0)
|
||||||
|
|
||||||
timeout = time.time() + 4
|
timeout = time.time() + 4
|
||||||
while time.time() < timeout:
|
while time.time() < timeout:
|
||||||
|
@ -1607,19 +1689,20 @@ class DATA:
|
||||||
+ "]"
|
+ "]"
|
||||||
)
|
)
|
||||||
self.datachannel_timeout = True
|
self.datachannel_timeout = True
|
||||||
self.arq_cleanup()
|
|
||||||
|
|
||||||
# Attempt to cleanup the far-side, if it received the
|
# Attempt to clean up the far-side, if it received the
|
||||||
# open_session frame and can still hear us.
|
# open_session frame and can still hear us.
|
||||||
self.close_session()
|
self.close_session()
|
||||||
|
|
||||||
|
self.arq_cleanup()
|
||||||
return False
|
return False
|
||||||
|
|
||||||
# Shouldn't get here..
|
# Shouldn't get here...
|
||||||
return True
|
return True
|
||||||
|
|
||||||
def arq_received_data_channel_opener(self, data_in: bytes):
|
def arq_received_data_channel_opener(self, data_in: bytes):
|
||||||
"""
|
"""
|
||||||
Received request to open data channel framt
|
Received request to open data channel frame
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
data_in:bytes:
|
data_in:bytes:
|
||||||
|
@ -1699,7 +1782,7 @@ class DATA:
|
||||||
)
|
)
|
||||||
|
|
||||||
# Update modes we are listening to
|
# Update modes we are listening to
|
||||||
self.set_listening_modes(self.mode_list[self.speed_level])
|
self.set_listening_modes(True, True, self.mode_list[self.speed_level])
|
||||||
|
|
||||||
helpers.add_to_heard_stations(
|
helpers.add_to_heard_stations(
|
||||||
static.DXCALLSIGN,
|
static.DXCALLSIGN,
|
||||||
|
@ -1710,6 +1793,9 @@ class DATA:
|
||||||
static.HAMLIB_FREQUENCY,
|
static.HAMLIB_FREQUENCY,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
self.session_id = data_in[13:14]
|
||||||
|
print(self.session_id)
|
||||||
|
|
||||||
# check if callsign ssid override
|
# check if callsign ssid override
|
||||||
_, mycallsign = helpers.check_callsign(self.mycallsign, data_in[1:4])
|
_, mycallsign = helpers.check_callsign(self.mycallsign, data_in[1:4])
|
||||||
|
|
||||||
|
@ -1738,16 +1824,17 @@ class DATA:
|
||||||
frametype = bytes([FR_TYPE.ARQ_DC_OPEN_ACK_W.value])
|
frametype = bytes([FR_TYPE.ARQ_DC_OPEN_ACK_W.value])
|
||||||
self.log.debug("[TNC] Responding with high bandwidth mode")
|
self.log.debug("[TNC] Responding with high bandwidth mode")
|
||||||
|
|
||||||
connection_frame = bytearray(self.length_sig_frame)
|
connection_frame = bytearray(self.length_sig0_frame)
|
||||||
connection_frame[:1] = frametype
|
connection_frame[:1] = frametype
|
||||||
connection_frame[1:4] = static.DXCALLSIGN_CRC
|
connection_frame[1:2] = self.session_id
|
||||||
connection_frame[4:7] = static.MYCALLSIGN_CRC
|
# connection_frame[1:4] = static.DXCALLSIGN_CRC
|
||||||
|
# connection_frame[4:7] = static.MYCALLSIGN_CRC
|
||||||
connection_frame[8:9] = bytes([self.speed_level])
|
connection_frame[8:9] = bytes([self.speed_level])
|
||||||
|
|
||||||
# For checking protocol version on the receiving side
|
# For checking protocol version on the receiving side
|
||||||
connection_frame[13:14] = bytes([static.ARQ_PROTOCOL_VERSION])
|
connection_frame[13:14] = bytes([static.ARQ_PROTOCOL_VERSION])
|
||||||
|
|
||||||
self.enqueue_frame_for_tx(connection_frame)
|
self.enqueue_frame_for_tx([connection_frame], c2_mode=FREEDV_MODE.datac0.value, copies=1, repeat_delay=0)
|
||||||
|
|
||||||
self.log.info(
|
self.log.info(
|
||||||
"[TNC] ARQ | DATA | RX | ["
|
"[TNC] ARQ | DATA | RX | ["
|
||||||
|
@ -1860,7 +1947,7 @@ class DATA:
|
||||||
+ "]"
|
+ "]"
|
||||||
)
|
)
|
||||||
|
|
||||||
ping_frame = bytearray(self.length_sig_frame)
|
ping_frame = bytearray(self.length_sig0_frame)
|
||||||
ping_frame[:1] = bytes([FR_TYPE.PING.value])
|
ping_frame[:1] = bytes([FR_TYPE.PING.value])
|
||||||
ping_frame[1:4] = static.DXCALLSIGN_CRC
|
ping_frame[1:4] = static.DXCALLSIGN_CRC
|
||||||
ping_frame[4:7] = static.MYCALLSIGN_CRC
|
ping_frame[4:7] = static.MYCALLSIGN_CRC
|
||||||
|
@ -1868,9 +1955,9 @@ class DATA:
|
||||||
|
|
||||||
self.log.info("[TNC] ENABLE FSK", state=static.ENABLE_FSK)
|
self.log.info("[TNC] ENABLE FSK", state=static.ENABLE_FSK)
|
||||||
if static.ENABLE_FSK:
|
if static.ENABLE_FSK:
|
||||||
self.enqueue_frame_for_tx(ping_frame, c2_mode=FREEDV_MODE.fsk_ldpc_0.value)
|
self.enqueue_frame_for_tx([ping_frame], c2_mode=FREEDV_MODE.fsk_ldpc_0.value)
|
||||||
else:
|
else:
|
||||||
self.enqueue_frame_for_tx(ping_frame, c2_mode=FREEDV_MODE.datac0.value)
|
self.enqueue_frame_for_tx([ping_frame], c2_mode=FREEDV_MODE.datac0.value)
|
||||||
|
|
||||||
def received_ping(self, data_in: bytes) -> None:
|
def received_ping(self, data_in: bytes) -> None:
|
||||||
"""
|
"""
|
||||||
|
@ -1919,7 +2006,7 @@ class DATA:
|
||||||
snr=static.SNR,
|
snr=static.SNR,
|
||||||
)
|
)
|
||||||
|
|
||||||
ping_frame = bytearray(self.length_sig_frame)
|
ping_frame = bytearray(self.length_sig0_frame)
|
||||||
ping_frame[:1] = bytes([FR_TYPE.PING_ACK.value])
|
ping_frame[:1] = bytes([FR_TYPE.PING_ACK.value])
|
||||||
ping_frame[1:4] = static.DXCALLSIGN_CRC
|
ping_frame[1:4] = static.DXCALLSIGN_CRC
|
||||||
ping_frame[4:7] = static.MYCALLSIGN_CRC
|
ping_frame[4:7] = static.MYCALLSIGN_CRC
|
||||||
|
@ -1927,9 +2014,9 @@ class DATA:
|
||||||
|
|
||||||
self.log.info("[TNC] ENABLE FSK", state=static.ENABLE_FSK)
|
self.log.info("[TNC] ENABLE FSK", state=static.ENABLE_FSK)
|
||||||
if static.ENABLE_FSK:
|
if static.ENABLE_FSK:
|
||||||
self.enqueue_frame_for_tx(ping_frame, c2_mode=FREEDV_MODE.fsk_ldpc_0.value)
|
self.enqueue_frame_for_tx([ping_frame], c2_mode=FREEDV_MODE.fsk_ldpc_0.value)
|
||||||
else:
|
else:
|
||||||
self.enqueue_frame_for_tx(ping_frame, c2_mode=FREEDV_MODE.datac0.value)
|
self.enqueue_frame_for_tx([ping_frame], c2_mode=FREEDV_MODE.datac0.value)
|
||||||
|
|
||||||
def received_ping_ack(self, data_in: bytes) -> None:
|
def received_ping_ack(self, data_in: bytes) -> None:
|
||||||
"""
|
"""
|
||||||
|
@ -1976,13 +2063,14 @@ class DATA:
|
||||||
Force a stop of the running transmission
|
Force a stop of the running transmission
|
||||||
"""
|
"""
|
||||||
self.log.warning("[TNC] Stopping transmission!")
|
self.log.warning("[TNC] Stopping transmission!")
|
||||||
stop_frame = bytearray(self.length_sig_frame)
|
stop_frame = bytearray(self.length_sig0_frame)
|
||||||
stop_frame[:1] = bytes([FR_TYPE.ARQ_STOP.value])
|
stop_frame[:1] = bytes([FR_TYPE.ARQ_STOP.value])
|
||||||
stop_frame[1:4] = static.DXCALLSIGN_CRC
|
stop_frame[1:4] = static.DXCALLSIGN_CRC
|
||||||
stop_frame[4:7] = static.MYCALLSIGN_CRC
|
stop_frame[4:7] = static.MYCALLSIGN_CRC
|
||||||
|
# TODO: Not sure if we really need the session id when disconnecting
|
||||||
|
# stop_frame[1:2] = self.session_id
|
||||||
stop_frame[7:13] = helpers.callsign_to_bytes(self.mycallsign)
|
stop_frame[7:13] = helpers.callsign_to_bytes(self.mycallsign)
|
||||||
|
self.enqueue_frame_for_tx([stop_frame], c2_mode=FREEDV_MODE.sig1.value, copies=6, repeat_delay=0)
|
||||||
self.enqueue_frame_for_tx(stop_frame, copies=2, repeat_delay=250)
|
|
||||||
|
|
||||||
static.TNC_STATE = "IDLE"
|
static.TNC_STATE = "IDLE"
|
||||||
static.ARQ_STATE = False
|
static.ARQ_STATE = False
|
||||||
|
@ -2041,7 +2129,7 @@ class DATA:
|
||||||
"[TNC] Sending beacon!", interval=self.beacon_interval
|
"[TNC] Sending beacon!", interval=self.beacon_interval
|
||||||
)
|
)
|
||||||
|
|
||||||
beacon_frame = bytearray(self.length_sig_frame)
|
beacon_frame = bytearray(self.length_sig0_frame)
|
||||||
beacon_frame[:1] = bytes([FR_TYPE.BEACON.value])
|
beacon_frame[:1] = bytes([FR_TYPE.BEACON.value])
|
||||||
beacon_frame[1:7] = helpers.callsign_to_bytes(self.mycallsign)
|
beacon_frame[1:7] = helpers.callsign_to_bytes(self.mycallsign)
|
||||||
beacon_frame[9:13] = static.MYGRID[:4]
|
beacon_frame[9:13] = static.MYGRID[:4]
|
||||||
|
@ -2049,11 +2137,12 @@ class DATA:
|
||||||
|
|
||||||
if static.ENABLE_FSK:
|
if static.ENABLE_FSK:
|
||||||
self.enqueue_frame_for_tx(
|
self.enqueue_frame_for_tx(
|
||||||
beacon_frame,
|
[beacon_frame],
|
||||||
c2_mode=FREEDV_MODE.fsk_ldpc_0.value,
|
c2_mode=FREEDV_MODE.fsk_ldpc_0.value,
|
||||||
)
|
)
|
||||||
else:
|
else:
|
||||||
self.enqueue_frame_for_tx(beacon_frame)
|
self.enqueue_frame_for_tx([beacon_frame], c2_mode=FREEDV_MODE.datac0.value, copies=1,
|
||||||
|
repeat_delay=0)
|
||||||
|
|
||||||
interval_timer = time.time() + self.beacon_interval
|
interval_timer = time.time() + self.beacon_interval
|
||||||
while (
|
while (
|
||||||
|
@ -2109,7 +2198,7 @@ class DATA:
|
||||||
"""
|
"""
|
||||||
Transmit a CQ
|
Transmit a CQ
|
||||||
Args:
|
Args:
|
||||||
Nothing
|
self
|
||||||
|
|
||||||
Returns:
|
Returns:
|
||||||
Nothing
|
Nothing
|
||||||
|
@ -2119,7 +2208,7 @@ class DATA:
|
||||||
freedata="tnc-message",
|
freedata="tnc-message",
|
||||||
cq="transmitting",
|
cq="transmitting",
|
||||||
)
|
)
|
||||||
cq_frame = bytearray(self.length_sig_frame)
|
cq_frame = bytearray(self.length_sig0_frame)
|
||||||
cq_frame[:1] = bytes([FR_TYPE.CQ.value])
|
cq_frame[:1] = bytes([FR_TYPE.CQ.value])
|
||||||
cq_frame[1:7] = helpers.callsign_to_bytes(self.mycallsign)
|
cq_frame[1:7] = helpers.callsign_to_bytes(self.mycallsign)
|
||||||
cq_frame[7:11] = helpers.encode_grid(static.MYGRID.decode("UTF-8"))
|
cq_frame[7:11] = helpers.encode_grid(static.MYGRID.decode("UTF-8"))
|
||||||
|
@ -2128,9 +2217,9 @@ class DATA:
|
||||||
self.log.debug("[TNC] CQ Frame:", data=[cq_frame])
|
self.log.debug("[TNC] CQ Frame:", data=[cq_frame])
|
||||||
|
|
||||||
if static.ENABLE_FSK:
|
if static.ENABLE_FSK:
|
||||||
self.enqueue_frame_for_tx(cq_frame, c2_mode=FREEDV_MODE.fsk_ldpc_0.value)
|
self.enqueue_frame_for_tx([cq_frame], c2_mode=FREEDV_MODE.fsk_ldpc_0.value)
|
||||||
else:
|
else:
|
||||||
self.enqueue_frame_for_tx(cq_frame)
|
self.enqueue_frame_for_tx([cq_frame], c2_mode=FREEDV_MODE.datac0.value, copies=1, repeat_delay=0)
|
||||||
|
|
||||||
def received_cq(self, data_in: bytes) -> None:
|
def received_cq(self, data_in: bytes) -> None:
|
||||||
"""
|
"""
|
||||||
|
@ -2190,7 +2279,7 @@ class DATA:
|
||||||
)
|
)
|
||||||
self.log.info("[TNC] Sending QRV!")
|
self.log.info("[TNC] Sending QRV!")
|
||||||
|
|
||||||
qrv_frame = bytearray(self.length_sig_frame)
|
qrv_frame = bytearray(self.length_sig0_frame)
|
||||||
qrv_frame[:1] = bytes([FR_TYPE.QRV.value])
|
qrv_frame[:1] = bytes([FR_TYPE.QRV.value])
|
||||||
qrv_frame[1:7] = helpers.callsign_to_bytes(self.mycallsign)
|
qrv_frame[1:7] = helpers.callsign_to_bytes(self.mycallsign)
|
||||||
qrv_frame[7:11] = helpers.encode_grid(static.MYGRID.decode("UTF-8"))
|
qrv_frame[7:11] = helpers.encode_grid(static.MYGRID.decode("UTF-8"))
|
||||||
|
@ -2198,9 +2287,9 @@ class DATA:
|
||||||
self.log.info("[TNC] ENABLE FSK", state=static.ENABLE_FSK)
|
self.log.info("[TNC] ENABLE FSK", state=static.ENABLE_FSK)
|
||||||
|
|
||||||
if static.ENABLE_FSK:
|
if static.ENABLE_FSK:
|
||||||
self.enqueue_frame_for_tx(qrv_frame, c2_mode=FREEDV_MODE.fsk_ldpc_0.value)
|
self.enqueue_frame_for_tx([qrv_frame], c2_mode=FREEDV_MODE.fsk_ldpc_0.value)
|
||||||
else:
|
else:
|
||||||
self.enqueue_frame_for_tx(qrv_frame)
|
self.enqueue_frame_for_tx([qrv_frame], c2_mode=FREEDV_MODE.datac0.value, copies=1, repeat_delay=0)
|
||||||
|
|
||||||
def received_qrv(self, data_in: bytes) -> None:
|
def received_qrv(self, data_in: bytes) -> None:
|
||||||
"""
|
"""
|
||||||
|
@ -2235,7 +2324,7 @@ class DATA:
|
||||||
static.HAMLIB_FREQUENCY,
|
static.HAMLIB_FREQUENCY,
|
||||||
)
|
)
|
||||||
|
|
||||||
# ------------ CALUCLATE TRANSFER RATES
|
# ------------ CALCULATE TRANSFER RATES
|
||||||
def calculate_transfer_rate_rx(
|
def calculate_transfer_rate_rx(
|
||||||
self, rx_start_of_transmission: float, receivedbytes: int
|
self, rx_start_of_transmission: float, receivedbytes: int
|
||||||
) -> list:
|
) -> list:
|
||||||
|
@ -2258,7 +2347,7 @@ class DATA:
|
||||||
(
|
(
|
||||||
receivedbytes
|
receivedbytes
|
||||||
* static.ARQ_COMPRESSION_FACTOR
|
* static.ARQ_COMPRESSION_FACTOR
|
||||||
/ (static.TOTAL_BYTES)
|
/ static.TOTAL_BYTES
|
||||||
)
|
)
|
||||||
* 100
|
* 100
|
||||||
),
|
),
|
||||||
|
@ -2270,7 +2359,7 @@ class DATA:
|
||||||
if receivedbytes > 0:
|
if receivedbytes > 0:
|
||||||
static.ARQ_BITS_PER_SECOND = int((receivedbytes * 8) / transmissiontime)
|
static.ARQ_BITS_PER_SECOND = int((receivedbytes * 8) / transmissiontime)
|
||||||
static.ARQ_BYTES_PER_MINUTE = int(
|
static.ARQ_BYTES_PER_MINUTE = int(
|
||||||
(receivedbytes) / (transmissiontime / 60)
|
receivedbytes / (transmissiontime / 60)
|
||||||
)
|
)
|
||||||
|
|
||||||
else:
|
else:
|
||||||
|
@ -2324,7 +2413,7 @@ class DATA:
|
||||||
|
|
||||||
if sentbytes > 0:
|
if sentbytes > 0:
|
||||||
static.ARQ_BITS_PER_SECOND = int((sentbytes * 8) / transmissiontime)
|
static.ARQ_BITS_PER_SECOND = int((sentbytes * 8) / transmissiontime)
|
||||||
static.ARQ_BYTES_PER_MINUTE = int((sentbytes) / (transmissiontime / 60))
|
static.ARQ_BYTES_PER_MINUTE = int(sentbytes / (transmissiontime / 60))
|
||||||
|
|
||||||
else:
|
else:
|
||||||
static.ARQ_BITS_PER_SECOND = 0
|
static.ARQ_BITS_PER_SECOND = 0
|
||||||
|
@ -2353,6 +2442,7 @@ class DATA:
|
||||||
|
|
||||||
self.log.debug("[TNC] arq_cleanup")
|
self.log.debug("[TNC] arq_cleanup")
|
||||||
|
|
||||||
|
self.session_id = bytes(1)
|
||||||
self.rx_frame_bof_received = False
|
self.rx_frame_bof_received = False
|
||||||
self.rx_frame_eof_received = False
|
self.rx_frame_eof_received = False
|
||||||
self.burst_ack = False
|
self.burst_ack = False
|
||||||
|
@ -2363,6 +2453,8 @@ class DATA:
|
||||||
self.burst_ack_snr = 255
|
self.burst_ack_snr = 255
|
||||||
|
|
||||||
# reset modem receiving state to reduce cpu load
|
# reset modem receiving state to reduce cpu load
|
||||||
|
modem.RECEIVE_SIG0 = True
|
||||||
|
modem.RECEIVE_SIG1 = False
|
||||||
modem.RECEIVE_DATAC1 = False
|
modem.RECEIVE_DATAC1 = False
|
||||||
modem.RECEIVE_DATAC3 = False
|
modem.RECEIVE_DATAC3 = False
|
||||||
# modem.RECEIVE_FSK_LDPC_0 = False
|
# modem.RECEIVE_FSK_LDPC_0 = False
|
||||||
|
@ -2403,15 +2495,20 @@ class DATA:
|
||||||
self.rpt_request_received = state
|
self.rpt_request_received = state
|
||||||
self.data_frame_ack_received = state
|
self.data_frame_ack_received = state
|
||||||
|
|
||||||
def set_listening_modes(self, mode: int) -> None:
|
def set_listening_modes(self, enable_sig0: bool, enable_sig1: bool, mode: int) -> None:
|
||||||
"""
|
"""
|
||||||
Function for setting the data modes we are listening to for saving cpu power
|
Function for setting the data modes we are listening to for saving cpu power
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
|
enable_sig0:int: Enable/Disable signalling mode 0
|
||||||
|
enable_sig1:int: Enable/Disable signalling mode 1
|
||||||
mode:int: Codec2 mode to listen for
|
mode:int: Codec2 mode to listen for
|
||||||
|
|
||||||
"""
|
"""
|
||||||
# set modes we want to listen to
|
# set modes we want to listen to
|
||||||
|
modem.RECEIVE_SIG0 = enable_sig0
|
||||||
|
modem.RECEIVE_SIG1 = enable_sig1
|
||||||
|
|
||||||
if mode == FREEDV_MODE.datac1.value:
|
if mode == FREEDV_MODE.datac1.value:
|
||||||
modem.RECEIVE_DATAC1 = True
|
modem.RECEIVE_DATAC1 = True
|
||||||
self.log.debug("[TNC] Changing listening data mode", mode="datac1")
|
self.log.debug("[TNC] Changing listening data mode", mode="datac1")
|
||||||
|
@ -2476,7 +2573,7 @@ class DATA:
|
||||||
static.ARQ_SPEED_LEVEL = self.speed_level
|
static.ARQ_SPEED_LEVEL = self.speed_level
|
||||||
|
|
||||||
# Update modes we are listening to
|
# Update modes we are listening to
|
||||||
self.set_listening_modes(self.mode_list[self.speed_level])
|
self.set_listening_modes(True, True, self.mode_list[self.speed_level])
|
||||||
|
|
||||||
# Why not pass `snr` or `static.SNR`?
|
# Why not pass `snr` or `static.SNR`?
|
||||||
self.send_burst_nack_frame_watchdog(0)
|
self.send_burst_nack_frame_watchdog(0)
|
||||||
|
@ -2504,7 +2601,10 @@ class DATA:
|
||||||
self.data_channel_last_received + self.transmission_timeout
|
self.data_channel_last_received + self.transmission_timeout
|
||||||
> time.time()
|
> time.time()
|
||||||
):
|
):
|
||||||
time.sleep(0.01)
|
time.sleep(5)
|
||||||
|
timeleft = (self.data_channel_last_received + self.transmission_timeout) - time.time()
|
||||||
|
self.log.debug("Time left until timeout", seconds=timeleft)
|
||||||
|
|
||||||
# print(self.data_channel_last_received + self.transmission_timeout - time.time())
|
# print(self.data_channel_last_received + self.transmission_timeout - time.time())
|
||||||
# pass
|
# pass
|
||||||
else:
|
else:
|
||||||
|
@ -2559,18 +2659,21 @@ class DATA:
|
||||||
"""
|
"""
|
||||||
while True:
|
while True:
|
||||||
time.sleep(0.01)
|
time.sleep(0.01)
|
||||||
if (
|
# additional check for smoother stopping if heartbeat transmission
|
||||||
static.ARQ_SESSION
|
while not self.arq_file_transfer:
|
||||||
and self.IS_ARQ_SESSION_MASTER
|
time.sleep(0.01)
|
||||||
and static.ARQ_SESSION_STATE == "connected"
|
if (
|
||||||
and not self.arq_file_transfer
|
static.ARQ_SESSION
|
||||||
):
|
and self.IS_ARQ_SESSION_MASTER
|
||||||
time.sleep(1)
|
and static.ARQ_SESSION_STATE == "connected"
|
||||||
self.transmit_session_heartbeat()
|
# and not self.arq_file_transfer
|
||||||
time.sleep(2)
|
):
|
||||||
|
time.sleep(1)
|
||||||
|
self.transmit_session_heartbeat()
|
||||||
|
time.sleep(2)
|
||||||
|
|
||||||
def send_test_frame(self) -> None:
|
def send_test_frame(self) -> None:
|
||||||
"""Send an empty test frame"""
|
"""Send an empty test frame"""
|
||||||
self.enqueue_frame_for_tx(
|
self.enqueue_frame_for_tx(
|
||||||
frame_to_tx=bytearray(126), c2_mode=FREEDV_MODE.datac3.value
|
frame_to_tx=[bytearray(126)], c2_mode=FREEDV_MODE.datac3.value
|
||||||
)
|
)
|
||||||
|
|
|
@ -316,6 +316,21 @@ def check_callsign(callsign: bytes, crc_to_check: bytes):
|
||||||
|
|
||||||
return [False, ""]
|
return [False, ""]
|
||||||
|
|
||||||
|
def check_session_id(id: bytes, id_to_check: bytes):
|
||||||
|
"""
|
||||||
|
Funktion to check if we received the correct session id
|
||||||
|
|
||||||
|
Args:
|
||||||
|
id: our own session id
|
||||||
|
id_to_check: The session id byte we want to check
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
True
|
||||||
|
False
|
||||||
|
"""
|
||||||
|
log.debug("[HLP] check_sessionid: Checking:", ownid=id, check=id_to_check)
|
||||||
|
return id == id_to_check
|
||||||
|
|
||||||
|
|
||||||
def encode_grid(grid):
|
def encode_grid(grid):
|
||||||
"""
|
"""
|
||||||
|
|
361
tnc/modem.py
361
tnc/modem.py
|
@ -32,6 +32,8 @@ TXCHANNEL = ""
|
||||||
static.TRANSMITTING = False
|
static.TRANSMITTING = False
|
||||||
|
|
||||||
# Receive only specific modes to reduce CPU load
|
# Receive only specific modes to reduce CPU load
|
||||||
|
RECEIVE_SIG0 = True
|
||||||
|
RECEIVE_SIG1 = False
|
||||||
RECEIVE_DATAC1 = False
|
RECEIVE_DATAC1 = False
|
||||||
RECEIVE_DATAC3 = False
|
RECEIVE_DATAC3 = False
|
||||||
RECEIVE_FSK_LDPC_1 = False
|
RECEIVE_FSK_LDPC_1 = False
|
||||||
|
@ -81,110 +83,63 @@ class RF:
|
||||||
self.fft_data = bytes()
|
self.fft_data = bytes()
|
||||||
|
|
||||||
# Open codec2 instances
|
# Open codec2 instances
|
||||||
# Datac0 - control frames
|
|
||||||
self.datac0_freedv = ctypes.cast(
|
|
||||||
codec2.api.freedv_open(codec2.api.FREEDV_MODE_DATAC0), ctypes.c_void_p
|
|
||||||
)
|
|
||||||
self.c_lib.freedv_set_tuning_range(
|
|
||||||
self.datac0_freedv,
|
|
||||||
ctypes.c_float(static.TUNING_RANGE_FMIN),
|
|
||||||
ctypes.c_float(static.TUNING_RANGE_FMAX),
|
|
||||||
)
|
|
||||||
self.datac0_bytes_per_frame = int(
|
|
||||||
codec2.api.freedv_get_bits_per_modem_frame(self.datac0_freedv) / 8
|
|
||||||
)
|
|
||||||
self.datac0_bytes_out = ctypes.create_string_buffer(self.datac0_bytes_per_frame)
|
|
||||||
codec2.api.freedv_set_frames_per_burst(self.datac0_freedv, 1)
|
|
||||||
self.datac0_buffer = codec2.audio_buffer(2 * self.AUDIO_FRAMES_PER_BUFFER_RX)
|
|
||||||
|
|
||||||
# Additional Datac0-specific information - these are not referenced anywhere else.
|
# DATAC0
|
||||||
# self.datac0_payload_per_frame = self.datac0_bytes_per_frame - 2
|
# SIGNALLING MODE 0 - Used for Connecting - Payload 14 Bytes
|
||||||
# self.datac0_n_nom_modem_samples = self.c_lib.freedv_get_n_nom_modem_samples(
|
self.sig0_datac0_freedv, \
|
||||||
# self.datac0_freedv
|
self.sig0_datac0_bytes_per_frame, \
|
||||||
# )
|
self.sig0_datac0_bytes_out, \
|
||||||
# self.datac0_n_tx_modem_samples = self.c_lib.freedv_get_n_tx_modem_samples(
|
self.sig0_datac0_buffer, \
|
||||||
# self.datac0_freedv
|
self.sig0_datac0_nin = \
|
||||||
# )
|
self.init_codec2_mode(codec2.api.FREEDV_MODE_DATAC0, None)
|
||||||
# self.datac0_n_tx_preamble_modem_samples = (
|
|
||||||
# self.c_lib.freedv_get_n_tx_preamble_modem_samples(self.datac0_freedv)
|
|
||||||
# )
|
|
||||||
# self.datac0_n_tx_postamble_modem_samples = (
|
|
||||||
# self.c_lib.freedv_get_n_tx_postamble_modem_samples(self.datac0_freedv)
|
|
||||||
# )
|
|
||||||
|
|
||||||
# Datac1 - higher-bandwidth data frames
|
# DATAC0
|
||||||
self.datac1_freedv = ctypes.cast(
|
# SIGNALLING MODE 1 - Used for ACK/NACK - Payload 5 Bytes
|
||||||
codec2.api.freedv_open(codec2.api.FREEDV_MODE_DATAC1), ctypes.c_void_p
|
self.sig1_datac0_freedv, \
|
||||||
)
|
self.sig1_datac0_bytes_per_frame, \
|
||||||
self.c_lib.freedv_set_tuning_range(
|
self.sig1_datac0_bytes_out, \
|
||||||
self.datac1_freedv,
|
self.sig1_datac0_buffer, \
|
||||||
ctypes.c_float(static.TUNING_RANGE_FMIN),
|
self.sig1_datac0_nin = \
|
||||||
ctypes.c_float(static.TUNING_RANGE_FMAX),
|
self.init_codec2_mode(codec2.api.FREEDV_MODE_DATAC0, None)
|
||||||
)
|
|
||||||
self.datac1_bytes_per_frame = int(
|
|
||||||
codec2.api.freedv_get_bits_per_modem_frame(self.datac1_freedv) / 8
|
|
||||||
)
|
|
||||||
self.datac1_bytes_out = ctypes.create_string_buffer(self.datac1_bytes_per_frame)
|
|
||||||
codec2.api.freedv_set_frames_per_burst(self.datac1_freedv, 1)
|
|
||||||
self.datac1_buffer = codec2.audio_buffer(2 * self.AUDIO_FRAMES_PER_BUFFER_RX)
|
|
||||||
|
|
||||||
# Datac3 - lower-bandwidth data frames
|
|
||||||
self.datac3_freedv = ctypes.cast(
|
|
||||||
codec2.api.freedv_open(codec2.api.FREEDV_MODE_DATAC3), ctypes.c_void_p
|
|
||||||
)
|
|
||||||
self.c_lib.freedv_set_tuning_range(
|
|
||||||
self.datac3_freedv,
|
|
||||||
ctypes.c_float(static.TUNING_RANGE_FMIN),
|
|
||||||
ctypes.c_float(static.TUNING_RANGE_FMAX),
|
|
||||||
)
|
|
||||||
self.datac3_bytes_per_frame = int(
|
|
||||||
codec2.api.freedv_get_bits_per_modem_frame(self.datac3_freedv) / 8
|
|
||||||
)
|
|
||||||
self.datac3_bytes_out = ctypes.create_string_buffer(self.datac3_bytes_per_frame)
|
|
||||||
codec2.api.freedv_set_frames_per_burst(self.datac3_freedv, 1)
|
|
||||||
self.datac3_buffer = codec2.audio_buffer(2 * self.AUDIO_FRAMES_PER_BUFFER_RX)
|
|
||||||
|
|
||||||
# FSK Long-distance Parity Code 0 - data frames
|
# DATAC1
|
||||||
self.fsk_ldpc_freedv_0 = ctypes.cast(
|
self.dat0_datac1_freedv, \
|
||||||
codec2.api.freedv_open_advanced(
|
self.dat0_datac1_bytes_per_frame, \
|
||||||
|
self.dat0_datac1_bytes_out, \
|
||||||
|
self.dat0_datac1_buffer, \
|
||||||
|
self.dat0_datac1_nin = \
|
||||||
|
self.init_codec2_mode(codec2.api.FREEDV_MODE_DATAC1, None)
|
||||||
|
|
||||||
|
# DATAC3
|
||||||
|
self.dat0_datac3_freedv, \
|
||||||
|
self.dat0_datac3_bytes_per_frame, \
|
||||||
|
self.dat0_datac3_bytes_out, \
|
||||||
|
self.dat0_datac3_buffer, \
|
||||||
|
self.dat0_datac3_nin = \
|
||||||
|
self.init_codec2_mode(codec2.api.FREEDV_MODE_DATAC3, None)
|
||||||
|
|
||||||
|
# FSK LDPC - 0
|
||||||
|
self.fsk_ldpc_freedv_0, \
|
||||||
|
self.fsk_ldpc_bytes_per_frame_0, \
|
||||||
|
self.fsk_ldpc_bytes_out_0, \
|
||||||
|
self.fsk_ldpc_buffer_0, \
|
||||||
|
self.fsk_ldpc_nin_0 = \
|
||||||
|
self.init_codec2_mode(
|
||||||
codec2.api.FREEDV_MODE_FSK_LDPC,
|
codec2.api.FREEDV_MODE_FSK_LDPC,
|
||||||
ctypes.byref(codec2.api.FREEDV_MODE_FSK_LDPC_0_ADV),
|
codec2.api.FREEDV_MODE_FSK_LDPC_0_ADV
|
||||||
),
|
)
|
||||||
ctypes.c_void_p,
|
|
||||||
)
|
|
||||||
self.fsk_ldpc_bytes_per_frame_0 = int(
|
|
||||||
codec2.api.freedv_get_bits_per_modem_frame(self.fsk_ldpc_freedv_0) / 8
|
|
||||||
)
|
|
||||||
self.fsk_ldpc_bytes_out_0 = ctypes.create_string_buffer(
|
|
||||||
self.fsk_ldpc_bytes_per_frame_0
|
|
||||||
)
|
|
||||||
# codec2.api.freedv_set_frames_per_burst(self.fsk_ldpc_freedv_0, 1)
|
|
||||||
self.fsk_ldpc_buffer_0 = codec2.audio_buffer(self.AUDIO_FRAMES_PER_BUFFER_RX)
|
|
||||||
|
|
||||||
# FSK Long-distance Parity Code 1 - data frames
|
# FSK LDPC - 1
|
||||||
self.fsk_ldpc_freedv_1 = ctypes.cast(
|
self.fsk_ldpc_freedv_1, \
|
||||||
codec2.api.freedv_open_advanced(
|
self.fsk_ldpc_bytes_per_frame_1, \
|
||||||
|
self.fsk_ldpc_bytes_out_1, \
|
||||||
|
self.fsk_ldpc_buffer_1, \
|
||||||
|
self.fsk_ldpc_nin_1 = \
|
||||||
|
self.init_codec2_mode(
|
||||||
codec2.api.FREEDV_MODE_FSK_LDPC,
|
codec2.api.FREEDV_MODE_FSK_LDPC,
|
||||||
ctypes.byref(codec2.api.FREEDV_MODE_FSK_LDPC_1_ADV),
|
codec2.api.FREEDV_MODE_FSK_LDPC_1_ADV
|
||||||
),
|
)
|
||||||
ctypes.c_void_p,
|
|
||||||
)
|
|
||||||
self.fsk_ldpc_bytes_per_frame_1 = int(
|
|
||||||
codec2.api.freedv_get_bits_per_modem_frame(self.fsk_ldpc_freedv_1) / 8
|
|
||||||
)
|
|
||||||
self.fsk_ldpc_bytes_out_1 = ctypes.create_string_buffer(
|
|
||||||
self.fsk_ldpc_bytes_per_frame_1
|
|
||||||
)
|
|
||||||
# codec2.api.freedv_set_frames_per_burst(self.fsk_ldpc_freedv_0, 1)
|
|
||||||
self.fsk_ldpc_buffer_1 = codec2.audio_buffer(self.AUDIO_FRAMES_PER_BUFFER_RX)
|
|
||||||
|
|
||||||
# initial nin values
|
|
||||||
self.datac0_nin = codec2.api.freedv_nin(self.datac0_freedv)
|
|
||||||
self.datac1_nin = codec2.api.freedv_nin(self.datac1_freedv)
|
|
||||||
self.datac3_nin = codec2.api.freedv_nin(self.datac3_freedv)
|
|
||||||
self.fsk_ldpc_nin_0 = codec2.api.freedv_nin(self.fsk_ldpc_freedv_0)
|
|
||||||
self.fsk_ldpc_nin_1 = codec2.api.freedv_nin(self.fsk_ldpc_freedv_1)
|
|
||||||
# self.log.debug("[MDM] RF: ",datac0_nin=self.datac0_nin)
|
|
||||||
|
|
||||||
# --------------------------------------------CREATE PYAUDIO INSTANCE
|
# --------------------------------------------CREATE PYAUDIO INSTANCE
|
||||||
if not TESTMODE:
|
if not TESTMODE:
|
||||||
|
@ -242,6 +197,7 @@ class RF:
|
||||||
|
|
||||||
# --------------------------------------------INIT AND OPEN HAMLIB
|
# --------------------------------------------INIT AND OPEN HAMLIB
|
||||||
# Check how we want to control the radio
|
# Check how we want to control the radio
|
||||||
|
# TODO: deprecated feature - we can remove this possibly
|
||||||
if static.HAMLIB_RADIOCONTROL == "direct":
|
if static.HAMLIB_RADIOCONTROL == "direct":
|
||||||
import rig
|
import rig
|
||||||
elif static.HAMLIB_RADIOCONTROL == "rigctl":
|
elif static.HAMLIB_RADIOCONTROL == "rigctl":
|
||||||
|
@ -272,20 +228,25 @@ class RF:
|
||||||
)
|
)
|
||||||
fft_thread.start()
|
fft_thread.start()
|
||||||
|
|
||||||
audio_thread_datac0 = threading.Thread(
|
audio_thread_sig0_datac0 = threading.Thread(
|
||||||
target=self.audio_datac0, name="AUDIO_THREAD DATAC0", daemon=True
|
target=self.audio_sig0_datac0, name="AUDIO_THREAD DATAC0 - 0", daemon=True
|
||||||
)
|
)
|
||||||
audio_thread_datac0.start()
|
audio_thread_sig0_datac0.start()
|
||||||
|
|
||||||
audio_thread_datac1 = threading.Thread(
|
audio_thread_sig1_datac0 = threading.Thread(
|
||||||
target=self.audio_datac1, name="AUDIO_THREAD DATAC1", daemon=True
|
target=self.audio_sig1_datac0, name="AUDIO_THREAD DATAC0 - 1", daemon=True
|
||||||
)
|
)
|
||||||
audio_thread_datac1.start()
|
audio_thread_sig1_datac0.start()
|
||||||
|
|
||||||
audio_thread_datac3 = threading.Thread(
|
audio_thread_dat0_datac1 = threading.Thread(
|
||||||
target=self.audio_datac3, name="AUDIO_THREAD DATAC3", daemon=True
|
target=self.audio_dat0_datac1, name="AUDIO_THREAD DATAC1", daemon=True
|
||||||
)
|
)
|
||||||
audio_thread_datac3.start()
|
audio_thread_dat0_datac1.start()
|
||||||
|
|
||||||
|
audio_thread_dat0_datac3 = threading.Thread(
|
||||||
|
target=self.audio_dat0_datac3, name="AUDIO_THREAD DATAC3", daemon=True
|
||||||
|
)
|
||||||
|
audio_thread_dat0_datac3.start()
|
||||||
|
|
||||||
if static.ENABLE_FSK:
|
if static.ENABLE_FSK:
|
||||||
audio_thread_fsk_ldpc0 = threading.Thread(
|
audio_thread_fsk_ldpc0 = threading.Thread(
|
||||||
|
@ -335,9 +296,10 @@ class RF:
|
||||||
|
|
||||||
length_x = len(x)
|
length_x = len(x)
|
||||||
for data_buffer, receive in [
|
for data_buffer, receive in [
|
||||||
(self.datac0_buffer, True),
|
(self.sig0_datac0_buffer, RECEIVE_SIG0),
|
||||||
(self.datac1_buffer, RECEIVE_DATAC1),
|
(self.sig1_datac0_buffer, RECEIVE_SIG1),
|
||||||
(self.datac3_buffer, RECEIVE_DATAC3),
|
(self.dat0_datac1_buffer, RECEIVE_DATAC1),
|
||||||
|
(self.dat0_datac3_buffer, RECEIVE_DATAC3),
|
||||||
# Not enabled yet.
|
# Not enabled yet.
|
||||||
# (self.fsk_ldpc_buffer_0, static.ENABLE_FSK),
|
# (self.fsk_ldpc_buffer_0, static.ENABLE_FSK),
|
||||||
# (self.fsk_ldpc_buffer_1, static.ENABLE_FSK),
|
# (self.fsk_ldpc_buffer_1, static.ENABLE_FSK),
|
||||||
|
@ -391,11 +353,12 @@ class RF:
|
||||||
# Avoid buffer overflow by filling only if buffer for
|
# Avoid buffer overflow by filling only if buffer for
|
||||||
# selected datachannel mode is not full
|
# selected datachannel mode is not full
|
||||||
for audiobuffer, receive, index in [
|
for audiobuffer, receive, index in [
|
||||||
(self.datac0_buffer, True, 0),
|
(self.sig0_datac0_buffer, RECEIVE_SIG0, 0),
|
||||||
(self.datac1_buffer, RECEIVE_DATAC1, 1),
|
(self.sig1_datac0_buffer, RECEIVE_SIG1, 1),
|
||||||
(self.datac3_buffer, RECEIVE_DATAC3, 2),
|
(self.dat0_datac1_buffer, RECEIVE_DATAC1, 2),
|
||||||
(self.fsk_ldpc_buffer_0, static.ENABLE_FSK, 3),
|
(self.dat0_datac3_buffer, RECEIVE_DATAC3, 3),
|
||||||
(self.fsk_ldpc_buffer_1, static.ENABLE_FSK, 4),
|
(self.fsk_ldpc_buffer_0, static.ENABLE_FSK, 4),
|
||||||
|
(self.fsk_ldpc_buffer_1, static.ENABLE_FSK, 5),
|
||||||
]:
|
]:
|
||||||
if audiobuffer.nbuffer + length_x > audiobuffer.size:
|
if audiobuffer.nbuffer + length_x > audiobuffer.size:
|
||||||
static.BUFFER_OVERFLOW_COUNTER[index] += 1
|
static.BUFFER_OVERFLOW_COUNTER[index] += 1
|
||||||
|
@ -618,38 +581,119 @@ class RF:
|
||||||
"[MDM] [demod_audio] Pushing received data to received_queue"
|
"[MDM] [demod_audio] Pushing received data to received_queue"
|
||||||
)
|
)
|
||||||
self.modem_received_queue.put([bytes_out, freedv, bytes_per_frame])
|
self.modem_received_queue.put([bytes_out, freedv, bytes_per_frame])
|
||||||
# self.get_scatter(freedv)
|
self.get_scatter(freedv)
|
||||||
self.calculate_snr(freedv)
|
self.calculate_snr(freedv)
|
||||||
return nin
|
return nin
|
||||||
|
|
||||||
def audio_datac0(self) -> None:
|
def init_codec2_mode(self, mode, adv):
|
||||||
"""Receive data encoded with datac0"""
|
"""
|
||||||
self.datac0_nin = self.demodulate_audio(
|
Init codec2 and return some important parameters
|
||||||
self.datac0_buffer,
|
|
||||||
self.datac0_nin,
|
Args:
|
||||||
self.datac0_freedv,
|
self:
|
||||||
self.datac0_bytes_out,
|
mode:
|
||||||
self.datac0_bytes_per_frame,
|
adv:
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
c2instance, bytes_per_frame, bytes_out, audio_buffer, nin
|
||||||
|
"""
|
||||||
|
if adv:
|
||||||
|
# FSK Long-distance Parity Code 1 - data frames
|
||||||
|
c2instance = ctypes.cast(
|
||||||
|
codec2.api.freedv_open_advanced(
|
||||||
|
codec2.api.FREEDV_MODE_FSK_LDPC,
|
||||||
|
ctypes.byref(adv),
|
||||||
|
),
|
||||||
|
ctypes.c_void_p,
|
||||||
|
)
|
||||||
|
else:
|
||||||
|
|
||||||
|
# create codec2 instance
|
||||||
|
c2instance = ctypes.cast(
|
||||||
|
codec2.api.freedv_open(mode), ctypes.c_void_p
|
||||||
|
)
|
||||||
|
|
||||||
|
# set tuning range
|
||||||
|
self.c_lib.freedv_set_tuning_range(
|
||||||
|
c2instance,
|
||||||
|
ctypes.c_float(static.TUNING_RANGE_FMIN),
|
||||||
|
ctypes.c_float(static.TUNING_RANGE_FMAX),
|
||||||
)
|
)
|
||||||
|
|
||||||
def audio_datac1(self) -> None:
|
# get bytes per frame
|
||||||
|
bytes_per_frame = int(
|
||||||
|
codec2.api.freedv_get_bits_per_modem_frame(c2instance) / 8
|
||||||
|
)
|
||||||
|
|
||||||
|
# create byte out buffer
|
||||||
|
bytes_out = ctypes.create_string_buffer(bytes_per_frame)
|
||||||
|
|
||||||
|
# set initial frames per burst
|
||||||
|
codec2.api.freedv_set_frames_per_burst(c2instance, 1)
|
||||||
|
|
||||||
|
# init audio buffer
|
||||||
|
audio_buffer = codec2.audio_buffer(2 * self.AUDIO_FRAMES_PER_BUFFER_RX)
|
||||||
|
|
||||||
|
# get initial nin
|
||||||
|
nin = codec2.api.freedv_nin(c2instance)
|
||||||
|
|
||||||
|
# Additional Datac0-specific information - these are not referenced anywhere else.
|
||||||
|
# self.sig0_datac0_payload_per_frame = self.sig0_datac0_bytes_per_frame - 2
|
||||||
|
# self.sig0_datac0_n_nom_modem_samples = self.c_lib.freedv_get_n_nom_modem_samples(
|
||||||
|
# self.sig0_datac0_freedv
|
||||||
|
# )
|
||||||
|
# self.sig0_datac0_n_tx_modem_samples = self.c_lib.freedv_get_n_tx_modem_samples(
|
||||||
|
# self.sig0_datac0_freedv
|
||||||
|
# )
|
||||||
|
# self.sig0_datac0_n_tx_preamble_modem_samples = (
|
||||||
|
# self.c_lib.freedv_get_n_tx_preamble_modem_samples(self.sig0_datac0_freedv)
|
||||||
|
# )
|
||||||
|
# self.sig0_datac0_n_tx_postamble_modem_samples = (
|
||||||
|
# self.c_lib.freedv_get_n_tx_postamble_modem_samples(self.sig0_datac0_freedv)
|
||||||
|
# )
|
||||||
|
|
||||||
|
# return values
|
||||||
|
return c2instance, bytes_per_frame, bytes_out, audio_buffer, nin
|
||||||
|
|
||||||
|
def audio_sig0_datac0(self) -> None:
|
||||||
|
"""Receive data encoded with datac0 - 0"""
|
||||||
|
self.sig0_datac0_nin = self.demodulate_audio(
|
||||||
|
self.sig0_datac0_buffer,
|
||||||
|
self.sig0_datac0_nin,
|
||||||
|
self.sig0_datac0_freedv,
|
||||||
|
self.sig0_datac0_bytes_out,
|
||||||
|
self.sig0_datac0_bytes_per_frame,
|
||||||
|
)
|
||||||
|
|
||||||
|
def audio_sig1_datac0(self) -> None:
|
||||||
|
"""Receive data encoded with datac0 - 1"""
|
||||||
|
self.sig1_datac0_nin = self.demodulate_audio(
|
||||||
|
self.sig1_datac0_buffer,
|
||||||
|
self.sig1_datac0_nin,
|
||||||
|
self.sig1_datac0_freedv,
|
||||||
|
self.sig1_datac0_bytes_out,
|
||||||
|
self.sig1_datac0_bytes_per_frame,
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def audio_dat0_datac1(self) -> None:
|
||||||
"""Receive data encoded with datac1"""
|
"""Receive data encoded with datac1"""
|
||||||
self.datac1_nin = self.demodulate_audio(
|
self.dat0_datac1_nin = self.demodulate_audio(
|
||||||
self.datac1_buffer,
|
self.dat0_datac1_buffer,
|
||||||
self.datac1_nin,
|
self.dat0_datac1_nin,
|
||||||
self.datac1_freedv,
|
self.dat0_datac1_freedv,
|
||||||
self.datac1_bytes_out,
|
self.dat0_datac1_bytes_out,
|
||||||
self.datac1_bytes_per_frame,
|
self.dat0_datac1_bytes_per_frame,
|
||||||
)
|
)
|
||||||
|
|
||||||
def audio_datac3(self) -> None:
|
def audio_dat0_datac3(self) -> None:
|
||||||
"""Receive data encoded with datac3"""
|
"""Receive data encoded with datac3"""
|
||||||
self.datac3_nin = self.demodulate_audio(
|
self.dat0_datac3_nin = self.demodulate_audio(
|
||||||
self.datac3_buffer,
|
self.dat0_datac3_buffer,
|
||||||
self.datac3_nin,
|
self.dat0_datac3_nin,
|
||||||
self.datac3_freedv,
|
self.dat0_datac3_freedv,
|
||||||
self.datac3_bytes_out,
|
self.dat0_datac3_bytes_out,
|
||||||
self.datac3_bytes_per_frame,
|
self.dat0_datac3_bytes_per_frame,
|
||||||
)
|
)
|
||||||
|
|
||||||
def audio_fsk_ldpc_0(self) -> None:
|
def audio_fsk_ldpc_0(self) -> None:
|
||||||
|
@ -705,7 +749,6 @@ class RF:
|
||||||
:rtype: float
|
:rtype: float
|
||||||
"""
|
"""
|
||||||
modemStats = codec2.MODEMSTATS()
|
modemStats = codec2.MODEMSTATS()
|
||||||
self.c_lib.freedv_get_modem_extended_stats.restype = None
|
|
||||||
self.c_lib.freedv_get_modem_extended_stats(freedv, ctypes.byref(modemStats))
|
self.c_lib.freedv_get_modem_extended_stats(freedv, ctypes.byref(modemStats))
|
||||||
offset = round(modemStats.foff) * (-1)
|
offset = round(modemStats.foff) * (-1)
|
||||||
static.FREQ_OFFSET = offset
|
static.FREQ_OFFSET = offset
|
||||||
|
@ -723,28 +766,27 @@ class RF:
|
||||||
return
|
return
|
||||||
|
|
||||||
modemStats = codec2.MODEMSTATS()
|
modemStats = codec2.MODEMSTATS()
|
||||||
self.c_lib.freedv_get_modem_extended_stats.restype = None
|
ctypes.cast(
|
||||||
self.c_lib.freedv_get_modem_extended_stats(freedv, ctypes.byref(modemStats))
|
self.c_lib.freedv_get_modem_extended_stats(freedv, ctypes.byref(modemStats)),
|
||||||
|
ctypes.c_void_p,
|
||||||
|
)
|
||||||
|
|
||||||
scatterdata = []
|
scatterdata = []
|
||||||
scatterdata_small = []
|
|
||||||
for i in range(codec2.MODEM_STATS_NC_MAX):
|
for i in range(codec2.MODEM_STATS_NC_MAX):
|
||||||
for j in range(codec2.MODEM_STATS_NR_MAX):
|
for j in range(1, codec2.MODEM_STATS_NR_MAX, 2):
|
||||||
# check if odd or not to get every 2nd item for x
|
# print(f"{modemStats.rx_symbols[i][j]} - {modemStats.rx_symbols[i][j]}")
|
||||||
if (j % 2) == 0:
|
xsymbols = round(modemStats.rx_symbols[i][j - 1] // 1000)
|
||||||
xsymbols = round(modemStats.rx_symbols[i][j] / 1000)
|
ysymbols = round(modemStats.rx_symbols[i][j] // 1000)
|
||||||
ysymbols = round(modemStats.rx_symbols[i][j + 1] / 1000)
|
if xsymbols != 0.0 and ysymbols != 0.0:
|
||||||
# check if value 0.0 or has real data
|
scatterdata.append({"x": str(xsymbols), "y": str(ysymbols)})
|
||||||
if xsymbols != 0.0 and ysymbols != 0.0:
|
|
||||||
scatterdata.append({"x": xsymbols, "y": ysymbols})
|
|
||||||
|
|
||||||
# Send all the data if we have too-few samples, otherwise send a sampling
|
# Send all the data if we have too-few samples, otherwise send a sampling
|
||||||
if 150 > len(scatterdata) > 0:
|
if 150 > len(scatterdata) > 0:
|
||||||
static.SCATTER = scatterdata
|
static.SCATTER = scatterdata
|
||||||
else:
|
else:
|
||||||
# only take every tenth data point
|
# only take every tenth data point
|
||||||
scatterdata_small = scatterdata[::10]
|
static.SCATTER = scatterdata[::10]
|
||||||
static.SCATTER = scatterdata_small
|
|
||||||
|
|
||||||
def calculate_snr(self, freedv: ctypes.c_void_p) -> float:
|
def calculate_snr(self, freedv: ctypes.c_void_p) -> float:
|
||||||
"""
|
"""
|
||||||
|
@ -801,6 +843,9 @@ class RF:
|
||||||
# Initialize channel_busy_delay counter
|
# Initialize channel_busy_delay counter
|
||||||
channel_busy_delay = 0
|
channel_busy_delay = 0
|
||||||
|
|
||||||
|
# Initialize rms counter
|
||||||
|
rms_counter = 0
|
||||||
|
|
||||||
while True:
|
while True:
|
||||||
# time.sleep(0.01)
|
# time.sleep(0.01)
|
||||||
threading.Event().wait(0.01)
|
threading.Event().wait(0.01)
|
||||||
|
@ -830,8 +875,14 @@ class RF:
|
||||||
if not static.TRANSMITTING:
|
if not static.TRANSMITTING:
|
||||||
dfft[dfft > avg + 10] = 100
|
dfft[dfft > avg + 10] = 100
|
||||||
|
|
||||||
# Calculate audio max value
|
# Calculate audio RMS
|
||||||
# static.AUDIO_RMS = np.amax(self.fft_data)
|
# https://stackoverflow.com/a/9763652
|
||||||
|
# calculate RMS every 50 cycles for reducing CPU load
|
||||||
|
rms_counter += 1
|
||||||
|
if rms_counter > 50:
|
||||||
|
d = np.frombuffer(self.fft_data, np.int16).astype(np.float)
|
||||||
|
static.AUDIO_RMS = int(np.sqrt(np.mean(d ** 2)))
|
||||||
|
rms_counter = 0
|
||||||
|
|
||||||
# Check for signals higher than average by checking for "100"
|
# Check for signals higher than average by checking for "100"
|
||||||
# If we have a signal, increment our channel_busy delay counter
|
# If we have a signal, increment our channel_busy delay counter
|
||||||
|
@ -870,8 +921,8 @@ class RF:
|
||||||
frames_per_burst = min(frames_per_burst, 1)
|
frames_per_burst = min(frames_per_burst, 1)
|
||||||
frames_per_burst = max(frames_per_burst, 5)
|
frames_per_burst = max(frames_per_burst, 5)
|
||||||
|
|
||||||
codec2.api.freedv_set_frames_per_burst(self.datac1_freedv, frames_per_burst)
|
codec2.api.freedv_set_frames_per_burst(self.dat0_datac1_freedv, frames_per_burst)
|
||||||
codec2.api.freedv_set_frames_per_burst(self.datac3_freedv, frames_per_burst)
|
codec2.api.freedv_set_frames_per_burst(self.dat0_datac3_freedv, frames_per_burst)
|
||||||
codec2.api.freedv_set_frames_per_burst(self.fsk_ldpc_freedv_0, frames_per_burst)
|
codec2.api.freedv_set_frames_per_burst(self.fsk_ldpc_freedv_0, frames_per_burst)
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -85,7 +85,7 @@ ENABLE_FFT: bool = False
|
||||||
CHANNEL_BUSY: bool = False
|
CHANNEL_BUSY: bool = False
|
||||||
|
|
||||||
# ARQ PROTOCOL VERSION
|
# ARQ PROTOCOL VERSION
|
||||||
ARQ_PROTOCOL_VERSION: int = 2
|
ARQ_PROTOCOL_VERSION: int = 3
|
||||||
|
|
||||||
# ARQ statistics
|
# ARQ statistics
|
||||||
ARQ_BYTES_PER_MINUTE_BURST: int = 0
|
ARQ_BYTES_PER_MINUTE_BURST: int = 0
|
||||||
|
@ -149,4 +149,5 @@ class FRAME_TYPE(Enum):
|
||||||
ARQ_DC_OPEN_ACK_N = 228
|
ARQ_DC_OPEN_ACK_N = 228
|
||||||
ARQ_STOP = 249
|
ARQ_STOP = 249
|
||||||
BEACON = 250
|
BEACON = 250
|
||||||
|
IDENT = 254
|
||||||
TEST_FRAME = 255
|
TEST_FRAME = 255
|
||||||
|
|
Loading…
Reference in a new issue