diff --git a/.github/workflows/ctest.yml b/.github/workflows/ctest.yml
index da07df8d..8be768ed 100644
--- a/.github/workflows/ctest.yml
+++ b/.github/workflows/ctest.yml
@@ -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
diff --git a/gui/preload-main.js b/gui/preload-main.js
index b0f4ef1f..e0903593 100644
--- a/gui/preload-main.js
+++ b/gui/preload-main.js
@@ -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;
diff --git a/gui/src/index.html b/gui/src/index.html
index be0588ee..ec8976eb 100644
--- a/gui/src/index.html
+++ b/gui/src/index.html
@@ -746,11 +746,11 @@
-
RX AUDIO LEVEL - not implemented yet
+
RX AUDIO LEVEL
diff --git a/test/test_tnc_states.py b/test/test_tnc_states.py
index 7cc5575a..affbaef1 100644
--- a/test/test_tnc_states.py
+++ b/test/test_tnc_states.py
@@ -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.")
diff --git a/tnc/audio.py b/tnc/audio.py
index 475cf780..41190bb6 100644
--- a/tnc/audio.py
+++ b/tnc/audio.py
@@ -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)
diff --git a/tnc/codec2.py b/tnc/codec2.py
index 18b5ee46..f9219c2f 100644
--- a/tnc/codec2.py
+++ b/tnc/codec2.py
@@ -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)
]
diff --git a/tnc/data_handler.py b/tnc/data_handler.py
index f43449ec..6a187719 100644
--- a/tnc/data_handler.py
+++ b/tnc/data_handler.py
@@ -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
)
diff --git a/tnc/helpers.py b/tnc/helpers.py
index c1e36626..0154cf43 100644
--- a/tnc/helpers.py
+++ b/tnc/helpers.py
@@ -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):
"""
diff --git a/tnc/modem.py b/tnc/modem.py
index 1c394daa..488d5ca8 100644
--- a/tnc/modem.py
+++ b/tnc/modem.py
@@ -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)
diff --git a/tnc/static.py b/tnc/static.py
index 86f44ea7..ef0e2b09 100644
--- a/tnc/static.py
+++ b/tnc/static.py
@@ -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