Merge pull request #259 from DJ2LS/ls-arq

WIP: Move to session id instead of callsign crc check
This commit is contained in:
DJ2LS 2022-11-03 09:27:49 +01:00 committed by GitHub
commit aaea0d1509
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
10 changed files with 560 additions and 346 deletions

View file

@ -13,12 +13,18 @@ jobs:
steps:
- uses: actions/checkout@v3
- name: Set up Python 3.9
uses: actions/setup-python@v4
with:
python-version: 3.9
- name: Install packages
shell: bash
run: |
sudo apt-get update
sudo apt-get install octave octave-common octave-signal sox python3 python3-pip portaudio19-dev python3-pyaudio
pip3 install psutil crcengine ujson pyserial numpy structlog sounddevice
sudo apt-get install octave octave-common octave-signal sox portaudio19-dev python3-pyaudio
pip3 install psutil crcengine ujson pyserial numpy structlog sounddevice pyaudio
pip3 install pytest pytest-rerunfailures
- name: Build codec2

View file

@ -1458,11 +1458,10 @@ ipcRenderer.on('action-update-tnc-state', (event, arg) => {
document.getElementById("beaconInterval").disabled = false;
}
// RMS
/*
var rms_level = Math.round((arg.rms_level/60) * 100)
var rms_level = (arg.rms_level / 32767) * 100
document.getElementById("rms_level").setAttribute("aria-valuenow", rms_level);
document.getElementById("rms_level").setAttribute("style", "width:" + rms_level + "%;");
*/
// SET FREQUENCY
document.getElementById("frequency").innerHTML = arg.frequency;

View file

@ -746,11 +746,11 @@
<div class="card-body p-2">
<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>
<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 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 bg-success" role="progressbar" style="width: 80%" aria-valuenow="80" 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: 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>
</div>

View file

@ -15,6 +15,7 @@ Uses util_datac0.py in separate process to perform the data transfer.
"""
import multiprocessing
from random import randbytes
import sys
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_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[:1] = bytes([frame_type])
frame[1:4] = dxcallsign_crc
frame[4:7] = mycallsign_crc
frame[7:13] = mycallsign_bytes
frame[1:2] = session_id
frame[2:5] = dxcallsign_crc
frame[5:8] = mycallsign_crc
frame[8:14] = mycallsign_bytes
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.
@ -85,6 +93,23 @@ def t_create_session_close(mycall: str, dxcall: str) -> bytearray:
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:
"""
Generate the create_session frame.
@ -150,18 +175,24 @@ def t_foreign_disconnect(mycall: str, dxcall: str):
assert static.ARQ_SESSION_STATE == "connecting"
# Set up a frame from a non-associated station.
foreigncall_bytes = helpers.callsign_to_bytes("ZZ0ZZ-0")
foreigncall = helpers.bytes_to_callsign(foreigncall_bytes)
# foreigncall_bytes = helpers.callsign_to_bytes("ZZ0ZZ-0")
# 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)
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 (
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."
# 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 (
# 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
tnc.received_session_close(close_frame)
@ -221,7 +252,9 @@ def t_valid_disconnect(mycall: str, dxcall: str):
assert static.ARQ_SESSION_STATE == "connecting"
# 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)
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("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):
proc = multiprocessing.Process(target=t_foreign_disconnect, args=(mycall, dxcall))
# print("Starting threads.")

View file

@ -42,8 +42,8 @@ def get_audio_devices():
proc.start()
proc.join()
#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: input_devices:", list=f"{proxy_input_devices}")
log.debug("[AUD] get_audio_devices: output_devices:", list=f"{proxy_output_devices}")
return list(proxy_input_devices), list(proxy_output_devices)

View file

@ -23,7 +23,8 @@ class FREEDV_MODE(Enum):
"""
Enumeration for codec2 modes and names
"""
sig0 = 14
sig1 = 14
datac0 = 14
datac1 = 10
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.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.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
# ------- MODEM STATS STRUCTURES
MODEM_STATS_NC_MAX = 50 + 1
MODEM_STATS_NR_MAX = 160
MODEM_STATS_NC_MAX = 50 + 1 * 2
MODEM_STATS_NR_MAX = 160 * 2
MODEM_STATS_ET_MAX = 8
MODEM_STATS_EYE_IND_MAX = 160
MODEM_STATS_NSPEC = 512
@ -233,10 +237,12 @@ class MODEMSTATS(ctypes.Structure):
("pre", ctypes.c_int),
("post", 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
("neyesamp", ctypes.c_int), # How many samples in the eye diagram
("f_est", (ctypes.c_float * MODEM_STATS_MAX_F_EST)),
("fft_buf", (ctypes.c_float * MODEM_STATS_NSPEC * 2)),
("fft_cfg", ctypes.c_void_p)
]

View file

@ -13,7 +13,7 @@ import threading
import time
import uuid
import zlib
from random import randrange
from random import randrange, randbytes
import codec2
import helpers
@ -44,7 +44,11 @@ class DATA:
self.data_queue_received = DATA_QUEUE_RECEIVED
# 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
self.arq_file_transfer = False
@ -53,6 +57,9 @@ class DATA:
self.arq_session_timeout = 30
self.session_connect_max_retries = 15
# actual n retries of burst
self.tx_n_retry_of_burst = 0
self.transmission_uuid = ""
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
self.data_frame_eof = b"EOF"
self.tx_n_max_retries_per_burst = 50
self.rx_n_max_retries_per_burst = 50
self.n_retries_per_burst = 0
@ -128,7 +136,11 @@ class DATA:
self.rx_frame_bof_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
# instead of a long series of if-elif-else statements.
@ -278,23 +290,33 @@ class DATA:
# bytes_out[2:5] == transmission
# we could also create an own function, which returns True.
frametype = int.from_bytes(bytes(bytes_out[:1]), "big")
# check for callsign CRC
_valid1, _ = helpers.check_callsign(self.mycallsign, bytes(bytes_out[1:4]))
_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 (
_valid1
or _valid2
or _valid3
or _valid4
or frametype
in [
FR_TYPE.CQ.value,
FR_TYPE.QRV.value,
FR_TYPE.PING.value,
FR_TYPE.BEACON.value,
]
FR_TYPE.CQ.value,
FR_TYPE.QRV.value,
FR_TYPE.PING.value,
FR_TYPE.BEACON.value,
]
):
# CHECK IF FRAMETYPE IS BETWEEN 10 and 50 ------------------------
frame = frametype - 10
n_frames_per_burst = int.from_bytes(bytes(bytes_out[1:2]), "big")
# frame = frametype - 10
# n_frames_per_burst = int.from_bytes(bytes(bytes_out[1:2]), "big")
# Dispatch activity based on received frametype
if frametype in self.rx_dispatcher:
@ -339,7 +361,7 @@ class DATA:
def enqueue_frame_for_tx(
self,
frame_to_tx: bytearray,
frame_to_tx: list[bytearray],
c2_mode=FREEDV_MODE.datac0.value,
copies=1,
repeat_delay=0,
@ -348,7 +370,7 @@ class DATA:
Send (transmit) supplied frame to TNC
: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)
:type c2_mode: int, optional
: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
# TODO: This is not that nice, we could improve this somehow
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
while static.TRANSMITTING:
@ -390,32 +412,48 @@ class DATA:
json_data_out = json.dumps(jsondata)
sock.SOCKET_QUEUE.put(json_data_out)
def send_burst_ack_frame(self, snr) -> None:
"""Build and send ACK frame for burst DATA frame"""
ack_frame = bytearray(self.length_sig_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[7:8] = bytes([int(snr)])
ack_frame[8:9] = bytes([int(self.speed_level)])
def send_ident_frame(self, transmit) -> None:
"""Build and send IDENT frame """
ident_frame = bytearray(self.length_sig1_frame)
ident_frame[:1] = bytes([FR_TYPE.IDENT.value])
ident_frame[1:self.length_sig1_frame] = self.mycallsign
# 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:
"""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:4] = static.DXCALLSIGN_CRC
ack_frame[4:7] = static.MYCALLSIGN_CRC
ack_frame[7:8] = bytes([int(snr)])
ack_frame[8:9] = bytes([int(self.speed_level)])
ack_frame[1:2] = self.session_id
# ack_frame[1:4] = static.DXCALLSIGN_CRC
# 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
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:
# 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.
missing_frames = [
frame + 1
@ -429,51 +467,60 @@ class DATA:
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: Instead of using int we could use a binary flag
# 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:4] = static.DXCALLSIGN_CRC
rpt_frame[4:7] = static.MYCALLSIGN_CRC
rpt_frame[7:13] = missing_frames
rpt_frame[1:2] = self.session_id
# rpt_frame[1:4] = static.DXCALLSIGN_CRC
# rpt_frame[4:7] = static.MYCALLSIGN_CRC
# rpt_frame[7:13] = missing_frames
self.log.info("[TNC] ARQ | RX | Requesting", frames=missing_frames)
# 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:
"""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:4] = static.DXCALLSIGN_CRC
nack_frame[4:7] = static.MYCALLSIGN_CRC
nack_frame[7:8] = bytes([int(snr)])
nack_frame[8:9] = bytes([int(self.speed_level)])
nack_frame[1:2] = self.session_id
# nack_frame[1:4] = static.DXCALLSIGN_CRC
# nack_frame[4:7] = static.MYCALLSIGN_CRC
nack_frame[2:3] = bytes([int(snr)])
nack_frame[3:4] = bytes([int(self.speed_level)])
# 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:
"""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:4] = static.DXCALLSIGN_CRC
nack_frame[4:7] = static.MYCALLSIGN_CRC
nack_frame[7:8] = bytes([int(snr)])
nack_frame[8:9] = bytes([int(self.speed_level)])
nack_frame[1:2] = self.session_id
# nack_frame[1:4] = static.DXCALLSIGN_CRC
# nack_frame[4:7] = static.MYCALLSIGN_CRC
nack_frame[2:3] = bytes([int(snr)])
nack_frame[3:4] = bytes([int(self.speed_level)])
# 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:
"""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:4] = static.DXCALLSIGN_CRC
disconnection_frame[4:7] = static.MYCALLSIGN_CRC
disconnection_frame[7:13] = helpers.callsign_to_bytes(self.mycallsign)
self.enqueue_frame_for_tx(disconnection_frame, copies=5, repeat_delay=250)
disconnection_frame[1:2] = self.session_id
# disconnection_frame[1:4] = static.DXCALLSIGN_CRC
# 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, 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(
self, data_in: bytes, bytes_per_frame: int, snr: float, freedv
@ -491,11 +538,14 @@ class DATA:
# is intended for this station.
data_in = bytes(data_in)
# TODO: this seems not to work anymore
# get received crc for different mycall ssids
# check if callsign ssid override
_, mycallsign = helpers.check_callsign(
self.mycallsign, data_in[2:5]
)
# _, mycallsign = helpers.check_callsign(
# self.mycallsign, data_in[2:5]
# )
# attempt fixing this
mycallsign = self.mycallsign
# only process data if we are in ARQ and BUSY state else return to quit
if not static.ARQ_STATE and static.TNC_STATE != "BUSY":
@ -511,19 +561,20 @@ class DATA:
# Extract some important data from the 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
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".
# We need this later for counting the "Nones" to detect missing data.
# Check if burst buffer has expected length else create it
if len(static.RX_BURST_BUFFER) != RX_N_FRAMES_PER_BURST:
static.RX_BURST_BUFFER = [None] * 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
# Append data to rx burst buffer
# [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)
@ -540,7 +591,7 @@ class DATA:
# This is the ideal case because we received all data
if None not in static.RX_BURST_BUFFER:
# 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""
for value in static.RX_BURST_BUFFER:
# static.RX_FRAME_BUFFER += static.RX_BURST_BUFFER[i]
@ -603,9 +654,8 @@ class DATA:
)
static.ARQ_SPEED_LEVEL = self.speed_level
# 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
self.log.info("[TNC] ARQ | RX | SENDING ACK")
@ -619,7 +669,7 @@ class DATA:
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,
# 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
@ -627,8 +677,8 @@ class DATA:
# TODO: See if a timeout on the send side with re-transmit last burst would help.
self.log.debug(
"[TNC] all frames in burst received:",
frame=RX_N_FRAME_OF_BURST,
frames=RX_N_FRAMES_PER_BURST,
frame=rx_n_frame_of_burst,
frames=rx_n_frames_per_burst,
)
self.send_retransmit_request_frame(freedv)
self.calculate_transfer_rate_rx(
@ -639,8 +689,8 @@ class DATA:
else:
self.log.error(
"[TNC] data_handler: Should not reach this point...",
frame=RX_N_FRAME_OF_BURST,
frames=RX_N_FRAMES_PER_BURST,
frame=rx_n_frame_of_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.
@ -649,7 +699,7 @@ class DATA:
bof_position = static.RX_FRAME_BUFFER.find(self.data_frame_bof)
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:
payload = static.RX_FRAME_BUFFER[
@ -755,6 +805,7 @@ class DATA:
snr=snr,
crc=data_frame_crc.hex(),
)
self.send_data_ack_frame(snr)
# Update statistics AFTER the frame ACK is sent
self.calculate_transfer_rate_rx(
@ -807,15 +858,13 @@ class DATA:
"""
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
# 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
# static.TOTAL_BYTES = round(len(data_out) / 1024, 2)
@ -867,9 +916,9 @@ class DATA:
# Iterate through data_out buffer
while not self.data_frame_ack_received and static.ARQ_STATE:
# we have 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):
data_mode = mode
# we have self.tx_n_max_retries_per_burst attempts for sending a burst
for self.tx_n_retry_of_burst in range(self.tx_n_max_retries_per_burst):
# data_mode = mode
# self.log.debug("[TNC] FIXED MODE:", mode=FREEDV_MODE(data_mode).name)
# we are doing a modulo check of transmission retries of the actual burst
@ -906,16 +955,17 @@ class DATA:
# Tempbuffer list for storing our data frames
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!
# TX_N_FRAMES_PER_BURST = 1 is working
# n_frames_per_burst = 1 is working
arqheader = bytearray()
# arqheader[:1] = bytes([FR_TYPE.BURST_01.value + i])
arqheader[:1] = bytes([FR_TYPE.BURST_01.value])
arqheader[1:2] = bytes([TX_N_FRAMES_PER_BURST])
arqheader[2:5] = static.DXCALLSIGN_CRC
arqheader[5:8] = static.MYCALLSIGN_CRC
arqheader[1:2] = bytes([n_frames_per_burst])
arqheader[2:3] = self.session_id
# arqheader[2:5] = static.DXCALLSIGN_CRC
# arqheader[5:8] = static.MYCALLSIGN_CRC
bufferposition_end = bufferposition + payload_per_frame - len(arqheader)
@ -939,21 +989,21 @@ class DATA:
self.log.info(
"[TNC] ARQ | TX | FRAMES",
mode=FREEDV_MODE(data_mode).name,
fpb=TX_N_FRAMES_PER_BURST,
fpb=n_frames_per_burst,
retry=self.tx_n_retry_of_burst,
)
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
# 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
# not self.rpt_request_received and not self.data_frame_ack_received and
# time.time() < burstacktimeout and static.ARQ_STATE):
# time.sleep(0.01)
# burstacktimeout = time.time() + BURST_ACK_TIMEOUT_SECONDS + 100
# burstacktimeout = time.time() + self.burst_ack_timeout_seconds + 100
while static.ARQ_STATE and not (
self.burst_ack
or self.burst_nack
@ -1000,7 +1050,7 @@ class DATA:
self.log.debug(
"[TNC] ATTEMPT:",
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,
)
# End of FOR loop
@ -1022,7 +1072,7 @@ class DATA:
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
# wait for an acknowledgement.
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:
"""
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.
Args:
@ -1118,8 +1168,8 @@ class DATA:
# Update data_channel timestamp
self.data_channel_last_received = int(time.time())
self.burst_ack_snr = int.from_bytes(bytes(data_in[7:8]), "big")
self.speed_level = int.from_bytes(bytes(data_in[8:9]), "big")
self.burst_ack_snr = int.from_bytes(bytes(data_in[2:3]), "big")
self.speed_level = int.from_bytes(bytes(data_in[3:4]), "big")
static.ARQ_SPEED_LEVEL = self.speed_level
self.log.debug(
@ -1272,11 +1322,16 @@ class DATA:
self.IS_ARQ_SESSION_MASTER = True
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:4] = static.DXCALLSIGN_CRC
connection_frame[4:7] = static.MYCALLSIGN_CRC
connection_frame[7:13] = helpers.callsign_to_bytes(self.mycallsign)
connection_frame[1:2] = self.session_id
connection_frame[2:5] = static.DXCALLSIGN_CRC
connection_frame[5:8] = static.MYCALLSIGN_CRC
connection_frame[8:14] = helpers.callsign_to_bytes(self.mycallsign)
while not static.ARQ_SESSION:
time.sleep(0.01)
@ -1291,7 +1346,7 @@ class DATA:
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`
# indicates we've received a positive response from the far station.
@ -1325,8 +1380,9 @@ class DATA:
# Update arq_session timestamp
self.arq_session_last_received = int(time.time())
static.DXCALLSIGN_CRC = bytes(data_in[4:7])
static.DXCALLSIGN = helpers.bytes_to_callsign(bytes(data_in[7:13]))
self.session_id = bytes(data_in[1:2])
static.DXCALLSIGN_CRC = bytes(data_in[5:8])
static.DXCALLSIGN = helpers.bytes_to_callsign(bytes(data_in[8:14]))
helpers.add_to_heard_stations(
static.DXCALLSIGN,
@ -1377,9 +1433,11 @@ class DATA:
self.IS_ARQ_SESSION_MASTER = 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.arq_cleanup()
def received_session_close(self, data_in: bytes):
"""
@ -1394,7 +1452,8 @@ class DATA:
# is intended for this station.
# Close the session if the CRC matches the remote station in static.
_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"
helpers.add_to_heard_stations(
static.DXCALLSIGN,
@ -1429,12 +1488,13 @@ class DATA:
# static.TNC_STATE = "BUSY"
# 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:4] = static.DXCALLSIGN_CRC
connection_frame[4:7] = static.MYCALLSIGN_CRC
connection_frame[1:2] = self.session_id
# 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:
"""
@ -1443,9 +1503,10 @@ class DATA:
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]))
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")
helpers.add_to_heard_stations(
static.DXCALLSIGN,
@ -1463,8 +1524,19 @@ class DATA:
# Update the timeout timestamps
self.arq_session_last_received = int(time.time())
self.data_channel_last_received = int(time.time())
if not self.IS_ARQ_SESSION_MASTER and not self.arq_file_transfer:
# transmit session heartbeat only
# -> 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()
##########################################################################################################
@ -1485,6 +1557,8 @@ class DATA:
data_out:bytes:
mode:int:
n_frames_per_burst:int:
transmission_uuid:str:
mycallsign:bytes:
Returns:
True if the data session was opened and the data was sent
@ -1499,12 +1573,13 @@ class DATA:
self.transmission_uuid = transmission_uuid
# 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:
time.sleep(0.5)
time.sleep(2)
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
# for calculating transmission statistics
# static.ARQ_COMPRESSION_FACTOR = len(data_out) / len(zlib.compress(data_out))
@ -1530,6 +1605,7 @@ class DATA:
Args:
mode:int:
n_frames_per_burst:int:
mycallsign:bytes:
Returns:
True if the data channel was opened successfully
@ -1537,6 +1613,11 @@ class DATA:
"""
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
self.data_channel_last_received = int(time.time())
@ -1548,12 +1629,13 @@ class DATA:
frametype = bytes([FR_TYPE.ARQ_DC_OPEN_W.value])
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:4] = static.DXCALLSIGN_CRC
connection_frame[4:7] = static.MYCALLSIGN_CRC
connection_frame[7:13] = helpers.callsign_to_bytes(mycallsign)
connection_frame[13:14] = bytes([n_frames_per_burst])
# connection_frame[13:14] = bytes([n_frames_per_burst])
connection_frame[13:14] = self.session_id
while not static.ARQ_STATE:
time.sleep(0.01)
@ -1574,7 +1656,7 @@ class DATA:
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
while time.time() < timeout:
@ -1607,19 +1689,20 @@ class DATA:
+ "]"
)
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.
self.close_session()
self.arq_cleanup()
return False
# Shouldn't get here..
# Shouldn't get here...
return True
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:
data_in:bytes:
@ -1699,7 +1782,7 @@ class DATA:
)
# 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(
static.DXCALLSIGN,
@ -1710,6 +1793,9 @@ class DATA:
static.HAMLIB_FREQUENCY,
)
self.session_id = data_in[13:14]
print(self.session_id)
# check if callsign ssid override
_, 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])
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:4] = static.DXCALLSIGN_CRC
connection_frame[4:7] = static.MYCALLSIGN_CRC
connection_frame[1:2] = self.session_id
# connection_frame[1:4] = static.DXCALLSIGN_CRC
# connection_frame[4:7] = static.MYCALLSIGN_CRC
connection_frame[8:9] = bytes([self.speed_level])
# For checking protocol version on the receiving side
connection_frame[13:14] = bytes([static.ARQ_PROTOCOL_VERSION])
self.enqueue_frame_for_tx(connection_frame)
self.enqueue_frame_for_tx([connection_frame], c2_mode=FREEDV_MODE.datac0.value, copies=1, repeat_delay=0)
self.log.info(
"[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:4] = static.DXCALLSIGN_CRC
ping_frame[4:7] = static.MYCALLSIGN_CRC
@ -1868,9 +1955,9 @@ class DATA:
self.log.info("[TNC] ENABLE FSK", state=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:
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:
"""
@ -1919,7 +2006,7 @@ class DATA:
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:4] = static.DXCALLSIGN_CRC
ping_frame[4:7] = static.MYCALLSIGN_CRC
@ -1927,9 +2014,9 @@ class DATA:
self.log.info("[TNC] ENABLE FSK", state=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:
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:
"""
@ -1976,13 +2063,14 @@ class DATA:
Force a stop of the running 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:4] = static.DXCALLSIGN_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)
self.enqueue_frame_for_tx(stop_frame, copies=2, repeat_delay=250)
self.enqueue_frame_for_tx([stop_frame], c2_mode=FREEDV_MODE.sig1.value, copies=6, repeat_delay=0)
static.TNC_STATE = "IDLE"
static.ARQ_STATE = False
@ -2041,7 +2129,7 @@ class DATA:
"[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:7] = helpers.callsign_to_bytes(self.mycallsign)
beacon_frame[9:13] = static.MYGRID[:4]
@ -2049,11 +2137,12 @@ class DATA:
if static.ENABLE_FSK:
self.enqueue_frame_for_tx(
beacon_frame,
[beacon_frame],
c2_mode=FREEDV_MODE.fsk_ldpc_0.value,
)
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
while (
@ -2109,7 +2198,7 @@ class DATA:
"""
Transmit a CQ
Args:
Nothing
self
Returns:
Nothing
@ -2119,7 +2208,7 @@ class DATA:
freedata="tnc-message",
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:7] = helpers.callsign_to_bytes(self.mycallsign)
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])
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:
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:
"""
@ -2190,7 +2279,7 @@ class DATA:
)
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:7] = helpers.callsign_to_bytes(self.mycallsign)
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)
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:
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:
"""
@ -2235,7 +2324,7 @@ class DATA:
static.HAMLIB_FREQUENCY,
)
# ------------ CALUCLATE TRANSFER RATES
# ------------ CALCULATE TRANSFER RATES
def calculate_transfer_rate_rx(
self, rx_start_of_transmission: float, receivedbytes: int
) -> list:
@ -2258,7 +2347,7 @@ class DATA:
(
receivedbytes
* static.ARQ_COMPRESSION_FACTOR
/ (static.TOTAL_BYTES)
/ static.TOTAL_BYTES
)
* 100
),
@ -2270,7 +2359,7 @@ class DATA:
if receivedbytes > 0:
static.ARQ_BITS_PER_SECOND = int((receivedbytes * 8) / transmissiontime)
static.ARQ_BYTES_PER_MINUTE = int(
(receivedbytes) / (transmissiontime / 60)
receivedbytes / (transmissiontime / 60)
)
else:
@ -2324,7 +2413,7 @@ class DATA:
if sentbytes > 0:
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:
static.ARQ_BITS_PER_SECOND = 0
@ -2353,6 +2442,7 @@ class DATA:
self.log.debug("[TNC] arq_cleanup")
self.session_id = bytes(1)
self.rx_frame_bof_received = False
self.rx_frame_eof_received = False
self.burst_ack = False
@ -2363,6 +2453,8 @@ class DATA:
self.burst_ack_snr = 255
# reset modem receiving state to reduce cpu load
modem.RECEIVE_SIG0 = True
modem.RECEIVE_SIG1 = False
modem.RECEIVE_DATAC1 = False
modem.RECEIVE_DATAC3 = False
# modem.RECEIVE_FSK_LDPC_0 = False
@ -2403,15 +2495,20 @@ class DATA:
self.rpt_request_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
Args:
enable_sig0:int: Enable/Disable signalling mode 0
enable_sig1:int: Enable/Disable signalling mode 1
mode:int: Codec2 mode to listen for
"""
# set modes we want to listen to
modem.RECEIVE_SIG0 = enable_sig0
modem.RECEIVE_SIG1 = enable_sig1
if mode == FREEDV_MODE.datac1.value:
modem.RECEIVE_DATAC1 = True
self.log.debug("[TNC] Changing listening data mode", mode="datac1")
@ -2476,7 +2573,7 @@ class DATA:
static.ARQ_SPEED_LEVEL = self.speed_level
# 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`?
self.send_burst_nack_frame_watchdog(0)
@ -2504,7 +2601,10 @@ class DATA:
self.data_channel_last_received + self.transmission_timeout
> 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())
# pass
else:
@ -2559,18 +2659,21 @@ class DATA:
"""
while True:
time.sleep(0.01)
if (
static.ARQ_SESSION
and self.IS_ARQ_SESSION_MASTER
and static.ARQ_SESSION_STATE == "connected"
and not self.arq_file_transfer
):
time.sleep(1)
self.transmit_session_heartbeat()
time.sleep(2)
# additional check for smoother stopping if heartbeat transmission
while not self.arq_file_transfer:
time.sleep(0.01)
if (
static.ARQ_SESSION
and self.IS_ARQ_SESSION_MASTER
and static.ARQ_SESSION_STATE == "connected"
# and not self.arq_file_transfer
):
time.sleep(1)
self.transmit_session_heartbeat()
time.sleep(2)
def send_test_frame(self) -> None:
"""Send an empty test frame"""
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
)

View file

@ -316,6 +316,21 @@ def check_callsign(callsign: bytes, crc_to_check: bytes):
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):
"""

View file

@ -32,6 +32,8 @@ TXCHANNEL = ""
static.TRANSMITTING = False
# Receive only specific modes to reduce CPU load
RECEIVE_SIG0 = True
RECEIVE_SIG1 = False
RECEIVE_DATAC1 = False
RECEIVE_DATAC3 = False
RECEIVE_FSK_LDPC_1 = False
@ -81,110 +83,63 @@ class RF:
self.fft_data = bytes()
# 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.
# self.datac0_payload_per_frame = self.datac0_bytes_per_frame - 2
# self.datac0_n_nom_modem_samples = self.c_lib.freedv_get_n_nom_modem_samples(
# self.datac0_freedv
# )
# self.datac0_n_tx_modem_samples = self.c_lib.freedv_get_n_tx_modem_samples(
# self.datac0_freedv
# )
# 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)
# )
# DATAC0
# SIGNALLING MODE 0 - Used for Connecting - Payload 14 Bytes
self.sig0_datac0_freedv, \
self.sig0_datac0_bytes_per_frame, \
self.sig0_datac0_bytes_out, \
self.sig0_datac0_buffer, \
self.sig0_datac0_nin = \
self.init_codec2_mode(codec2.api.FREEDV_MODE_DATAC0, None)
# Datac1 - higher-bandwidth data frames
self.datac1_freedv = ctypes.cast(
codec2.api.freedv_open(codec2.api.FREEDV_MODE_DATAC1), ctypes.c_void_p
)
self.c_lib.freedv_set_tuning_range(
self.datac1_freedv,
ctypes.c_float(static.TUNING_RANGE_FMIN),
ctypes.c_float(static.TUNING_RANGE_FMAX),
)
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)
# DATAC0
# SIGNALLING MODE 1 - Used for ACK/NACK - Payload 5 Bytes
self.sig1_datac0_freedv, \
self.sig1_datac0_bytes_per_frame, \
self.sig1_datac0_bytes_out, \
self.sig1_datac0_buffer, \
self.sig1_datac0_nin = \
self.init_codec2_mode(codec2.api.FREEDV_MODE_DATAC0, None)
# 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
self.fsk_ldpc_freedv_0 = ctypes.cast(
codec2.api.freedv_open_advanced(
# DATAC1
self.dat0_datac1_freedv, \
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,
ctypes.byref(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)
codec2.api.FREEDV_MODE_FSK_LDPC_0_ADV
)
# FSK Long-distance Parity Code 1 - data frames
self.fsk_ldpc_freedv_1 = ctypes.cast(
codec2.api.freedv_open_advanced(
# FSK LDPC - 1
self.fsk_ldpc_freedv_1, \
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,
ctypes.byref(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)
codec2.api.FREEDV_MODE_FSK_LDPC_1_ADV
)
# --------------------------------------------CREATE PYAUDIO INSTANCE
if not TESTMODE:
@ -242,6 +197,7 @@ class RF:
# --------------------------------------------INIT AND OPEN HAMLIB
# Check how we want to control the radio
# TODO: deprecated feature - we can remove this possibly
if static.HAMLIB_RADIOCONTROL == "direct":
import rig
elif static.HAMLIB_RADIOCONTROL == "rigctl":
@ -272,20 +228,25 @@ class RF:
)
fft_thread.start()
audio_thread_datac0 = threading.Thread(
target=self.audio_datac0, name="AUDIO_THREAD DATAC0", daemon=True
audio_thread_sig0_datac0 = threading.Thread(
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(
target=self.audio_datac1, name="AUDIO_THREAD DATAC1", daemon=True
audio_thread_sig1_datac0 = threading.Thread(
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(
target=self.audio_datac3, name="AUDIO_THREAD DATAC3", daemon=True
audio_thread_dat0_datac1 = threading.Thread(
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:
audio_thread_fsk_ldpc0 = threading.Thread(
@ -335,9 +296,10 @@ class RF:
length_x = len(x)
for data_buffer, receive in [
(self.datac0_buffer, True),
(self.datac1_buffer, RECEIVE_DATAC1),
(self.datac3_buffer, RECEIVE_DATAC3),
(self.sig0_datac0_buffer, RECEIVE_SIG0),
(self.sig1_datac0_buffer, RECEIVE_SIG1),
(self.dat0_datac1_buffer, RECEIVE_DATAC1),
(self.dat0_datac3_buffer, RECEIVE_DATAC3),
# Not enabled yet.
# (self.fsk_ldpc_buffer_0, 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
# selected datachannel mode is not full
for audiobuffer, receive, index in [
(self.datac0_buffer, True, 0),
(self.datac1_buffer, RECEIVE_DATAC1, 1),
(self.datac3_buffer, RECEIVE_DATAC3, 2),
(self.fsk_ldpc_buffer_0, static.ENABLE_FSK, 3),
(self.fsk_ldpc_buffer_1, static.ENABLE_FSK, 4),
(self.sig0_datac0_buffer, RECEIVE_SIG0, 0),
(self.sig1_datac0_buffer, RECEIVE_SIG1, 1),
(self.dat0_datac1_buffer, RECEIVE_DATAC1, 2),
(self.dat0_datac3_buffer, RECEIVE_DATAC3, 3),
(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:
static.BUFFER_OVERFLOW_COUNTER[index] += 1
@ -618,38 +581,119 @@ class RF:
"[MDM] [demod_audio] Pushing received data to received_queue"
)
self.modem_received_queue.put([bytes_out, freedv, bytes_per_frame])
# self.get_scatter(freedv)
self.get_scatter(freedv)
self.calculate_snr(freedv)
return nin
def audio_datac0(self) -> None:
"""Receive data encoded with datac0"""
self.datac0_nin = self.demodulate_audio(
self.datac0_buffer,
self.datac0_nin,
self.datac0_freedv,
self.datac0_bytes_out,
self.datac0_bytes_per_frame,
def init_codec2_mode(self, mode, adv):
"""
Init codec2 and return some important parameters
Args:
self:
mode:
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"""
self.datac1_nin = self.demodulate_audio(
self.datac1_buffer,
self.datac1_nin,
self.datac1_freedv,
self.datac1_bytes_out,
self.datac1_bytes_per_frame,
self.dat0_datac1_nin = self.demodulate_audio(
self.dat0_datac1_buffer,
self.dat0_datac1_nin,
self.dat0_datac1_freedv,
self.dat0_datac1_bytes_out,
self.dat0_datac1_bytes_per_frame,
)
def audio_datac3(self) -> None:
def audio_dat0_datac3(self) -> None:
"""Receive data encoded with datac3"""
self.datac3_nin = self.demodulate_audio(
self.datac3_buffer,
self.datac3_nin,
self.datac3_freedv,
self.datac3_bytes_out,
self.datac3_bytes_per_frame,
self.dat0_datac3_nin = self.demodulate_audio(
self.dat0_datac3_buffer,
self.dat0_datac3_nin,
self.dat0_datac3_freedv,
self.dat0_datac3_bytes_out,
self.dat0_datac3_bytes_per_frame,
)
def audio_fsk_ldpc_0(self) -> None:
@ -705,7 +749,6 @@ class RF:
:rtype: float
"""
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))
offset = round(modemStats.foff) * (-1)
static.FREQ_OFFSET = offset
@ -723,28 +766,27 @@ class RF:
return
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))
ctypes.cast(
self.c_lib.freedv_get_modem_extended_stats(freedv, ctypes.byref(modemStats)),
ctypes.c_void_p,
)
scatterdata = []
scatterdata_small = []
for i in range(codec2.MODEM_STATS_NC_MAX):
for j in range(codec2.MODEM_STATS_NR_MAX):
# check if odd or not to get every 2nd item for x
if (j % 2) == 0:
xsymbols = round(modemStats.rx_symbols[i][j] / 1000)
ysymbols = round(modemStats.rx_symbols[i][j + 1] / 1000)
# check if value 0.0 or has real data
if xsymbols != 0.0 and ysymbols != 0.0:
scatterdata.append({"x": xsymbols, "y": ysymbols})
for j in range(1, codec2.MODEM_STATS_NR_MAX, 2):
# print(f"{modemStats.rx_symbols[i][j]} - {modemStats.rx_symbols[i][j]}")
xsymbols = round(modemStats.rx_symbols[i][j - 1] // 1000)
ysymbols = round(modemStats.rx_symbols[i][j] // 1000)
if xsymbols != 0.0 and ysymbols != 0.0:
scatterdata.append({"x": str(xsymbols), "y": str(ysymbols)})
# Send all the data if we have too-few samples, otherwise send a sampling
if 150 > len(scatterdata) > 0:
static.SCATTER = scatterdata
else:
# only take every tenth data point
scatterdata_small = scatterdata[::10]
static.SCATTER = scatterdata_small
static.SCATTER = scatterdata[::10]
def calculate_snr(self, freedv: ctypes.c_void_p) -> float:
"""
@ -801,6 +843,9 @@ class RF:
# Initialize channel_busy_delay counter
channel_busy_delay = 0
# Initialize rms counter
rms_counter = 0
while True:
# time.sleep(0.01)
threading.Event().wait(0.01)
@ -830,8 +875,14 @@ class RF:
if not static.TRANSMITTING:
dfft[dfft > avg + 10] = 100
# Calculate audio max value
# static.AUDIO_RMS = np.amax(self.fft_data)
# Calculate audio RMS
# 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"
# 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 = 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.datac3_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.dat0_datac3_freedv, frames_per_burst)
codec2.api.freedv_set_frames_per_burst(self.fsk_ldpc_freedv_0, frames_per_burst)

View file

@ -85,7 +85,7 @@ ENABLE_FFT: bool = False
CHANNEL_BUSY: bool = False
# ARQ PROTOCOL VERSION
ARQ_PROTOCOL_VERSION: int = 2
ARQ_PROTOCOL_VERSION: int = 3
# ARQ statistics
ARQ_BYTES_PER_MINUTE_BURST: int = 0
@ -149,4 +149,5 @@ class FRAME_TYPE(Enum):
ARQ_DC_OPEN_ACK_N = 228
ARQ_STOP = 249
BEACON = 250
IDENT = 254
TEST_FRAME = 255