Merge pull request #399 from DJ2LS/ls-modem

Work on datac4, datac5, datac13, selective repeat ARQ
This commit is contained in:
DJ2LS 2023-05-04 17:34:32 +02:00 committed by GitHub
commit 3cfaaec3da
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
54 changed files with 1979 additions and 1539 deletions

View file

@ -112,6 +112,8 @@ jobs:
steps: steps:
- uses: actions/checkout@v3 - uses: actions/checkout@v3
with:
ref: ${{ github.head_ref }}
- uses: uraimo/run-on-arch-action@v2 - uses: uraimo/run-on-arch-action@v2
name: Build artifact name: Build artifact
id: build id: build

View file

@ -40,6 +40,8 @@ jobs:
steps: steps:
- name: Checkout repository - name: Checkout repository
uses: actions/checkout@v3 uses: actions/checkout@v3
with:
ref: ${{ github.head_ref }}
# Initializes the CodeQL tools for scanning. # Initializes the CodeQL tools for scanning.
- name: Initialize CodeQL - name: Initialize CodeQL

View file

@ -44,7 +44,7 @@ jobs:
shell: bash shell: bash
run: | run: |
git clone https://github.com/drowe67/codec2.git git clone https://github.com/drowe67/codec2.git
cd codec2 && git checkout master # This should be pinned to a release cd codec2
mkdir -p build_linux && cd build_linux && cmake .. && make mkdir -p build_linux && cd build_linux && cmake .. && make
- name: run ctests - name: run ctests

View file

@ -58,19 +58,19 @@ add_test(NAME tnc_irs_iss
# python3 test_chat_text.py") # python3 test_chat_text.py")
# set_tests_properties(chat_text PROPERTIES PASS_REGULAR_EXPRESSION "errors: 0") # set_tests_properties(chat_text PROPERTIES PASS_REGULAR_EXPRESSION "errors: 0")
add_test(NAME datac0_frames add_test(NAME datac13_frames
COMMAND sh -c "export LD_LIBRARY_PATH=${CODEC2_BUILD_DIR}/src; COMMAND sh -c "export LD_LIBRARY_PATH=${CODEC2_BUILD_DIR}/src;
export PYTHONPATH=../tnc; export PYTHONPATH=../tnc;
cd ${CMAKE_CURRENT_SOURCE_DIR}/test; cd ${CMAKE_CURRENT_SOURCE_DIR}/test;
python3 test_datac0.py") python3 test_datac13.py")
set_tests_properties(datac0_frames PROPERTIES PASS_REGULAR_EXPRESSION "errors: 0") set_tests_properties(datac13_frames PROPERTIES PASS_REGULAR_EXPRESSION "errors: 0")
add_test(NAME datac0_frames_negative add_test(NAME datac13_frames_negative
COMMAND sh -c "export LD_LIBRARY_PATH=${CODEC2_BUILD_DIR}/src; COMMAND sh -c "export LD_LIBRARY_PATH=${CODEC2_BUILD_DIR}/src;
export PYTHONPATH=../tnc; export PYTHONPATH=../tnc;
cd ${CMAKE_CURRENT_SOURCE_DIR}/test; cd ${CMAKE_CURRENT_SOURCE_DIR}/test;
python3 test_datac0_negative.py") python3 test_datac13_negative.py")
set_tests_properties(datac0_frames_negative PROPERTIES PASS_REGULAR_EXPRESSION "errors: 0") set_tests_properties(datac13_frames_negative PROPERTIES PASS_REGULAR_EXPRESSION "errors: 0")
add_test(NAME helper_routines add_test(NAME helper_routines
COMMAND sh -c "export LD_LIBRARY_PATH=${CODEC2_BUILD_DIR}/src; COMMAND sh -c "export LD_LIBRARY_PATH=${CODEC2_BUILD_DIR}/src;
@ -87,7 +87,7 @@ add_test(NAME py_highsnr_stdio_P_P_multi
PATH=$PATH:${CODEC2_BUILD_DIR}/src; PATH=$PATH:${CODEC2_BUILD_DIR}/src;
cd ${CMAKE_CURRENT_SOURCE_DIR}/test; cd ${CMAKE_CURRENT_SOURCE_DIR}/test;
python3 test_highsnr_stdio_P_P_multi.py") python3 test_highsnr_stdio_P_P_multi.py")
set_tests_properties(py_highsnr_stdio_P_P_multi PROPERTIES PASS_REGULAR_EXPRESSION "DATAC0: ${BURSTS}/${FRAMESPERBURST} DATAC1: ${BURSTS}/${FRAMESPERBURST} DATAC3: ${BURSTS}/${FRAMESPERBURST}") set_tests_properties(py_highsnr_stdio_P_P_multi PROPERTIES PASS_REGULAR_EXPRESSION "DATAC13: ${BURSTS}/${FRAMESPERBURST} DATAC1: ${BURSTS}/${FRAMESPERBURST} DATAC3: ${BURSTS}/${FRAMESPERBURST}")
add_test(NAME py_highsnr_stdio_P_P_datacx add_test(NAME py_highsnr_stdio_P_P_datacx
COMMAND sh -c "export LD_LIBRARY_PATH=${CODEC2_BUILD_DIR}/src; COMMAND sh -c "export LD_LIBRARY_PATH=${CODEC2_BUILD_DIR}/src;
@ -124,26 +124,26 @@ add_test(NAME highsnr_stdio_P_C_single
COMMAND sh -c "export LD_LIBRARY_PATH=${CODEC2_BUILD_DIR}/src; COMMAND sh -c "export LD_LIBRARY_PATH=${CODEC2_BUILD_DIR}/src;
PATH=$PATH:${CODEC2_BUILD_DIR}/src; PATH=$PATH:${CODEC2_BUILD_DIR}/src;
cd ${CMAKE_CURRENT_SOURCE_DIR}/test; cd ${CMAKE_CURRENT_SOURCE_DIR}/test;
python3 util_tx.py --mode datac0 --delay 500 --framesperburst ${FRAMESPERBURST} --bursts ${BURSTS} | python3 util_tx.py --mode datac13 --delay 500 --framesperburst ${FRAMESPERBURST} --bursts ${BURSTS} |
sox -t .s16 -r 48000 -c 1 - -t .s16 -r 8000 -c 1 - | sox -t .s16 -r 48000 -c 1 - -t .s16 -r 8000 -c 1 - |
freedv_data_raw_rx datac0 - - --framesperburst ${FRAMESPERBURST} | hexdump -C") freedv_data_raw_rx datac13 - - --framesperburst ${FRAMESPERBURST} | hexdump -C")
set_tests_properties(highsnr_stdio_P_C_single PROPERTIES PASS_REGULAR_EXPRESSION "HELLO WORLD") set_tests_properties(highsnr_stdio_P_C_single PROPERTIES PASS_REGULAR_EXPRESSION "HELLO WORLD")
add_test(NAME highsnr_stdio_C_P_single add_test(NAME highsnr_stdio_C_P_single
COMMAND sh -c "export LD_LIBRARY_PATH=${CODEC2_BUILD_DIR}/src; COMMAND sh -c "export LD_LIBRARY_PATH=${CODEC2_BUILD_DIR}/src;
PATH=$PATH:${CODEC2_BUILD_DIR}/src; PATH=$PATH:${CODEC2_BUILD_DIR}/src;
cd ${CMAKE_CURRENT_SOURCE_DIR}/test; cd ${CMAKE_CURRENT_SOURCE_DIR}/test;
freedv_data_raw_tx datac0 --testframes ${TESTFRAMES} --bursts ${BURSTS} --framesperburst ${FRAMESPERBURST} /dev/zero - | freedv_data_raw_tx datac13 --testframes ${TESTFRAMES} --bursts ${BURSTS} --framesperburst ${FRAMESPERBURST} /dev/zero - |
sox -t .s16 -r 8000 -c 1 - -t .s16 -r 48000 -c 1 - | sox -t .s16 -r 8000 -c 1 - -t .s16 -r 48000 -c 1 - |
python3 util_rx.py --mode datac0 --framesperburst ${FRAMESPERBURST} --bursts ${BURSTS}") python3 util_rx.py --mode datac13 --framesperburst ${FRAMESPERBURST} --bursts ${BURSTS}")
set_tests_properties(highsnr_stdio_C_P_single PROPERTIES PASS_REGULAR_EXPRESSION "RECEIVED BURSTS: ${BURSTS} RECEIVED FRAMES: ${FRAMESPERBURST}") set_tests_properties(highsnr_stdio_C_P_single PROPERTIES PASS_REGULAR_EXPRESSION "RECEIVED BURSTS: ${BURSTS} RECEIVED FRAMES: ${FRAMESPERBURST}")
add_test(NAME highsnr_stdio_P_P_single add_test(NAME highsnr_stdio_P_P_single
COMMAND sh -c "export LD_LIBRARY_PATH=${CODEC2_BUILD_DIR}/src; COMMAND sh -c "export LD_LIBRARY_PATH=${CODEC2_BUILD_DIR}/src;
PATH=$PATH:${CODEC2_BUILD_DIR}/src; PATH=$PATH:${CODEC2_BUILD_DIR}/src;
cd ${CMAKE_CURRENT_SOURCE_DIR}/test; cd ${CMAKE_CURRENT_SOURCE_DIR}/test;
python3 util_tx.py --mode datac0 --delay 500 --framesperburst ${FRAMESPERBURST} --bursts ${BURSTS} | python3 util_tx.py --mode datac13 --delay 500 --framesperburst ${FRAMESPERBURST} --bursts ${BURSTS} |
python3 util_rx.py --debug --mode datac0 --framesperburst ${FRAMESPERBURST} --bursts ${BURSTS}") python3 util_rx.py --debug --mode datac13 --framesperburst ${FRAMESPERBURST} --bursts ${BURSTS}")
set_tests_properties(highsnr_stdio_P_P_single PROPERTIES PASS_REGULAR_EXPRESSION "RECEIVED BURSTS: ${BURSTS} RECEIVED FRAMES: ${FRAMESPERBURST}") set_tests_properties(highsnr_stdio_P_P_single PROPERTIES PASS_REGULAR_EXPRESSION "RECEIVED BURSTS: ${BURSTS} RECEIVED FRAMES: ${FRAMESPERBURST}")
add_test(NAME highsnr_stdio_P_P_multi add_test(NAME highsnr_stdio_P_P_multi
@ -151,8 +151,8 @@ add_test(NAME highsnr_stdio_P_P_multi
PATH=$PATH:${CODEC2_BUILD_DIR}/src; PATH=$PATH:${CODEC2_BUILD_DIR}/src;
cd ${CMAKE_CURRENT_SOURCE_DIR}/test; cd ${CMAKE_CURRENT_SOURCE_DIR}/test;
python3 util_multimode_tx.py --delay 500 --framesperburst ${FRAMESPERBURST} --bursts ${BURSTS} | python3 util_multimode_tx.py --delay 500 --framesperburst ${FRAMESPERBURST} --bursts ${BURSTS} |
python3 util_multimode_rx.py --framesperburst ${FRAMESPERBURST} --bursts ${BURSTS} --timeout 20") python3 util_multimode_rx.py --framesperburst ${FRAMESPERBURST} --bursts ${BURSTS} --timeout 60")
set_tests_properties(highsnr_stdio_P_P_multi PROPERTIES PASS_REGULAR_EXPRESSION "DATAC0: ${BURSTS}/${FRAMESPERBURST} DATAC1: ${BURSTS}/${FRAMESPERBURST} DATAC3: ${BURSTS}/${FRAMESPERBURST}") set_tests_properties(highsnr_stdio_P_P_multi PROPERTIES PASS_REGULAR_EXPRESSION "DATAC13: ${BURSTS}/${FRAMESPERBURST} DATAC1: ${BURSTS}/${FRAMESPERBURST} DATAC3: ${BURSTS}/${FRAMESPERBURST}")
# These tests can't run on GitHub actions as we don't have a virtual sound card # These tests can't run on GitHub actions as we don't have a virtual sound card
if(NOT DEFINED ENV{GITHUB_RUN_ID}) if(NOT DEFINED ENV{GITHUB_RUN_ID})
@ -179,7 +179,7 @@ add_test(NAME highsnr_virtual3_P_P_multi
PATH=$PATH:${CODEC2_BUILD_DIR}/src; PATH=$PATH:${CODEC2_BUILD_DIR}/src;
cd ${CMAKE_CURRENT_SOURCE_DIR}/test; cd ${CMAKE_CURRENT_SOURCE_DIR}/test;
./test_virtual_mm.sh") ./test_virtual_mm.sh")
set_tests_properties(highsnr_virtual3_P_P_multi PROPERTIES PASS_REGULAR_EXPRESSION "DATAC0: 2/4 DATAC1: 2/4 DATAC3: 2/4") set_tests_properties(highsnr_virtual3_P_P_multi PROPERTIES PASS_REGULAR_EXPRESSION "DATAC13: 2/4 DATAC1: 2/4 DATAC3: 2/4")
# let Python do audio I/O via pyaudio callback mode # let Python do audio I/O via pyaudio callback mode
add_test(NAME highsnr_virtual4_P_P_single_callback add_test(NAME highsnr_virtual4_P_P_single_callback
@ -203,7 +203,7 @@ add_test(NAME highsnr_virtual5_P_P_multi_callback
PATH=$PATH:${CODEC2_BUILD_DIR}/src; PATH=$PATH:${CODEC2_BUILD_DIR}/src;
cd ${CMAKE_CURRENT_SOURCE_DIR}/test; cd ${CMAKE_CURRENT_SOURCE_DIR}/test;
./test_virtual4a.sh") ./test_virtual4a.sh")
set_tests_properties(highsnr_virtual5_P_P_multi_callback PROPERTIES PASS_REGULAR_EXPRESSION "DATAC0: 2/4 DATAC1: 2/4 DATAC3: 2/4") set_tests_properties(highsnr_virtual5_P_P_multi_callback PROPERTIES PASS_REGULAR_EXPRESSION "DATAC13: 2/4 DATAC1: 2/4 DATAC3: 2/4")
# let Python do audio I/O via pyaudio callback mode with code outside of callback # let Python do audio I/O via pyaudio callback mode with code outside of callback
add_test(NAME highsnr_virtual5_P_P_multi_callback_outside add_test(NAME highsnr_virtual5_P_P_multi_callback_outside
@ -211,7 +211,7 @@ add_test(NAME highsnr_virtual5_P_P_multi_callback_outside
PATH=$PATH:${CODEC2_BUILD_DIR}/src; PATH=$PATH:${CODEC2_BUILD_DIR}/src;
cd ${CMAKE_CURRENT_SOURCE_DIR}/test; cd ${CMAKE_CURRENT_SOURCE_DIR}/test;
./test_virtual4b.sh") ./test_virtual4b.sh")
set_tests_properties(highsnr_virtual5_P_P_multi_callback_outside PROPERTIES PASS_REGULAR_EXPRESSION "DATAC0: 2/4 DATAC1: 2/4 DATAC3: 2/4") set_tests_properties(highsnr_virtual5_P_P_multi_callback_outside PROPERTIES PASS_REGULAR_EXPRESSION "DATAC13: 2/4 DATAC1: 2/4 DATAC3: 2/4")
endif() endif()

View file

@ -483,7 +483,7 @@ window.addEventListener("DOMContentLoaded", () => {
command: "msg", command: "msg",
dxcallsign: dxcallsign, dxcallsign: dxcallsign,
mode: 255, mode: 255,
frames: 1, frames: 5,
data: data_with_attachment, data: data_with_attachment,
checksum: file_checksum, checksum: file_checksum,
uuid: uuid, uuid: uuid,
@ -1443,7 +1443,7 @@ update_chat = function (obj) {
command: "msg", command: "msg",
dxcallsign: doc.dxcallsign, dxcallsign: doc.dxcallsign,
mode: 255, mode: 255,
frames: 1, frames: 5,
data: data_with_attachment, data: data_with_attachment,
checksum: doc.checksum, checksum: doc.checksum,
uuid: doc.uuid, uuid: doc.uuid,

View file

@ -1634,6 +1634,7 @@ ipcRenderer.on("action-update-reception-status", (event, arg) => {
var time_left = "<strong>" + time_left + " || Speed/min: "; var time_left = "<strong>" + time_left + " || Speed/min: ";
// SET BYTES PER MINUTE // SET BYTES PER MINUTE
if (typeof data.bytesperminute == "undefined") { if (typeof data.bytesperminute == "undefined") {
var arq_bytes_per_minute = 0; var arq_bytes_per_minute = 0;
} else { } else {
@ -1648,7 +1649,7 @@ ipcRenderer.on("action-update-reception-status", (event, arg) => {
var arq_bytes_per_minute_compressed = Math.round( var arq_bytes_per_minute_compressed = Math.round(
arq_bytes_per_minute * compress arq_bytes_per_minute * compress
); );
console.log(arq_bytes_per_minute);
time_left += time_left +=
formatBytes(arq_bytes_per_minute, 1) + formatBytes(arq_bytes_per_minute, 1) +
" (comp: " + " (comp: " +
@ -2210,11 +2211,14 @@ function updateHeardStations(arg) {
//https://stackoverflow.com/a/847196 //https://stackoverflow.com/a/847196
timestampRaw = arg.stations[i]["timestamp"]; timestampRaw = arg.stations[i]["timestamp"];
var date = new Date(timestampRaw * 1000);
var hours = date.getHours(); var datetime = new Date(timestampRaw * 1000).toLocaleString(
var minutes = "0" + date.getMinutes(); navigator.language
var seconds = "0" + date.getSeconds(); );
var datetime = hours + ":" + minutes.substr(-2) + ":" + seconds.substr(-2); //var hours = date.getHours();
//var minutes = "0" + date.getMinutes();
//var seconds = "0" + date.getSeconds();
//var datetime = hours + ":" + minutes.substr(-2) + ":" + seconds.substr(-2);
var timestamp = document.createElement("td"); var timestamp = document.createElement("td");
var timestampText = document.createElement("span"); var timestampText = document.createElement("span");

View file

@ -198,6 +198,7 @@ client.on("data", function (socketdata) {
dbfs_level: data["audio_dbfs"], dbfs_level: data["audio_dbfs"],
fft: data["fft"], fft: data["fft"],
channel_busy: data["channel_busy"], channel_busy: data["channel_busy"],
channel_busy_slot: data["channel_busy_slot"],
scatter: data["scatter"], scatter: data["scatter"],
info: data["info"], info: data["info"],
rx_buffer_length: data["rx_buffer_length"], rx_buffer_length: data["rx_buffer_length"],

View file

@ -16,8 +16,8 @@ check_alsa_loopback
# make sure all child processes are killed when we exit # make sure all child processes are killed when we exit
trap 'jobs -p | xargs -r kill' EXIT trap 'jobs -p | xargs -r kill' EXIT
python3 util_callback_rx.py --mode datac0 --frames 2 --bursts 3 --audiodev -2 --debug & python3 util_callback_rx.py --mode datac13 --frames 2 --bursts 3 --audiodev -2 --debug &
rx_pid=$! rx_pid=$!
#sleep 1 #sleep 1
python3 util_tx.py --mode datac0 --frames 2 --bursts 3 --audiodev -2 python3 util_tx.py --mode datac13 --frames 2 --bursts 3 --audiodev -2
wait ${rx_pid} wait ${rx_pid}

View file

@ -204,7 +204,7 @@ for i in range(N_BURSTS):
# time.sleep(DELAY_BETWEEN_BURSTS) # time.sleep(DELAY_BETWEEN_BURSTS)
# WAIT UNTIL WE RECEIVD AN ACK/DATAC0 FRAME # WAIT UNTIL WE RECEIVD AN ACK/datac13 FRAME
while ACK_TIMEOUT >= time.time(): while ACK_TIMEOUT >= time.time():
time.sleep(0.01) time.sleep(0.01)

View file

@ -9,7 +9,7 @@ queue used by the daemon process into and out of the TNC.
Can be invoked from CMake, pytest, coverage or directly. Can be invoked from CMake, pytest, coverage or directly.
Uses util_datac0.py in separate process to perform the data transfer. Uses util_datac13.py in separate process to perform the data transfer.
@author: N2KIQ @author: N2KIQ
""" """
@ -28,9 +28,9 @@ import pytest
import structlog import structlog
try: try:
import test.util_datac0 as util import test.util_datac13 as util
except ImportError: except ImportError:
import util_datac0 as util import util_datac13 as util
STATIONS = ["AA2BB", "ZZ9YY"] STATIONS = ["AA2BB", "ZZ9YY"]
@ -48,11 +48,11 @@ def parameters() -> dict:
connect_data = {"type": "arq", "command": "connect", "dxcallsign": "ZZ9YY-0"} connect_data = {"type": "arq", "command": "connect", "dxcallsign": "ZZ9YY-0"}
stop_data = {"type": "arq", "command": "stop_transmission", "dxcallsign": "ZZ9YY-0"} stop_data = {"type": "arq", "command": "stop_transmission", "dxcallsign": "ZZ9YY-0"}
beacon_timeout = 6 beacon_timeout = 1
cq_timeout = 8 ping_timeout = 1
ping_timeout = 5 cq_timeout = 1
connect_timeout = 10 connect_timeout = 1
stop_timeout = 5 stop_timeout = 1
beacon_tx_check = '"beacon":"transmitting"' beacon_tx_check = '"beacon":"transmitting"'
cq_tx_check = '"qrv":"received"' cq_tx_check = '"qrv":"received"'
@ -193,12 +193,12 @@ def analyze_results(station1: list, station2: list, call_list: list):
pytest.param("ping", marks=pytest.mark.flaky(reruns=2)), pytest.param("ping", marks=pytest.mark.flaky(reruns=2)),
pytest.param("cq", marks=pytest.mark.flaky(reruns=20)), pytest.param("cq", marks=pytest.mark.flaky(reruns=20)),
#pytest.param("cq", marks=pytest.mark.xfail(reason="Too unstable for CI")), #pytest.param("cq", marks=pytest.mark.xfail(reason="Too unstable for CI")),
pytest.param("stop", marks=pytest.mark.flaky(reruns=0)), pytest.param("stop", marks=pytest.mark.flaky(reruns=2)),
], ],
) )
def test_datac0(frame_type: str, tmp_path): def test_datac13(frame_type: str, tmp_path):
log_handler.setup_logging(filename=tmp_path / "test_datac0", level="DEBUG") log_handler.setup_logging(filename=tmp_path / "test_datac13", level="DEBUG")
log = structlog.get_logger("test_datac0") log = structlog.get_logger("test_datac13")
s1_data = [] s1_data = []
s2_data = [] s2_data = []
@ -227,7 +227,7 @@ def test_datac0(frame_type: str, tmp_path):
from_s2, s2_send = multiprocessing.Pipe() from_s2, s2_send = multiprocessing.Pipe()
proc = [ proc = [
multiprocessing.Process( multiprocessing.Process(
target=util.t_datac0_1, target=util.t_datac13_1,
args=( args=(
s1_send, s1_send,
STATIONS[0], STATIONS[0],
@ -238,7 +238,7 @@ def test_datac0(frame_type: str, tmp_path):
daemon=True, daemon=True,
), ),
multiprocessing.Process( multiprocessing.Process(
target=util.t_datac0_2, target=util.t_datac13_2,
args=( args=(
s2_send, s2_send,
STATIONS[1], STATIONS[1],

View file

@ -1,7 +1,7 @@
#!/usr/bin/env python3 #!/usr/bin/env python3
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
""" """
Negative tests for datac0 frames. Negative tests for datac13 frames.
@author: kronenpj @author: kronenpj
""" """
@ -20,9 +20,9 @@ import pytest
import structlog import structlog
try: try:
import test.util_datac0_negative as util import test.util_datac13_negative as util
except ImportError: except ImportError:
import util_datac0_negative as util import util_datac13_negative as util
STATIONS = ["AA2BB", "ZZ9YY"] STATIONS = ["AA2BB", "ZZ9YY"]
@ -162,10 +162,13 @@ def analyze_results(station1: list, station2: list, call_list: list):
# @pytest.mark.parametrize("frame_type", ["beacon", "connect", "ping"]) # @pytest.mark.parametrize("frame_type", ["beacon", "connect", "ping"])
@pytest.mark.parametrize("frame_type", ["ping", "stop"]) @pytest.mark.parametrize("frame_type", [
def test_datac0_negative(frame_type: str, tmp_path): "ping",
log_handler.setup_logging(filename=tmp_path / "test_datac0", level="DEBUG") pytest.param("stop", marks=pytest.mark.flaky(reruns=10))
log = structlog.get_logger("test_datac0") ])
def test_datac13_negative(frame_type: str, tmp_path):
log_handler.setup_logging(filename=tmp_path / "test_datac13", level="DEBUG")
log = structlog.get_logger("test_datac13")
s1_data = [] s1_data = []
s2_data = [] s2_data = []
@ -194,7 +197,7 @@ def test_datac0_negative(frame_type: str, tmp_path):
from_s2, s2_send = multiprocessing.Pipe() from_s2, s2_send = multiprocessing.Pipe()
proc = [ proc = [
multiprocessing.Process( multiprocessing.Process(
target=util.t_datac0_1, target=util.t_datac13_1,
args=( args=(
s1_send, s1_send,
STATIONS[0], STATIONS[0],
@ -205,7 +208,7 @@ def test_datac0_negative(frame_type: str, tmp_path):
daemon=True, daemon=True,
), ),
multiprocessing.Process( multiprocessing.Process(
target=util.t_datac0_2, target=util.t_datac13_2,
args=( args=(
s2_send, s2_send,
STATIONS[1], STATIONS[1],

View file

@ -14,7 +14,7 @@ import sys
import helpers import helpers
import pytest import pytest
import static from static import ARQ, AudioParam, Beacon, Channel, Daemon, HamlibParam, ModemParam, Station, Statistics, TCIParam, TNC
@pytest.mark.parametrize("callsign", ["AA1AA", "DE2DE", "E4AWQ-4"]) @pytest.mark.parametrize("callsign", ["AA1AA", "DE2DE", "E4AWQ-4"])
@ -22,7 +22,7 @@ def test_check_callsign(callsign: str):
""" """
Execute test to demonstrate how to create and verify callsign checksums. Execute test to demonstrate how to create and verify callsign checksums.
""" """
static.SSID_LIST = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15] Station.ssid_list = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15]
t_callsign_bytes = helpers.callsign_to_bytes(callsign) t_callsign_bytes = helpers.callsign_to_bytes(callsign)
t_callsign = helpers.bytes_to_callsign(t_callsign_bytes) t_callsign = helpers.bytes_to_callsign(t_callsign_bytes)
@ -41,7 +41,7 @@ def test_callsign_to_bytes(callsign: str):
""" """
Execute test to demonsrate symmetry when converting callsigns to and from byte arrays. Execute test to demonsrate symmetry when converting callsigns to and from byte arrays.
""" """
static.SSID_LIST = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15] Station.ssid_list = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15]
t_callsign_crc = helpers.get_crc_24(bytes(callsign, "UTF-8")) t_callsign_crc = helpers.get_crc_24(bytes(callsign, "UTF-8"))
t_callsign_bytes = helpers.callsign_to_bytes(callsign) t_callsign_bytes = helpers.callsign_to_bytes(callsign)

View file

@ -49,7 +49,7 @@ def t_HighSNR_C_P_DATACx(
bursts: int, frames_per_burst: int, testframes: int, mode: str bursts: int, frames_per_burst: int, testframes: int, mode: str
): ):
""" """
Test a high signal-to-noise ratio path with DATAC0. Test a high signal-to-noise ratio path with datac13.
:param bursts: Number of bursts :param bursts: Number of bursts
:type bursts: str :type bursts: str
@ -152,7 +152,7 @@ def t_HighSNR_C_P_DATACx(
@pytest.mark.parametrize("bursts", [BURSTS]) @pytest.mark.parametrize("bursts", [BURSTS])
@pytest.mark.parametrize("frames_per_burst", [FRAMESPERBURST]) @pytest.mark.parametrize("frames_per_burst", [FRAMESPERBURST])
@pytest.mark.parametrize("testframes", [TESTFRAMES]) @pytest.mark.parametrize("testframes", [TESTFRAMES])
@pytest.mark.parametrize("mode", ["datac0", "datac1", "datac3"]) @pytest.mark.parametrize("mode", ["datac13", "datac1", "datac3"])
def test_HighSNR_C_P_DATACx( def test_HighSNR_C_P_DATACx(
bursts: int, frames_per_burst: int, testframes: int, mode: str bursts: int, frames_per_burst: int, testframes: int, mode: str
): ):

View file

@ -47,7 +47,7 @@ if os.path.exists("test"):
def t_HighSNR_P_C_DATACx(bursts: int, frames_per_burst: int, mode: str): def t_HighSNR_P_C_DATACx(bursts: int, frames_per_burst: int, mode: str):
""" """
Test a high signal-to-noise ratio path with DATAC0. Test a high signal-to-noise ratio path with datac13.
:param bursts: Number of bursts :param bursts: Number of bursts
:type bursts: str :type bursts: str
@ -154,7 +154,7 @@ def t_HighSNR_P_C_DATACx(bursts: int, frames_per_burst: int, mode: str):
# @pytest.mark.parametrize("frames_per_burst", [FRAMESPERBURST, 2, 3]) # @pytest.mark.parametrize("frames_per_burst", [FRAMESPERBURST, 2, 3])
@pytest.mark.parametrize("bursts", [BURSTS]) @pytest.mark.parametrize("bursts", [BURSTS])
@pytest.mark.parametrize("frames_per_burst", [FRAMESPERBURST]) @pytest.mark.parametrize("frames_per_burst", [FRAMESPERBURST])
@pytest.mark.parametrize("mode", ["datac0", "datac1", "datac3"]) @pytest.mark.parametrize("mode", ["datac13", "datac1", "datac3"])
def test_HighSNR_P_C_DATACx(bursts: int, frames_per_burst: int, mode: str): def test_HighSNR_P_C_DATACx(bursts: int, frames_per_burst: int, mode: str):
proc = multiprocessing.Process( proc = multiprocessing.Process(
target=t_HighSNR_P_C_DATACx, target=t_HighSNR_P_C_DATACx,

View file

@ -115,7 +115,7 @@ def t_HighSNR_P_P_DATACx(bursts: int, frames_per_burst: int, mode: str):
# @pytest.mark.parametrize("frames_per_burst", [FRAMESPERBURST, 2, 3]) # @pytest.mark.parametrize("frames_per_burst", [FRAMESPERBURST, 2, 3])
@pytest.mark.parametrize("bursts", [BURSTS]) @pytest.mark.parametrize("bursts", [BURSTS])
@pytest.mark.parametrize("frames_per_burst", [FRAMESPERBURST]) @pytest.mark.parametrize("frames_per_burst", [FRAMESPERBURST])
@pytest.mark.parametrize("mode", ["datac0", "datac1", "datac3"]) @pytest.mark.parametrize("mode", ["datac13", "datac1", "datac3"])
def test_HighSNR_P_P_DATACx(bursts: int, frames_per_burst: int, mode: str): def test_HighSNR_P_P_DATACx(bursts: int, frames_per_burst: int, mode: str):
proc = multiprocessing.Process( proc = multiprocessing.Process(
target=t_HighSNR_P_P_DATACx, target=t_HighSNR_P_P_DATACx,

View file

@ -46,7 +46,7 @@ if os.path.exists("test"):
def t_HighSNR_P_P_Multi(bursts: int, frames_per_burst: int): def t_HighSNR_P_P_Multi(bursts: int, frames_per_burst: int):
""" """
Test a high signal-to-noise ratio path with DATAC0, DATAC1 and DATAC3. Test a high signal-to-noise ratio path with datac13, DATAC1 and DATAC3.
:param bursts: Number of bursts :param bursts: Number of bursts
:type bursts: int :type bursts: int
@ -101,7 +101,7 @@ def t_HighSNR_P_P_Multi(bursts: int, frames_per_burst: int):
if "DATAC" in str(line, "UTF-8") if "DATAC" in str(line, "UTF-8")
] ]
) )
assert f"DATAC0: {bursts}/{frames_per_burst * bursts}" in lastline assert f"DATAC13: {bursts}/{frames_per_burst * bursts}" in lastline
assert f"DATAC1: {bursts}/{frames_per_burst * bursts}" in lastline assert f"DATAC1: {bursts}/{frames_per_burst * bursts}" in lastline
assert f"DATAC3: {bursts}/{frames_per_burst * bursts}" in lastline assert f"DATAC3: {bursts}/{frames_per_burst * bursts}" in lastline
print(lastline) print(lastline)

View file

@ -9,7 +9,7 @@ queue used by the daemon process into and out of the TNC.
Can be invoked from CMake, pytest, coverage or directly. Can be invoked from CMake, pytest, coverage or directly.
Uses util_datac0.py in separate process to perform the data transfer. Uses util_datac13.py in separate process to perform the data transfer.
@author: N2KIQ @author: N2KIQ
""" """
@ -26,7 +26,7 @@ sys.path.insert(0, "..")
sys.path.insert(0, "../tnc") sys.path.insert(0, "../tnc")
import data_handler import data_handler
import helpers import helpers
import static from static import ARQ, AudioParam, Beacon, Channel, Daemon, HamlibParam, ModemParam, Station, Statistics, TCIParam, TNC
def print_frame(data: bytearray): def print_frame(data: bytearray):
@ -148,18 +148,18 @@ def t_foreign_disconnect(mycall: str, dxcall: str):
:rtype: bytearray :rtype: bytearray
""" """
# Set the SSIDs we'll use for this test. # Set the SSIDs we'll use for this test.
static.SSID_LIST = [0, 1, 2, 3, 4] Station.ssid_list = [0, 1, 2, 3, 4]
# Setup the static parameters for the connection. # Setup the static parameters for the connection.
mycallsign_bytes = helpers.callsign_to_bytes(mycall) mycallsign_bytes = helpers.callsign_to_bytes(mycall)
mycallsign = helpers.bytes_to_callsign(mycallsign_bytes) mycallsign = helpers.bytes_to_callsign(mycallsign_bytes)
static.MYCALLSIGN = mycallsign Station.mycallsign = mycallsign
static.MYCALLSIGN_CRC = helpers.get_crc_24(mycallsign) Station.mycallsign_crc = helpers.get_crc_24(mycallsign)
dxcallsign_bytes = helpers.callsign_to_bytes(dxcall) dxcallsign_bytes = helpers.callsign_to_bytes(dxcall)
dxcallsign = helpers.bytes_to_callsign(dxcallsign_bytes) dxcallsign = helpers.bytes_to_callsign(dxcallsign_bytes)
static.DXCALLSIGN = dxcallsign Station.dxcallsign = dxcallsign
static.DXCALLSIGN_CRC = helpers.get_crc_24(dxcallsign) Station.dxcallsign_crc = helpers.get_crc_24(dxcallsign)
# Create the TNC # Create the TNC
tnc = data_handler.DATA() tnc = data_handler.DATA()
@ -173,12 +173,12 @@ def t_foreign_disconnect(mycall: str, dxcall: str):
print_frame(create_frame) print_frame(create_frame)
tnc.received_session_opener(create_frame) tnc.received_session_opener(create_frame)
assert helpers.callsign_to_bytes(static.MYCALLSIGN) == mycallsign_bytes assert helpers.callsign_to_bytes(Station.mycallsign) == mycallsign_bytes
assert helpers.callsign_to_bytes(static.DXCALLSIGN) == dxcallsign_bytes assert helpers.callsign_to_bytes(Station.dxcallsign) == dxcallsign_bytes
assert static.ARQ_SESSION is True assert ARQ.arq_session is True
assert static.TNC_STATE == "BUSY" assert TNC.tnc_state == "BUSY"
assert static.ARQ_SESSION_STATE == "connecting" assert ARQ.arq_session_state == "connecting"
# Set up a frame from a non-associated station. # Set up a frame from a non-associated station.
# foreigncall_bytes = helpers.callsign_to_bytes("ZZ0ZZ-0") # foreigncall_bytes = helpers.callsign_to_bytes("ZZ0ZZ-0")
@ -193,8 +193,8 @@ def t_foreign_disconnect(mycall: str, dxcall: str):
print_frame(close_frame) print_frame(close_frame)
# assert ( # assert (
# helpers.check_callsign(static.DXCALLSIGN, bytes(close_frame[4:7]))[0] is False # helpers.check_callsign(Station.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." # ), f"{helpers.get_crc_24(Station.dxcallsign)} == {bytes(close_frame[4:7])} but should be not equal."
# assert ( # assert (
# helpers.check_callsign(foreigncall, bytes(close_frame[4:7]))[0] is True # helpers.check_callsign(foreigncall, bytes(close_frame[4:7]))[0] is True
@ -203,16 +203,16 @@ def t_foreign_disconnect(mycall: str, dxcall: str):
# Send the non-associated session close frame to the TNC # Send the non-associated session close frame to the TNC
tnc.received_session_close(close_frame) tnc.received_session_close(close_frame)
assert helpers.callsign_to_bytes(static.MYCALLSIGN) == helpers.callsign_to_bytes( assert helpers.callsign_to_bytes(Station.mycallsign) == helpers.callsign_to_bytes(
mycall mycall
), f"{static.MYCALLSIGN} != {mycall} but should equal." ), f"{Station.mycallsign} != {mycall} but should equal."
assert helpers.callsign_to_bytes(static.DXCALLSIGN) == helpers.callsign_to_bytes( assert helpers.callsign_to_bytes(Station.dxcallsign) == helpers.callsign_to_bytes(
dxcall dxcall
), f"{static.DXCALLSIGN} != {dxcall} but should equal." ), f"{Station.dxcallsign} != {dxcall} but should equal."
assert static.ARQ_SESSION is True assert ARQ.arq_session is True
assert static.TNC_STATE == "BUSY" assert TNC.tnc_state == "BUSY"
assert static.ARQ_SESSION_STATE == "connecting" assert ARQ.arq_session_state == "connecting"
def t_valid_disconnect(mycall: str, dxcall: str): def t_valid_disconnect(mycall: str, dxcall: str):
@ -228,18 +228,18 @@ def t_valid_disconnect(mycall: str, dxcall: str):
:rtype: bytearray :rtype: bytearray
""" """
# Set the SSIDs we'll use for this test. # Set the SSIDs we'll use for this test.
static.SSID_LIST = [0, 1, 2, 3, 4] Station.ssid_list = [0, 1, 2, 3, 4]
# Setup the static parameters for the connection. # Setup the static parameters for the connection.
mycallsign_bytes = helpers.callsign_to_bytes(mycall) mycallsign_bytes = helpers.callsign_to_bytes(mycall)
mycallsign = helpers.bytes_to_callsign(mycallsign_bytes) mycallsign = helpers.bytes_to_callsign(mycallsign_bytes)
static.MYCALLSIGN = mycallsign Station.mycallsign = mycallsign
static.MYCALLSIGN_CRC = helpers.get_crc_24(mycallsign) Station.mycallsign_crc = helpers.get_crc_24(mycallsign)
dxcallsign_bytes = helpers.callsign_to_bytes(dxcall) dxcallsign_bytes = helpers.callsign_to_bytes(dxcall)
dxcallsign = helpers.bytes_to_callsign(dxcallsign_bytes) dxcallsign = helpers.bytes_to_callsign(dxcallsign_bytes)
static.DXCALLSIGN = dxcallsign Station.dxcallsign = dxcallsign
static.DXCALLSIGN_CRC = helpers.get_crc_24(dxcallsign) Station.dxcallsign_crc = helpers.get_crc_24(dxcallsign)
# Create the TNC # Create the TNC
tnc = data_handler.DATA() tnc = data_handler.DATA()
@ -253,9 +253,9 @@ def t_valid_disconnect(mycall: str, dxcall: str):
print_frame(create_frame) print_frame(create_frame)
tnc.received_session_opener(create_frame) tnc.received_session_opener(create_frame)
assert static.ARQ_SESSION is True assert ARQ.arq_session is True
assert static.TNC_STATE == "BUSY" assert TNC.tnc_state == "BUSY"
assert static.ARQ_SESSION_STATE == "connecting" assert ARQ.arq_session_state == "connecting"
# Create packet to be 'received' by this station. # Create packet to be 'received' by this station.
# close_frame = t_create_session_close_old(mycall=dxcall, dxcall=mycall) # close_frame = t_create_session_close_old(mycall=dxcall, dxcall=mycall)
@ -267,12 +267,12 @@ def t_valid_disconnect(mycall: str, dxcall: str):
print_frame(close_frame) print_frame(close_frame)
tnc.received_session_close(close_frame) tnc.received_session_close(close_frame)
assert helpers.callsign_to_bytes(static.MYCALLSIGN) == mycallsign_bytes assert helpers.callsign_to_bytes(Station.mycallsign) == mycallsign_bytes
assert helpers.callsign_to_bytes(static.DXCALLSIGN) == dxcallsign_bytes assert helpers.callsign_to_bytes(Station.dxcallsign) == dxcallsign_bytes
assert static.ARQ_SESSION is False assert ARQ.arq_session is False
assert static.TNC_STATE == "IDLE" assert TNC.tnc_state == "IDLE"
assert static.ARQ_SESSION_STATE == "disconnected" assert ARQ.arq_session_state == "disconnected"
# These tests are pushed into separate processes as a workaround. These tests # These tests are pushed into separate processes as a workaround. These tests

View file

@ -15,13 +15,13 @@ function check_alsa_loopback {
check_alsa_loopback check_alsa_loopback
RX_LOG=$(mktemp) RX_LOG=$(mktemp)
MAX_RUN_TIME=2600 MAX_RUN_TIME=2700
# make sure all child processes are killed when we exit # make sure all child processes are killed when we exit
trap 'jobs -p | xargs -r kill' EXIT trap 'jobs -p | xargs -r kill' EXIT
arecord --device="plughw:CARD=CHAT2,DEV=0" -r 48000 -f S16_LE -d $MAX_RUN_TIME | python3 util_rx.py --mode datac0 --frames 2 --bursts 5 --debug & arecord --device="plughw:CARD=CHAT2,DEV=0" -r 48000 -f S16_LE -d $MAX_RUN_TIME | python3 util_rx.py --mode datac13 --frames 2 --bursts 5 --debug &
rx_pid=$! rx_pid=$!
sleep 1 sleep 1
python3 util_tx.py --mode datac0 --frames 2 --bursts 5 --delay 500 | aplay --device="plughw:CARD=CHAT2,DEV=1" -r 48000 -f S16_LE python3 util_tx.py --mode datac13 --frames 2 --bursts 5 --delay 500 | aplay --device="plughw:CARD=CHAT2,DEV=1" -r 48000 -f S16_LE
wait ${rx_pid} wait ${rx_pid}

View file

@ -8,9 +8,9 @@ MAX_RUN_TIME=2600
trap 'jobs -p | xargs -r kill' EXIT trap 'jobs -p | xargs -r kill' EXIT
arecord -r 48000 --device="plughw:CARD=CHAT1,DEV=0" -f S16_LE -d $MAX_RUN_TIME | \ arecord -r 48000 --device="plughw:CARD=CHAT1,DEV=0" -f S16_LE -d $MAX_RUN_TIME | \
python3 util_rx.py --mode datac0 --frames 2 --bursts 5 --debug & python3 util_rx.py --mode datac13 --frames 2 --bursts 5 --debug &
rx_pid=$! rx_pid=$!
sleep 1 sleep 1
python3 util_tx.py --mode datac0 --frames 2 --bursts 5 --delay 500 | \ python3 util_tx.py --mode datac13 --frames 2 --bursts 5 --delay 500 | \
aplay -r 48000 --device="plughw:CARD=CHAT1,DEV=1" -f S16_LE aplay -r 48000 --device="plughw:CARD=CHAT1,DEV=1" -f S16_LE
wait ${rx_pid} wait ${rx_pid}

View file

@ -8,8 +8,8 @@ MAX_RUN_TIME=2600
trap 'jobs -p | xargs -r kill' EXIT trap 'jobs -p | xargs -r kill' EXIT
arecord -r 48000 --device="plughw:CARD=CHAT1,DEV=0" -f S16_LE -d $MAX_RUN_TIME | \ arecord -r 48000 --device="plughw:CARD=CHAT1,DEV=0" -f S16_LE -d $MAX_RUN_TIME | \
python3 util_rx.py --mode datac0 --frames 2 --bursts 5 --debug --timeout 20 & python3 util_rx.py --mode datac13 --frames 2 --bursts 5 --debug --timeout 20 &
rx_pid=$! rx_pid=$!
sleep 1 sleep 1
python3 util_tx.py --mode datac0 --frames 2 --bursts 5 --delay 2000 --audiodev -2 python3 util_tx.py --mode datac13 --frames 2 --bursts 5 --delay 2000 --audiodev -2
wait ${rx_pid} wait ${rx_pid}

View file

@ -7,9 +7,9 @@ MAX_RUN_TIME=2600
# make sure all child processes are killed when we exit # make sure all child processes are killed when we exit
trap 'jobs -p | xargs -r kill' EXIT trap 'jobs -p | xargs -r kill' EXIT
python3 util_rx.py --mode datac0 --frames 2 --bursts 5 --debug --audiodev -2 & python3 util_rx.py --mode datac13 --frames 2 --bursts 5 --debug --audiodev -2 &
rx_pid=$! rx_pid=$!
sleep 1 sleep 1
python3 util_tx.py --mode datac0 --frames 2 --bursts 5 | \ python3 util_tx.py --mode datac13 --frames 2 --bursts 5 | \
aplay -r 48000 --device="plughw:CARD=CHAT1,DEV=1" -f S16_LE aplay -r 48000 --device="plughw:CARD=CHAT1,DEV=1" -f S16_LE
wait ${rx_pid} wait ${rx_pid}

View file

@ -16,8 +16,8 @@ check_alsa_loopback
# make sure all child processes are killed when we exit # make sure all child processes are killed when we exit
trap 'jobs -p | xargs -r kill' EXIT trap 'jobs -p | xargs -r kill' EXIT
python3 util_callback_rx.py --mode datac0 --frames 2 --bursts 3 --audiodev -2 --debug & python3 util_callback_rx.py --mode datac13 --frames 2 --bursts 3 --audiodev -2 --debug &
rx_pid=$! rx_pid=$!
sleep 1 sleep 1
python3 util_tx.py --mode datac0 --frames 2 --bursts 3 --audiodev -2 python3 util_tx.py --mode datac13 --frames 2 --bursts 3 --audiodev -2
wait ${rx_pid} wait ${rx_pid}

View file

@ -16,8 +16,8 @@ check_alsa_loopback
# make sure all child processes are killed when we exit # make sure all child processes are killed when we exit
trap 'jobs -p | xargs -r kill' EXIT trap 'jobs -p | xargs -r kill' EXIT
python3 util_callback_rx.py --mode datac0 --frames 2 --bursts 3 --audiodev -2 --debug & python3 util_callback_rx.py --mode datac13 --frames 2 --bursts 3 --audiodev -2 --debug &
rx_pid=$! rx_pid=$!
#sleep 1 #sleep 1
python3 util_tx.py --mode datac0 --frames 2 --bursts 3 --audiodev -2 python3 util_tx.py --mode datac13 --frames 2 --bursts 3 --audiodev -2
wait ${rx_pid} wait ${rx_pid}

View file

@ -16,8 +16,8 @@ check_alsa_loopback
# make sure all child processes are killed when we exit # make sure all child processes are killed when we exit
trap 'jobs -p | xargs -r kill' EXIT trap 'jobs -p | xargs -r kill' EXIT
python3 util_callback_rx_outside.py --mode datac0 --frames 2 --bursts 3 --audiodev -2 --debug & python3 util_callback_rx_outside.py --mode datac13 --frames 2 --bursts 3 --audiodev -2 --debug &
rx_pid=$! rx_pid=$!
#sleep 1 #sleep 1
python3 util_tx.py --mode datac0 --frames 2 --bursts 3 --audiodev -2 python3 util_tx.py --mode datac13 --frames 2 --bursts 3 --audiodev -2
wait ${rx_pid} wait ${rx_pid}

View file

@ -112,20 +112,20 @@ class Test:
sys.exit() sys.exit()
# open codec2 instance # open codec2 instance
self.datac0_freedv = ctypes.cast( self.datac13_freedv = ctypes.cast(
codec2.api.freedv_open(codec2.api.FREEDV_MODE_DATAC0), ctypes.c_void_p codec2.api.freedv_open(codec2.FREEDV_MODE.datac13.value), ctypes.c_void_p
) )
self.datac0_bytes_per_frame = int( self.datac13_bytes_per_frame = int(
codec2.api.freedv_get_bits_per_modem_frame(self.datac0_freedv) / 8 codec2.api.freedv_get_bits_per_modem_frame(self.datac13_freedv) / 8
) )
self.datac0_bytes_out = ctypes.create_string_buffer(self.datac0_bytes_per_frame) self.datac13_bytes_out = ctypes.create_string_buffer(self.datac13_bytes_per_frame)
codec2.api.freedv_set_frames_per_burst( codec2.api.freedv_set_frames_per_burst(
self.datac0_freedv, self.N_FRAMES_PER_BURST self.datac13_freedv, self.N_FRAMES_PER_BURST
) )
self.datac0_buffer = codec2.audio_buffer(2 * self.AUDIO_FRAMES_PER_BUFFER) self.datac13_buffer = codec2.audio_buffer(2 * self.AUDIO_FRAMES_PER_BUFFER)
self.datac1_freedv = ctypes.cast( self.datac1_freedv = ctypes.cast(
codec2.api.freedv_open(codec2.api.FREEDV_MODE_DATAC1), ctypes.c_void_p codec2.api.freedv_open(codec2.FREEDV_MODE.datac1.value), ctypes.c_void_p
) )
self.datac1_bytes_per_frame = int( self.datac1_bytes_per_frame = int(
codec2.api.freedv_get_bits_per_modem_frame(self.datac1_freedv) / 8 codec2.api.freedv_get_bits_per_modem_frame(self.datac1_freedv) / 8
@ -137,7 +137,7 @@ class Test:
self.datac1_buffer = codec2.audio_buffer(2 * self.AUDIO_FRAMES_PER_BUFFER) self.datac1_buffer = codec2.audio_buffer(2 * self.AUDIO_FRAMES_PER_BUFFER)
self.datac3_freedv = ctypes.cast( self.datac3_freedv = ctypes.cast(
codec2.api.freedv_open(codec2.api.FREEDV_MODE_DATAC3), ctypes.c_void_p codec2.api.freedv_open(codec2.FREEDV_MODE.datac3.value), ctypes.c_void_p
) )
self.datac3_bytes_per_frame = int( self.datac3_bytes_per_frame = int(
codec2.api.freedv_get_bits_per_modem_frame(self.datac3_freedv) / 8 codec2.api.freedv_get_bits_per_modem_frame(self.datac3_freedv) / 8
@ -149,9 +149,9 @@ class Test:
self.datac3_buffer = codec2.audio_buffer(2 * self.AUDIO_FRAMES_PER_BUFFER) self.datac3_buffer = codec2.audio_buffer(2 * self.AUDIO_FRAMES_PER_BUFFER)
# SET COUNTERS # SET COUNTERS
self.rx_total_frames_datac0 = 0 self.rx_total_frames_datac13 = 0
self.rx_frames_datac0 = 0 self.rx_frames_datac13 = 0
self.rx_bursts_datac0 = 0 self.rx_bursts_datac13 = 0
self.rx_total_frames_datac1 = 0 self.rx_total_frames_datac1 = 0
self.rx_frames_datac1 = 0 self.rx_frames_datac1 = 0
@ -173,7 +173,7 @@ class Test:
self.frx = open("rx48_callback_multimode.raw", mode="wb") self.frx = open("rx48_callback_multimode.raw", mode="wb")
# initial nin values # initial nin values
self.datac0_nin = codec2.api.freedv_nin(self.datac0_freedv) self.datac13_nin = codec2.api.freedv_nin(self.datac13_freedv)
self.datac1_nin = codec2.api.freedv_nin(self.datac1_freedv) self.datac1_nin = codec2.api.freedv_nin(self.datac1_freedv)
self.datac3_nin = codec2.api.freedv_nin(self.datac3_freedv) self.datac3_nin = codec2.api.freedv_nin(self.datac3_freedv)
@ -187,26 +187,26 @@ class Test:
x.tofile(self.frx) x.tofile(self.frx)
x = self.resampler.resample48_to_8(x) x = self.resampler.resample48_to_8(x)
self.datac0_buffer.push(x) self.datac13_buffer.push(x)
self.datac1_buffer.push(x) self.datac1_buffer.push(x)
self.datac3_buffer.push(x) self.datac3_buffer.push(x)
while self.datac0_buffer.nbuffer >= self.datac0_nin: while self.datac13_buffer.nbuffer >= self.datac13_nin:
# demodulate audio # demodulate audio
nbytes = codec2.api.freedv_rawdatarx( nbytes = codec2.api.freedv_rawdatarx(
self.datac0_freedv, self.datac13_freedv,
self.datac0_bytes_out, self.datac13_bytes_out,
self.datac0_buffer.buffer.ctypes, self.datac13_buffer.buffer.ctypes,
) )
self.datac0_buffer.pop(self.datac0_nin) self.datac13_buffer.pop(self.datac13_nin)
self.datac0_nin = codec2.api.freedv_nin(self.datac0_freedv) self.datac13_nin = codec2.api.freedv_nin(self.datac13_freedv)
if nbytes == self.datac0_bytes_per_frame: if nbytes == self.datac13_bytes_per_frame:
self.rx_total_frames_datac0 = self.rx_total_frames_datac0 + 1 self.rx_total_frames_datac13 = self.rx_total_frames_datac13 + 1
self.rx_frames_datac0 = self.rx_frames_datac0 + 1 self.rx_frames_datac13 = self.rx_frames_datac13 + 1
if self.rx_frames_datac0 == self.N_FRAMES_PER_BURST: if self.rx_frames_datac13 == self.N_FRAMES_PER_BURST:
self.rx_frames_datac0 = 0 self.rx_frames_datac13 = 0
self.rx_bursts_datac0 = self.rx_bursts_datac0 + 1 self.rx_bursts_datac13 = self.rx_bursts_datac13 + 1
while self.datac1_buffer.nbuffer >= self.datac1_nin: while self.datac1_buffer.nbuffer >= self.datac1_nin:
# demodulate audio # demodulate audio
@ -243,7 +243,7 @@ class Test:
self.rx_bursts_datac3 = self.rx_bursts_datac3 + 1 self.rx_bursts_datac3 = self.rx_bursts_datac3 + 1
if ( if (
self.rx_bursts_datac0 and self.rx_bursts_datac1 and self.rx_bursts_datac3 self.rx_bursts_datac13 and self.rx_bursts_datac1 and self.rx_bursts_datac3
) == self.N_BURSTS: ) == self.N_BURSTS:
self.receive = False self.receive = False
@ -253,11 +253,11 @@ class Test:
while self.receive: while self.receive:
time.sleep(0.01) time.sleep(0.01)
if self.DEBUGGING_MODE: if self.DEBUGGING_MODE:
self.datac0_rxstatus = codec2.api.freedv_get_rx_status( self.datac13_rxstatus = codec2.api.freedv_get_rx_status(
self.datac0_freedv self.datac13_freedv
) )
self.datac0_rxstatus = codec2.api.rx_sync_flags_to_text[ self.datac13_rxstatus = codec2.api.rx_sync_flags_to_text[
self.datac0_rxstatus self.datac13_rxstatus
] ]
self.datac1_rxstatus = codec2.api.freedv_get_rx_status( self.datac1_rxstatus = codec2.api.freedv_get_rx_status(
@ -277,8 +277,8 @@ class Test:
print( print(
"NIN0: %5d RX_STATUS0: %4s NIN1: %5d RX_STATUS1: %4s NIN3: %5d RX_STATUS3: %4s" "NIN0: %5d RX_STATUS0: %4s NIN1: %5d RX_STATUS1: %4s NIN3: %5d RX_STATUS3: %4s"
% ( % (
self.datac0_nin, self.datac13_nin,
self.datac0_rxstatus, self.datac13_rxstatus,
self.datac1_nin, self.datac1_nin,
self.datac1_rxstatus, self.datac1_rxstatus,
self.datac3_nin, self.datac3_nin,
@ -309,7 +309,7 @@ class Test:
) )
print( print(
f"DATAC0: {self.rx_bursts_datac0}/{self.rx_total_frames_datac0} DATAC1: {self.rx_bursts_datac1}/{self.rx_total_frames_datac1} DATAC3: {self.rx_bursts_datac3}/{self.rx_total_frames_datac3}", f"datac13: {self.rx_bursts_datac13}/{self.rx_total_frames_datac13} DATAC1: {self.rx_bursts_datac1}/{self.rx_total_frames_datac1} DATAC3: {self.rx_bursts_datac3}/{self.rx_total_frames_datac3}",
file=sys.stderr, file=sys.stderr,
) )
self.frx.close() self.frx.close()

View file

@ -110,20 +110,20 @@ class Test:
sys.exit() sys.exit()
# open codec2 instance # open codec2 instance
self.datac0_freedv = ctypes.cast( self.datac13_freedv = ctypes.cast(
codec2.api.freedv_open(codec2.api.FREEDV_MODE_DATAC0), ctypes.c_void_p codec2.api.freedv_open(codec2.FREEDV_MODE.datac13.value), ctypes.c_void_p
) )
self.datac0_bytes_per_frame = int( self.datac13_bytes_per_frame = int(
codec2.api.freedv_get_bits_per_modem_frame(self.datac0_freedv) / 8 codec2.api.freedv_get_bits_per_modem_frame(self.datac13_freedv) / 8
) )
self.datac0_bytes_out = ctypes.create_string_buffer(self.datac0_bytes_per_frame) self.datac13_bytes_out = ctypes.create_string_buffer(self.datac13_bytes_per_frame)
codec2.api.freedv_set_frames_per_burst( codec2.api.freedv_set_frames_per_burst(
self.datac0_freedv, self.N_FRAMES_PER_BURST self.datac13_freedv, self.N_FRAMES_PER_BURST
) )
self.datac0_buffer = codec2.audio_buffer(2 * self.AUDIO_FRAMES_PER_BUFFER) self.datac13_buffer = codec2.audio_buffer(2 * self.AUDIO_FRAMES_PER_BUFFER)
self.datac1_freedv = ctypes.cast( self.datac1_freedv = ctypes.cast(
codec2.api.freedv_open(codec2.api.FREEDV_MODE_DATAC1), ctypes.c_void_p codec2.api.freedv_open(codec2.FREEDV_MODE.datac1.value), ctypes.c_void_p
) )
self.datac1_bytes_per_frame = int( self.datac1_bytes_per_frame = int(
codec2.api.freedv_get_bits_per_modem_frame(self.datac1_freedv) / 8 codec2.api.freedv_get_bits_per_modem_frame(self.datac1_freedv) / 8
@ -135,7 +135,7 @@ class Test:
self.datac1_buffer = codec2.audio_buffer(2 * self.AUDIO_FRAMES_PER_BUFFER) self.datac1_buffer = codec2.audio_buffer(2 * self.AUDIO_FRAMES_PER_BUFFER)
self.datac3_freedv = ctypes.cast( self.datac3_freedv = ctypes.cast(
codec2.api.freedv_open(codec2.api.FREEDV_MODE_DATAC3), ctypes.c_void_p codec2.api.freedv_open(codec2.FREEDV_MODE.datac3.value), ctypes.c_void_p
) )
self.datac3_bytes_per_frame = int( self.datac3_bytes_per_frame = int(
codec2.api.freedv_get_bits_per_modem_frame(self.datac3_freedv) / 8 codec2.api.freedv_get_bits_per_modem_frame(self.datac3_freedv) / 8
@ -147,9 +147,9 @@ class Test:
self.datac3_buffer = codec2.audio_buffer(2 * self.AUDIO_FRAMES_PER_BUFFER) self.datac3_buffer = codec2.audio_buffer(2 * self.AUDIO_FRAMES_PER_BUFFER)
# SET COUNTERS # SET COUNTERS
self.rx_total_frames_datac0 = 0 self.rx_total_frames_datac13 = 0
self.rx_frames_datac0 = 0 self.rx_frames_datac13 = 0
self.rx_bursts_datac0 = 0 self.rx_bursts_datac13 = 0
self.rx_total_frames_datac1 = 0 self.rx_total_frames_datac1 = 0
self.rx_frames_datac1 = 0 self.rx_frames_datac1 = 0
@ -171,7 +171,7 @@ class Test:
self.frx = open("rx48_callback_multimode.raw", mode="wb") self.frx = open("rx48_callback_multimode.raw", mode="wb")
# initial nin values # initial nin values
self.datac0_nin = codec2.api.freedv_nin(self.datac0_freedv) self.datac13_nin = codec2.api.freedv_nin(self.datac13_freedv)
self.datac1_nin = codec2.api.freedv_nin(self.datac1_freedv) self.datac1_nin = codec2.api.freedv_nin(self.datac1_freedv)
self.datac3_nin = codec2.api.freedv_nin(self.datac3_freedv) self.datac3_nin = codec2.api.freedv_nin(self.datac3_freedv)
@ -181,7 +181,7 @@ class Test:
x.tofile(self.frx) x.tofile(self.frx)
x = self.resampler.resample48_to_8(x) x = self.resampler.resample48_to_8(x)
self.datac0_buffer.push(x) self.datac13_buffer.push(x)
self.datac1_buffer.push(x) self.datac1_buffer.push(x)
self.datac3_buffer.push(x) self.datac3_buffer.push(x)
@ -189,9 +189,9 @@ class Test:
def print_stats(self): def print_stats(self):
if self.DEBUGGING_MODE: if self.DEBUGGING_MODE:
self.datac0_rxstatus = codec2.api.freedv_get_rx_status(self.datac0_freedv) self.datac13_rxstatus = codec2.api.freedv_get_rx_status(self.datac13_freedv)
self.datac0_rxstatus = codec2.api.rx_sync_flags_to_text[ self.datac13_rxstatus = codec2.api.rx_sync_flags_to_text[
self.datac0_rxstatus self.datac13_rxstatus
] ]
self.datac1_rxstatus = codec2.api.freedv_get_rx_status(self.datac1_freedv) self.datac1_rxstatus = codec2.api.freedv_get_rx_status(self.datac1_freedv)
@ -207,8 +207,8 @@ class Test:
print( print(
"NIN0: %5d RX_STATUS0: %4s NIN1: %5d RX_STATUS1: %4s NIN3: %5d RX_STATUS3: %4s" "NIN0: %5d RX_STATUS0: %4s NIN1: %5d RX_STATUS1: %4s NIN3: %5d RX_STATUS3: %4s"
% ( % (
self.datac0_nin, self.datac13_nin,
self.datac0_rxstatus, self.datac13_rxstatus,
self.datac1_nin, self.datac1_nin,
self.datac1_rxstatus, self.datac1_rxstatus,
self.datac3_nin, self.datac3_nin,
@ -225,22 +225,22 @@ class Test:
print(f"pyAudio error: {e}", file=sys.stderr) print(f"pyAudio error: {e}", file=sys.stderr)
while self.receive and time.time() < self.timeout: while self.receive and time.time() < self.timeout:
while self.datac0_buffer.nbuffer >= self.datac0_nin: while self.datac13_buffer.nbuffer >= self.datac13_nin:
# demodulate audio # demodulate audio
nbytes = codec2.api.freedv_rawdatarx( nbytes = codec2.api.freedv_rawdatarx(
self.datac0_freedv, self.datac13_freedv,
self.datac0_bytes_out, self.datac13_bytes_out,
self.datac0_buffer.buffer.ctypes, self.datac13_buffer.buffer.ctypes,
) )
self.datac0_buffer.pop(self.datac0_nin) self.datac13_buffer.pop(self.datac13_nin)
self.datac0_nin = codec2.api.freedv_nin(self.datac0_freedv) self.datac13_nin = codec2.api.freedv_nin(self.datac13_freedv)
if nbytes == self.datac0_bytes_per_frame: if nbytes == self.datac13_bytes_per_frame:
self.rx_total_frames_datac0 = self.rx_total_frames_datac0 + 1 self.rx_total_frames_datac13 = self.rx_total_frames_datac13 + 1
self.rx_frames_datac0 = self.rx_frames_datac0 + 1 self.rx_frames_datac13 = self.rx_frames_datac13 + 1
if self.rx_frames_datac0 == self.N_FRAMES_PER_BURST: if self.rx_frames_datac13 == self.N_FRAMES_PER_BURST:
self.rx_frames_datac0 = 0 self.rx_frames_datac13 = 0
self.rx_bursts_datac0 = self.rx_bursts_datac0 + 1 self.rx_bursts_datac13 = self.rx_bursts_datac13 + 1
self.print_stats() self.print_stats()
while self.datac1_buffer.nbuffer >= self.datac1_nin: while self.datac1_buffer.nbuffer >= self.datac1_nin:
@ -280,7 +280,7 @@ class Test:
self.print_stats() self.print_stats()
if ( if (
self.rx_bursts_datac0 self.rx_bursts_datac13
and self.rx_bursts_datac1 and self.rx_bursts_datac1
and self.rx_bursts_datac3 and self.rx_bursts_datac3
) == self.N_BURSTS: ) == self.N_BURSTS:
@ -297,7 +297,7 @@ class Test:
) )
print( print(
f"DATAC0: {self.rx_bursts_datac0}/{self.rx_total_frames_datac0} DATAC1: {self.rx_bursts_datac1}/{self.rx_total_frames_datac1} DATAC3: {self.rx_bursts_datac3}/{self.rx_total_frames_datac3}", f"datac13: {self.rx_bursts_datac13}/{self.rx_total_frames_datac13} DATAC1: {self.rx_bursts_datac1}/{self.rx_total_frames_datac1} DATAC3: {self.rx_bursts_datac3}/{self.rx_total_frames_datac3}",
file=sys.stderr, file=sys.stderr,
) )
self.frx.close() self.frx.close()

View file

@ -158,9 +158,9 @@ class Test:
def create_modulation(self): def create_modulation(self):
modes = [ modes = [
codec2.api.FREEDV_MODE_DATAC0, codec2.FREEDV_MODE.datac13.value,
codec2.api.FREEDV_MODE_DATAC1, codec2.FREEDV_MODE.datac1.value,
codec2.api.FREEDV_MODE_DATAC3, codec2.FREEDV_MODE.datac3.value,
] ]
for m in modes: for m in modes:

View file

@ -22,7 +22,7 @@ parser = argparse.ArgumentParser(description="FreeDATA audio test")
parser.add_argument("--bursts", dest="N_BURSTS", default=1, type=int) parser.add_argument("--bursts", dest="N_BURSTS", default=1, type=int)
parser.add_argument("--framesperburst", dest="N_FRAMES_PER_BURST", default=1, type=int) parser.add_argument("--framesperburst", dest="N_FRAMES_PER_BURST", default=1, type=int)
parser.add_argument( parser.add_argument(
"--mode", dest="FREEDV_MODE", type=str, choices=["datac0", "datac1", "datac3"] "--mode", dest="FREEDV_MODE", type=str, choices=["datac13", "datac1", "datac3"]
) )
parser.add_argument( parser.add_argument(
"--audiodev", "--audiodev",
@ -35,7 +35,7 @@ parser.add_argument("--debug", dest="DEBUGGING_MODE", action="store_true")
parser.add_argument( parser.add_argument(
"--timeout", "--timeout",
dest="TIMEOUT", dest="TIMEOUT",
default=10, default=60,
type=int, type=int,
help="Timeout (seconds) before test ends", help="Timeout (seconds) before test ends",
) )

View file

@ -22,7 +22,7 @@ parser = argparse.ArgumentParser(description="FreeDATA audio test")
parser.add_argument("--bursts", dest="N_BURSTS", default=1, type=int) parser.add_argument("--bursts", dest="N_BURSTS", default=1, type=int)
parser.add_argument("--framesperburst", dest="N_FRAMES_PER_BURST", default=1, type=int) parser.add_argument("--framesperburst", dest="N_FRAMES_PER_BURST", default=1, type=int)
parser.add_argument( parser.add_argument(
"--mode", dest="FREEDV_MODE", type=str, choices=["datac0", "datac1", "datac3"] "--mode", dest="FREEDV_MODE", type=str, choices=["datac13", "datac1", "datac3"]
) )
parser.add_argument( parser.add_argument(
"--audiodev", "--audiodev",

View file

@ -25,7 +25,7 @@ parser.add_argument("--bursts", dest="N_BURSTS", default=1, type=int)
parser.add_argument("--framesperburst", dest="N_FRAMES_PER_BURST", default=1, type=int) parser.add_argument("--framesperburst", dest="N_FRAMES_PER_BURST", default=1, type=int)
parser.add_argument("--delay", dest="DELAY_BETWEEN_BURSTS", default=500, type=int) parser.add_argument("--delay", dest="DELAY_BETWEEN_BURSTS", default=500, type=int)
parser.add_argument( parser.add_argument(
"--mode", dest="FREEDV_MODE", type=str, choices=["datac0", "datac1", "datac3"] "--mode", dest="FREEDV_MODE", type=str, choices=["datac13", "datac1", "datac3"]
) )
parser.add_argument( parser.add_argument(
"--audiodev", "--audiodev",

View file

@ -22,7 +22,7 @@ import data_handler
import helpers import helpers
import modem import modem
import sock import sock
import static from static import ARQ, AudioParam, Beacon, Channel, Daemon, HamlibParam, ModemParam, Station, Statistics, TCIParam, TNC
import structlog import structlog
@ -39,23 +39,23 @@ def t_setup(
modem.RXCHANNEL = tmp_path / "hfchannel1" modem.RXCHANNEL = tmp_path / "hfchannel1"
modem.TESTMODE = True modem.TESTMODE = True
modem.TXCHANNEL = tmp_path / "hfchannel2" modem.TXCHANNEL = tmp_path / "hfchannel2"
static.HAMLIB_RADIOCONTROL = "disabled" HamlibParam.hamlib_radiocontrol = "disabled"
static.LOW_BANDWIDTH_MODE = lowbwmode TNC.low_bandwidth_mode = lowbwmode
static.MYGRID = bytes("AA12aa", "utf-8") Station.mygrid = bytes("AA12aa", "utf-8")
static.RESPOND_TO_CQ = True TNC.respond_to_cq = True
static.SSID_LIST = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9] Station.ssid_list = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
# override ARQ SESSION STATE for allowing disconnect command # override ARQ SESSION STATE for allowing disconnect command
static.ARQ_SESSION_STATE = "connected" ARQ.arq_session_state = "connected"
mycallsign = helpers.callsign_to_bytes(mycall) mycallsign = helpers.callsign_to_bytes(mycall)
mycallsign = helpers.bytes_to_callsign(mycallsign) mycallsign = helpers.bytes_to_callsign(mycallsign)
static.MYCALLSIGN = mycallsign Station.mycallsign = mycallsign
static.MYCALLSIGN_CRC = helpers.get_crc_24(static.MYCALLSIGN) Station.mycallsign_crc = helpers.get_crc_24(Station.mycallsign)
dxcallsign = helpers.callsign_to_bytes(dxcall) dxcallsign = helpers.callsign_to_bytes(dxcall)
dxcallsign = helpers.bytes_to_callsign(dxcallsign) dxcallsign = helpers.bytes_to_callsign(dxcallsign)
static.DXCALLSIGN = dxcallsign Station.dxcallsign = dxcallsign
static.DXCALLSIGN_CRC = helpers.get_crc_24(static.DXCALLSIGN) Station.dxcallsign_crc = helpers.get_crc_24(Station.dxcallsign)
# Create the TNC # Create the TNC
tnc = data_handler.DATA() tnc = data_handler.DATA()
@ -105,7 +105,7 @@ def t_highsnr_arq_short_station1(
log.info("S1 TX: ", mode=static.FRAME_TYPE(frametype).name) log.info("S1 TX: ", mode=static.FRAME_TYPE(frametype).name)
if ( if (
static.LOW_BANDWIDTH_MODE TNC.low_bandwidth_mode
and frametype == static.FRAME_TYPE.ARQ_DC_OPEN_W.value and frametype == static.FRAME_TYPE.ARQ_DC_OPEN_W.value
): ):
mesg = ( mesg = (
@ -116,7 +116,7 @@ def t_highsnr_arq_short_station1(
log.error(mesg) log.error(mesg)
assert False, mesg assert False, mesg
if ( if (
not static.LOW_BANDWIDTH_MODE not TNC.low_bandwidth_mode
and frametype == static.FRAME_TYPE.ARQ_DC_OPEN_N.value and frametype == static.FRAME_TYPE.ARQ_DC_OPEN_N.value
): ):
mesg = ( mesg = (
@ -184,23 +184,23 @@ def t_highsnr_arq_short_station1(
log.warning("station1 TIMEOUT", first=True) log.warning("station1 TIMEOUT", first=True)
break break
time.sleep(0.1) time.sleep(0.1)
log.info("station1, first", arq_state=pformat(static.ARQ_STATE)) log.info("station1, first", arq_state=pformat(ARQ.arq_state))
data = {"type": "arq", "command": "disconnect", "dxcallsign": dxcall} data = {"type": "arq", "command": "disconnect", "dxcallsign": dxcall}
sock.process_tnc_commands(json.dumps(data, indent=None)) sock.process_tnc_commands(json.dumps(data, indent=None))
time.sleep(0.5) time.sleep(0.5)
# override ARQ SESSION STATE for allowing disconnect command # override ARQ SESSION STATE for allowing disconnect command
static.ARQ_SESSION_STATE = "connected" ARQ.arq_session_state = "connected"
sock.process_tnc_commands(json.dumps(data, indent=None)) sock.process_tnc_commands(json.dumps(data, indent=None))
# Allow enough time for this side to process the disconnect frame. # Allow enough time for this side to process the disconnect frame.
timeout = time.time() + 20 timeout = time.time() + 20
while static.ARQ_STATE or tnc.data_queue_transmit.queue: while ARQ.arq_state or tnc.data_queue_transmit.queue:
if time.time() > timeout: if time.time() > timeout:
log.error("station1", TIMEOUT=True) log.error("station1", TIMEOUT=True)
break break
time.sleep(0.5) time.sleep(0.5)
log.info("station1", arq_state=pformat(static.ARQ_STATE)) log.info("station1", arq_state=pformat(ARQ.arq_state))
# log.info("S1 DQT: ", DQ_Tx=pformat(tnc.data_queue_transmit.queue)) # log.info("S1 DQT: ", DQ_Tx=pformat(tnc.data_queue_transmit.queue))
# log.info("S1 DQR: ", DQ_Rx=pformat(tnc.data_queue_received.queue)) # log.info("S1 DQR: ", DQ_Rx=pformat(tnc.data_queue_received.queue))

View file

@ -19,7 +19,7 @@ import data_handler
import helpers import helpers
import modem import modem
import sock import sock
import static from static import ARQ, AudioParam, Beacon, Channel, Daemon, HamlibParam, ModemParam, Station, Statistics, TCIParam, TNC
import structlog import structlog
@ -36,23 +36,23 @@ def t_setup(
modem.RXCHANNEL = tmp_path / "hfchannel2" modem.RXCHANNEL = tmp_path / "hfchannel2"
modem.TESTMODE = True modem.TESTMODE = True
modem.TXCHANNEL = tmp_path / "hfchannel1" modem.TXCHANNEL = tmp_path / "hfchannel1"
static.HAMLIB_RADIOCONTROL = "disabled" HamlibParam.hamlib_radiocontrol = "disabled"
static.LOW_BANDWIDTH_MODE = lowbwmode TNC.low_bandwidth_mode = lowbwmode
static.MYGRID = bytes("AA12aa", "utf-8") Station.mygrid = bytes("AA12aa", "utf-8")
static.RESPOND_TO_CQ = True TNC.respond_to_cq = True
static.SSID_LIST = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9] Station.ssid_list = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
# override ARQ SESSION STATE for allowing disconnect command # override ARQ SESSION STATE for allowing disconnect command
static.ARQ_SESSION_STATE = "connected" ARQ.arq_session_state = "connected"
mycallsign = helpers.callsign_to_bytes(mycall) mycallsign = helpers.callsign_to_bytes(mycall)
mycallsign = helpers.bytes_to_callsign(mycallsign) mycallsign = helpers.bytes_to_callsign(mycallsign)
static.MYCALLSIGN = mycallsign Station.mycallsign = mycallsign
static.MYCALLSIGN_CRC = helpers.get_crc_24(static.MYCALLSIGN) Station.mycallsign_crc = helpers.get_crc_24(Station.mycallsign)
dxcallsign = helpers.callsign_to_bytes(dxcall) dxcallsign = helpers.callsign_to_bytes(dxcall)
dxcallsign = helpers.bytes_to_callsign(dxcallsign) dxcallsign = helpers.bytes_to_callsign(dxcallsign)
static.DXCALLSIGN = dxcallsign Station.dxcallsign = dxcallsign
static.DXCALLSIGN_CRC = helpers.get_crc_24(static.DXCALLSIGN) Station.dxcallsign_crc = helpers.get_crc_24(Station.dxcallsign)
# Create the TNC # Create the TNC
tnc = data_handler.DATA() tnc = data_handler.DATA()
@ -136,13 +136,13 @@ def t_highsnr_arq_short_station2(
# the queue to an object for comparisons. # the queue to an object for comparisons.
while ( while (
'"arq":"transmission","status":"received"' not in str(sock.SOCKET_QUEUE.queue) '"arq":"transmission","status":"received"' not in str(sock.SOCKET_QUEUE.queue)
or static.ARQ_STATE or ARQ.arq_state
): ):
if time.time() > timeout: if time.time() > timeout:
log.warning("station2 TIMEOUT", first=True) log.warning("station2 TIMEOUT", first=True)
break break
time.sleep(0.5) time.sleep(0.5)
log.info("station2, first", arq_state=pformat(static.ARQ_STATE)) log.info("station2, first", arq_state=pformat(ARQ.arq_state))
# Allow enough time for this side to receive the disconnect frame. # Allow enough time for this side to receive the disconnect frame.
timeout = time.time() + 20 timeout = time.time() + 20
@ -151,7 +151,7 @@ def t_highsnr_arq_short_station2(
log.warning("station2", TIMEOUT=True) log.warning("station2", TIMEOUT=True)
break break
time.sleep(0.5) time.sleep(0.5)
log.info("station2", arq_state=pformat(static.ARQ_STATE)) log.info("station2", arq_state=pformat(ARQ.arq_state))
# log.info("S2 DQT: ", DQ_Tx=pformat(tnc.data_queue_transmit.queue)) # log.info("S2 DQT: ", DQ_Tx=pformat(tnc.data_queue_transmit.queue))
# log.info("S2 DQR: ", DQ_Rx=pformat(tnc.data_queue_received.queue)) # log.info("S2 DQR: ", DQ_Rx=pformat(tnc.data_queue_received.queue))

View file

@ -5,9 +5,9 @@ simulated audio channel.
Near end-to-end test for sending / receiving control frames through the TNC and modem Near end-to-end test for sending / receiving control frames through the TNC and modem
and back through on the other station. Data injection initiates from the queue used and back through on the other station. Data injection initiates from the queue used
by the daemon process into and out of the TNC. by the daemon process into and out of the TNCParam.
Invoked from test_datac0.py. Invoked from test_datac13.py.
@author: N2KIQ @author: N2KIQ
""" """
@ -21,9 +21,10 @@ import data_handler
import helpers import helpers
import modem import modem
import sock import sock
import static from static import ARQ, HamlibParam, ModemParam, Station, TNC as TNCParam
import structlog
from static import FRAME_TYPE as FR_TYPE from static import FRAME_TYPE as FR_TYPE
import structlog
#from static import FRAME_TYPE as FR_TYPE
def t_setup( def t_setup(
@ -46,33 +47,35 @@ def t_setup(
modem.RXCHANNEL = tmp_path / rx_channel modem.RXCHANNEL = tmp_path / rx_channel
modem.TESTMODE = True modem.TESTMODE = True
modem.TXCHANNEL = tmp_path / tx_channel modem.TXCHANNEL = tmp_path / tx_channel
static.HAMLIB_RADIOCONTROL = "disabled" HamlibParam.hamlib_radiocontrol = "disabled"
static.LOW_BANDWIDTH_MODE = lowbwmode or True TNCParam.low_bandwidth_mode = lowbwmode or True
static.MYGRID = bytes("AA12aa", "utf-8") Station.mygrid = bytes("AA12aa", "utf-8")
static.RESPOND_TO_CQ = True TNCParam.respond_to_cq = True
static.SSID_LIST = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9] Station.ssid_list = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
mycallsign = helpers.callsign_to_bytes(mycall) mycallsign = helpers.callsign_to_bytes(mycall)
mycallsign = helpers.bytes_to_callsign(mycallsign) mycallsign = helpers.bytes_to_callsign(mycallsign)
static.MYCALLSIGN = mycallsign Station.mycallsign = mycallsign
static.MYCALLSIGN_CRC = helpers.get_crc_24(static.MYCALLSIGN) Station.mycallsign_crc = helpers.get_crc_24(Station.mycallsign)
dxcallsign = helpers.callsign_to_bytes(dxcall) dxcallsign = helpers.callsign_to_bytes(dxcall)
dxcallsign = helpers.bytes_to_callsign(dxcallsign) dxcallsign = helpers.bytes_to_callsign(dxcallsign)
static.DXCALLSIGN = dxcallsign Station.dxcallsign = dxcallsign
static.DXCALLSIGN_CRC = helpers.get_crc_24(static.DXCALLSIGN) Station.dxcallsign_crc = helpers.get_crc_24(Station.dxcallsign)
# Create the TNC # Create the TNC
tnc = data_handler.DATA() tnc_data_handler = data_handler.DATA()
orig_rx_func = data_handler.DATA.process_data orig_rx_func = data_handler.DATA.process_data
data_handler.DATA.process_data = t_process_data data_handler.DATA.process_data = t_process_data
tnc.log = structlog.get_logger(f"station{station}_DATA") tnc_data_handler.log = structlog.get_logger(f"station{station}_DATA")
# Limit the frame-ack timeout # Limit the frame-ack timeout
tnc.time_list_low_bw = [3, 1, 1] tnc_data_handler.time_list_low_bw = [8, 8, 8]
tnc.time_list_high_bw = [3, 1, 1] tnc_data_handler.time_list_high_bw = [8, 8, 8]
tnc.time_list = [3, 1, 1] tnc_data_handler.time_list = [8, 8, 8]
# Limit number of retries # Limit number of retries
tnc.rx_n_max_retries_per_burst = 4 tnc_data_handler.rx_n_max_retries_per_burst = 4
ModemParam.tx_delay = 50 # add additional delay time for passing test
# Create the modem # Create the modem
t_modem = modem.RF() t_modem = modem.RF()
@ -80,10 +83,10 @@ def t_setup(
modem.RF.transmit = t_transmit modem.RF.transmit = t_transmit
t_modem.log = structlog.get_logger(f"station{station}_RF") t_modem.log = structlog.get_logger(f"station{station}_RF")
return tnc, orig_rx_func, orig_tx_func return tnc_data_handler, orig_rx_func, orig_tx_func
def t_datac0_1( def t_datac13_1(
parent_pipe, parent_pipe,
mycall: str, mycall: str,
dxcall: str, dxcall: str,
@ -93,7 +96,7 @@ def t_datac0_1(
log = structlog.get_logger("station1") log = structlog.get_logger("station1")
orig_tx_func: Callable orig_tx_func: Callable
orig_rx_func: Callable orig_rx_func: Callable
log.debug("t_datac0_1:", TMP_PATH=tmp_path) log.debug("t_datac13_1:", TMP_PATH=tmp_path)
# Unpack tuple # Unpack tuple
data, timeout_duration, tx_check, _, final_tx_check, _ = config data, timeout_duration, tx_check, _, final_tx_check, _ = config
@ -131,7 +134,7 @@ def t_datac0_1(
# original function captured before this one was put in place. # original function captured before this one was put in place.
orig_rx_func(self, bytes_out, freedv, bytes_per_frame) # type: ignore orig_rx_func(self, bytes_out, freedv, bytes_per_frame) # type: ignore
tnc, orig_rx_func, orig_tx_func = t_setup( tnc_data_handler, orig_rx_func, orig_tx_func = t_setup(
1, 1,
mycall, mycall,
dxcall, dxcall,
@ -143,14 +146,14 @@ def t_datac0_1(
tmp_path, tmp_path,
) )
log.info("t_datac0_1:", RXCHANNEL=modem.RXCHANNEL) log.info("t_datac13_1:", RXCHANNEL=modem.RXCHANNEL)
log.info("t_datac0_1:", TXCHANNEL=modem.TXCHANNEL) log.info("t_datac13_1:", TXCHANNEL=modem.TXCHANNEL)
time.sleep(0.5) time.sleep(0.5)
if "stop" in data["command"]: if "stop" in data["command"]:
log.debug("t_datac0_1: STOP test, setting TNC state") log.debug("t_datac13_1: STOP test, setting TNC state")
static.TNC_STATE = "BUSY" TNCParam.tnc_state = "BUSY"
static.ARQ_STATE = True ARQ.arq_state = True
sock.ThreadedTCPRequestHandler.process_tnc_commands(None,json.dumps(data, indent=None)) sock.ThreadedTCPRequestHandler.process_tnc_commands(None,json.dumps(data, indent=None))
time.sleep(0.5) time.sleep(0.5)
sock.ThreadedTCPRequestHandler.process_tnc_commands(None,json.dumps(data, indent=None)) sock.ThreadedTCPRequestHandler.process_tnc_commands(None,json.dumps(data, indent=None))
@ -166,25 +169,25 @@ def t_datac0_1(
tx_check=tx_check, tx_check=tx_check,
) )
break break
time.sleep(0.1) time.sleep(0.5)
log.info("station1, first") log.info("station1, first")
# override ARQ SESSION STATE for allowing disconnect command # override ARQ SESSION STATE for allowing disconnect command
static.ARQ_SESSION_STATE = "connected" ARQ.arq_session_state = "connected"
data = {"type": "arq", "command": "disconnect", "dxcallsign": dxcall} data = {"type": "arq", "command": "disconnect", "dxcallsign": dxcall}
sock.ThreadedTCPRequestHandler.process_tnc_commands(None,json.dumps(data, indent=None)) sock.ThreadedTCPRequestHandler.process_tnc_commands(None,json.dumps(data, indent=None))
time.sleep(0.5) time.sleep(0.5)
# Allow enough time for this side to process the disconnect frame. # Allow enough time for this side to process the disconnect frame.
timeout = time.time() + timeout_duration timeout = time.time() + timeout_duration
while tnc.data_queue_transmit.queue: while tnc_data_handler.data_queue_transmit.queue:
if time.time() > timeout: if time.time() > timeout:
log.warning("station1", TIMEOUT=True, dq_tx=tnc.data_queue_transmit.queue) log.warning("station1", TIMEOUT=True, dq_tx=tnc_data_handler.data_queue_transmit.queue)
break break
time.sleep(0.5) time.sleep(0.5)
log.info("station1, final") log.info("station1, final")
# log.info("S1 DQT: ", DQ_Tx=pformat(tnc.data_queue_transmit.queue)) # log.info("S1 DQT: ", DQ_Tx=pformat(TNCParam.data_queue_transmit.queue))
# log.info("S1 DQR: ", DQ_Rx=pformat(tnc.data_queue_received.queue)) # log.info("S1 DQR: ", DQ_Rx=pformat(TNCParam.data_queue_received.queue))
log.debug("S1 Socket: ", socket_queue=pformat(sock.SOCKET_QUEUE.queue)) log.debug("S1 Socket: ", socket_queue=pformat(sock.SOCKET_QUEUE.queue))
for item in final_tx_check: for item in final_tx_check:
@ -199,7 +202,7 @@ def t_datac0_1(
log.warning("station1: Exiting!") log.warning("station1: Exiting!")
def t_datac0_2( def t_datac13_2(
parent_pipe, parent_pipe,
mycall: str, mycall: str,
dxcall: str, dxcall: str,
@ -209,7 +212,7 @@ def t_datac0_2(
log = structlog.get_logger("station2") log = structlog.get_logger("station2")
orig_tx_func: Callable orig_tx_func: Callable
orig_rx_func: Callable orig_rx_func: Callable
log.debug("t_datac0_2:", TMP_PATH=tmp_path) log.debug("t_datac13_2:", TMP_PATH=tmp_path)
# Unpack tuple # Unpack tuple
data, timeout_duration, _, rx_check, _, final_rx_check = config data, timeout_duration, _, rx_check, _, final_rx_check = config
@ -259,8 +262,8 @@ def t_datac0_2(
tmp_path, tmp_path,
) )
log.info("t_datac0_2:", RXCHANNEL=modem.RXCHANNEL) log.info("t_datac13_2:", RXCHANNEL=modem.RXCHANNEL)
log.info("t_datac0_2:", TXCHANNEL=modem.TXCHANNEL) log.info("t_datac13_2:", TXCHANNEL=modem.TXCHANNEL)
if "cq" in data: if "cq" in data:
t_data = {"type": "arq", "command": "stop_transmission"} t_data = {"type": "arq", "command": "stop_transmission"}
@ -292,8 +295,8 @@ def t_datac0_2(
time.sleep(0.5) time.sleep(0.5)
log.info("station2, final") log.info("station2, final")
# log.info("S2 DQT: ", DQ_Tx=pformat(tnc.data_queue_transmit.queue)) # log.info("S2 DQT: ", DQ_Tx=pformat(TNCParam.data_queue_transmit.queue))
# log.info("S2 DQR: ", DQ_Rx=pformat(tnc.data_queue_received.queue)) # log.info("S2 DQR: ", DQ_Rx=pformat(TNCParam.data_queue_received.queue))
log.debug("S2 Socket: ", socket_queue=pformat(sock.SOCKET_QUEUE.queue)) log.debug("S2 Socket: ", socket_queue=pformat(sock.SOCKET_QUEUE.queue))
for item in final_rx_check: for item in final_rx_check:

View file

@ -1,7 +1,7 @@
#!/usr/bin/env python3 #!/usr/bin/env python3
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
""" """
Negative test utilities for datac0 frames. Negative test utilities for datac13 frames.
@author: kronenpj @author: kronenpj
""" """
@ -15,9 +15,9 @@ import data_handler
import helpers import helpers
import modem import modem
import sock import sock
import static from static import ARQ, AudioParam, Beacon, Channel, Daemon, HamlibParam, ModemParam, Station, Statistics, TCIParam, TNC, FRAME_TYPE as FR_TYPE
import structlog import structlog
from static import FRAME_TYPE as FR_TYPE #from static import FRAME_TYPE as FR_TYPE
def t_setup( def t_setup(
@ -40,44 +40,44 @@ def t_setup(
modem.RXCHANNEL = tmp_path / rx_channel modem.RXCHANNEL = tmp_path / rx_channel
modem.TESTMODE = True modem.TESTMODE = True
modem.TXCHANNEL = tmp_path / tx_channel modem.TXCHANNEL = tmp_path / tx_channel
static.HAMLIB_RADIOCONTROL = "disabled" HamlibParam.hamlib_radiocontrol = "disabled"
static.LOW_BANDWIDTH_MODE = lowbwmode or True TNC.low_bandwidth_mode = lowbwmode or True
static.MYGRID = bytes("AA12aa", "utf-8") Station.mygrid = bytes("AA12aa", "utf-8")
static.RESPOND_TO_CQ = True TNC.respond_to_cq = True
static.SSID_LIST = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9] Station.ssid_list = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
mycallsign = helpers.callsign_to_bytes(mycall) mycallsign_bytes = helpers.callsign_to_bytes(mycall)
mycallsign = helpers.bytes_to_callsign(mycallsign) mycallsign = helpers.bytes_to_callsign(mycallsign_bytes)
static.MYCALLSIGN = mycallsign Station.mycallsign = mycallsign
static.MYCALLSIGN_CRC = helpers.get_crc_24(static.MYCALLSIGN) Station.mycallsign_crc = helpers.get_crc_24(mycallsign)
dxcallsign = helpers.callsign_to_bytes(dxcall) dxcallsign_bytes = helpers.callsign_to_bytes(dxcall)
dxcallsign = helpers.bytes_to_callsign(dxcallsign) dxcallsign = helpers.bytes_to_callsign(dxcallsign_bytes)
static.DXCALLSIGN = dxcallsign Station.dxcallsign = dxcallsign
static.DXCALLSIGN_CRC = helpers.get_crc_24(static.DXCALLSIGN) Station.dxcallsign_crc = helpers.get_crc_24(dxcallsign)
# Create the TNC # Create the TNC
tnc = data_handler.DATA() tnc_data_handler = data_handler.DATA()
orig_rx_func = data_handler.DATA.process_data orig_rx_func = data_handler.DATA.process_data
data_handler.DATA.process_data = t_process_data data_handler.DATA.process_data = t_process_data
tnc.log = structlog.get_logger(f"station{station}_DATA") tnc_data_handler.log = structlog.get_logger(f"station{station}_DATA")
# Limit the frame-ack timeout # Limit the frame-ack timeout
tnc.time_list_low_bw = [3, 1, 1] tnc_data_handler.time_list_low_bw = [8, 8, 8]
tnc.time_list_high_bw = [3, 1, 1] tnc_data_handler.time_list_high_bw = [8, 8, 8]
tnc.time_list = [3, 1, 1] tnc_data_handler.time_list = [8, 8, 8]
# Limit number of retries # Limit number of retries
tnc.rx_n_max_retries_per_burst = 4 tnc_data_handler.rx_n_max_retries_per_burst = 4
ModemParam.tx_delay = 50 # add additional delay time for passing test
# Create the modem # Create the modem
t_modem = modem.RF() t_modem = modem.RF()
orig_tx_func = modem.RF.transmit orig_tx_func = modem.RF.transmit
modem.RF.transmit = t_transmit modem.RF.transmit = t_transmit
t_modem.log = structlog.get_logger(f"station{station}_RF") t_modem.log = structlog.get_logger(f"station{station}_RF")
return tnc, orig_rx_func, orig_tx_func return tnc_data_handler, orig_rx_func, orig_tx_func
def t_datac0_1( def t_datac13_1(
parent_pipe, parent_pipe,
mycall: str, mycall: str,
dxcall: str, dxcall: str,
@ -87,7 +87,7 @@ def t_datac0_1(
log = structlog.get_logger("station1") log = structlog.get_logger("station1")
orig_tx_func: Callable orig_tx_func: Callable
orig_rx_func: Callable orig_rx_func: Callable
log.debug("t_datac0_1:", TMP_PATH=tmp_path) log.debug("t_datac13_1:", TMP_PATH=tmp_path)
# Unpack tuple # Unpack tuple
data, timeout_duration, tx_check, _, final_tx_check, _ = config data, timeout_duration, tx_check, _, final_tx_check, _ = config
@ -125,7 +125,7 @@ def t_datac0_1(
# original function captured before this one was put in place. # original function captured before this one was put in place.
orig_rx_func(self, bytes_out, freedv, bytes_per_frame) # type: ignore orig_rx_func(self, bytes_out, freedv, bytes_per_frame) # type: ignore
tnc, orig_rx_func, orig_tx_func = t_setup( tnc_data_handler, orig_rx_func, orig_tx_func = t_setup(
1, 1,
mycall, mycall,
dxcall, dxcall,
@ -137,21 +137,21 @@ def t_datac0_1(
tmp_path, tmp_path,
) )
log.info("t_datac0_1:", RXCHANNEL=modem.RXCHANNEL) log.info("t_datac13_1:", RXCHANNEL=modem.RXCHANNEL)
log.info("t_datac0_1:", TXCHANNEL=modem.TXCHANNEL) log.info("t_datac13_1:", TXCHANNEL=modem.TXCHANNEL)
orig_dxcall = static.DXCALLSIGN orig_dxcall = Station.dxcallsign
if "stop" in data["command"]: if "stop" in data["command"]:
time.sleep(0.5) time.sleep(0.5)
log.debug( log.debug(
"t_datac0_1: STOP test, setting TNC state", "t_datac13_1: STOP test, setting TNC state",
mycall=static.MYCALLSIGN, mycall=Station.mycallsign,
dxcall=static.DXCALLSIGN, dxcall=Station.dxcallsign,
) )
static.DXCALLSIGN = helpers.callsign_to_bytes(data["dxcallsign"]) Station.dxcallsign = helpers.callsign_to_bytes(data["dxcallsign"])
static.DXCALLSIGN_CRC = helpers.get_crc_24(static.DXCALLSIGN) Station.dxcallsign_CRC = helpers.get_crc_24(Station.dxcallsign)
static.TNC_STATE = "BUSY" TNC.tnc_state = "BUSY"
static.ARQ_STATE = True ARQ.arq_state = True
sock.ThreadedTCPRequestHandler.process_tnc_commands(None,json.dumps(data, indent=None)) sock.ThreadedTCPRequestHandler.process_tnc_commands(None,json.dumps(data, indent=None))
sock.ThreadedTCPRequestHandler.process_tnc_commands(None,json.dumps(data, indent=None)) sock.ThreadedTCPRequestHandler.process_tnc_commands(None,json.dumps(data, indent=None))
@ -172,19 +172,19 @@ def t_datac0_1(
if "stop" in data["command"]: if "stop" in data["command"]:
time.sleep(0.5) time.sleep(0.5)
log.debug("STOP test, resetting DX callsign") log.debug("STOP test, resetting DX callsign")
static.DXCALLSIGN = orig_dxcall Station.dxcallsign = orig_dxcall
static.DXCALLSIGN_CRC = helpers.get_crc_24(static.DXCALLSIGN) Station.dxcallsign_CRC = helpers.get_crc_24(Station.dxcallsign)
# override ARQ SESSION STATE for allowing disconnect command # override ARQ SESSION STATE for allowing disconnect command
static.ARQ_SESSION_STATE = "connected" ARQ.arq_session_state = "connected"
data = {"type": "arq", "command": "disconnect", "dxcallsign": dxcall} data = {"type": "arq", "command": "disconnect", "dxcallsign": dxcall}
sock.ThreadedTCPRequestHandler.process_tnc_commands(None,json.dumps(data, indent=None)) sock.ThreadedTCPRequestHandler.process_tnc_commands(None,json.dumps(data, indent=None))
time.sleep(0.5) time.sleep(0.5)
# Allow enough time for this side to process the disconnect frame. # Allow enough time for this side to process the disconnect frame.
timeout = time.time() + timeout_duration timeout = time.time() + timeout_duration
while tnc.data_queue_transmit.queue: while tnc_data_handler.data_queue_transmit.queue:
if time.time() > timeout: if time.time() > timeout:
log.warning("station1", TIMEOUT=True, dq_tx=tnc.data_queue_transmit.queue) log.warning("station1", TIMEOUT=True, dq_tx=tnc_data_handler.data_queue_transmit.queue)
break break
time.sleep(0.5) time.sleep(0.5)
log.info("station1, final") log.info("station1, final")
@ -204,7 +204,7 @@ def t_datac0_1(
log.warning("station1: Exiting!") log.warning("station1: Exiting!")
def t_datac0_2( def t_datac13_2(
parent_pipe, parent_pipe,
mycall: str, mycall: str,
dxcall: str, dxcall: str,
@ -214,7 +214,7 @@ def t_datac0_2(
log = structlog.get_logger("station2") log = structlog.get_logger("station2")
orig_tx_func: Callable orig_tx_func: Callable
orig_rx_func: Callable orig_rx_func: Callable
log.debug("t_datac0_2:", TMP_PATH=tmp_path) log.debug("t_datac13_2:", TMP_PATH=tmp_path)
# Unpack tuple # Unpack tuple
data, timeout_duration, _, rx_check, _, final_rx_check = config data, timeout_duration, _, rx_check, _, final_rx_check = config
@ -264,9 +264,9 @@ def t_datac0_2(
tmp_path, tmp_path,
) )
log.info("t_datac0_2:", RXCHANNEL=modem.RXCHANNEL) log.info("t_datac13_2:", RXCHANNEL=modem.RXCHANNEL)
log.info("t_datac0_2:", TXCHANNEL=modem.TXCHANNEL) log.info("t_datac13_2:", TXCHANNEL=modem.TXCHANNEL)
log.info("t_datac0_2:", mycall=static.MYCALLSIGN) log.info("t_datac13_2:", mycall=Station.mycallsign)
if "cq" in data: if "cq" in data:
t_data = {"type": "arq", "command": "stop_transmission"} t_data = {"type": "arq", "command": "stop_transmission"}
@ -292,9 +292,6 @@ def t_datac0_2(
# Allow enough time for this side to receive the disconnect frame. # Allow enough time for this side to receive the disconnect frame.
timeout = time.time() + timeout_duration timeout = time.time() + timeout_duration
while '"arq":"session", "status":"close"' not in str(sock.SOCKET_QUEUE.queue): while '"arq":"session", "status":"close"' not in str(sock.SOCKET_QUEUE.queue):
if time.time() > timeout: if time.time() > timeout:
log.warning("station2", TIMEOUT=True, queue=str(sock.SOCKET_QUEUE.queue)) log.warning("station2", TIMEOUT=True, queue=str(sock.SOCKET_QUEUE.queue))
break break

View file

@ -67,7 +67,7 @@ def test_mm_rx():
for idx in range(3): for idx in range(3):
datac_freedv.append( datac_freedv.append(
ctypes.cast( ctypes.cast(
codec2.api.freedv_open(codec2.api.FREEDV_MODE_DATAC0), ctypes.c_void_p codec2.api.freedv_open(codec2.FREEDV_MODE.datac13.value), ctypes.c_void_p
) )
) )
datac_bytes_per_frame.append( datac_bytes_per_frame.append(
@ -121,11 +121,11 @@ def test_mm_rx():
for idx in range(3): for idx in range(3):
datac_nin[idx] = codec2.api.freedv_nin(datac_freedv[idx]) datac_nin[idx] = codec2.api.freedv_nin(datac_freedv[idx])
def print_stats(time_datac0, time_datac1, time_datac3): def print_stats(time_datac13, time_datac1, time_datac3):
if not DEBUGGING_MODE: if not DEBUGGING_MODE:
return return
time_datac = [time_datac0, time_datac1, time_datac3] time_datac = [time_datac13, time_datac1, time_datac3]
datac_rxstatus = ["", "", ""] datac_rxstatus = ["", "", ""]
for idx in range(3): for idx in range(3):
datac_rxstatus[idx] = codec2.api.rx_sync_flags_to_text[ datac_rxstatus[idx] = codec2.api.rx_sync_flags_to_text[
@ -206,7 +206,7 @@ def test_mm_rx():
print("TIMEOUT REACHED", file=sys.stderr) print("TIMEOUT REACHED", file=sys.stderr)
print( print(
f"DATAC0: {rx_bursts_datac[0]}/{rx_total_frames_datac[0]} " f"DATAC13: {rx_bursts_datac[0]}/{rx_total_frames_datac[0]} "
f"DATAC1: {rx_bursts_datac[1]}/{rx_total_frames_datac[1]} " f"DATAC1: {rx_bursts_datac[1]}/{rx_total_frames_datac[1]} "
f"DATAC3: {rx_bursts_datac[2]}/{rx_total_frames_datac[2]}", f"DATAC3: {rx_bursts_datac[2]}/{rx_total_frames_datac[2]}",
file=sys.stderr, file=sys.stderr,
@ -241,7 +241,7 @@ def parse_arguments():
parser.add_argument( parser.add_argument(
"--timeout", "--timeout",
dest="TIMEOUT", dest="TIMEOUT",
default=10, default=60,
type=int, type=int,
help="Timeout (seconds) before test ends", help="Timeout (seconds) before test ends",
) )

View file

@ -49,9 +49,9 @@ def test_mm_tx():
data_out = b"HELLO WORLD!" data_out = b"HELLO WORLD!"
modes = [ modes = [
codec2.api.FREEDV_MODE_DATAC0, codec2.FREEDV_MODE.datac13.value,
codec2.api.FREEDV_MODE_DATAC1, codec2.FREEDV_MODE.datac1.value,
codec2.api.FREEDV_MODE_DATAC3, codec2.FREEDV_MODE.datac3.value,
] ]
if AUDIO_OUTPUT_DEVICE != -1: if AUDIO_OUTPUT_DEVICE != -1:

View file

@ -221,7 +221,7 @@ def parse_arguments():
"--framesperburst", dest="N_FRAMES_PER_BURST", default=1, type=int "--framesperburst", dest="N_FRAMES_PER_BURST", default=1, type=int
) )
parser.add_argument( parser.add_argument(
"--mode", dest="FREEDV_MODE", type=str, choices=["datac0", "datac1", "datac3"] "--mode", dest="FREEDV_MODE", type=str, choices=["datac13", "datac1", "datac3"]
) )
parser.add_argument( parser.add_argument(
"--audiodev", "--audiodev",
@ -234,7 +234,7 @@ def parse_arguments():
parser.add_argument( parser.add_argument(
"--timeout", "--timeout",
dest="TIMEOUT", dest="TIMEOUT",
default=10, default=60,
type=int, type=int,
help="Timeout (seconds) before test ends", help="Timeout (seconds) before test ends",
) )

View file

@ -122,18 +122,18 @@ def t_arq_iss(*args):
else: else:
assert not MESSAGE, f"{MESSAGE} not known to test." assert not MESSAGE, f"{MESSAGE} not known to test."
time.sleep(0.5) time.sleep(2.5)
sock.ThreadedTCPRequestHandler.process_tnc_commands(None,json.dumps(data, indent=None)) sock.ThreadedTCPRequestHandler.process_tnc_commands(None,json.dumps(data, indent=None))
sock.ThreadedTCPRequestHandler.process_tnc_commands(None,json.dumps(data, indent=None)) sock.ThreadedTCPRequestHandler.process_tnc_commands(None,json.dumps(data, indent=None))
sock.ThreadedTCPRequestHandler.process_tnc_commands(None,json.dumps(data, indent=None)) sock.ThreadedTCPRequestHandler.process_tnc_commands(None,json.dumps(data, indent=None))
time.sleep(1.5) time.sleep(7.5)
data = {"type": "arq", "command": "stop_transmission"} data = {"type": "arq", "command": "stop_transmission"}
sock.ThreadedTCPRequestHandler.process_tnc_commands(None,json.dumps(data, indent=None)) sock.ThreadedTCPRequestHandler.process_tnc_commands(None,json.dumps(data, indent=None))
time.sleep(0.5) time.sleep(2.5)
sock.ThreadedTCPRequestHandler.process_tnc_commands(None,json.dumps(data, indent=None)) sock.ThreadedTCPRequestHandler.process_tnc_commands(None,json.dumps(data, indent=None))
# Set timeout # Set timeout

View file

@ -198,7 +198,7 @@ def parse_arguments():
help="delay between bursts in ms", help="delay between bursts in ms",
) )
parser.add_argument( parser.add_argument(
"--mode", dest="FREEDV_MODE", type=str, choices=["datac0", "datac1", "datac3"] "--mode", dest="FREEDV_MODE", type=str, choices=["datac13", "datac1", "datac3"]
) )
parser.add_argument( parser.add_argument(
"--audiodev", "--audiodev",

View file

@ -18,20 +18,38 @@ import structlog
log = structlog.get_logger("codec2") log = structlog.get_logger("codec2")
# Enum for codec2 modes # Enum for codec2 modes
class FREEDV_MODE(Enum): class FREEDV_MODE(Enum):
""" """
Enumeration for codec2 modes and names Enumeration for codec2 modes and names
""" """
sig0 = 14 sig0 = 19
sig1 = 14 sig1 = 19
datac0 = 14 datac0 = 14
datac1 = 10 datac1 = 10
datac3 = 12 datac3 = 12
datac4 = 18
datac13 = 19
fsk_ldpc = 9 fsk_ldpc = 9
fsk_ldpc_0 = 200 fsk_ldpc_0 = 200
fsk_ldpc_1 = 201 fsk_ldpc_1 = 201
class FREEDV_MODE_USED_SLOTS(Enum):
"""
Enumeration for codec2 used slots
"""
sig0 = [False, False, True, False, False]
sig1 = [False, False, True, False, False]
datac0 = [False, False, True, False, False]
datac1 = [False, True, True, True, False]
datac3 = [False, False, True, False, False]
datac4 = [False, False, True, False, False]
datac13 = [False, False, True, False, False]
fsk_ldpc = [False, False, True, False, False]
fsk_ldpc_0 = [False, False, True, False, False]
fsk_ldpc_1 = [False, False, True, False, False]
# Function for returning the mode value # Function for returning the mode value
def freedv_get_mode_value_by_name(mode: str) -> int: def freedv_get_mode_value_by_name(mode: str) -> int:
""" """
@ -101,6 +119,9 @@ if api is None or "api" not in locals():
api.freedv_open.argype = [ctypes.c_int] # type: ignore api.freedv_open.argype = [ctypes.c_int] # type: ignore
api.freedv_open.restype = ctypes.c_void_p api.freedv_open.restype = ctypes.c_void_p
api.freedv_set_sync.argype = [ctypes.c_void_p, ctypes.c_int] # type: ignore
api.freedv_set_sync.restype = ctypes.c_void_p
api.freedv_open_advanced.argtype = [ctypes.c_int, ctypes.c_void_p] # type: ignore api.freedv_open_advanced.argtype = [ctypes.c_int, ctypes.c_void_p] # type: ignore
api.freedv_open_advanced.restype = ctypes.c_void_p api.freedv_open_advanced.restype = ctypes.c_void_p
@ -150,10 +171,6 @@ api.freedv_get_n_max_modem_samples.argtype = [ctypes.c_void_p] # type: ignore
api.freedv_get_n_max_modem_samples.restype = ctypes.c_int api.freedv_get_n_max_modem_samples.restype = ctypes.c_int
api.FREEDV_FS_8000 = 8000 # type: ignore api.FREEDV_FS_8000 = 8000 # type: ignore
api.FREEDV_MODE_DATAC1 = 10 # type: ignore
api.FREEDV_MODE_DATAC3 = 12 # type: ignore
api.FREEDV_MODE_DATAC0 = 14 # type: ignore
api.FREEDV_MODE_FSK_LDPC = 9 # type: ignore
# -------------------------------- FSK LDPC MODE SETTINGS # -------------------------------- FSK LDPC MODE SETTINGS
@ -216,7 +233,7 @@ api.FREEDV_MODE_FSK_LDPC_1_ADV.codename = "H_4096_8192_3d".encode("utf-8") # co
# ------- MODEM STATS STRUCTURES # ------- MODEM STATS STRUCTURES
MODEM_STATS_NC_MAX = 50 + 1 * 2 MODEM_STATS_NC_MAX = 50 + 1 * 2
MODEM_STATS_NR_MAX = 160 * 2 MODEM_STATS_NR_MAX = 320 * 2
MODEM_STATS_ET_MAX = 8 MODEM_STATS_ET_MAX = 8
MODEM_STATS_EYE_IND_MAX = 160 MODEM_STATS_EYE_IND_MAX = 160
MODEM_STATS_NSPEC = 512 MODEM_STATS_NSPEC = 512

View file

@ -4,20 +4,20 @@ tncport = 3000
[STATION] [STATION]
#station settings #station settings
mycall = DJ2LS-9 mycall = DN2LS-0
mygrid = JN12AA mygrid = JN48cs
ssid_list = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9] ssid_list = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
[AUDIO] [AUDIO]
#audio settings #audio settings
rx = 0 rx = 0
tx = 1 tx = 0
txaudiolevel = 78 txaudiolevel = 250
auto_tune = False auto_tune = False
[RADIO] [RADIO]
#radio settings #radio settings
radiocontrol = rigctld radiocontrol = disabled
rigctld_ip = 127.0.0.1 rigctld_ip = 127.0.0.1
rigctld_port = 4532 rigctld_port = 4532
@ -26,12 +26,12 @@ rigctld_port = 4532
scatter = True scatter = True
fft = True fft = True
narrowband = False narrowband = False
fmin = -250.0 fmin = -150.0
fmax = 250.0 fmax = 150.0
qrv = True qrv = True
rxbuffersize = 16 rxbuffersize = 16
explorer = False explorer = False
stats = False stats = False
fsk = False fsk = False
tx_delay = 0 tx_delay = 800

View file

@ -26,7 +26,8 @@ import crcengine
import log_handler import log_handler
import serial.tools.list_ports import serial.tools.list_ports
import sock import sock
import static from static import ARQ, AudioParam, Beacon, Channel, Daemon, HamlibParam, ModemParam, Station, Statistics, TCIParam, TNC
import structlog import structlog
import ujson as json import ujson as json
import config import config
@ -82,10 +83,10 @@ class DAEMON:
""" """
while True: while True:
try: try:
if not static.TNCSTARTED: if not Daemon.tncstarted:
( (
static.AUDIO_INPUT_DEVICES, AudioParam.audio_input_devices,
static.AUDIO_OUTPUT_DEVICES, AudioParam.audio_output_devices,
) = audio.get_audio_devices() ) = audio.get_audio_devices()
except Exception as err1: except Exception as err1:
self.log.error( self.log.error(
@ -112,7 +113,7 @@ class DAEMON:
{"port": str(port), "description": str(description)} {"port": str(port), "description": str(description)}
) )
static.SERIAL_DEVICES = serial_devices Daemon.serial_devices = serial_devices
threading.Event().wait(1) threading.Event().wait(1)
except Exception as err1: except Exception as err1:
self.log.error( self.log.error(
@ -210,10 +211,10 @@ class DAEMON:
self.log.warning("[DMN] Starting TNC", rig=data[5], port=data[6]) self.log.warning("[DMN] Starting TNC", rig=data[5], port=data[6])
# list of parameters, necessary for running subprocess command as a list # list of parameters, necessary for running subprocess command as a list
options = ["--port", str(static.DAEMONPORT - 1)] options = ["--port", str(DAEMON.port - 1)]
# create an additional list entry for parameters not covered by gui # create an additional list entry for parameters not covered by gui
data[50] = int(static.DAEMONPORT - 1) data[50] = int(DAEMON.port - 1)
options.append("--mycall") options.append("--mycall")
options.extend((data[1], "--mygrid")) options.extend((data[1], "--mygrid"))
@ -317,8 +318,8 @@ class DAEMON:
self.log.info("[DMN] TNC started", path="source") self.log.info("[DMN] TNC started", path="source")
static.TNCPROCESS = proc Daemon.tncprocess = proc
static.TNCSTARTED = True Daemon.tncstarted = True
if __name__ == "__main__": if __name__ == "__main__":
mainlog = structlog.get_logger(__file__) mainlog = structlog.get_logger(__file__)
# we need to run this on Windows for multiprocessing support # we need to run this on Windows for multiprocessing support
@ -335,7 +336,7 @@ if __name__ == "__main__":
) )
ARGS = PARSER.parse_args() ARGS = PARSER.parse_args()
static.DAEMONPORT = ARGS.socket_port DAEMON.port = ARGS.socket_port
try: try:
if sys.platform == "linux": if sys.platform == "linux":
@ -363,11 +364,11 @@ if __name__ == "__main__":
config = config.CONFIG("config.ini") config = config.CONFIG("config.ini")
try: try:
mainlog.info("[DMN] Starting TCP/IP socket", port=static.DAEMONPORT) mainlog.info("[DMN] Starting TCP/IP socket", port=DAEMON.port)
# https://stackoverflow.com/a/16641793 # https://stackoverflow.com/a/16641793
socketserver.TCPServer.allow_reuse_address = True socketserver.TCPServer.allow_reuse_address = True
cmdserver = sock.ThreadedTCPServer( cmdserver = sock.ThreadedTCPServer(
(static.HOST, static.DAEMONPORT), sock.ThreadedTCPRequestHandler (TNC.host, DAEMON.port), sock.ThreadedTCPRequestHandler
) )
server_thread = threading.Thread(target=cmdserver.serve_forever) server_thread = threading.Thread(target=cmdserver.serve_forever)
server_thread.daemon = True server_thread.daemon = True
@ -375,7 +376,7 @@ if __name__ == "__main__":
except Exception as err: except Exception as err:
mainlog.error( mainlog.error(
"[DMN] Starting TCP/IP socket failed", port=static.DAEMONPORT, e=err "[DMN] Starting TCP/IP socket failed", port=DAEMON.port, e=err
) )
sys.exit(1) sys.exit(1)
daemon = DAEMON() daemon = DAEMON()
@ -384,7 +385,7 @@ if __name__ == "__main__":
"[DMN] Starting FreeDATA Daemon", "[DMN] Starting FreeDATA Daemon",
author="DJ2LS", author="DJ2LS",
year="2023", year="2023",
version=static.VERSION, version=TNC.version,
) )
while True: while True:
threading.Event().wait(1) threading.Event().wait(1)

File diff suppressed because it is too large Load diff

View file

@ -13,6 +13,8 @@ import time
import ujson as json import ujson as json
import structlog import structlog
import static import static
from static import ARQ, AudioParam, Beacon, Channel, Daemon, HamlibParam, ModemParam, Station, Statistics, TCIParam, TNC
log = structlog.get_logger("explorer") log = structlog.get_logger("explorer")
@ -32,30 +34,31 @@ class explorer():
def push(self): def push(self):
frequency = 0 if static.HAMLIB_FREQUENCY is None else static.HAMLIB_FREQUENCY frequency = 0 if HamlibParam.hamlib_frequency is None else HamlibParam.hamlib_frequency
band = "USB" band = "USB"
callsign = str(static.MYCALLSIGN, "utf-8") callsign = str(Station.mycallsign, "utf-8")
gridsquare = str(static.MYGRID, "utf-8") gridsquare = str(Station.mygrid, "utf-8")
version = str(static.VERSION) version = str(TNC.version)
bandwidth = str(static.LOW_BANDWIDTH_MODE) bandwidth = str(TNC.low_bandwidth_mode)
beacon = str(static.BEACON_STATE) beacon = str(Beacon.beacon_state)
strength = str(static.HAMLIB_STRENGTH) strength = str(HamlibParam.hamlib_strength)
log.info("[EXPLORER] publish", frequency=frequency, band=band, callsign=callsign, gridsquare=gridsquare, version=version, bandwidth=bandwidth) log.info("[EXPLORER] publish", frequency=frequency, band=band, callsign=callsign, gridsquare=gridsquare, version=version, bandwidth=bandwidth)
headers = {"Content-Type": "application/json"} headers = {"Content-Type": "application/json"}
station_data = {'callsign': callsign, 'gridsquare': gridsquare, 'frequency': frequency, 'strength': strength, 'band': band, 'version': version, 'bandwidth': bandwidth, 'beacon': beacon, "lastheard": []} station_data = {'callsign': callsign, 'gridsquare': gridsquare, 'frequency': frequency, 'strength': strength, 'band': band, 'version': version, 'bandwidth': bandwidth, 'beacon': beacon, "lastheard": []}
for i in static.HEARD_STATIONS: for i in TNC.heard_stations:
try: try:
callsign = str(i[0], "UTF-8") callsign = str(i[0], "UTF-8")
grid = str(i[1], "UTF-8") grid = str(i[1], "UTF-8")
timestamp = i[2] timestamp = i[2]
frequency = i[6]
try: try:
snr = i[4].split("/")[1] snr = i[4].split("/")[1]
except AttributeError: except AttributeError:
snr = str(i[4]) snr = str(i[4])
station_data["lastheard"].append({"callsign": callsign, "grid": grid, "snr": snr, "timestamp": timestamp}) station_data["lastheard"].append({"callsign": callsign, "grid": grid, "snr": snr, "timestamp": timestamp, "frequency": frequency})
except Exception as e: except Exception as e:
log.debug("[EXPLORER] not publishing station", e=e) log.debug("[EXPLORER] not publishing station", e=e)

View file

@ -8,6 +8,7 @@ import time
from datetime import datetime,timezone from datetime import datetime,timezone
import crcengine import crcengine
import static import static
from static import ARQ, AudioParam, Beacon, Channel, Daemon, HamlibParam, ModemParam, Station, Statistics, TCIParam, TNC
import structlog import structlog
import numpy as np import numpy as np
import threading import threading
@ -130,16 +131,16 @@ def add_to_heard_stations(dxcallsign, dxgrid, datatype, snr, offset, frequency):
Nothing Nothing
""" """
# check if buffer empty # check if buffer empty
if len(static.HEARD_STATIONS) == 0: if len(TNC.heard_stations) == 0:
static.HEARD_STATIONS.append( TNC.heard_stations.append(
[dxcallsign, dxgrid, int(datetime.now(timezone.utc).timestamp()), datatype, snr, offset, frequency] [dxcallsign, dxgrid, int(datetime.now(timezone.utc).timestamp()), datatype, snr, offset, frequency]
) )
# if not, we search and update # if not, we search and update
else: else:
for i in range(len(static.HEARD_STATIONS)): for i in range(len(TNC.heard_stations)):
# Update callsign with new timestamp # Update callsign with new timestamp
if static.HEARD_STATIONS[i].count(dxcallsign) > 0: if TNC.heard_stations[i].count(dxcallsign) > 0:
static.HEARD_STATIONS[i] = [ TNC.heard_stations[i] = [
dxcallsign, dxcallsign,
dxgrid, dxgrid,
int(time.time()), int(time.time()),
@ -150,8 +151,8 @@ def add_to_heard_stations(dxcallsign, dxgrid, datatype, snr, offset, frequency):
] ]
break break
# Insert if nothing found # Insert if nothing found
if i == len(static.HEARD_STATIONS) - 1: if i == len(TNC.heard_stations) - 1:
static.HEARD_STATIONS.append( TNC.heard_stations.append(
[ [
dxcallsign, dxcallsign,
dxgrid, dxgrid,
@ -165,10 +166,10 @@ def add_to_heard_stations(dxcallsign, dxgrid, datatype, snr, offset, frequency):
break break
# for idx, item in enumerate(static.HEARD_STATIONS): # for idx, item in enumerate(TNC.heard_stations):
# if dxcallsign in item: # if dxcallsign in item:
# item = [dxcallsign, int(time.time())] # item = [dxcallsign, int(time.time())]
# static.HEARD_STATIONS[idx] = item # TNC.heard_stations[idx] = item
def callsign_to_bytes(callsign) -> bytes: def callsign_to_bytes(callsign) -> bytes:
@ -305,7 +306,7 @@ def check_callsign(callsign: bytes, crc_to_check: bytes):
except Exception as err: except Exception as err:
log.debug("[HLP] check_callsign: Error callsign SSID to integer:", e=err) log.debug("[HLP] check_callsign: Error callsign SSID to integer:", e=err)
for ssid in static.SSID_LIST: for ssid in Station.ssid_list:
call_with_ssid = bytearray(callsign) call_with_ssid = bytearray(callsign)
call_with_ssid.extend("-".encode("utf-8")) call_with_ssid.extend("-".encode("utf-8"))
call_with_ssid.extend(str(ssid).encode("utf-8")) call_with_ssid.extend(str(ssid).encode("utf-8"))

View file

@ -29,6 +29,7 @@ import helpers
import log_handler import log_handler
import modem import modem
import static import static
from static import ARQ, AudioParam, Beacon, Channel, Daemon, HamlibParam, ModemParam, Station, Statistics, TCIParam, TNC
import structlog import structlog
import explorer import explorer
import json import json
@ -255,7 +256,7 @@ if __name__ == "__main__":
ARGS = PARSER.parse_args() ARGS = PARSER.parse_args()
# set save to folder state for allowing downloading files to local file system # set save to folder state for allowing downloading files to local file system
static.ARQ_SAVE_TO_FOLDER = ARGS.savetofolder ARQ.arq_save_to_folder = ARGS.savetofolder
if not ARGS.configfile: if not ARGS.configfile:
@ -266,46 +267,46 @@ if __name__ == "__main__":
try: try:
mycallsign = bytes(ARGS.mycall.upper(), "utf-8") mycallsign = bytes(ARGS.mycall.upper(), "utf-8")
mycallsign = helpers.callsign_to_bytes(mycallsign) mycallsign = helpers.callsign_to_bytes(mycallsign)
static.MYCALLSIGN = helpers.bytes_to_callsign(mycallsign) Station.mycallsign = helpers.bytes_to_callsign(mycallsign)
static.MYCALLSIGN_CRC = helpers.get_crc_24(static.MYCALLSIGN) Station.mycallsign_crc = helpers.get_crc_24(Station.mycallsign)
static.SSID_LIST = ARGS.ssid_list Station.ssid_list = ARGS.ssid_list
# check if own ssid is always part of ssid list # check if own ssid is always part of ssid list
own_ssid = int(static.MYCALLSIGN.split(b"-")[1]) own_ssid = int(Station.mycallsign.split(b"-")[1])
if own_ssid not in static.SSID_LIST: if own_ssid not in Station.ssid_list:
static.SSID_LIST.append(own_ssid) Station.ssid_list.append(own_ssid)
static.MYGRID = bytes(ARGS.mygrid, "utf-8") Station.mygrid = bytes(ARGS.mygrid, "utf-8")
# check if we have an int or str as device name # check if we have an int or str as device name
try: try:
static.AUDIO_INPUT_DEVICE = int(ARGS.audio_input_device) AudioParam.audio_input_device = int(ARGS.audio_input_device)
except ValueError: except ValueError:
static.AUDIO_INPUT_DEVICE = ARGS.audio_input_device AudioParam.audio_input_device = ARGS.audio_input_device
try: try:
static.AUDIO_OUTPUT_DEVICE = int(ARGS.audio_output_device) AudioParam.audio_output_device = int(ARGS.audio_output_device)
except ValueError: except ValueError:
static.AUDIO_OUTPUT_DEVICE = ARGS.audio_output_device AudioParam.audio_output_device = ARGS.audio_output_device
static.PORT = ARGS.socket_port TNC.port = ARGS.socket_port
static.HAMLIB_RADIOCONTROL = ARGS.hamlib_radiocontrol HamlibParam.hamlib_radiocontrol = ARGS.hamlib_radiocontrol
static.HAMLIB_RIGCTLD_IP = ARGS.rigctld_ip HamlibParam.hamlib_rigctld_ip = ARGS.rigctld_ip
static.HAMLIB_RIGCTLD_PORT = str(ARGS.rigctld_port) HamlibParam.hamlib_rigctld_port = str(ARGS.rigctld_port)
static.ENABLE_SCATTER = ARGS.send_scatter ModemParam.enable_scatter = ARGS.send_scatter
static.ENABLE_FFT = ARGS.send_fft AudioParam.enable_fft = ARGS.send_fft
static.ENABLE_FSK = ARGS.enable_fsk TNC.enable_fsk = ARGS.enable_fsk
static.LOW_BANDWIDTH_MODE = ARGS.low_bandwidth_mode TNC.low_bandwidth_mode = ARGS.low_bandwidth_mode
static.TUNING_RANGE_FMIN = ARGS.tuning_range_fmin ModemParam.tuning_range_fmin = ARGS.tuning_range_fmin
static.TUNING_RANGE_FMAX = ARGS.tuning_range_fmax ModemParam.tuning_range_fmax = ARGS.tuning_range_fmax
static.TX_AUDIO_LEVEL = ARGS.tx_audio_level AudioParam.tx_audio_level = ARGS.tx_audio_level
static.RESPOND_TO_CQ = ARGS.enable_respond_to_cq TNC.respond_to_cq = ARGS.enable_respond_to_cq
static.RX_BUFFER_SIZE = ARGS.rx_buffer_size ARQ.rx_buffer_size = ARGS.rx_buffer_size
static.ENABLE_EXPLORER = ARGS.enable_explorer TNC.enable_explorer = ARGS.enable_explorer
static.AUDIO_AUTO_TUNE = ARGS.enable_audio_auto_tune AudioParam.audio_auto_tune = ARGS.enable_audio_auto_tune
static.ENABLE_STATS = ARGS.enable_stats TNC.enable_stats = ARGS.enable_stats
static.AUDIO_ENABLE_TCI = ARGS.audio_enable_tci AudioParam.audio_enable_tci = ARGS.audio_enable_tci
static.TCI_IP = ARGS.tci_ip TCIParam.ip = ARGS.tci_ip
static.TCI_PORT = ARGS.tci_port TCIParam.port = ARGS.tci_port
static.TX_DELAY = ARGS.tx_delay ModemParam.tx_delay = ARGS.tx_delay
except Exception as e: except Exception as e:
log.error("[DMN] Error reading config file", exception=e) log.error("[DMN] Error reading config file", exception=e)
@ -320,52 +321,52 @@ if __name__ == "__main__":
# then we are forcing a station ssid = 0 # then we are forcing a station ssid = 0
mycallsign = bytes(conf.get('STATION', 'mycall', 'AA0AA'), "utf-8") mycallsign = bytes(conf.get('STATION', 'mycall', 'AA0AA'), "utf-8")
mycallsign = helpers.callsign_to_bytes(mycallsign) mycallsign = helpers.callsign_to_bytes(mycallsign)
static.MYCALLSIGN = helpers.bytes_to_callsign(mycallsign) Station.mycallsign = helpers.bytes_to_callsign(mycallsign)
static.MYCALLSIGN_CRC = helpers.get_crc_24(static.MYCALLSIGN) Station.mycallsign_crc = helpers.get_crc_24(Station.mycallsign)
#json.loads = for converting str list to list #json.loads = for converting str list to list
static.SSID_LIST = json.loads(conf.get('STATION', 'ssid_list', '[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]')) Station.ssid_list = json.loads(conf.get('STATION', 'ssid_list', '[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]'))
static.MYGRID = bytes(conf.get('STATION', 'mygrid', 'JN12aa'), "utf-8") Station.mygrid = bytes(conf.get('STATION', 'mygrid', 'JN12aa'), "utf-8")
# check if we have an int or str as device name # check if we have an int or str as device name
try: try:
static.AUDIO_INPUT_DEVICE = int(conf.get('AUDIO', 'rx', '0')) AudioParam.audio_input_device = int(conf.get('AUDIO', 'rx', '0'))
except ValueError: except ValueError:
static.AUDIO_INPUT_DEVICE = conf.get('AUDIO', 'rx', '0') AudioParam.audio_input_device = conf.get('AUDIO', 'rx', '0')
try: try:
static.AUDIO_OUTPUT_DEVICE = int(conf.get('AUDIO', 'tx', '0')) AudioParam.audio_output_device = int(conf.get('AUDIO', 'tx', '0'))
except ValueError: except ValueError:
static.AUDIO_OUTPUT_DEVICE = conf.get('AUDIO', 'tx', '0') AudioParam.audio_output_device = conf.get('AUDIO', 'tx', '0')
static.PORT = int(conf.get('NETWORK', 'tncport', '3000')) TNC.port = int(conf.get('NETWORK', 'tncport', '3000'))
static.HAMLIB_RADIOCONTROL = conf.get('RADIO', 'radiocontrol', 'rigctld') HamlibParam.hamlib_radiocontrol = conf.get('RADIO', 'radiocontrol', 'rigctld')
static.HAMLIB_RIGCTLD_IP = conf.get('RADIO', 'rigctld_ip', '127.0.0.1') HamlibParam.hamlib_rigctld_ip = conf.get('RADIO', 'rigctld_ip', '127.0.0.1')
static.HAMLIB_RIGCTLD_PORT = str(conf.get('RADIO', 'rigctld_port', '4532')) HamlibParam.hamlib_rigctld_port = str(conf.get('RADIO', 'rigctld_port', '4532'))
static.ENABLE_SCATTER = conf.get('TNC', 'scatter', 'True') ModemParam.enable_scatter = conf.get('TNC', 'scatter', 'True')
static.ENABLE_FFT = conf.get('TNC', 'fft', 'True') AudioParam.enable_fft = conf.get('TNC', 'fft', 'True')
static.ENABLE_FSK = conf.get('TNC', 'fsk', 'False') TNC.enable_fsk = conf.get('TNC', 'fsk', 'False')
static.LOW_BANDWIDTH_MODE = conf.get('TNC', 'narrowband', 'False') TNC.low_bandwidth_mode = conf.get('TNC', 'narrowband', 'False')
static.TUNING_RANGE_FMIN = float(conf.get('TNC', 'fmin', '-50.0')) ModemParam.tuning_range_fmin = float(conf.get('TNC', 'fmin', '-50.0'))
static.TUNING_RANGE_FMAX = float(conf.get('TNC', 'fmax', '50.0')) ModemParam.tuning_range_fmax = float(conf.get('TNC', 'fmax', '50.0'))
static.TX_AUDIO_LEVEL = int(conf.get('AUDIO', 'txaudiolevel', '100')) AudioParam.tx_audio_level = int(conf.get('AUDIO', 'txaudiolevel', '100'))
static.RESPOND_TO_CQ = conf.get('TNC', 'qrv', 'True') TNC.respond_to_cq = conf.get('TNC', 'qrv', 'True')
static.RX_BUFFER_SIZE = int(conf.get('TNC', 'rxbuffersize', '16')) ARQ.rx_buffer_size = int(conf.get('TNC', 'rxbuffersize', '16'))
static.ENABLE_EXPLORER = conf.get('TNC', 'explorer', 'False') TNC.enable_explorer = conf.get('TNC', 'explorer', 'False')
static.AUDIO_AUTO_TUNE = conf.get('AUDIO', 'auto_tune', 'False') AudioParam.audio_auto_tune = conf.get('AUDIO', 'auto_tune', 'False')
static.ENABLE_STATS = conf.get('TNC', 'stats', 'False') TNC.enable_stats = conf.get('TNC', 'stats', 'False')
static.AUDIO_ENABLE_TCI = conf.get('AUDIO', 'enable_tci', 'False') AudioParam.audio_enable_tci = conf.get('AUDIO', 'enable_tci', 'False')
static.TCI_IP = str(conf.get('AUDIO', 'tci_ip', 'localhost')) TCIParam.ip = str(conf.get('AUDIO', 'tci_ip', 'localhost'))
static.TCI_PORT = int(conf.get('AUDIO', 'tci_port', '50001')) TCIParam.port = int(conf.get('AUDIO', 'tci_port', '50001'))
static.TX_DELAY = int(conf.get('TNC', 'tx_delay', '0')) ModemParam.tx_delay = int(conf.get('TNC', 'tx_delay', '0'))
except KeyError as e: except KeyError as e:
log.warning("[CFG] Error reading config file near", key=str(e)) log.warning("[CFG] Error reading config file near", key=str(e))
except Exception as e: except Exception as e:
log.warning("[CFG] Error", e=e) log.warning("[CFG] Error", e=e)
# make sure the own ssid is always part of the ssid list # make sure the own ssid is always part of the ssid list
my_ssid = int(static.MYCALLSIGN.split(b'-')[1]) my_ssid = int(Station.mycallsign.split(b'-')[1])
if my_ssid not in static.SSID_LIST: if my_ssid not in Station.ssid_list:
static.SSID_LIST.append(my_ssid) Station.ssid_list.append(my_ssid)
# we need to wait until we got all parameters from argparse first before we can load the other modules # we need to wait until we got all parameters from argparse first before we can load the other modules
import sock import sock
@ -394,7 +395,7 @@ if __name__ == "__main__":
log.error("[DMN] logger init error", exception=err) log.error("[DMN] logger init error", exception=err)
log.info( log.info(
"[TNC] Starting FreeDATA", author="DJ2LS", version=static.VERSION "[TNC] Starting FreeDATA", author="DJ2LS", version=TNC.version
) )
# start data handler # start data handler
@ -404,17 +405,17 @@ if __name__ == "__main__":
modem = modem.RF() modem = modem.RF()
# optionally start explorer module # optionally start explorer module
if static.ENABLE_EXPLORER: if TNC.enable_explorer:
log.info("[EXPLORER] Publishing to https://explorer.freedata.app", state=static.ENABLE_EXPLORER) log.info("[EXPLORER] Publishing to https://explorer.freedata.app", state=TNC.enable_explorer)
explorer = explorer.explorer() explorer = explorer.explorer()
# --------------------------------------------START CMD SERVER # --------------------------------------------START CMD SERVER
try: try:
log.info("[TNC] Starting TCP/IP socket", port=static.PORT) log.info("[TNC] Starting TCP/IP socket", port=TNC.port)
# https://stackoverflow.com/a/16641793 # https://stackoverflow.com/a/16641793
socketserver.TCPServer.allow_reuse_address = True socketserver.TCPServer.allow_reuse_address = True
cmdserver = sock.ThreadedTCPServer( cmdserver = sock.ThreadedTCPServer(
(static.HOST, static.PORT), sock.ThreadedTCPRequestHandler (TNC.host, TNC.port), sock.ThreadedTCPRequestHandler
) )
server_thread = threading.Thread(target=cmdserver.serve_forever) server_thread = threading.Thread(target=cmdserver.serve_forever)
@ -422,7 +423,7 @@ if __name__ == "__main__":
server_thread.start() server_thread.start()
except Exception as err: except Exception as err:
log.error("[TNC] Starting TCP/IP socket failed", port=static.PORT, e=err) log.error("[TNC] Starting TCP/IP socket failed", port=TNC.port, e=err)
sys.exit(1) sys.exit(1)
while True: while True:
threading.Event().wait(1) threading.Event().wait(1)

View file

@ -23,6 +23,8 @@ import numpy as np
import sock import sock
import sounddevice as sd import sounddevice as sd
import static import static
from static import ARQ, AudioParam, Beacon, Channel, Daemon, HamlibParam, ModemParam, Station, Statistics, TCIParam, TNC
from static import FRAME_TYPE
import structlog import structlog
import ujson as json import ujson as json
import tci import tci
@ -33,19 +35,24 @@ TESTMODE = False
RXCHANNEL = "" RXCHANNEL = ""
TXCHANNEL = "" TXCHANNEL = ""
static.TRANSMITTING = False TNC.transmitting = False
# Receive only specific modes to reduce CPU load # Receive only specific modes to reduce CPU load
RECEIVE_SIG0 = True RECEIVE_SIG0 = True
RECEIVE_SIG1 = False RECEIVE_SIG1 = False
RECEIVE_DATAC1 = False RECEIVE_DATAC1 = False
RECEIVE_DATAC3 = False RECEIVE_DATAC3 = False
RECEIVE_DATAC4 = False
# state buffer # state buffer
SIG0_DATAC0_STATE = []
SIG1_DATAC0_STATE = [] SIG0_DATAC13_STATE = []
SIG1_DATAC13_STATE = []
DAT0_DATAC1_STATE = [] DAT0_DATAC1_STATE = []
DAT0_DATAC3_STATE = [] DAT0_DATAC3_STATE = []
DAT0_DATAC4_STATE = []
FSK_LDPC0_STATE = [] FSK_LDPC0_STATE = []
FSK_LDPC1_STATE = [] FSK_LDPC1_STATE = []
@ -66,7 +73,7 @@ class RF:
self.AUDIO_FRAMES_PER_BUFFER_RX = 2400 * 2 # 8192 self.AUDIO_FRAMES_PER_BUFFER_RX = 2400 * 2 # 8192
# 8192 Let's do some tests with very small chunks for TX # 8192 Let's do some tests with very small chunks for TX
self.AUDIO_FRAMES_PER_BUFFER_TX = 1200 if static.AUDIO_ENABLE_TCI else 2400 * 2 self.AUDIO_FRAMES_PER_BUFFER_TX = 1200 if AudioParam.audio_enable_tci else 2400 * 2
# 8 * (self.AUDIO_SAMPLE_RATE_RX/self.MODEM_SAMPLE_RATE) == 48 # 8 * (self.AUDIO_SAMPLE_RATE_RX/self.MODEM_SAMPLE_RATE) == 48
self.AUDIO_CHANNELS = 1 self.AUDIO_CHANNELS = 1
self.MODE = 0 self.MODE = 0
@ -79,9 +86,7 @@ class RF:
# Make sure our resampler will work # Make sure our resampler will work
assert (self.AUDIO_SAMPLE_RATE_RX / self.MODEM_SAMPLE_RATE) == codec2.api.FDMDV_OS_48 # type: ignore assert (self.AUDIO_SAMPLE_RATE_RX / self.MODEM_SAMPLE_RATE) == codec2.api.FDMDV_OS_48 # type: ignore
# Small hack for initializing codec2 via codec2.py module # init codec2 resampler
# TODO: Need to change the entire modem module to integrate codec2 module
self.c_lib = codec2.api
self.resampler = codec2.resampler() self.resampler = codec2.resampler()
self.modem_transmit_queue = MODEM_TRANSMIT_QUEUE self.modem_transmit_queue = MODEM_TRANSMIT_QUEUE
@ -98,23 +103,23 @@ class RF:
# Open codec2 instances # Open codec2 instances
# DATAC0 # DATAC13
# SIGNALLING MODE 0 - Used for Connecting - Payload 14 Bytes # SIGNALLING MODE 0 - Used for Connecting - Payload 14 Bytes
self.sig0_datac0_freedv, \ self.sig0_datac13_freedv, \
self.sig0_datac0_bytes_per_frame, \ self.sig0_datac13_bytes_per_frame, \
self.sig0_datac0_bytes_out, \ self.sig0_datac13_bytes_out, \
self.sig0_datac0_buffer, \ self.sig0_datac13_buffer, \
self.sig0_datac0_nin = \ self.sig0_datac13_nin = \
self.init_codec2_mode(codec2.api.FREEDV_MODE_DATAC0, None) self.init_codec2_mode(codec2.FREEDV_MODE.datac13.value, None)
# DATAC0 # DATAC13
# SIGNALLING MODE 1 - Used for ACK/NACK - Payload 5 Bytes # SIGNALLING MODE 1 - Used for ACK/NACK - Payload 5 Bytes
self.sig1_datac0_freedv, \ self.sig1_datac13_freedv, \
self.sig1_datac0_bytes_per_frame, \ self.sig1_datac13_bytes_per_frame, \
self.sig1_datac0_bytes_out, \ self.sig1_datac13_bytes_out, \
self.sig1_datac0_buffer, \ self.sig1_datac13_buffer, \
self.sig1_datac0_nin = \ self.sig1_datac13_nin = \
self.init_codec2_mode(codec2.api.FREEDV_MODE_DATAC0, None) self.init_codec2_mode(codec2.FREEDV_MODE.datac13.value, None)
# DATAC1 # DATAC1
self.dat0_datac1_freedv, \ self.dat0_datac1_freedv, \
@ -122,7 +127,7 @@ class RF:
self.dat0_datac1_bytes_out, \ self.dat0_datac1_bytes_out, \
self.dat0_datac1_buffer, \ self.dat0_datac1_buffer, \
self.dat0_datac1_nin = \ self.dat0_datac1_nin = \
self.init_codec2_mode(codec2.api.FREEDV_MODE_DATAC1, None) self.init_codec2_mode(codec2.FREEDV_MODE.datac1.value, None)
# DATAC3 # DATAC3
self.dat0_datac3_freedv, \ self.dat0_datac3_freedv, \
@ -130,7 +135,16 @@ class RF:
self.dat0_datac3_bytes_out, \ self.dat0_datac3_bytes_out, \
self.dat0_datac3_buffer, \ self.dat0_datac3_buffer, \
self.dat0_datac3_nin = \ self.dat0_datac3_nin = \
self.init_codec2_mode(codec2.api.FREEDV_MODE_DATAC3, None) self.init_codec2_mode(codec2.FREEDV_MODE.datac3.value, None)
# DATAC4
self.dat0_datac4_freedv, \
self.dat0_datac4_bytes_per_frame, \
self.dat0_datac4_bytes_out, \
self.dat0_datac4_buffer, \
self.dat0_datac4_nin = \
self.init_codec2_mode(codec2.FREEDV_MODE.datac4.value, None)
# FSK LDPC - 0 # FSK LDPC - 0
self.fsk_ldpc_freedv_0, \ self.fsk_ldpc_freedv_0, \
@ -139,7 +153,7 @@ class RF:
self.fsk_ldpc_buffer_0, \ self.fsk_ldpc_buffer_0, \
self.fsk_ldpc_nin_0 = \ self.fsk_ldpc_nin_0 = \
self.init_codec2_mode( self.init_codec2_mode(
codec2.api.FREEDV_MODE_FSK_LDPC, codec2.FREEDV_MODE.fsk_ldpc.value,
codec2.api.FREEDV_MODE_FSK_LDPC_0_ADV codec2.api.FREEDV_MODE_FSK_LDPC_0_ADV
) )
@ -150,24 +164,27 @@ class RF:
self.fsk_ldpc_buffer_1, \ self.fsk_ldpc_buffer_1, \
self.fsk_ldpc_nin_1 = \ self.fsk_ldpc_nin_1 = \
self.init_codec2_mode( self.init_codec2_mode(
codec2.api.FREEDV_MODE_FSK_LDPC, codec2.FREEDV_MODE.fsk_ldpc.value,
codec2.api.FREEDV_MODE_FSK_LDPC_1_ADV codec2.api.FREEDV_MODE_FSK_LDPC_1_ADV
) )
# INIT TX MODES # INIT TX MODES - here we need all modes.
self.freedv_datac0_tx = open_codec2_instance(14) self.freedv_datac0_tx = open_codec2_instance(codec2.FREEDV_MODE.datac0.value)
self.freedv_datac1_tx = open_codec2_instance(10) self.freedv_datac1_tx = open_codec2_instance(codec2.FREEDV_MODE.datac1.value)
self.freedv_datac3_tx = open_codec2_instance(12) self.freedv_datac3_tx = open_codec2_instance(codec2.FREEDV_MODE.datac3.value)
self.freedv_ldpc0_tx = open_codec2_instance(200) self.freedv_datac4_tx = open_codec2_instance(codec2.FREEDV_MODE.datac4.value)
self.freedv_ldpc1_tx = open_codec2_instance(201) self.freedv_datac13_tx = open_codec2_instance(codec2.FREEDV_MODE.datac13.value)
# --------------------------------------------CREATE PYAUDIO INSTANCE self.freedv_ldpc0_tx = open_codec2_instance(codec2.FREEDV_MODE.fsk_ldpc_0.value)
if not TESTMODE and not static.AUDIO_ENABLE_TCI: self.freedv_ldpc1_tx = open_codec2_instance(codec2.FREEDV_MODE.fsk_ldpc_1.value)
# --------------------------------------------CREATE PORTAUDIO INSTANCE
if not TESTMODE and not AudioParam.audio_enable_tci:
try: try:
self.stream = sd.RawStream( self.stream = sd.RawStream(
channels=1, channels=1,
dtype="int16", dtype="int16",
callback=self.callback, callback=self.callback,
device=(static.AUDIO_INPUT_DEVICE, static.AUDIO_OUTPUT_DEVICE), device=(AudioParam.audio_input_device, AudioParam.audio_output_device),
samplerate=self.AUDIO_SAMPLE_RATE_RX, samplerate=self.AUDIO_SAMPLE_RATE_RX,
blocksize=4800, blocksize=4800,
) )
@ -179,7 +196,7 @@ class RF:
try: try:
self.log.debug("[MDM] init: starting pyaudio callback") self.log.debug("[MDM] init: starting pyaudio callback")
# self.audio_stream.start_stream() # self.audio_stream.start_stream(
self.stream.start() self.stream.start()
except Exception as err: except Exception as err:
self.log.error("[MDM] init: starting pyaudio callback failed", e=err) self.log.error("[MDM] init: starting pyaudio callback failed", e=err)
@ -187,7 +204,7 @@ class RF:
elif not TESTMODE: elif not TESTMODE:
# placeholder area for processing audio via TCI # placeholder area for processing audio via TCI
# https://github.com/maksimus1210/TCI # https://github.com/maksimus1210/TCI
self.log.warning("[MDM] [TCI] Not yet fully implemented", ip=static.TCI_IP, port=static.TCI_PORT) self.log.warning("[MDM] [TCI] Not yet fully implemented", ip=TCIParam.tci_ip, port=TCIParam.tci_port)
# we are trying this by simulating an audio stream Object like with mkfifo # we are trying this by simulating an audio stream Object like with mkfifo
class Object: class Object:
@ -197,7 +214,7 @@ class RF:
self.stream = Object() self.stream = Object()
# lets init TCI module # lets init TCI module
self.tci_module = tci.TCI() self.tci_module = tci.TCICtrl()
tci_rx_callback_thread = threading.Thread( tci_rx_callback_thread = threading.Thread(
target=self.tci_rx_callback, target=self.tci_rx_callback,
@ -247,35 +264,28 @@ class RF:
# --------------------------------------------INIT AND OPEN HAMLIB # --------------------------------------------INIT AND OPEN HAMLIB
# Check how we want to control the radio # Check how we want to control the radio
# TODO: deprecated feature - we can remove this possibly if HamlibParam.hamlib_radiocontrol == "rigctld":
if static.HAMLIB_RADIOCONTROL == "direct":
print("direct hamlib support deprecated - not usable anymore")
sys.exit(1)
elif static.HAMLIB_RADIOCONTROL == "rigctl":
print("rigctl support deprecated - not usable anymore")
sys.exit(1)
elif static.HAMLIB_RADIOCONTROL == "rigctld":
import rigctld as rig import rigctld as rig
elif static.AUDIO_ENABLE_TCI: elif AudioParam.audio_enable_tci:
self.radio = self.tci_module self.radio = self.tci_module
else: else:
import rigdummy as rig import rigdummy as rig
if not static.AUDIO_ENABLE_TCI: if not AudioParam.audio_enable_tci:
self.radio = rig.radio() self.radio = rig.radio()
self.radio.open_rig( self.radio.open_rig(
rigctld_ip=static.HAMLIB_RIGCTLD_IP, rigctld_ip=HamlibParam.hamlib_rigctld_ip,
rigctld_port=static.HAMLIB_RIGCTLD_PORT, rigctld_port=HamlibParam.hamlib_rigctld_port,
) )
# --------------------------------------------START DECODER THREAD # --------------------------------------------START DECODER THREAD
if static.ENABLE_FFT: if AudioParam.enable_fft:
fft_thread = threading.Thread( fft_thread = threading.Thread(
target=self.calculate_fft, name="FFT_THREAD", daemon=True target=self.calculate_fft, name="FFT_THREAD", daemon=True
) )
fft_thread.start() fft_thread.start()
if static.ENABLE_FSK: if TNC.enable_fsk:
audio_thread_fsk_ldpc0 = threading.Thread( audio_thread_fsk_ldpc0 = threading.Thread(
target=self.audio_fsk_ldpc_0, name="AUDIO_THREAD FSK LDPC0", daemon=True target=self.audio_fsk_ldpc_0, name="AUDIO_THREAD FSK LDPC0", daemon=True
) )
@ -287,15 +297,15 @@ class RF:
audio_thread_fsk_ldpc1.start() audio_thread_fsk_ldpc1.start()
else: else:
audio_thread_sig0_datac0 = threading.Thread( audio_thread_sig0_datac13 = threading.Thread(
target=self.audio_sig0_datac0, name="AUDIO_THREAD DATAC0 - 0", daemon=True target=self.audio_sig0_datac13, name="AUDIO_THREAD DATAC13 - 0", daemon=True
) )
audio_thread_sig0_datac0.start() audio_thread_sig0_datac13.start()
audio_thread_sig1_datac0 = threading.Thread( audio_thread_sig1_datac13 = threading.Thread(
target=self.audio_sig1_datac0, name="AUDIO_THREAD DATAC0 - 1", daemon=True target=self.audio_sig1_datac13, name="AUDIO_THREAD DATAC13 - 1", daemon=True
) )
audio_thread_sig1_datac0.start() audio_thread_sig1_datac13.start()
audio_thread_dat0_datac1 = threading.Thread( audio_thread_dat0_datac1 = threading.Thread(
target=self.audio_dat0_datac1, name="AUDIO_THREAD DATAC1", daemon=True target=self.audio_dat0_datac1, name="AUDIO_THREAD DATAC1", daemon=True
@ -307,6 +317,11 @@ class RF:
) )
audio_thread_dat0_datac3.start() audio_thread_dat0_datac3.start()
audio_thread_dat0_datac4 = threading.Thread(
target=self.audio_dat0_datac4, name="AUDIO_THREAD DATAC4", daemon=True
)
audio_thread_dat0_datac4.start()
hamlib_thread = threading.Thread( hamlib_thread = threading.Thread(
target=self.update_rig_data, name="HAMLIB_THREAD", daemon=True target=self.update_rig_data, name="HAMLIB_THREAD", daemon=True
) )
@ -337,7 +352,7 @@ class RF:
threading.Event().wait(0.01) threading.Event().wait(0.01)
if len(self.modoutqueue) > 0 and not self.mod_out_locked: if len(self.modoutqueue) > 0 and not self.mod_out_locked:
static.PTT_STATE = self.radio.set_ptt(True) HamlibParam.ptt_state = self.radio.set_ptt(True)
jsondata = {"ptt": "True"} jsondata = {"ptt": "True"}
data_out = json.dumps(jsondata) data_out = json.dumps(jsondata)
sock.SOCKET_QUEUE.put(data_out) sock.SOCKET_QUEUE.put(data_out)
@ -364,12 +379,13 @@ class RF:
length_x = len(x) length_x = len(x)
for data_buffer, receive in [ for data_buffer, receive in [
(self.sig0_datac0_buffer, RECEIVE_SIG0), (self.sig0_datac13_buffer, RECEIVE_SIG0),
(self.sig1_datac0_buffer, RECEIVE_SIG1), (self.sig1_datac13_buffer, RECEIVE_SIG1),
(self.dat0_datac1_buffer, RECEIVE_DATAC1), (self.dat0_datac1_buffer, RECEIVE_DATAC1),
(self.dat0_datac3_buffer, RECEIVE_DATAC3), (self.dat0_datac3_buffer, RECEIVE_DATAC3),
(self.fsk_ldpc_buffer_0, static.ENABLE_FSK), (self.dat0_datac4_buffer, RECEIVE_DATAC4),
(self.fsk_ldpc_buffer_1, static.ENABLE_FSK), (self.fsk_ldpc_buffer_0, TNC.enable_fsk),
(self.fsk_ldpc_buffer_1, TNC.enable_fsk),
]: ]:
if ( if (
not (data_buffer.nbuffer + length_x) > data_buffer.size not (data_buffer.nbuffer + length_x) > data_buffer.size
@ -397,12 +413,13 @@ class RF:
length_x = len(x) length_x = len(x)
for data_buffer, receive in [ for data_buffer, receive in [
(self.sig0_datac0_buffer, RECEIVE_SIG0), (self.sig0_datac13_buffer, RECEIVE_SIG0),
(self.sig1_datac0_buffer, RECEIVE_SIG1), (self.sig1_datac13_buffer, RECEIVE_SIG1),
(self.dat0_datac1_buffer, RECEIVE_DATAC1), (self.dat0_datac1_buffer, RECEIVE_DATAC1),
(self.dat0_datac3_buffer, RECEIVE_DATAC3), (self.dat0_datac3_buffer, RECEIVE_DATAC3),
(self.fsk_ldpc_buffer_0, static.ENABLE_FSK), (self.dat0_datac4_buffer, RECEIVE_DATAC4),
(self.fsk_ldpc_buffer_1, static.ENABLE_FSK), (self.fsk_ldpc_buffer_0, TNC.enable_fsk),
(self.fsk_ldpc_buffer_1, TNC.enable_fsk),
]: ]:
if ( if (
not (data_buffer.nbuffer + length_x) > data_buffer.size not (data_buffer.nbuffer + length_x) > data_buffer.size
@ -443,38 +460,39 @@ class RF:
x = self.resampler.resample48_to_8(x) x = self.resampler.resample48_to_8(x)
# audio recording for debugging purposes # audio recording for debugging purposes
if static.AUDIO_RECORD: if AudioParam.audio_record:
# static.AUDIO_RECORD_FILE.write(x) # AudioParam.audio_record_file.write(x)
static.AUDIO_RECORD_FILE.writeframes(x) AudioParam.audio_record_file.writeframes(x)
# Avoid decoding when transmitting to reduce CPU # Avoid decoding when transmitting to reduce CPU
# TODO: Overriding this for testing purposes # TODO: Overriding this for testing purposes
# if not static.TRANSMITTING: # if not TNC.transmitting:
length_x = len(x) length_x = len(x)
# Avoid buffer overflow by filling only if buffer for # Avoid buffer overflow by filling only if buffer for
# selected datachannel mode is not full # selected datachannel mode is not full
for audiobuffer, receive, index in [ for audiobuffer, receive, index in [
(self.sig0_datac0_buffer, RECEIVE_SIG0, 0), (self.sig0_datac13_buffer, RECEIVE_SIG0, 0),
(self.sig1_datac0_buffer, RECEIVE_SIG1, 1), (self.sig1_datac13_buffer, RECEIVE_SIG1, 1),
(self.dat0_datac1_buffer, RECEIVE_DATAC1, 2), (self.dat0_datac1_buffer, RECEIVE_DATAC1, 2),
(self.dat0_datac3_buffer, RECEIVE_DATAC3, 3), (self.dat0_datac3_buffer, RECEIVE_DATAC3, 3),
(self.fsk_ldpc_buffer_0, static.ENABLE_FSK, 4), (self.dat0_datac4_buffer, RECEIVE_DATAC4, 4),
(self.fsk_ldpc_buffer_1, static.ENABLE_FSK, 5), (self.fsk_ldpc_buffer_0, TNC.enable_fsk, 5),
(self.fsk_ldpc_buffer_1, TNC.enable_fsk, 6),
]: ]:
if (audiobuffer.nbuffer + length_x) > audiobuffer.size: if (audiobuffer.nbuffer + length_x) > audiobuffer.size:
static.BUFFER_OVERFLOW_COUNTER[index] += 1 AudioParam.buffer_overflow_counter[index] += 1
elif receive: elif receive:
audiobuffer.push(x) audiobuffer.push(x)
# end of "not static.TRANSMITTING" if block # end of "not TNC.transmitting" if block
if not self.modoutqueue or self.mod_out_locked: if not self.modoutqueue or self.mod_out_locked:
data_out48k = np.zeros(frames, dtype=np.int16) data_out48k = np.zeros(frames, dtype=np.int16)
self.fft_data = x self.fft_data = x
else: else:
if not static.PTT_STATE: if not HamlibParam.ptt_state:
# TODO: Moved to this place for testing # TODO: Moved to this place for testing
# Maybe we can avoid moments of silence before transmitting # Maybe we can avoid moments of silence before transmitting
static.PTT_STATE = self.radio.set_ptt(True) HamlibParam.ptt_state = self.radio.set_ptt(True)
jsondata = {"ptt": "True"} jsondata = {"ptt": "True"}
data_out = json.dumps(jsondata) data_out = json.dumps(jsondata)
sock.SOCKET_QUEUE.put(data_out) sock.SOCKET_QUEUE.put(data_out)
@ -502,38 +520,33 @@ class RF:
frames: frames:
""" """
self.reset_data_sync()
""" if mode == codec2.FREEDV_MODE.datac0.value:
sig0 = 14
sig1 = 14
datac0 = 14
datac1 = 10
datac3 = 12
fsk_ldpc = 9
fsk_ldpc_0 = 200
fsk_ldpc_1 = 201
"""
if mode == 14:
freedv = self.freedv_datac0_tx freedv = self.freedv_datac0_tx
elif mode == 10: elif mode == codec2.FREEDV_MODE.datac1.value:
freedv = self.freedv_datac1_tx freedv = self.freedv_datac1_tx
elif mode == 12: elif mode == codec2.FREEDV_MODE.datac3.value:
freedv = self.freedv_datac3_tx freedv = self.freedv_datac3_tx
elif mode == 200: elif mode == codec2.FREEDV_MODE.datac4.value:
freedv = self.freedv_datac4_tx
elif mode == codec2.FREEDV_MODE.datac13.value:
freedv = self.freedv_datac13_tx
elif mode == codec2.FREEDV_MODE.fsk_ldpc_0.value:
freedv = self.freedv_ldpc0_tx freedv = self.freedv_ldpc0_tx
elif mode == 201: elif mode == codec2.FREEDV_MODE.fsk_ldpc_1.value:
freedv = self.freedv_ldpc1_tx freedv = self.freedv_ldpc1_tx
else: else:
return False return False
static.TRANSMITTING = True TNC.transmitting = True
# if we're transmitting FreeDATA signals, reset channel busy state # if we're transmitting FreeDATA signals, reset channel busy state
static.CHANNEL_BUSY = False ModemParam.channel_busy = False
start_of_transmission = time.time() start_of_transmission = time.time()
# TODO: Moved ptt toggle some steps before audio is ready for testing # TODO: Moved ptt toggle some steps before audio is ready for testing
# Toggle ptt early to save some time and send ptt state via socket # Toggle ptt early to save some time and send ptt state via socket
# static.PTT_STATE = self.radio.set_ptt(True) # HamlibParam.ptt_state = self.radio.set_ptt(True)
# jsondata = {"ptt": "True"} # jsondata = {"ptt": "True"}
# data_out = json.dumps(jsondata) # data_out = json.dumps(jsondata)
# sock.SOCKET_QUEUE.put(data_out) # sock.SOCKET_QUEUE.put(data_out)
@ -564,18 +577,22 @@ class RF:
) )
# Add empty data to handle ptt toggle time # Add empty data to handle ptt toggle time
if static.TX_DELAY > 0: if ModemParam.tx_delay > 0:
data_delay = int(self.MODEM_SAMPLE_RATE * (static.TX_DELAY / 1000)) # type: ignore data_delay = int(self.MODEM_SAMPLE_RATE * (ModemParam.tx_delay / 1000)) # type: ignore
mod_out_silence = ctypes.create_string_buffer(data_delay * 2) mod_out_silence = ctypes.create_string_buffer(data_delay * 2)
txbuffer = bytes(mod_out_silence) txbuffer = bytes(mod_out_silence)
else: else:
txbuffer = bytes() txbuffer = bytes()
self.log.debug( self.log.debug(
"[MDM] TRANSMIT", mode=self.MODE, payload=payload_bytes_per_frame, delay=static.TX_DELAY "[MDM] TRANSMIT", mode=self.MODE, payload=payload_bytes_per_frame, delay=ModemParam.tx_delay
) )
for _ in range(repeats): for _ in range(repeats):
# Create modulation for all frames in the list
for frame in frames:
# Write preamble to txbuffer
# codec2 fsk preamble may be broken - # codec2 fsk preamble may be broken -
# at least it sounds like that, so we are disabling it for testing # at least it sounds like that, so we are disabling it for testing
if self.MODE not in [ if self.MODE not in [
@ -586,8 +603,6 @@ class RF:
codec2.api.freedv_rawdatapreambletx(freedv, mod_out_preamble) codec2.api.freedv_rawdatapreambletx(freedv, mod_out_preamble)
txbuffer += bytes(mod_out_preamble) txbuffer += bytes(mod_out_preamble)
# Create modulaton for all frames in the list
for frame in frames:
# Create buffer for data # Create buffer for data
# Use this if CRC16 checksum is required (DATAc1-3) # Use this if CRC16 checksum is required (DATAc1-3)
buffer = bytearray(payload_bytes_per_frame) buffer = bytearray(payload_bytes_per_frame)
@ -631,35 +646,35 @@ class RF:
x = np.frombuffer(txbuffer, dtype=np.int16) x = np.frombuffer(txbuffer, dtype=np.int16)
# enable / disable AUDIO TUNE Feature / ALC correction # enable / disable AUDIO TUNE Feature / ALC correction
if static.AUDIO_AUTO_TUNE: if AudioParam.audio_auto_tune:
if static.HAMLIB_ALC == 0.0: if HamlibParam.alc == 0.0:
static.TX_AUDIO_LEVEL = static.TX_AUDIO_LEVEL + 20 AudioParam.tx_audio_level = AudioParam.tx_audio_level + 20
elif 0.0 < static.HAMLIB_ALC <= 0.1: elif 0.0 < HamlibParam.alc <= 0.1:
print("0.0 < static.HAMLIB_ALC <= 0.1") print("0.0 < HamlibParam.alc <= 0.1")
static.TX_AUDIO_LEVEL = static.TX_AUDIO_LEVEL + 2 AudioParam.tx_audio_level = AudioParam.tx_audio_level + 2
self.log.debug("[MDM] AUDIO TUNE", audio_level=str(static.TX_AUDIO_LEVEL), self.log.debug("[MDM] AUDIO TUNE", audio_level=str(AudioParam.tx_audio_level),
alc_level=str(static.HAMLIB_ALC)) alc_level=str(HamlibParam.alc))
elif 0.1 < static.HAMLIB_ALC < 0.2: elif 0.1 < HamlibParam.alc < 0.2:
print("0.1 < static.HAMLIB_ALC < 0.2") print("0.1 < HamlibParam.alc < 0.2")
static.TX_AUDIO_LEVEL = static.TX_AUDIO_LEVEL AudioParam.tx_audio_level = AudioParam.tx_audio_level
self.log.debug("[MDM] AUDIO TUNE", audio_level=str(static.TX_AUDIO_LEVEL), self.log.debug("[MDM] AUDIO TUNE", audio_level=str(AudioParam.tx_audio_level),
alc_level=str(static.HAMLIB_ALC)) alc_level=str(HamlibParam.alc))
elif 0.2 < static.HAMLIB_ALC < 0.99: elif 0.2 < HamlibParam.alc < 0.99:
print("0.2 < static.HAMLIB_ALC < 0.99") print("0.2 < HamlibParam.alc < 0.99")
static.TX_AUDIO_LEVEL = static.TX_AUDIO_LEVEL - 20 AudioParam.tx_audio_level = AudioParam.tx_audio_level - 20
self.log.debug("[MDM] AUDIO TUNE", audio_level=str(static.TX_AUDIO_LEVEL), self.log.debug("[MDM] AUDIO TUNE", audio_level=str(AudioParam.tx_audio_level),
alc_level=str(static.HAMLIB_ALC)) alc_level=str(HamlibParam.alc))
elif 1.0 >= static.HAMLIB_ALC: elif 1.0 >= HamlibParam.alc:
print("1.0 >= static.HAMLIB_ALC") print("1.0 >= HamlibParam.alc")
static.TX_AUDIO_LEVEL = static.TX_AUDIO_LEVEL - 40 AudioParam.tx_audio_level = AudioParam.tx_audio_level - 40
self.log.debug("[MDM] AUDIO TUNE", audio_level=str(static.TX_AUDIO_LEVEL), self.log.debug("[MDM] AUDIO TUNE", audio_level=str(AudioParam.tx_audio_level),
alc_level=str(static.HAMLIB_ALC)) alc_level=str(HamlibParam.alc))
else: else:
self.log.debug("[MDM] AUDIO TUNE", audio_level=str(static.TX_AUDIO_LEVEL), self.log.debug("[MDM] AUDIO TUNE", audio_level=str(AudioParam.tx_audio_level),
alc_level=str(static.HAMLIB_ALC)) alc_level=str(HamlibParam.alc))
x = set_audio_volume(x, static.TX_AUDIO_LEVEL) x = set_audio_volume(x, AudioParam.tx_audio_level)
if not static.AUDIO_ENABLE_TCI: if not AudioParam.audio_enable_tci:
txbuffer_out = self.resampler.resample8_to_48(x) txbuffer_out = self.resampler.resample8_to_48(x)
else: else:
txbuffer_out = x txbuffer_out = x
@ -689,7 +704,7 @@ class RF:
self.mod_out_locked = False self.mod_out_locked = False
# we need to wait manually for tci processing # we need to wait manually for tci processing
if static.AUDIO_ENABLE_TCI: if AudioParam.audio_enable_tci:
duration = len(txbuffer_out) / 8000 duration = len(txbuffer_out) / 8000
timestamp_to_sleep = time.time() + duration timestamp_to_sleep = time.time() + duration
self.log.debug("[MDM] TCI calculated duration", duration=duration) self.log.debug("[MDM] TCI calculated duration", duration=duration)
@ -702,7 +717,7 @@ class RF:
tci_timeout_reached = True tci_timeout_reached = True
while self.modoutqueue or not tci_timeout_reached: while self.modoutqueue or not tci_timeout_reached:
if static.AUDIO_ENABLE_TCI: if AudioParam.audio_enable_tci:
if time.time() < timestamp_to_sleep: if time.time() < timestamp_to_sleep:
tci_timeout_reached = False tci_timeout_reached = False
else: else:
@ -710,9 +725,9 @@ class RF:
threading.Event().wait(0.01) threading.Event().wait(0.01)
# if we're transmitting FreeDATA signals, reset channel busy state # if we're transmitting FreeDATA signals, reset channel busy state
static.CHANNEL_BUSY = False ModemParam.channel_busy = False
static.PTT_STATE = self.radio.set_ptt(False) HamlibParam.ptt_state = self.radio.set_ptt(False)
# Push ptt state to socket stream # Push ptt state to socket stream
jsondata = {"ptt": "False"} jsondata = {"ptt": "False"}
@ -723,7 +738,7 @@ class RF:
self.mod_out_locked = True self.mod_out_locked = True
self.modem_transmit_queue.task_done() self.modem_transmit_queue.task_done()
static.TRANSMITTING = False TNC.transmitting = False
threading.Event().set() threading.Event().set()
end_of_transmission = time.time() end_of_transmission = time.time()
@ -781,14 +796,14 @@ class RF:
if rx_status != 0: if rx_status != 0:
# we need to disable this if in testmode as its causing problems with FIFO it seems # we need to disable this if in testmode as its causing problems with FIFO it seems
if not TESTMODE: if not TESTMODE:
static.IS_CODEC2_TRAFFIC = True ModemParam.is_codec2_traffic = True
self.log.debug( self.log.debug(
"[MDM] [demod_audio] modem state", mode=mode_name, rx_status=rx_status, "[MDM] [demod_audio] modem state", mode=mode_name, rx_status=rx_status,
sync_flag=codec2.api.rx_sync_flags_to_text[rx_status] sync_flag=codec2.api.rx_sync_flags_to_text[rx_status]
) )
else: else:
static.IS_CODEC2_TRAFFIC = False ModemParam.is_codec2_traffic = False
if rx_status == 10: if rx_status == 10:
state_buffer.append(rx_status) state_buffer.append(rx_status)
@ -796,11 +811,27 @@ class RF:
audiobuffer.pop(nin) audiobuffer.pop(nin)
nin = codec2.api.freedv_nin(freedv) nin = codec2.api.freedv_nin(freedv)
if nbytes == bytes_per_frame: if nbytes == bytes_per_frame:
# process commands only if static.LISTEN = True
if static.LISTEN: # process commands only if TNC.listen = True
if TNC.listen:
# ignore data channel opener frames for avoiding toggle states
# use case: opener already received, but ack got lost and we are receiving
# an opener again
if mode_name in ["sig1-datac13"] and int.from_bytes(bytes(bytes_out[:1]), "big") in [
FRAME_TYPE.ARQ_SESSION_OPEN.value,
FRAME_TYPE.ARQ_DC_OPEN_W.value,
FRAME_TYPE.ARQ_DC_OPEN_ACK_W.value,
FRAME_TYPE.ARQ_DC_OPEN_N.value,
FRAME_TYPE.ARQ_DC_OPEN_ACK_N.value
]:
print("dropp")
else:
self.log.debug( self.log.debug(
"[MDM] [demod_audio] Pushing received data to received_queue", nbytes=nbytes "[MDM] [demod_audio] Pushing received data to received_queue", nbytes=nbytes
) )
self.modem_received_queue.put([bytes_out, freedv, bytes_per_frame]) self.modem_received_queue.put([bytes_out, freedv, bytes_per_frame])
self.get_scatter(freedv) self.get_scatter(freedv)
self.calculate_snr(freedv) self.calculate_snr(freedv)
@ -808,7 +839,7 @@ class RF:
else: else:
self.log.warning( self.log.warning(
"[MDM] [demod_audio] received frame but ignored processing", "[MDM] [demod_audio] received frame but ignored processing",
listen=static.LISTEN listen=TNC.listen
) )
except Exception as e: except Exception as e:
self.log.warning("[MDM] [demod_audio] Stream not active anymore", e=e) self.log.warning("[MDM] [demod_audio] Stream not active anymore", e=e)
@ -830,7 +861,7 @@ class RF:
# FSK Long-distance Parity Code 1 - data frames # FSK Long-distance Parity Code 1 - data frames
c2instance = ctypes.cast( c2instance = ctypes.cast(
codec2.api.freedv_open_advanced( codec2.api.freedv_open_advanced(
codec2.api.FREEDV_MODE_FSK_LDPC, codec2.FREEDV_MODE.fsk_ldpc.value,
ctypes.byref(adv), ctypes.byref(adv),
), ),
ctypes.c_void_p, ctypes.c_void_p,
@ -843,10 +874,10 @@ class RF:
) )
# set tuning range # set tuning range
self.c_lib.freedv_set_tuning_range( codec2.api.freedv_set_tuning_range(
c2instance, c2instance,
ctypes.c_float(static.TUNING_RANGE_FMIN), ctypes.c_float(ModemParam.tuning_range_fmin),
ctypes.c_float(static.TUNING_RANGE_FMAX), ctypes.c_float(ModemParam.tuning_range_fmax),
) )
# get bytes per frame # get bytes per frame
@ -868,44 +899,56 @@ class RF:
# Additional Datac0-specific information - these are not referenced anywhere else. # 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_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_n_nom_modem_samples = codec2.api.freedv_get_n_nom_modem_samples(
# self.sig0_datac0_freedv # self.sig0_datac0_freedv
# ) # )
# self.sig0_datac0_n_tx_modem_samples = self.c_lib.freedv_get_n_tx_modem_samples( # self.sig0_datac0_n_tx_modem_samples = codec2.api.freedv_get_n_tx_modem_samples(
# self.sig0_datac0_freedv # self.sig0_datac0_freedv
# ) # )
# self.sig0_datac0_n_tx_preamble_modem_samples = ( # self.sig0_datac0_n_tx_preamble_modem_samples = (
# self.c_lib.freedv_get_n_tx_preamble_modem_samples(self.sig0_datac0_freedv) # codec2.api.freedv_get_n_tx_preamble_modem_samples(self.sig0_datac0_freedv)
# ) # )
# self.sig0_datac0_n_tx_postamble_modem_samples = ( # self.sig0_datac0_n_tx_postamble_modem_samples = (
# self.c_lib.freedv_get_n_tx_postamble_modem_samples(self.sig0_datac0_freedv) # codec2.api.freedv_get_n_tx_postamble_modem_samples(self.sig0_datac0_freedv)
# ) # )
# return values # return values
return c2instance, bytes_per_frame, bytes_out, audio_buffer, nin return c2instance, bytes_per_frame, bytes_out, audio_buffer, nin
def audio_sig0_datac0(self) -> None: def audio_sig0_datac13(self) -> None:
"""Receive data encoded with datac0 - 0""" """Receive data encoded with datac13 - 0"""
self.sig0_datac0_nin = self.demodulate_audio( self.sig0_datac13_nin = self.demodulate_audio(
self.sig0_datac0_buffer, self.sig0_datac13_buffer,
self.sig0_datac0_nin, self.sig0_datac13_nin,
self.sig0_datac0_freedv, self.sig0_datac13_freedv,
self.sig0_datac0_bytes_out, self.sig0_datac13_bytes_out,
self.sig0_datac0_bytes_per_frame, self.sig0_datac13_bytes_per_frame,
SIG0_DATAC0_STATE, SIG0_DATAC13_STATE,
"sig0-datac0" "sig0-datac13"
) )
def audio_sig1_datac0(self) -> None: def audio_sig1_datac13(self) -> None:
"""Receive data encoded with datac0 - 1""" """Receive data encoded with datac13 - 1"""
self.sig1_datac0_nin = self.demodulate_audio( self.sig1_datac13_nin = self.demodulate_audio(
self.sig1_datac0_buffer, self.sig1_datac13_buffer,
self.sig1_datac0_nin, self.sig1_datac13_nin,
self.sig1_datac0_freedv, self.sig1_datac13_freedv,
self.sig1_datac0_bytes_out, self.sig1_datac13_bytes_out,
self.sig1_datac0_bytes_per_frame, self.sig1_datac13_bytes_per_frame,
SIG1_DATAC0_STATE, SIG1_DATAC13_STATE,
"sig1-datac0" "sig1-datac13"
)
def audio_dat0_datac4(self) -> None:
"""Receive data encoded with datac4"""
self.dat0_datac4_nin = self.demodulate_audio(
self.dat0_datac4_buffer,
self.dat0_datac4_nin,
self.dat0_datac4_freedv,
self.dat0_datac4_bytes_out,
self.dat0_datac4_bytes_per_frame,
DAT0_DATAC4_STATE,
"dat0-datac4"
) )
def audio_dat0_datac1(self) -> None: def audio_dat0_datac1(self) -> None:
@ -986,7 +1029,7 @@ class RF:
def get_frequency_offset(self, freedv: ctypes.c_void_p) -> float: def get_frequency_offset(self, freedv: ctypes.c_void_p) -> float:
""" """
Ask codec2 for the calculated (audio) frequency offset of the received signal. Ask codec2 for the calculated (audio) frequency offset of the received signal.
Side-effect: sets static.FREQ_OFFSET Side-effect: sets ModemParam.frequency_offset
:param freedv: codec2 instance to query :param freedv: codec2 instance to query
:type freedv: ctypes.c_void_p :type freedv: ctypes.c_void_p
@ -994,25 +1037,25 @@ class RF:
:rtype: float :rtype: float
""" """
modemStats = codec2.MODEMSTATS() modemStats = codec2.MODEMSTATS()
self.c_lib.freedv_get_modem_extended_stats(freedv, ctypes.byref(modemStats)) codec2.api.freedv_get_modem_extended_stats(freedv, ctypes.byref(modemStats))
offset = round(modemStats.foff) * (-1) offset = round(modemStats.foff) * (-1)
static.FREQ_OFFSET = offset ModemParam.frequency_offset = offset
return offset return offset
def get_scatter(self, freedv: ctypes.c_void_p) -> None: def get_scatter(self, freedv: ctypes.c_void_p) -> None:
""" """
Ask codec2 for data about the received signal and calculate the scatter plot. Ask codec2 for data about the received signal and calculate the scatter plot.
Side-effect: sets static.SCATTER Side-effect: sets ModemParam.scatter
:param freedv: codec2 instance to query :param freedv: codec2 instance to query
:type freedv: ctypes.c_void_p :type freedv: ctypes.c_void_p
""" """
if not static.ENABLE_SCATTER: if not ModemParam.enable_scatter:
return return
modemStats = codec2.MODEMSTATS() modemStats = codec2.MODEMSTATS()
ctypes.cast( ctypes.cast(
self.c_lib.freedv_get_modem_extended_stats(freedv, ctypes.byref(modemStats)), codec2.api.freedv_get_modem_extended_stats(freedv, ctypes.byref(modemStats)),
ctypes.c_void_p, ctypes.c_void_p,
) )
@ -1035,16 +1078,16 @@ class RF:
# Send all the data if we have too-few samples, otherwise send a sampling # Send all the data if we have too-few samples, otherwise send a sampling
if 150 > len(scatterdata) > 0: if 150 > len(scatterdata) > 0:
static.SCATTER = scatterdata ModemParam.scatter = scatterdata
else: else:
# only take every tenth data point # only take every tenth data point
static.SCATTER = scatterdata[::10] ModemParam.scatter = scatterdata[::10]
def calculate_snr(self, freedv: ctypes.c_void_p) -> float: def calculate_snr(self, freedv: ctypes.c_void_p) -> float:
""" """
Ask codec2 for data about the received signal and calculate Ask codec2 for data about the received signal and calculate
the signal-to-noise ratio. the signal-to-noise ratio.
Side-effect: sets static.SNR Side-effect: sets ModemParam.snr
:param freedv: codec2 instance to query :param freedv: codec2 instance to query
:type freedv: ctypes.c_void_p :type freedv: ctypes.c_void_p
@ -1055,7 +1098,7 @@ class RF:
modem_stats_snr = ctypes.c_float() modem_stats_snr = ctypes.c_float()
modem_stats_sync = ctypes.c_int() modem_stats_sync = ctypes.c_int()
self.c_lib.freedv_get_modem_stats( codec2.api.freedv_get_modem_stats(
freedv, ctypes.byref(modem_stats_sync), ctypes.byref(modem_stats_snr) freedv, ctypes.byref(modem_stats_sync), ctypes.byref(modem_stats_snr)
) )
modem_stats_snr = modem_stats_snr.value modem_stats_snr = modem_stats_snr.value
@ -1063,15 +1106,15 @@ class RF:
snr = round(modem_stats_snr, 1) snr = round(modem_stats_snr, 1)
self.log.info("[MDM] calculate_snr: ", snr=snr) self.log.info("[MDM] calculate_snr: ", snr=snr)
static.SNR = snr ModemParam.snr = snr
# static.SNR = np.clip( # ModemParam.snr = np.clip(
# snr, -127, 127 # snr, -127, 127
# ) # limit to max value of -128/128 as a possible fix of #188 # ) # limit to max value of -128/128 as a possible fix of #188
return static.SNR return ModemParam.snr
except Exception as err: except Exception as err:
self.log.error(f"[MDM] calculate_snr: Exception: {err}") self.log.error(f"[MDM] calculate_snr: Exception: {err}")
static.SNR = 0 ModemParam.snr = 0
return static.SNR return ModemParam.snr
def set_rig_data(self) -> None: def set_rig_data(self) -> None:
""" """
@ -1091,29 +1134,29 @@ class RF:
""" """
Request information about the current state of the radio via hamlib Request information about the current state of the radio via hamlib
Side-effect: sets Side-effect: sets
- static.HAMLIB_FREQUENCY - HamlibParam.hamlib_frequency
- static.HAMLIB_MODE - HamlibParam.hamlib_mode
- static.HAMLIB_BANDWIDTH - HamlibParam.hamlib_bandwidth
""" """
while True: while True:
# this looks weird, but is necessary for avoiding rigctld packet colission sock # this looks weird, but is necessary for avoiding rigctld packet colission sock
threading.Event().wait(0.25) threading.Event().wait(0.25)
static.HAMLIB_FREQUENCY = self.radio.get_frequency() HamlibParam.hamlib_frequency = self.radio.get_frequency()
threading.Event().wait(0.1) threading.Event().wait(0.1)
static.HAMLIB_MODE = self.radio.get_mode() HamlibParam.hamlib_mode = self.radio.get_mode()
threading.Event().wait(0.1) threading.Event().wait(0.1)
static.HAMLIB_BANDWIDTH = self.radio.get_bandwidth() HamlibParam.hamlib_bandwidth = self.radio.get_bandwidth()
threading.Event().wait(0.1) threading.Event().wait(0.1)
static.HAMLIB_STATUS = self.radio.get_status() HamlibParam.hamlib_status = self.radio.get_status()
threading.Event().wait(0.1) threading.Event().wait(0.1)
if static.TRANSMITTING: if TNC.transmitting:
static.HAMLIB_ALC = self.radio.get_alc() HamlibParam.alc = self.radio.get_alc()
threading.Event().wait(0.1) threading.Event().wait(0.1)
# static.HAMLIB_RF = self.radio.get_level() # HamlibParam.hamlib_rf = self.radio.get_level()
# threading.Event().wait(0.1) # threading.Event().wait(0.1)
static.HAMLIB_STRENGTH = self.radio.get_strength() HamlibParam.hamlib_strength = self.radio.get_strength()
# print(f"ALC: {static.HAMLIB_ALC}, RF: {static.HAMLIB_RF}, STRENGTH: {static.HAMLIB_STRENGTH}") # print(f"ALC: {HamlibParam.alc}, RF: {HamlibParam.hamlib_rf}, STRENGTH: {HamlibParam.hamlib_strength}")
def calculate_fft(self) -> None: def calculate_fft(self) -> None:
""" """
@ -1152,7 +1195,7 @@ class RF:
# Therefore we are setting it to 100 so it will be highlighted # Therefore we are setting it to 100 so it will be highlighted
# Have to do this when we are not transmitting so our # Have to do this when we are not transmitting so our
# own sending data will not affect this too much # own sending data will not affect this too much
if not static.TRANSMITTING: if not TNC.transmitting:
dfft[dfft > avg + 15] = 100 dfft[dfft > avg + 15] = 100
# Calculate audio dbfs # Calculate audio dbfs
@ -1162,27 +1205,26 @@ class RF:
if rms_counter > 50: if rms_counter > 50:
d = np.frombuffer(self.fft_data, np.int16).astype(np.float32) d = np.frombuffer(self.fft_data, np.int16).astype(np.float32)
# calculate RMS and then dBFS # calculate RMS and then dBFS
# TODO: Need to change static.AUDIO_RMS to AUDIO_DBFS somewhen
# https://dsp.stackexchange.com/questions/8785/how-to-compute-dbfs # https://dsp.stackexchange.com/questions/8785/how-to-compute-dbfs
# try except for avoiding runtime errors by division/0 # try except for avoiding runtime errors by division/0
try: try:
rms = int(np.sqrt(np.max(d ** 2))) rms = int(np.sqrt(np.max(d ** 2)))
if rms == 0: if rms == 0:
raise ZeroDivisionError raise ZeroDivisionError
static.AUDIO_DBFS = 20 * np.log10(rms / 32768) AudioParam.audio_dbfs = 20 * np.log10(rms / 32768)
except Exception as e: except Exception as e:
self.log.warning( self.log.warning(
"[MDM] fft calculation error - please check your audio setup", "[MDM] fft calculation error - please check your audio setup",
e=e, e=e,
) )
static.AUDIO_DBFS = -100 AudioParam.audio_dbfs = -100
rms_counter = 0 rms_counter = 0
# Convert data to int to decrease size # Convert data to int to decrease size
dfft = dfft.astype(int) dfft = dfft.astype(int)
# Create list of dfft for later pushing to static.FFT # Create list of dfft for later pushing to AudioParam.fft
dfftlist = dfft.tolist() dfftlist = dfft.tolist()
# Reduce area where the busy detection is enabled # Reduce area where the busy detection is enabled
@ -1196,14 +1238,26 @@ class RF:
# 2700Hz = 266 # 2700Hz = 266
# 3200Hz = 315 # 3200Hz = 315
# define the area, we are detecting busy state # slot
dfft = dfft[120:176] if static.LOW_BANDWIDTH_MODE else dfft[65:231] slot = 0
slot1 = [0, 65]
slot2 = [65,120]
slot3 = [120, 176]
slot4 = [176, 231]
slot5 = [231, len(dfftlist)]
for range in [slot1, slot2, slot3, slot4, slot5]:
range_start = range[0]
range_end = range[1]
# define the area, we are detecting busy state
#dfft = dfft[120:176] if TNC.low_bandwidth_mode else dfft[65:231]
dfft = dfft[range_start:range_end]
# Check for signals higher than average by checking for "100" # Check for signals higher than average by checking for "100"
# If we have a signal, increment our channel_busy delay counter # If we have a signal, increment our channel_busy delay counter
# so we have a smoother state toggle # so we have a smoother state toggle
if np.sum(dfft[dfft > avg + 15]) >= 400 and not static.TRANSMITTING: if np.sum(dfft[dfft > avg + 15]) >= 400 and not TNC.transmitting:
static.CHANNEL_BUSY = True ModemParam.channel_busy = True
ModemParam.channel_busy_slot[slot] = True
# Limit delay counter to a maximum of 200. The higher this value, # Limit delay counter to a maximum of 200. The higher this value,
# the longer we will wait until releasing state # the longer we will wait until releasing state
channel_busy_delay = min(channel_busy_delay + 10, 200) channel_busy_delay = min(channel_busy_delay + 10, 200)
@ -1212,14 +1266,18 @@ class RF:
channel_busy_delay = max(channel_busy_delay - 1, 0) channel_busy_delay = max(channel_busy_delay - 1, 0)
# When our channel busy counter reaches 0, toggle state to False # When our channel busy counter reaches 0, toggle state to False
if channel_busy_delay == 0: if channel_busy_delay == 0:
static.CHANNEL_BUSY = False ModemParam.channel_busy = False
ModemParam.channel_busy_slot[slot] = False
static.FFT = dfftlist[:315] # 315 --> bandwidth 3200 # increment slot
slot += 1
AudioParam.fft = dfftlist[:315] # 315 --> bandwidth 3200
except Exception as err: except Exception as err:
self.log.error(f"[MDM] calculate_fft: Exception: {err}") self.log.error(f"[MDM] calculate_fft: Exception: {err}")
self.log.debug("[MDM] Setting fft=0") self.log.debug("[MDM] Setting fft=0")
# else 0 # else 0
static.FFT = [0] AudioParam.fft = [0]
def set_frames_per_burst(self, frames_per_burst: int) -> None: def set_frames_per_burst(self, frames_per_burst: int) -> None:
""" """
@ -1232,10 +1290,26 @@ class RF:
frames_per_burst = min(frames_per_burst, 1) frames_per_burst = min(frames_per_burst, 1)
frames_per_burst = max(frames_per_burst, 5) frames_per_burst = max(frames_per_burst, 5)
frames_per_burst = 1
codec2.api.freedv_set_frames_per_burst(self.dat0_datac1_freedv, frames_per_burst) codec2.api.freedv_set_frames_per_burst(self.dat0_datac1_freedv, frames_per_burst)
codec2.api.freedv_set_frames_per_burst(self.dat0_datac3_freedv, frames_per_burst) codec2.api.freedv_set_frames_per_burst(self.dat0_datac3_freedv, frames_per_burst)
codec2.api.freedv_set_frames_per_burst(self.dat0_datac4_freedv, frames_per_burst)
codec2.api.freedv_set_frames_per_burst(self.fsk_ldpc_freedv_0, frames_per_burst) codec2.api.freedv_set_frames_per_burst(self.fsk_ldpc_freedv_0, frames_per_burst)
def reset_data_sync(self) -> None:
"""
reset sync state for data modes
:param frames_per_burst: Number of frames per burst requested
:type frames_per_burst: int
"""
codec2.api.freedv_set_sync(self.dat0_datac1_freedv, 0)
codec2.api.freedv_set_sync(self.dat0_datac3_freedv, 0)
codec2.api.freedv_set_sync(self.dat0_datac4_freedv, 0)
codec2.api.freedv_set_sync(self.fsk_ldpc_freedv_0, 0)
def open_codec2_instance(mode: int) -> ctypes.c_void_p: def open_codec2_instance(mode: int) -> ctypes.c_void_p:
""" """
@ -1249,7 +1323,7 @@ def open_codec2_instance(mode: int) -> ctypes.c_void_p:
if mode in [codec2.FREEDV_MODE.fsk_ldpc_0.value]: if mode in [codec2.FREEDV_MODE.fsk_ldpc_0.value]:
return ctypes.cast( return ctypes.cast(
codec2.api.freedv_open_advanced( codec2.api.freedv_open_advanced(
codec2.api.FREEDV_MODE_FSK_LDPC, codec2.FREEDV_MODE.fsk_ldpc.value,
ctypes.byref(codec2.api.FREEDV_MODE_FSK_LDPC_0_ADV), ctypes.byref(codec2.api.FREEDV_MODE_FSK_LDPC_0_ADV),
), ),
ctypes.c_void_p, ctypes.c_void_p,
@ -1258,7 +1332,7 @@ def open_codec2_instance(mode: int) -> ctypes.c_void_p:
if mode in [codec2.FREEDV_MODE.fsk_ldpc_1.value]: if mode in [codec2.FREEDV_MODE.fsk_ldpc_1.value]:
return ctypes.cast( return ctypes.cast(
codec2.api.freedv_open_advanced( codec2.api.freedv_open_advanced(
codec2.api.FREEDV_MODE_FSK_LDPC, codec2.FREEDV_MODE.fsk_ldpc.value,
ctypes.byref(codec2.api.FREEDV_MODE_FSK_LDPC_1_ADV), ctypes.byref(codec2.api.FREEDV_MODE_FSK_LDPC_1_ADV),
), ),
ctypes.c_void_p, ctypes.c_void_p,
@ -1277,7 +1351,7 @@ def get_bytes_per_frame(mode: int) -> int:
:rtype: int :rtype: int
""" """
freedv = open_codec2_instance(mode) freedv = open_codec2_instance(mode)
# TODO: add close session
# get number of bytes per frame for mode # get number of bytes per frame for mode
return int(codec2.api.freedv_get_bits_per_modem_frame(freedv) / 8) return int(codec2.api.freedv_get_bits_per_modem_frame(freedv) / 8)
@ -1320,5 +1394,8 @@ def get_modem_error_state():
if RECEIVE_DATAC3 and 10 in DAT0_DATAC3_STATE: if RECEIVE_DATAC3 and 10 in DAT0_DATAC3_STATE:
DAT0_DATAC3_STATE.clear() DAT0_DATAC3_STATE.clear()
return True return True
if RECEIVE_DATAC4 and 10 in DAT0_DATAC4_STATE:
DAT0_DATAC4_STATE.clear()
return True
return False return False

View file

@ -3,6 +3,7 @@ Hold queues used by more than one module to eliminate cyclic imports.
""" """
import queue import queue
import static import static
from static import ARQ, AudioParam, Beacon, Channel, Daemon, HamlibParam, ModemParam, Station, TCIParam, TNC
DATA_QUEUE_TRANSMIT = queue.Queue() DATA_QUEUE_TRANSMIT = queue.Queue()
DATA_QUEUE_RECEIVED = queue.Queue() DATA_QUEUE_RECEIVED = queue.Queue()
@ -16,7 +17,7 @@ AUDIO_RECEIVED_QUEUE = queue.Queue()
AUDIO_TRANSMIT_QUEUE = queue.Queue() AUDIO_TRANSMIT_QUEUE = queue.Queue()
# Initialize FIFO queue to finally store received data # Initialize FIFO queue to finally store received data
RX_BUFFER = queue.Queue(maxsize=static.RX_BUFFER_SIZE) RX_BUFFER = queue.Queue(maxsize=ARQ.rx_buffer_size)
# Commands we want to send to rigctld # Commands we want to send to rigctld
RIGCTLD_COMMAND_QUEUE = queue.Queue() RIGCTLD_COMMAND_QUEUE = queue.Queue()

View file

@ -10,9 +10,7 @@ import time
import structlog import structlog
import threading import threading
import static import static
from static import ARQ, AudioParam, Beacon, Channel, Daemon, HamlibParam, ModemParam, Station, TCIParam
# set global hamlib version
hamlib_version = 0
class radio: class radio:
@ -243,7 +241,7 @@ class radio:
if 'RPRT' not in alc: if 'RPRT' not in alc:
try: try:
alc = float(alc) alc = float(alc)
self.alc = alc if alc != 0.0 else static.HAMLIB_ALC self.alc = alc if alc != 0.0 else HamlibParam.alc
except ValueError: except ValueError:
self.alc = 0.0 self.alc = 0.0

View file

@ -7,6 +7,8 @@ simple TNC self tests
# pylint: disable=import-outside-toplevel, attribute-defined-outside-init # pylint: disable=import-outside-toplevel, attribute-defined-outside-init
import sys import sys
import structlog import structlog
from static import ARQ, Audio, Beacon, Channel, Daemon, Hamlib, Modem, Station, TCI, TNC
log = structlog.get_logger("selftest") log = structlog.get_logger("selftest")

View file

@ -27,6 +27,7 @@ import time
import wave import wave
import helpers import helpers
import static import static
from static import ARQ, AudioParam, Beacon, Channel, Daemon, HamlibParam, ModemParam, Station, Statistics, TCIParam, TNC
import structlog import structlog
from random import randrange from random import randrange
import ujson as json import ujson as json
@ -67,7 +68,7 @@ class ThreadedTCPRequestHandler(socketserver.StreamRequestHandler):
while self.connection_alive and not CLOSE_SIGNAL: while self.connection_alive and not CLOSE_SIGNAL:
# send tnc state as network stream # send tnc state as network stream
# check server port against daemon port and send corresponding data # check server port against daemon port and send corresponding data
if self.server.server_address[1] == static.PORT and not static.TNCSTARTED: if self.server.server_address[1] == TNC.port and not Daemon.tncstarted:
data = send_tnc_state() data = send_tnc_state()
if data != tempdata: if data != tempdata:
tempdata = data tempdata = data
@ -98,9 +99,7 @@ class ThreadedTCPRequestHandler(socketserver.StreamRequestHandler):
self.log.debug("[SCK] catch harmless RuntimeError: Set changed size during iteration", e=err) self.log.debug("[SCK] catch harmless RuntimeError: Set changed size during iteration", e=err)
# we want to transmit scatter data only once to reduce network traffic # we want to transmit scatter data only once to reduce network traffic
static.SCATTER = [] ModemParam.scatter = []
# we want to display INFO messages only once
static.INFO = []
# self.request.sendall(sock_data) # self.request.sendall(sock_data)
threading.Event().wait(0.15) threading.Event().wait(0.15)
@ -127,7 +126,7 @@ class ThreadedTCPRequestHandler(socketserver.StreamRequestHandler):
# iterate thorugh data list # iterate thorugh data list
for commands in data: for commands in data:
if self.server.server_address[1] == static.PORT: if self.server.server_address[1] == TNC.port:
self.process_tnc_commands(commands) self.process_tnc_commands(commands)
else: else:
self.process_daemon_commands(commands) self.process_daemon_commands(commands)
@ -354,14 +353,14 @@ class ThreadedTCPRequestHandler(socketserver.StreamRequestHandler):
def tnc_set_listen(self, received_json): def tnc_set_listen(self, received_json):
try: try:
static.LISTEN = received_json["state"] in ['true', 'True', True, "ON", "on"] TNC.listen = received_json["state"] in ['true', 'True', True, "ON", "on"]
command_response("listen", True) command_response("listen", True)
# if tnc is connected, force disconnect when static.LISTEN == False # if tnc is connected, force disconnect when TNC.listen == False
if not static.LISTEN and static.ARQ_SESSION_STATE not in ["disconnecting", "disconnected", "failed"]: if not TNC.listen and ARQ.arq_session_state not in ["disconnecting", "disconnected", "failed"]:
DATA_QUEUE_TRANSMIT.put(["DISCONNECT"]) DATA_QUEUE_TRANSMIT.put(["DISCONNECT"])
# set early disconnecting state so we can interrupt connection attempts # set early disconnecting state so we can interrupt connection attempts
static.ARQ_SESSION_STATE = "disconnecting" ARQ.arq_session_state = "disconnecting"
command_response("disconnect", True) command_response("disconnect", True)
except Exception as err: except Exception as err:
@ -372,15 +371,15 @@ class ThreadedTCPRequestHandler(socketserver.StreamRequestHandler):
def tnc_set_record_audio(self, received_json): def tnc_set_record_audio(self, received_json):
try: try:
if not static.AUDIO_RECORD: if not AudioParam.audio_record:
static.AUDIO_RECORD_FILE = wave.open(f"{int(time.time())}_audio_recording.wav", 'w') AudioParam.audio_record_FILE = wave.open(f"{int(time.time())}_audio_recording.wav", 'w')
static.AUDIO_RECORD_FILE.setnchannels(1) AudioParam.audio_record_FILE.setnchannels(1)
static.AUDIO_RECORD_FILE.setsampwidth(2) AudioParam.audio_record_FILE.setsampwidth(2)
static.AUDIO_RECORD_FILE.setframerate(8000) AudioParam.audio_record_FILE.setframerate(8000)
static.AUDIO_RECORD = True AudioParam.audio_record = True
else: else:
static.AUDIO_RECORD = False AudioParam.audio_record = False
static.AUDIO_RECORD_FILE.close() AudioParam.audio_record_FILE.close()
command_response("respond_to_call", True) command_response("respond_to_call", True)
@ -392,7 +391,7 @@ class ThreadedTCPRequestHandler(socketserver.StreamRequestHandler):
def tnc_set_respond_to_call(self, received_json): def tnc_set_respond_to_call(self, received_json):
try: try:
static.RESPOND_TO_CALL = received_json["state"] in ['true', 'True', True] TNC.respond_to_call = received_json["state"] in ['true', 'True', True]
command_response("respond_to_call", True) command_response("respond_to_call", True)
except Exception as err: except Exception as err:
@ -403,7 +402,7 @@ class ThreadedTCPRequestHandler(socketserver.StreamRequestHandler):
def tnc_set_respond_to_cq(self, received_json): def tnc_set_respond_to_cq(self, received_json):
try: try:
static.RESPOND_TO_CQ = received_json["state"] in ['true', 'True', True] TNC.respond_to_cq = received_json["state"] in ['true', 'True', True]
command_response("respond_to_cq", True) command_response("respond_to_cq", True)
except Exception as err: except Exception as err:
@ -414,7 +413,7 @@ class ThreadedTCPRequestHandler(socketserver.StreamRequestHandler):
def tnc_set_tx_audio_level(self, received_json): def tnc_set_tx_audio_level(self, received_json):
try: try:
static.TX_AUDIO_LEVEL = int(received_json["value"]) AudioParam.tx_audio_level = int(received_json["value"])
command_response("tx_audio_level", True) command_response("tx_audio_level", True)
except Exception as err: except Exception as err:
@ -481,7 +480,7 @@ class ThreadedTCPRequestHandler(socketserver.StreamRequestHandler):
def tnc_start_beacon(self, received_json): def tnc_start_beacon(self, received_json):
try: try:
static.BEACON_STATE = True Beacon.beacon_state = True
interval = int(received_json["parameter"]) interval = int(received_json["parameter"])
DATA_QUEUE_TRANSMIT.put(["BEACON", interval, True]) DATA_QUEUE_TRANSMIT.put(["BEACON", interval, True])
command_response("start_beacon", True) command_response("start_beacon", True)
@ -496,7 +495,7 @@ class ThreadedTCPRequestHandler(socketserver.StreamRequestHandler):
def tnc_stop_beacon(self, received_json): def tnc_stop_beacon(self, received_json):
try: try:
log.warning("[SCK] Stopping beacon!") log.warning("[SCK] Stopping beacon!")
static.BEACON_STATE = False Beacon.beacon_state = False
DATA_QUEUE_TRANSMIT.put(["BEACON", None, False]) DATA_QUEUE_TRANSMIT.put(["BEACON", None, False])
command_response("stop_beacon", True) command_response("stop_beacon", True)
except Exception as err: except Exception as err:
@ -527,7 +526,7 @@ class ThreadedTCPRequestHandler(socketserver.StreamRequestHandler):
mycallsign = helpers.bytes_to_callsign(mycallsign) mycallsign = helpers.bytes_to_callsign(mycallsign)
except Exception: except Exception:
mycallsign = static.MYCALLSIGN mycallsign = Station.mycallsign
DATA_QUEUE_TRANSMIT.put(["PING", mycallsign, dxcallsign]) DATA_QUEUE_TRANSMIT.put(["PING", mycallsign, dxcallsign])
command_response("ping", True) command_response("ping", True)
@ -543,7 +542,7 @@ class ThreadedTCPRequestHandler(socketserver.StreamRequestHandler):
def tnc_arq_connect(self, received_json): def tnc_arq_connect(self, received_json):
# pause our beacon first # pause our beacon first
static.BEACON_PAUSE = True Beacon.beacon_pause = True
# check for connection attempts key # check for connection attempts key
try: try:
@ -561,7 +560,7 @@ class ThreadedTCPRequestHandler(socketserver.StreamRequestHandler):
mycallsign = helpers.bytes_to_callsign(mycallsign) mycallsign = helpers.bytes_to_callsign(mycallsign)
except Exception: except Exception:
mycallsign = static.MYCALLSIGN mycallsign = Station.mycallsign
# additional step for being sure our callsign is correctly # additional step for being sure our callsign is correctly
# in case we are not getting a station ssid # in case we are not getting a station ssid
@ -569,11 +568,11 @@ class ThreadedTCPRequestHandler(socketserver.StreamRequestHandler):
dxcallsign = helpers.callsign_to_bytes(dxcallsign) dxcallsign = helpers.callsign_to_bytes(dxcallsign)
dxcallsign = helpers.bytes_to_callsign(dxcallsign) dxcallsign = helpers.bytes_to_callsign(dxcallsign)
if static.ARQ_SESSION_STATE not in ["disconnected", "failed"]: if ARQ.arq_session_state not in ["disconnected", "failed"]:
command_response("connect", False) command_response("connect", False)
log.warning( log.warning(
"[SCK] Connect command execution error", "[SCK] Connect command execution error",
e=f"already connected to station:{static.DXCALLSIGN}", e=f"already connected to station:{Station.dxcallsign}",
command=received_json, command=received_json,
) )
else: else:
@ -593,24 +592,24 @@ class ThreadedTCPRequestHandler(socketserver.StreamRequestHandler):
command=received_json, command=received_json,
) )
# allow beacon transmission again # allow beacon transmission again
static.BEACON_PAUSE = False Beacon.beacon_pause = False
# allow beacon transmission again # allow beacon transmission again
static.BEACON_PAUSE = False Beacon.beacon_pause = False
def tnc_arq_disconnect(self, received_json): def tnc_arq_disconnect(self, received_json):
try: try:
if static.ARQ_SESSION_STATE not in ["disconnecting", "disconnected", "failed"]: if ARQ.arq_session_state not in ["disconnecting", "disconnected", "failed"]:
DATA_QUEUE_TRANSMIT.put(["DISCONNECT"]) DATA_QUEUE_TRANSMIT.put(["DISCONNECT"])
# set early disconnecting state so we can interrupt connection attempts # set early disconnecting state so we can interrupt connection attempts
static.ARQ_SESSION_STATE = "disconnecting" ARQ.arq_session_state = "disconnecting"
command_response("disconnect", True) command_response("disconnect", True)
else: else:
command_response("disconnect", False) command_response("disconnect", False)
log.warning( log.warning(
"[SCK] Disconnect command not possible", "[SCK] Disconnect command not possible",
state=static.ARQ_SESSION_STATE, state=ARQ.arq_session_state,
command=received_json, command=received_json,
) )
except Exception as err: except Exception as err:
@ -622,13 +621,13 @@ class ThreadedTCPRequestHandler(socketserver.StreamRequestHandler):
) )
def tnc_arq_send_raw(self, received_json): def tnc_arq_send_raw(self, received_json):
static.BEACON_PAUSE = True Beacon.beacon_pause = True
# wait some random time # wait some random time
helpers.wait(randrange(5, 25, 5) / 10.0) helpers.wait(randrange(5, 25, 5) / 10.0)
# we need to warn if already in arq state # we need to warn if already in arq state
if static.ARQ_STATE: if ARQ.arq_state:
command_response("send_raw", False) command_response("send_raw", False)
log.warning( log.warning(
"[SCK] Send raw command execution warning", "[SCK] Send raw command execution warning",
@ -638,19 +637,19 @@ class ThreadedTCPRequestHandler(socketserver.StreamRequestHandler):
) )
try: try:
if not static.ARQ_SESSION: if not ARQ.arq_session:
dxcallsign = received_json["parameter"][0]["dxcallsign"] dxcallsign = received_json["parameter"][0]["dxcallsign"]
# additional step for being sure our callsign is correctly # additional step for being sure our callsign is correctly
# in case we are not getting a station ssid # in case we are not getting a station ssid
# then we are forcing a station ssid = 0 # then we are forcing a station ssid = 0
dxcallsign = helpers.callsign_to_bytes(dxcallsign) dxcallsign = helpers.callsign_to_bytes(dxcallsign)
dxcallsign = helpers.bytes_to_callsign(dxcallsign) dxcallsign = helpers.bytes_to_callsign(dxcallsign)
static.DXCALLSIGN = dxcallsign Station.dxcallsign = dxcallsign
static.DXCALLSIGN_CRC = helpers.get_crc_24(static.DXCALLSIGN) Station.dxcallsign_crc = helpers.get_crc_24(Station.dxcallsign)
command_response("send_raw", True) command_response("send_raw", True)
else: else:
dxcallsign = static.DXCALLSIGN dxcallsign = Station.dxcallsign
static.DXCALLSIGN_CRC = helpers.get_crc_24(static.DXCALLSIGN) Station.dxcallsign_crc = helpers.get_crc_24(Station.dxcallsign)
mode = int(received_json["parameter"][0]["mode"]) mode = int(received_json["parameter"][0]["mode"])
n_frames = int(received_json["parameter"][0]["n_frames"]) n_frames = int(received_json["parameter"][0]["n_frames"])
@ -663,7 +662,7 @@ class ThreadedTCPRequestHandler(socketserver.StreamRequestHandler):
mycallsign = helpers.bytes_to_callsign(mycallsign) mycallsign = helpers.bytes_to_callsign(mycallsign)
except Exception: except Exception:
mycallsign = static.MYCALLSIGN mycallsign = Station.mycallsign
# check for connection attempts key # check for connection attempts key
try: try:
@ -697,11 +696,11 @@ class ThreadedTCPRequestHandler(socketserver.StreamRequestHandler):
def tnc_arq_stop_transmission(self, received_json): def tnc_arq_stop_transmission(self, received_json):
try: try:
if static.TNC_STATE == "BUSY" or static.ARQ_STATE: if TNC.tnc_state == "BUSY" or ARQ.arq_state:
DATA_QUEUE_TRANSMIT.put(["STOP"]) DATA_QUEUE_TRANSMIT.put(["STOP"])
log.warning("[SCK] Stopping transmission!") log.warning("[SCK] Stopping transmission!")
static.TNC_STATE = "IDLE" TNC.tnc_state = "IDLE"
static.ARQ_STATE = False ARQ.arq_state = False
command_response("stop_transmission", True) command_response("stop_transmission", True)
except Exception as err: except Exception as err:
command_response("stop_transmission", False) command_response("stop_transmission", False)
@ -803,7 +802,7 @@ class ThreadedTCPRequestHandler(socketserver.StreamRequestHandler):
if ( if (
received_json["type"] == "set" received_json["type"] == "set"
and received_json["command"] == "start_tnc" and received_json["command"] == "start_tnc"
and not static.TNCSTARTED and not Daemon.tncstarted
): ):
self.daemon_start_tnc(received_json) self.daemon_start_tnc(received_json)
@ -821,18 +820,18 @@ class ThreadedTCPRequestHandler(socketserver.StreamRequestHandler):
self.request.sendall(b"INVALID CALLSIGN") self.request.sendall(b"INVALID CALLSIGN")
log.warning( log.warning(
"[SCK] SET MYCALL FAILED", "[SCK] SET MYCALL FAILED",
call=static.MYCALLSIGN, call=Station.mycallsign,
crc=static.MYCALLSIGN_CRC.hex(), crc=Station.mycallsign_crc.hex(),
) )
else: else:
static.MYCALLSIGN = bytes(callsign, "utf-8") Station.mycallsign = bytes(callsign, "utf-8")
static.MYCALLSIGN_CRC = helpers.get_crc_24(static.MYCALLSIGN) Station.mycallsign_crc = helpers.get_crc_24(Station.mycallsign)
command_response("mycallsign", True) command_response("mycallsign", True)
log.info( log.info(
"[SCK] SET MYCALL", "[SCK] SET MYCALL",
call=static.MYCALLSIGN, call=Station.mycallsign,
crc=static.MYCALLSIGN_CRC.hex(), crc=Station.mycallsign_crc.hex(),
) )
except Exception as err: except Exception as err:
command_response("mycallsign", False) command_response("mycallsign", False)
@ -846,8 +845,8 @@ class ThreadedTCPRequestHandler(socketserver.StreamRequestHandler):
self.request.sendall(b"INVALID GRID") self.request.sendall(b"INVALID GRID")
command_response("mygrid", False) command_response("mygrid", False)
else: else:
static.MYGRID = bytes(mygrid, "utf-8") Station.mygrid = bytes(mygrid, "utf-8")
log.info("[SCK] SET MYGRID", grid=static.MYGRID) log.info("[SCK] SET MYGRID", grid=Station.mygrid)
command_response("mygrid", True) command_response("mygrid", True)
except Exception as err: except Exception as err:
command_response("mygrid", False) command_response("mygrid", False)
@ -928,12 +927,12 @@ class ThreadedTCPRequestHandler(socketserver.StreamRequestHandler):
def daemon_stop_tnc(self, received_json): def daemon_stop_tnc(self, received_json):
try: try:
static.TNCPROCESS.kill() Daemon.tncprocess.kill()
# unregister process from atexit to avoid process zombies # unregister process from atexit to avoid process zombies
atexit.unregister(static.TNCPROCESS.kill) atexit.unregister(Daemon.tncprocess.kill)
log.warning("[SCK] Stopping TNC") log.warning("[SCK] Stopping TNC")
static.TNCSTARTED = False Daemon.tncstarted = False
command_response("stop_tnc", True) command_response("stop_tnc", True)
except Exception as err: except Exception as err:
command_response("stop_tnc", False) command_response("stop_tnc", False)
@ -972,15 +971,15 @@ def send_daemon_state():
"command": "daemon_state", "command": "daemon_state",
"daemon_state": [], "daemon_state": [],
"python_version": str(python_version), "python_version": str(python_version),
"input_devices": static.AUDIO_INPUT_DEVICES, "input_devices": AudioParam.audio_input_devices,
"output_devices": static.AUDIO_OUTPUT_DEVICES, "output_devices": AudioParam.audio_output_devices,
"serial_devices": static.SERIAL_DEVICES, "serial_devices": Daemon.serial_devices,
# 'cpu': str(psutil.cpu_percent()), # 'cpu': str(psutil.cpu_percent()),
# 'ram': str(psutil.virtual_memory().percent), # 'ram': str(psutil.virtual_memory().percent),
"version": "0.1", "version": "0.1",
} }
if static.TNCSTARTED: if Daemon.tncstarted:
output["daemon_state"].append({"status": "running"}) output["daemon_state"].append({"status": "running"})
else: else:
output["daemon_state"].append({"status": "stopped"}) output["daemon_state"].append({"status": "stopped"})
@ -996,55 +995,55 @@ def send_tnc_state():
send the tnc state to network send the tnc state to network
""" """
encoding = "utf-8" encoding = "utf-8"
output = { output = {
"command": "tnc_state", "command": "tnc_state",
"ptt_state": str(static.PTT_STATE), "ptt_state": str(HamlibParam.ptt_state),
"tnc_state": str(static.TNC_STATE), "tnc_state": str(TNC.tnc_state),
"arq_state": str(static.ARQ_STATE), "arq_state": str(ARQ.arq_state),
"arq_session": str(static.ARQ_SESSION), "arq_session": str(ARQ.arq_session),
"arq_session_state": str(static.ARQ_SESSION_STATE), "arq_session_state": str(ARQ.arq_session_state),
"audio_dbfs": str(static.AUDIO_DBFS), "audio_dbfs": str(AudioParam.audio_dbfs),
"snr": str(static.SNR), "snr": str(ModemParam.snr),
"frequency": str(static.HAMLIB_FREQUENCY), "frequency": str(HamlibParam.hamlib_frequency),
"rf_level": str(static.HAMLIB_RF), "rf_level": str(HamlibParam.hamlib_rf),
"strength": str(static.HAMLIB_STRENGTH), "strength": str(HamlibParam.hamlib_strength),
"alc": str(static.HAMLIB_ALC), "alc": str(HamlibParam.alc),
"audio_level": str(static.TX_AUDIO_LEVEL), "audio_level": str(AudioParam.tx_audio_level),
"audio_auto_tune": str(static.AUDIO_AUTO_TUNE), "audio_auto_tune": str(AudioParam.audio_auto_tune),
"speed_level": str(static.ARQ_SPEED_LEVEL), "speed_level": str(ARQ.arq_speed_level),
"mode": str(static.HAMLIB_MODE), "mode": str(HamlibParam.hamlib_mode),
"bandwidth": str(static.HAMLIB_BANDWIDTH), "bandwidth": str(HamlibParam.hamlib_bandwidth),
"fft": str(static.FFT), "fft": str(AudioParam.fft),
"channel_busy": str(static.CHANNEL_BUSY), "channel_busy": str(ModemParam.channel_busy),
"is_codec2_traffic": str(static.IS_CODEC2_TRAFFIC), "channel_busy_slot": str(ModemParam.channel_busy_slot),
"scatter": static.SCATTER, "is_codec2_traffic": str(ModemParam.is_codec2_traffic),
"scatter": ModemParam.scatter,
"rx_buffer_length": str(RX_BUFFER.qsize()), "rx_buffer_length": str(RX_BUFFER.qsize()),
"rx_msg_buffer_length": str(len(static.RX_MSG_BUFFER)), "rx_msg_buffer_length": str(len(ARQ.rx_msg_buffer)),
"arq_bytes_per_minute": str(static.ARQ_BYTES_PER_MINUTE), "arq_bytes_per_minute": str(ARQ.bytes_per_minute),
"arq_bytes_per_minute_burst": str(static.ARQ_BYTES_PER_MINUTE_BURST), "arq_bytes_per_minute_burst": str(ARQ.bytes_per_minute_burst),
"arq_seconds_until_finish": str(static.ARQ_SECONDS_UNTIL_FINISH), "arq_seconds_until_finish": str(ARQ.arq_seconds_until_finish),
"arq_compression_factor": str(static.ARQ_COMPRESSION_FACTOR), "arq_compression_factor": str(ARQ.arq_compression_factor),
"arq_transmission_percent": str(static.ARQ_TRANSMISSION_PERCENT), "arq_transmission_percent": str(ARQ.arq_transmission_percent),
"speed_list": static.SPEED_LIST, "speed_list": ARQ.speed_list,
"total_bytes": str(static.TOTAL_BYTES), "total_bytes": str(ARQ.total_bytes),
"beacon_state": str(static.BEACON_STATE), "beacon_state": str(Beacon.beacon_state),
"stations": [], "stations": [],
"mycallsign": str(static.MYCALLSIGN, encoding), "mycallsign": str(Station.mycallsign, encoding),
"mygrid": str(static.MYGRID, encoding), "mygrid": str(Station.mygrid, encoding),
"dxcallsign": str(static.DXCALLSIGN, encoding), "dxcallsign": str(Station.dxcallsign, encoding),
"dxgrid": str(static.DXGRID, encoding), "dxgrid": str(Station.dxgrid, encoding),
"hamlib_status": static.HAMLIB_STATUS, "hamlib_status": HamlibParam.hamlib_status,
"listen": str(static.LISTEN), "listen": str(TNC.listen),
"audio_recording": str(static.AUDIO_RECORD), "audio_recording": str(AudioParam.audio_record),
} }
# add heard stations to heard stations object # add heard stations to heard stations object
for heard in static.HEARD_STATIONS: for heard in TNC.heard_stations:
output["stations"].append( output["stations"].append(
{ {
"dxcallsign": str(heard[0], "utf-8"), "dxcallsign": str(heard[0], encoding),
"dxgrid": str(heard[1], "utf-8"), "dxgrid": str(heard[1], encoding),
"timestamp": heard[2], "timestamp": heard[2],
"datatype": heard[3], "datatype": heard[3],
"snr": heard[4], "snr": heard[4],

View file

@ -5,13 +5,188 @@ Created on Wed Dec 23 11:13:57 2020
@author: DJ2LS @author: DJ2LS
Here we are saving application wide variables and stats, which have to be accessed everywhere. Here we are saving application wide variables and stats, which have to be accessed everywhere.
Not nice, suggestions are appreciated :-)
""" """
from dataclasses import dataclass, field
from typing import List
import subprocess import subprocess
from enum import Enum from enum import Enum
VERSION = "0.8.1-alpha"
# CHANNEL_STATE = 'RECEIVING_SIGNALLING'
# disconnected, connecting, connected, disconnecting, failed
# ------- RX BUFFER
@dataclass
class ARQ:
bytes_per_minute: int = 0
arq_transmission_percent: int = 0
arq_compression_factor: int = 0
arq_speed_level: int = 0
arq_bits_per_second_burst: int = 0
arq_bits_per_second: int = 0
arq_seconds_until_finish: int = 0
rx_buffer_size: int = 16
rx_frame_buffer: bytes = b""
rx_burst_buffer =[]
arq_session_state: str = "disconnected"
arq_session: bool = False
arq_state: bool = False
# ARQ PROTOCOL VERSION
# v.5 - signalling frame uses datac0
# v.6 - signalling frame uses datac13
arq_protocol_version: int = 6
total_bytes: int = 0
speed_list = []
# set save to folder state for allowing downloading files to local file system
arq_save_to_folder: bool = False
bytes_per_minute_burst: int = 0
rx_msg_buffer = []
@dataclass
class AudioParam:
tx_audio_level: int = 50
audio_input_devices = []
audio_output_devices = []
audio_input_device: int = -2
audio_output_device: int = -2
audio_record: bool = False
audio_record_file = ''
buffer_overflow_counter = []
audio_auto_tune: bool = False
# Audio TCI Support
audio_enable_tci: bool = False
audio_dbfs: int = 0
fft = []
enable_fft: bool = True
@dataclass
class Beacon:
beacon_state: bool = False
beacon_pause: bool = False
@dataclass
class Channel:
pass
@dataclass
class Daemon:
tncprocess: subprocess.Popen
tncstarted: bool = False
port: int = 3001
serial_devices = []
@dataclass
class HamlibParam:
alc: int = 0
hamlib_frequency: int = 0
hamlib_strength: int = 0
hamlib_radiocontrol: str = "disabled"
hamlib_rigctld_ip: str = "127.0.0.1"
hamlib_rigctld_port: str = "4532"
ptt_state: bool = False
hamlib_bandwidth: int = 0
hamlib_status: str = "unknown/disconnected"
hamlib_mode: str = ""
hamlib_rf: int = 0
@dataclass
class ModemParam:
tuning_range_fmin: float = -50.0
tuning_range_fmax: float = 50.0
channel_busy: bool = False
channel_busy_slot = [False] * 5
snr: float = 0
is_codec2_traffic: bool = False # true if we have codec2 signalling mode traffic on channel
frequency_offset: float = 0
tx_delay: int = 0 # delay in ms before sending modulation for triggering VOX for example or slow PTT radios
enable_scatter: bool = False
scatter = []
@dataclass
class Station:
mycallsign: bytes = b"AA0AA"
mycallsign_crc: bytes = b"A"
dxcallsign: bytes = b"ZZ9YY"
dxcallsign_crc: bytes = b"A"
mygrid: bytes = b""
dxgrid: bytes = b""
ssid_list = [] # ssid list we are responding to
@dataclass
class Statistics:
pass
@dataclass
class TCIParam:
ip: str = '127.0.0.1'
port: int = '9000'
@dataclass
class TNC:
version = "0.9.0-alpha-exp.6"
host: str = "0.0.0.0"
port: int = 3000
SOCKET_TIMEOUT: int = 1 # seconds
tnc_state: str = "IDLE"
enable_explorer = False
enable_stats = False
transmitting: bool = False
low_bandwidth_mode: bool = False
enable_fsk: bool = False
respond_to_cq: bool = True
respond_to_call: bool = True # respond to cq, ping, connection request, file request if not in session
heard_stations = []
listen: bool = True
# ------------
class FRAME_TYPE(Enum):
"""Lookup for frame types"""
BURST_01 = 10
BURST_02 = 11
BURST_03 = 12
BURST_04 = 13
# ...
BURST_51 = 50
BURST_ACK = 60
FR_ACK = 61
FR_REPEAT = 62
FR_NACK = 63
BURST_NACK = 64
CQ = 200
QRV = 201
PING = 210
PING_ACK = 211
IS_WRITING = 215
ARQ_SESSION_OPEN = 221
ARQ_SESSION_HB = 222
ARQ_SESSION_CLOSE = 223
ARQ_DC_OPEN_W = 225
ARQ_DC_OPEN_ACK_W = 226
ARQ_DC_OPEN_N = 227
ARQ_DC_OPEN_ACK_N = 228
ARQ_STOP = 249
BEACON = 250
FEC = 251
IDENT = 254
TEST_FRAME = 255
# ---------------------------------------------------------------
# DON'T USE THESE SETTINGS ANYMORE
# ---------------------------------------------------------------
# Fixme: REMOVE THESE OLD SETTINGS!
# REASON: For some reason ctests are failing when using dataclasses.
# We need to figure out whats happening and why the tests are failing.
CHANNEL_BUSY_SLOT = [False] * 5
ENABLE_EXPLORER = False ENABLE_EXPLORER = False
ENABLE_STATS = False ENABLE_STATS = False
@ -87,15 +262,15 @@ AUDIO_ENABLE_TCI: bool = False
TCI_IP: str = '127.0.0.1' TCI_IP: str = '127.0.0.1'
TCI_PORT: int = '9000' TCI_PORT: int = '9000'
AUDIO_DBFS: int = 0 AUDIO_DBFS: int = 0
FFT: list = [0] FFT: list = [0]
ENABLE_FFT: bool = True ENABLE_FFT: bool = True
CHANNEL_BUSY: bool = False CHANNEL_BUSY: bool = False
# ARQ PROTOCOL VERSION # ARQ PROTOCOL VERSION
ARQ_PROTOCOL_VERSION: int = 5 # v.5 - signalling frame uses datac0
# v.6 - signalling frame uses datac13
ARQ_PROTOCOL_VERSION: int = 6
# ARQ statistics # ARQ statistics
SPEED_LIST: list = [] SPEED_LIST: list = []
@ -139,32 +314,3 @@ INFO: list = []
TUNING_RANGE_FMIN: float = -50.0 TUNING_RANGE_FMIN: float = -50.0
TUNING_RANGE_FMAX: float = 50.0 TUNING_RANGE_FMAX: float = 50.0
IS_CODEC2_TRAFFIC: bool = False # true if we have codec2 signalling mode traffic on channel IS_CODEC2_TRAFFIC: bool = False # true if we have codec2 signalling mode traffic on channel
class FRAME_TYPE(Enum):
"""Lookup for frame types"""
BURST_01 = 10
# ...
BURST_51 = 50
BURST_ACK = 60
FR_ACK = 61
FR_REPEAT = 62
FR_NACK = 63
BURST_NACK = 64
CQ = 200
QRV = 201
PING = 210
PING_ACK = 211
IS_WRITING = 215
ARQ_SESSION_OPEN = 221
ARQ_SESSION_HB = 222
ARQ_SESSION_CLOSE = 223
ARQ_DC_OPEN_W = 225
ARQ_DC_OPEN_ACK_W = 226
ARQ_DC_OPEN_N = 227
ARQ_DC_OPEN_ACK_N = 228
ARQ_STOP = 249
BEACON = 250
FEC = 251
IDENT = 254
TEST_FRAME = 255

View file

@ -13,6 +13,7 @@ import time
import ujson as json import ujson as json
import structlog import structlog
import static import static
from static import ARQ, AudioParam, Beacon, Channel, Daemon, HamlibParam, ModemParam, Station, TCIParam, TNC
log = structlog.get_logger("stats") log = structlog.get_logger("stats")
@ -25,29 +26,29 @@ class stats():
crcerror = status in ["crc_error", "wrong_crc"] crcerror = status in ["crc_error", "wrong_crc"]
# get avg snr # get avg snr
try: try:
snr_raw = [item["snr"] for item in static.SPEED_LIST] snr_raw = [item["snr"] for item in ARQ.speed_list]
avg_snr = round(sum(snr_raw) / len(snr_raw), 2 ) avg_snr = round(sum(snr_raw) / len(snr_raw), 2 )
except Exception: except Exception:
avg_snr = 0 avg_snr = 0
headers = {"Content-Type": "application/json"} headers = {"Content-Type": "application/json"}
station_data = { station_data = {
'callsign': str(static.MYCALLSIGN, "utf-8"), 'callsign': str(Station.mycallsign, "utf-8"),
'dxcallsign': str(static.DXCALLSIGN, "utf-8"), 'dxcallsign': str(Station.dxcallsign, "utf-8"),
'gridsquare': str(static.MYGRID, "utf-8"), 'gridsquare': str(Station.mygrid, "utf-8"),
'dxgridsquare': str(static.DXGRID, "utf-8"), 'dxgridsquare': str(Station.dxgrid, "utf-8"),
'frequency': 0 if static.HAMLIB_FREQUENCY is None else static.HAMLIB_FREQUENCY, 'frequency': 0 if HamlibParam.hamlib_frequency is None else HamlibParam.hamlib_frequency,
'avgstrength': 0, 'avgstrength': 0,
'avgsnr': avg_snr, 'avgsnr': avg_snr,
'bytesperminute': static.ARQ_BYTES_PER_MINUTE, 'bytesperminute': ARQ.bytes_per_minute,
'filesize': static.TOTAL_BYTES, 'filesize': ARQ.total_bytes,
'compressionfactor': static.ARQ_COMPRESSION_FACTOR, 'compressionfactor': ARQ.arq_compression_factor,
'nacks': frame_nack_counter, 'nacks': frame_nack_counter,
'crcerror': crcerror, 'crcerror': crcerror,
'duration': duration, 'duration': duration,
'percentage': static.ARQ_TRANSMISSION_PERCENT, 'percentage': ARQ.arq_transmission_percent,
'status': status, 'status': status,
'version': static.VERSION 'version': TNC.version
} }
station_data = json.dumps(station_data) station_data = json.dumps(station_data)

View file

@ -7,8 +7,9 @@ import websocket
import numpy as np import numpy as np
import time import time
from queues import AUDIO_TRANSMIT_QUEUE, AUDIO_RECEIVED_QUEUE from queues import AUDIO_TRANSMIT_QUEUE, AUDIO_RECEIVED_QUEUE
from static import ARQ, AudioParam, Beacon, Channel, Daemon, HamlibParam, ModemParam, Station, TCIParam, TNC
class TCI: class TCICtrl:
def __init__(self, hostname='127.0.0.1', port=50001): def __init__(self, hostname='127.0.0.1', port=50001):
# websocket.enableTrace(True) # websocket.enableTrace(True)
self.log = structlog.get_logger("TCI") self.log = structlog.get_logger("TCI")