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:
- uses: actions/checkout@v3
with:
ref: ${{ github.head_ref }}
- uses: uraimo/run-on-arch-action@v2
name: Build artifact
id: build

View file

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

View file

@ -44,7 +44,7 @@ jobs:
shell: bash
run: |
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
- name: run ctests

View file

@ -58,19 +58,19 @@ add_test(NAME tnc_irs_iss
# python3 test_chat_text.py")
# 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;
export PYTHONPATH=../tnc;
cd ${CMAKE_CURRENT_SOURCE_DIR}/test;
python3 test_datac0.py")
set_tests_properties(datac0_frames PROPERTIES PASS_REGULAR_EXPRESSION "errors: 0")
python3 test_datac13.py")
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;
export PYTHONPATH=../tnc;
cd ${CMAKE_CURRENT_SOURCE_DIR}/test;
python3 test_datac0_negative.py")
set_tests_properties(datac0_frames_negative PROPERTIES PASS_REGULAR_EXPRESSION "errors: 0")
python3 test_datac13_negative.py")
set_tests_properties(datac13_frames_negative PROPERTIES PASS_REGULAR_EXPRESSION "errors: 0")
add_test(NAME helper_routines
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;
cd ${CMAKE_CURRENT_SOURCE_DIR}/test;
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
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;
PATH=$PATH:${CODEC2_BUILD_DIR}/src;
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 - |
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")
add_test(NAME highsnr_stdio_C_P_single
COMMAND sh -c "export LD_LIBRARY_PATH=${CODEC2_BUILD_DIR}/src;
PATH=$PATH:${CODEC2_BUILD_DIR}/src;
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 - |
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}")
add_test(NAME highsnr_stdio_P_P_single
COMMAND sh -c "export LD_LIBRARY_PATH=${CODEC2_BUILD_DIR}/src;
PATH=$PATH:${CODEC2_BUILD_DIR}/src;
cd ${CMAKE_CURRENT_SOURCE_DIR}/test;
python3 util_tx.py --mode datac0 --delay 500 --framesperburst ${FRAMESPERBURST} --bursts ${BURSTS} |
python3 util_rx.py --debug --mode datac0 --framesperburst ${FRAMESPERBURST} --bursts ${BURSTS}")
python3 util_tx.py --mode datac13 --delay 500 --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}")
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;
cd ${CMAKE_CURRENT_SOURCE_DIR}/test;
python3 util_multimode_tx.py --delay 500 --framesperburst ${FRAMESPERBURST} --bursts ${BURSTS} |
python3 util_multimode_rx.py --framesperburst ${FRAMESPERBURST} --bursts ${BURSTS} --timeout 20")
set_tests_properties(highsnr_stdio_P_P_multi PROPERTIES PASS_REGULAR_EXPRESSION "DATAC0: ${BURSTS}/${FRAMESPERBURST} DATAC1: ${BURSTS}/${FRAMESPERBURST} DATAC3: ${BURSTS}/${FRAMESPERBURST}")
python3 util_multimode_rx.py --framesperburst ${FRAMESPERBURST} --bursts ${BURSTS} --timeout 60")
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
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;
cd ${CMAKE_CURRENT_SOURCE_DIR}/test;
./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
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;
cd ${CMAKE_CURRENT_SOURCE_DIR}/test;
./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
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;
cd ${CMAKE_CURRENT_SOURCE_DIR}/test;
./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()

View file

@ -483,7 +483,7 @@ window.addEventListener("DOMContentLoaded", () => {
command: "msg",
dxcallsign: dxcallsign,
mode: 255,
frames: 1,
frames: 5,
data: data_with_attachment,
checksum: file_checksum,
uuid: uuid,
@ -1443,7 +1443,7 @@ update_chat = function (obj) {
command: "msg",
dxcallsign: doc.dxcallsign,
mode: 255,
frames: 1,
frames: 5,
data: data_with_attachment,
checksum: doc.checksum,
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: ";
// SET BYTES PER MINUTE
if (typeof data.bytesperminute == "undefined") {
var arq_bytes_per_minute = 0;
} else {
@ -1648,7 +1649,7 @@ ipcRenderer.on("action-update-reception-status", (event, arg) => {
var arq_bytes_per_minute_compressed = Math.round(
arq_bytes_per_minute * compress
);
console.log(arq_bytes_per_minute);
time_left +=
formatBytes(arq_bytes_per_minute, 1) +
" (comp: " +
@ -2210,11 +2211,14 @@ function updateHeardStations(arg) {
//https://stackoverflow.com/a/847196
timestampRaw = arg.stations[i]["timestamp"];
var date = new Date(timestampRaw * 1000);
var hours = date.getHours();
var minutes = "0" + date.getMinutes();
var seconds = "0" + date.getSeconds();
var datetime = hours + ":" + minutes.substr(-2) + ":" + seconds.substr(-2);
var datetime = new Date(timestampRaw * 1000).toLocaleString(
navigator.language
);
//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 timestampText = document.createElement("span");

View file

@ -198,6 +198,7 @@ client.on("data", function (socketdata) {
dbfs_level: data["audio_dbfs"],
fft: data["fft"],
channel_busy: data["channel_busy"],
channel_busy_slot: data["channel_busy_slot"],
scatter: data["scatter"],
info: data["info"],
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
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=$!
#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}

View file

@ -204,7 +204,7 @@ for i in range(N_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():
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.
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
"""
@ -28,9 +28,9 @@ import pytest
import structlog
try:
import test.util_datac0 as util
import test.util_datac13 as util
except ImportError:
import util_datac0 as util
import util_datac13 as util
STATIONS = ["AA2BB", "ZZ9YY"]
@ -48,11 +48,11 @@ def parameters() -> dict:
connect_data = {"type": "arq", "command": "connect", "dxcallsign": "ZZ9YY-0"}
stop_data = {"type": "arq", "command": "stop_transmission", "dxcallsign": "ZZ9YY-0"}
beacon_timeout = 6
cq_timeout = 8
ping_timeout = 5
connect_timeout = 10
stop_timeout = 5
beacon_timeout = 1
ping_timeout = 1
cq_timeout = 1
connect_timeout = 1
stop_timeout = 1
beacon_tx_check = '"beacon":"transmitting"'
cq_tx_check = '"qrv":"received"'
@ -192,13 +192,13 @@ def analyze_results(station1: list, station2: list, call_list: list):
pytest.param("beacon", 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.xfail(reason="Too unstable for CI")),
pytest.param("stop", marks=pytest.mark.flaky(reruns=0)),
#pytest.param("cq", marks=pytest.mark.xfail(reason="Too unstable for CI")),
pytest.param("stop", marks=pytest.mark.flaky(reruns=2)),
],
)
def test_datac0(frame_type: str, tmp_path):
log_handler.setup_logging(filename=tmp_path / "test_datac0", level="DEBUG")
log = structlog.get_logger("test_datac0")
def test_datac13(frame_type: str, tmp_path):
log_handler.setup_logging(filename=tmp_path / "test_datac13", level="DEBUG")
log = structlog.get_logger("test_datac13")
s1_data = []
s2_data = []
@ -227,7 +227,7 @@ def test_datac0(frame_type: str, tmp_path):
from_s2, s2_send = multiprocessing.Pipe()
proc = [
multiprocessing.Process(
target=util.t_datac0_1,
target=util.t_datac13_1,
args=(
s1_send,
STATIONS[0],
@ -238,7 +238,7 @@ def test_datac0(frame_type: str, tmp_path):
daemon=True,
),
multiprocessing.Process(
target=util.t_datac0_2,
target=util.t_datac13_2,
args=(
s2_send,
STATIONS[1],

View file

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

View file

@ -14,7 +14,7 @@ import sys
import helpers
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"])
@ -22,7 +22,7 @@ def test_check_callsign(callsign: str):
"""
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 = 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.
"""
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_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
):
"""
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
:type bursts: str
@ -152,7 +152,7 @@ def t_HighSNR_C_P_DATACx(
@pytest.mark.parametrize("bursts", [BURSTS])
@pytest.mark.parametrize("frames_per_burst", [FRAMESPERBURST])
@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(
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):
"""
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
: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("bursts", [BURSTS])
@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):
proc = multiprocessing.Process(
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("bursts", [BURSTS])
@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):
proc = multiprocessing.Process(
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):
"""
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
: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")
]
)
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"DATAC3: {bursts}/{frames_per_burst * bursts}" in 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.
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
"""
@ -26,7 +26,7 @@ sys.path.insert(0, "..")
sys.path.insert(0, "../tnc")
import data_handler
import helpers
import static
from static import ARQ, AudioParam, Beacon, Channel, Daemon, HamlibParam, ModemParam, Station, Statistics, TCIParam, TNC
def print_frame(data: bytearray):
@ -148,18 +148,18 @@ def t_foreign_disconnect(mycall: str, dxcall: str):
:rtype: bytearray
"""
# 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.
mycallsign_bytes = helpers.callsign_to_bytes(mycall)
mycallsign = helpers.bytes_to_callsign(mycallsign_bytes)
static.MYCALLSIGN = mycallsign
static.MYCALLSIGN_CRC = helpers.get_crc_24(mycallsign)
Station.mycallsign = mycallsign
Station.mycallsign_crc = helpers.get_crc_24(mycallsign)
dxcallsign_bytes = helpers.callsign_to_bytes(dxcall)
dxcallsign = helpers.bytes_to_callsign(dxcallsign_bytes)
static.DXCALLSIGN = dxcallsign
static.DXCALLSIGN_CRC = helpers.get_crc_24(dxcallsign)
Station.dxcallsign = dxcallsign
Station.dxcallsign_crc = helpers.get_crc_24(dxcallsign)
# Create the TNC
tnc = data_handler.DATA()
@ -173,12 +173,12 @@ def t_foreign_disconnect(mycall: str, dxcall: str):
print_frame(create_frame)
tnc.received_session_opener(create_frame)
assert helpers.callsign_to_bytes(static.MYCALLSIGN) == mycallsign_bytes
assert helpers.callsign_to_bytes(static.DXCALLSIGN) == dxcallsign_bytes
assert helpers.callsign_to_bytes(Station.mycallsign) == mycallsign_bytes
assert helpers.callsign_to_bytes(Station.dxcallsign) == dxcallsign_bytes
assert static.ARQ_SESSION is True
assert static.TNC_STATE == "BUSY"
assert static.ARQ_SESSION_STATE == "connecting"
assert ARQ.arq_session is True
assert TNC.tnc_state == "BUSY"
assert ARQ.arq_session_state == "connecting"
# Set up a frame from a non-associated station.
# foreigncall_bytes = helpers.callsign_to_bytes("ZZ0ZZ-0")
@ -193,8 +193,8 @@ def t_foreign_disconnect(mycall: str, dxcall: str):
print_frame(close_frame)
# assert (
# helpers.check_callsign(static.DXCALLSIGN, bytes(close_frame[4:7]))[0] is False
# ), f"{helpers.get_crc_24(static.DXCALLSIGN)} == {bytes(close_frame[4:7])} but should be not equal."
# helpers.check_callsign(Station.dxcallsign, bytes(close_frame[4:7]))[0] is False
# ), f"{helpers.get_crc_24(Station.dxcallsign)} == {bytes(close_frame[4:7])} but should be not equal."
# assert (
# 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
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
), f"{static.MYCALLSIGN} != {mycall} but should equal."
assert helpers.callsign_to_bytes(static.DXCALLSIGN) == helpers.callsign_to_bytes(
), f"{Station.mycallsign} != {mycall} but should equal."
assert helpers.callsign_to_bytes(Station.dxcallsign) == helpers.callsign_to_bytes(
dxcall
), f"{static.DXCALLSIGN} != {dxcall} but should equal."
), f"{Station.dxcallsign} != {dxcall} but should equal."
assert static.ARQ_SESSION is True
assert static.TNC_STATE == "BUSY"
assert static.ARQ_SESSION_STATE == "connecting"
assert ARQ.arq_session is True
assert TNC.tnc_state == "BUSY"
assert ARQ.arq_session_state == "connecting"
def t_valid_disconnect(mycall: str, dxcall: str):
@ -228,18 +228,18 @@ def t_valid_disconnect(mycall: str, dxcall: str):
:rtype: bytearray
"""
# 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.
mycallsign_bytes = helpers.callsign_to_bytes(mycall)
mycallsign = helpers.bytes_to_callsign(mycallsign_bytes)
static.MYCALLSIGN = mycallsign
static.MYCALLSIGN_CRC = helpers.get_crc_24(mycallsign)
Station.mycallsign = mycallsign
Station.mycallsign_crc = helpers.get_crc_24(mycallsign)
dxcallsign_bytes = helpers.callsign_to_bytes(dxcall)
dxcallsign = helpers.bytes_to_callsign(dxcallsign_bytes)
static.DXCALLSIGN = dxcallsign
static.DXCALLSIGN_CRC = helpers.get_crc_24(dxcallsign)
Station.dxcallsign = dxcallsign
Station.dxcallsign_crc = helpers.get_crc_24(dxcallsign)
# Create the TNC
tnc = data_handler.DATA()
@ -253,9 +253,9 @@ def t_valid_disconnect(mycall: str, dxcall: str):
print_frame(create_frame)
tnc.received_session_opener(create_frame)
assert static.ARQ_SESSION is True
assert static.TNC_STATE == "BUSY"
assert static.ARQ_SESSION_STATE == "connecting"
assert ARQ.arq_session is True
assert TNC.tnc_state == "BUSY"
assert ARQ.arq_session_state == "connecting"
# Create packet to be 'received' by this station.
# 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)
tnc.received_session_close(close_frame)
assert helpers.callsign_to_bytes(static.MYCALLSIGN) == mycallsign_bytes
assert helpers.callsign_to_bytes(static.DXCALLSIGN) == dxcallsign_bytes
assert helpers.callsign_to_bytes(Station.mycallsign) == mycallsign_bytes
assert helpers.callsign_to_bytes(Station.dxcallsign) == dxcallsign_bytes
assert static.ARQ_SESSION is False
assert static.TNC_STATE == "IDLE"
assert static.ARQ_SESSION_STATE == "disconnected"
assert ARQ.arq_session is False
assert TNC.tnc_state == "IDLE"
assert ARQ.arq_session_state == "disconnected"
# 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
RX_LOG=$(mktemp)
MAX_RUN_TIME=2600
MAX_RUN_TIME=2700
# make sure all child processes are killed when we 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=$!
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}

View file

@ -8,9 +8,9 @@ MAX_RUN_TIME=2600
trap 'jobs -p | xargs -r kill' EXIT
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=$!
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
wait ${rx_pid}

View file

@ -8,8 +8,8 @@ MAX_RUN_TIME=2600
trap 'jobs -p | xargs -r kill' EXIT
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=$!
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}

View file

@ -7,9 +7,9 @@ MAX_RUN_TIME=2600
# make sure all child processes are killed when we 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=$!
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
wait ${rx_pid}

View file

@ -16,8 +16,8 @@ check_alsa_loopback
# make sure all child processes are killed when we 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=$!
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}

View file

@ -16,8 +16,8 @@ check_alsa_loopback
# make sure all child processes are killed when we 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=$!
#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}

View file

@ -16,8 +16,8 @@ check_alsa_loopback
# make sure all child processes are killed when we 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=$!
#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}

View file

@ -112,20 +112,20 @@ class Test:
sys.exit()
# open codec2 instance
self.datac0_freedv = ctypes.cast(
codec2.api.freedv_open(codec2.api.FREEDV_MODE_DATAC0), ctypes.c_void_p
self.datac13_freedv = ctypes.cast(
codec2.api.freedv_open(codec2.FREEDV_MODE.datac13.value), ctypes.c_void_p
)
self.datac0_bytes_per_frame = int(
codec2.api.freedv_get_bits_per_modem_frame(self.datac0_freedv) / 8
self.datac13_bytes_per_frame = int(
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(
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(
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(
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.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(
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)
# SET COUNTERS
self.rx_total_frames_datac0 = 0
self.rx_frames_datac0 = 0
self.rx_bursts_datac0 = 0
self.rx_total_frames_datac13 = 0
self.rx_frames_datac13 = 0
self.rx_bursts_datac13 = 0
self.rx_total_frames_datac1 = 0
self.rx_frames_datac1 = 0
@ -173,7 +173,7 @@ class Test:
self.frx = open("rx48_callback_multimode.raw", mode="wb")
# 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.datac3_nin = codec2.api.freedv_nin(self.datac3_freedv)
@ -187,26 +187,26 @@ class Test:
x.tofile(self.frx)
x = self.resampler.resample48_to_8(x)
self.datac0_buffer.push(x)
self.datac13_buffer.push(x)
self.datac1_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
nbytes = codec2.api.freedv_rawdatarx(
self.datac0_freedv,
self.datac0_bytes_out,
self.datac0_buffer.buffer.ctypes,
self.datac13_freedv,
self.datac13_bytes_out,
self.datac13_buffer.buffer.ctypes,
)
self.datac0_buffer.pop(self.datac0_nin)
self.datac0_nin = codec2.api.freedv_nin(self.datac0_freedv)
if nbytes == self.datac0_bytes_per_frame:
self.rx_total_frames_datac0 = self.rx_total_frames_datac0 + 1
self.rx_frames_datac0 = self.rx_frames_datac0 + 1
self.datac13_buffer.pop(self.datac13_nin)
self.datac13_nin = codec2.api.freedv_nin(self.datac13_freedv)
if nbytes == self.datac13_bytes_per_frame:
self.rx_total_frames_datac13 = self.rx_total_frames_datac13 + 1
self.rx_frames_datac13 = self.rx_frames_datac13 + 1
if self.rx_frames_datac0 == self.N_FRAMES_PER_BURST:
self.rx_frames_datac0 = 0
self.rx_bursts_datac0 = self.rx_bursts_datac0 + 1
if self.rx_frames_datac13 == self.N_FRAMES_PER_BURST:
self.rx_frames_datac13 = 0
self.rx_bursts_datac13 = self.rx_bursts_datac13 + 1
while self.datac1_buffer.nbuffer >= self.datac1_nin:
# demodulate audio
@ -243,7 +243,7 @@ class Test:
self.rx_bursts_datac3 = self.rx_bursts_datac3 + 1
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.receive = False
@ -253,11 +253,11 @@ class Test:
while self.receive:
time.sleep(0.01)
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.datac0_rxstatus
self.datac13_rxstatus = codec2.api.rx_sync_flags_to_text[
self.datac13_rxstatus
]
self.datac1_rxstatus = codec2.api.freedv_get_rx_status(
@ -277,8 +277,8 @@ class Test:
print(
"NIN0: %5d RX_STATUS0: %4s NIN1: %5d RX_STATUS1: %4s NIN3: %5d RX_STATUS3: %4s"
% (
self.datac0_nin,
self.datac0_rxstatus,
self.datac13_nin,
self.datac13_rxstatus,
self.datac1_nin,
self.datac1_rxstatus,
self.datac3_nin,
@ -309,7 +309,7 @@ class Test:
)
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,
)
self.frx.close()

View file

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

View file

@ -158,9 +158,9 @@ class Test:
def create_modulation(self):
modes = [
codec2.api.FREEDV_MODE_DATAC0,
codec2.api.FREEDV_MODE_DATAC1,
codec2.api.FREEDV_MODE_DATAC3,
codec2.FREEDV_MODE.datac13.value,
codec2.FREEDV_MODE.datac1.value,
codec2.FREEDV_MODE.datac3.value,
]
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("--framesperburst", dest="N_FRAMES_PER_BURST", default=1, type=int)
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(
"--audiodev",
@ -35,7 +35,7 @@ parser.add_argument("--debug", dest="DEBUGGING_MODE", action="store_true")
parser.add_argument(
"--timeout",
dest="TIMEOUT",
default=10,
default=60,
type=int,
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("--framesperburst", dest="N_FRAMES_PER_BURST", default=1, type=int)
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(
"--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("--delay", dest="DELAY_BETWEEN_BURSTS", default=500, type=int)
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(
"--audiodev",

View file

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

View file

@ -19,7 +19,7 @@ import data_handler
import helpers
import modem
import sock
import static
from static import ARQ, AudioParam, Beacon, Channel, Daemon, HamlibParam, ModemParam, Station, Statistics, TCIParam, TNC
import structlog
@ -36,23 +36,23 @@ def t_setup(
modem.RXCHANNEL = tmp_path / "hfchannel2"
modem.TESTMODE = True
modem.TXCHANNEL = tmp_path / "hfchannel1"
static.HAMLIB_RADIOCONTROL = "disabled"
static.LOW_BANDWIDTH_MODE = lowbwmode
static.MYGRID = bytes("AA12aa", "utf-8")
static.RESPOND_TO_CQ = True
static.SSID_LIST = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
HamlibParam.hamlib_radiocontrol = "disabled"
TNC.low_bandwidth_mode = lowbwmode
Station.mygrid = bytes("AA12aa", "utf-8")
TNC.respond_to_cq = True
Station.ssid_list = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
# 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.bytes_to_callsign(mycallsign)
static.MYCALLSIGN = mycallsign
static.MYCALLSIGN_CRC = helpers.get_crc_24(static.MYCALLSIGN)
Station.mycallsign = mycallsign
Station.mycallsign_crc = helpers.get_crc_24(Station.mycallsign)
dxcallsign = helpers.callsign_to_bytes(dxcall)
dxcallsign = helpers.bytes_to_callsign(dxcallsign)
static.DXCALLSIGN = dxcallsign
static.DXCALLSIGN_CRC = helpers.get_crc_24(static.DXCALLSIGN)
Station.dxcallsign = dxcallsign
Station.dxcallsign_crc = helpers.get_crc_24(Station.dxcallsign)
# Create the TNC
tnc = data_handler.DATA()
@ -136,13 +136,13 @@ def t_highsnr_arq_short_station2(
# the queue to an object for comparisons.
while (
'"arq":"transmission","status":"received"' not in str(sock.SOCKET_QUEUE.queue)
or static.ARQ_STATE
or ARQ.arq_state
):
if time.time() > timeout:
log.warning("station2 TIMEOUT", first=True)
break
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.
timeout = time.time() + 20
@ -151,7 +151,7 @@ def t_highsnr_arq_short_station2(
log.warning("station2", TIMEOUT=True)
break
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 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
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
"""
@ -21,9 +21,10 @@ import data_handler
import helpers
import modem
import sock
import static
import structlog
from static import ARQ, HamlibParam, ModemParam, Station, TNC as TNCParam
from static import FRAME_TYPE as FR_TYPE
import structlog
#from static import FRAME_TYPE as FR_TYPE
def t_setup(
@ -46,33 +47,35 @@ def t_setup(
modem.RXCHANNEL = tmp_path / rx_channel
modem.TESTMODE = True
modem.TXCHANNEL = tmp_path / tx_channel
static.HAMLIB_RADIOCONTROL = "disabled"
static.LOW_BANDWIDTH_MODE = lowbwmode or True
static.MYGRID = bytes("AA12aa", "utf-8")
static.RESPOND_TO_CQ = True
static.SSID_LIST = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
HamlibParam.hamlib_radiocontrol = "disabled"
TNCParam.low_bandwidth_mode = lowbwmode or True
Station.mygrid = bytes("AA12aa", "utf-8")
TNCParam.respond_to_cq = True
Station.ssid_list = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
mycallsign = helpers.callsign_to_bytes(mycall)
mycallsign = helpers.bytes_to_callsign(mycallsign)
static.MYCALLSIGN = mycallsign
static.MYCALLSIGN_CRC = helpers.get_crc_24(static.MYCALLSIGN)
Station.mycallsign = mycallsign
Station.mycallsign_crc = helpers.get_crc_24(Station.mycallsign)
dxcallsign = helpers.callsign_to_bytes(dxcall)
dxcallsign = helpers.bytes_to_callsign(dxcallsign)
static.DXCALLSIGN = dxcallsign
static.DXCALLSIGN_CRC = helpers.get_crc_24(static.DXCALLSIGN)
Station.dxcallsign = dxcallsign
Station.dxcallsign_crc = helpers.get_crc_24(Station.dxcallsign)
# Create the TNC
tnc = data_handler.DATA()
tnc_data_handler = data_handler.DATA()
orig_rx_func = data_handler.DATA.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
tnc.time_list_low_bw = [3, 1, 1]
tnc.time_list_high_bw = [3, 1, 1]
tnc.time_list = [3, 1, 1]
tnc_data_handler.time_list_low_bw = [8, 8, 8]
tnc_data_handler.time_list_high_bw = [8, 8, 8]
tnc_data_handler.time_list = [8, 8, 8]
# 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
t_modem = modem.RF()
@ -80,10 +83,10 @@ def t_setup(
modem.RF.transmit = t_transmit
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,
mycall: str,
dxcall: str,
@ -93,7 +96,7 @@ def t_datac0_1(
log = structlog.get_logger("station1")
orig_tx_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
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.
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,
mycall,
dxcall,
@ -143,20 +146,20 @@ def t_datac0_1(
tmp_path,
)
log.info("t_datac0_1:", RXCHANNEL=modem.RXCHANNEL)
log.info("t_datac0_1:", TXCHANNEL=modem.TXCHANNEL)
log.info("t_datac13_1:", RXCHANNEL=modem.RXCHANNEL)
log.info("t_datac13_1:", TXCHANNEL=modem.TXCHANNEL)
time.sleep(0.5)
if "stop" in data["command"]:
log.debug("t_datac0_1: STOP test, setting TNC state")
static.TNC_STATE = "BUSY"
static.ARQ_STATE = True
log.debug("t_datac13_1: STOP test, setting TNC state")
TNCParam.tnc_state = "BUSY"
ARQ.arq_state = True
sock.ThreadedTCPRequestHandler.process_tnc_commands(None,json.dumps(data, indent=None))
time.sleep(0.5)
sock.ThreadedTCPRequestHandler.process_tnc_commands(None,json.dumps(data, indent=None))
# Assure the test completes.
timeout = time.time() + timeout_duration
timeout = time.time() + timeout_duration
while tx_check not in str(sock.SOCKET_QUEUE.queue):
if time.time() > timeout:
log.warning(
@ -166,25 +169,25 @@ def t_datac0_1(
tx_check=tx_check,
)
break
time.sleep(0.1)
time.sleep(0.5)
log.info("station1, first")
# 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}
sock.ThreadedTCPRequestHandler.process_tnc_commands(None,json.dumps(data, indent=None))
time.sleep(0.5)
# Allow enough time for this side to process the disconnect frame.
timeout = time.time() + timeout_duration
while tnc.data_queue_transmit.queue:
timeout = time.time() + timeout_duration
while tnc_data_handler.data_queue_transmit.queue:
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
time.sleep(0.5)
log.info("station1, final")
# 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 DQT: ", DQ_Tx=pformat(TNCParam.data_queue_transmit.queue))
# log.info("S1 DQR: ", DQ_Rx=pformat(TNCParam.data_queue_received.queue))
log.debug("S1 Socket: ", socket_queue=pformat(sock.SOCKET_QUEUE.queue))
for item in final_tx_check:
@ -199,7 +202,7 @@ def t_datac0_1(
log.warning("station1: Exiting!")
def t_datac0_2(
def t_datac13_2(
parent_pipe,
mycall: str,
dxcall: str,
@ -209,7 +212,7 @@ def t_datac0_2(
log = structlog.get_logger("station2")
orig_tx_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
data, timeout_duration, _, rx_check, _, final_rx_check = config
@ -259,8 +262,8 @@ def t_datac0_2(
tmp_path,
)
log.info("t_datac0_2:", RXCHANNEL=modem.RXCHANNEL)
log.info("t_datac0_2:", TXCHANNEL=modem.TXCHANNEL)
log.info("t_datac13_2:", RXCHANNEL=modem.RXCHANNEL)
log.info("t_datac13_2:", TXCHANNEL=modem.TXCHANNEL)
if "cq" in data:
t_data = {"type": "arq", "command": "stop_transmission"}
@ -268,7 +271,7 @@ def t_datac0_2(
sock.ThreadedTCPRequestHandler.process_tnc_commands(None,json.dumps(t_data, indent=None))
# Assure the test completes.
timeout = time.time() + timeout_duration
timeout = time.time() + timeout_duration
# Compare with the string conversion instead of repeatedly dumping
# the queue to an object for comparisons.
while rx_check not in str(sock.SOCKET_QUEUE.queue):
@ -284,7 +287,7 @@ def t_datac0_2(
log.info("station2, first")
# 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):
if time.time() > timeout:
log.warning("station2", TIMEOUT=True, queue=str(sock.SOCKET_QUEUE.queue))
@ -292,8 +295,8 @@ def t_datac0_2(
time.sleep(0.5)
log.info("station2, final")
# 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 DQT: ", DQ_Tx=pformat(TNCParam.data_queue_transmit.queue))
# log.info("S2 DQR: ", DQ_Rx=pformat(TNCParam.data_queue_received.queue))
log.debug("S2 Socket: ", socket_queue=pformat(sock.SOCKET_QUEUE.queue))
for item in final_rx_check:

View file

@ -1,7 +1,7 @@
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
Negative test utilities for datac0 frames.
Negative test utilities for datac13 frames.
@author: kronenpj
"""
@ -15,9 +15,9 @@ import data_handler
import helpers
import modem
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
from static import FRAME_TYPE as FR_TYPE
#from static import FRAME_TYPE as FR_TYPE
def t_setup(
@ -40,44 +40,44 @@ def t_setup(
modem.RXCHANNEL = tmp_path / rx_channel
modem.TESTMODE = True
modem.TXCHANNEL = tmp_path / tx_channel
static.HAMLIB_RADIOCONTROL = "disabled"
static.LOW_BANDWIDTH_MODE = lowbwmode or True
static.MYGRID = bytes("AA12aa", "utf-8")
static.RESPOND_TO_CQ = True
static.SSID_LIST = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
HamlibParam.hamlib_radiocontrol = "disabled"
TNC.low_bandwidth_mode = lowbwmode or True
Station.mygrid = bytes("AA12aa", "utf-8")
TNC.respond_to_cq = True
Station.ssid_list = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
mycallsign_bytes = helpers.callsign_to_bytes(mycall)
mycallsign = helpers.bytes_to_callsign(mycallsign_bytes)
Station.mycallsign = mycallsign
Station.mycallsign_crc = helpers.get_crc_24(mycallsign)
mycallsign = helpers.callsign_to_bytes(mycall)
mycallsign = helpers.bytes_to_callsign(mycallsign)
static.MYCALLSIGN = mycallsign
static.MYCALLSIGN_CRC = helpers.get_crc_24(static.MYCALLSIGN)
dxcallsign = helpers.callsign_to_bytes(dxcall)
dxcallsign = helpers.bytes_to_callsign(dxcallsign)
static.DXCALLSIGN = dxcallsign
static.DXCALLSIGN_CRC = helpers.get_crc_24(static.DXCALLSIGN)
dxcallsign_bytes = helpers.callsign_to_bytes(dxcall)
dxcallsign = helpers.bytes_to_callsign(dxcallsign_bytes)
Station.dxcallsign = dxcallsign
Station.dxcallsign_crc = helpers.get_crc_24(dxcallsign)
# Create the TNC
tnc = data_handler.DATA()
tnc_data_handler = data_handler.DATA()
orig_rx_func = data_handler.DATA.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
tnc.time_list_low_bw = [3, 1, 1]
tnc.time_list_high_bw = [3, 1, 1]
tnc.time_list = [3, 1, 1]
tnc_data_handler.time_list_low_bw = [8, 8, 8]
tnc_data_handler.time_list_high_bw = [8, 8, 8]
tnc_data_handler.time_list = [8, 8, 8]
# 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
t_modem = modem.RF()
orig_tx_func = modem.RF.transmit
modem.RF.transmit = t_transmit
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,
mycall: str,
dxcall: str,
@ -87,7 +87,7 @@ def t_datac0_1(
log = structlog.get_logger("station1")
orig_tx_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
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.
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,
mycall,
dxcall,
@ -137,21 +137,21 @@ def t_datac0_1(
tmp_path,
)
log.info("t_datac0_1:", RXCHANNEL=modem.RXCHANNEL)
log.info("t_datac0_1:", TXCHANNEL=modem.TXCHANNEL)
log.info("t_datac13_1:", RXCHANNEL=modem.RXCHANNEL)
log.info("t_datac13_1:", TXCHANNEL=modem.TXCHANNEL)
orig_dxcall = static.DXCALLSIGN
orig_dxcall = Station.dxcallsign
if "stop" in data["command"]:
time.sleep(0.5)
log.debug(
"t_datac0_1: STOP test, setting TNC state",
mycall=static.MYCALLSIGN,
dxcall=static.DXCALLSIGN,
"t_datac13_1: STOP test, setting TNC state",
mycall=Station.mycallsign,
dxcall=Station.dxcallsign,
)
static.DXCALLSIGN = helpers.callsign_to_bytes(data["dxcallsign"])
static.DXCALLSIGN_CRC = helpers.get_crc_24(static.DXCALLSIGN)
static.TNC_STATE = "BUSY"
static.ARQ_STATE = True
Station.dxcallsign = helpers.callsign_to_bytes(data["dxcallsign"])
Station.dxcallsign_CRC = helpers.get_crc_24(Station.dxcallsign)
TNC.tnc_state = "BUSY"
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))
@ -172,19 +172,19 @@ def t_datac0_1(
if "stop" in data["command"]:
time.sleep(0.5)
log.debug("STOP test, resetting DX callsign")
static.DXCALLSIGN = orig_dxcall
static.DXCALLSIGN_CRC = helpers.get_crc_24(static.DXCALLSIGN)
Station.dxcallsign = orig_dxcall
Station.dxcallsign_CRC = helpers.get_crc_24(Station.dxcallsign)
# 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}
sock.ThreadedTCPRequestHandler.process_tnc_commands(None,json.dumps(data, indent=None))
time.sleep(0.5)
# Allow enough time for this side to process the disconnect frame.
timeout = time.time() + timeout_duration
while tnc.data_queue_transmit.queue:
while tnc_data_handler.data_queue_transmit.queue:
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
time.sleep(0.5)
log.info("station1, final")
@ -204,7 +204,7 @@ def t_datac0_1(
log.warning("station1: Exiting!")
def t_datac0_2(
def t_datac13_2(
parent_pipe,
mycall: str,
dxcall: str,
@ -214,7 +214,7 @@ def t_datac0_2(
log = structlog.get_logger("station2")
orig_tx_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
data, timeout_duration, _, rx_check, _, final_rx_check = config
@ -264,9 +264,9 @@ def t_datac0_2(
tmp_path,
)
log.info("t_datac0_2:", RXCHANNEL=modem.RXCHANNEL)
log.info("t_datac0_2:", TXCHANNEL=modem.TXCHANNEL)
log.info("t_datac0_2:", mycall=static.MYCALLSIGN)
log.info("t_datac13_2:", RXCHANNEL=modem.RXCHANNEL)
log.info("t_datac13_2:", TXCHANNEL=modem.TXCHANNEL)
log.info("t_datac13_2:", mycall=Station.mycallsign)
if "cq" in data:
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.
timeout = time.time() + timeout_duration
while '"arq":"session", "status":"close"' not in str(sock.SOCKET_QUEUE.queue):
if time.time() > timeout:
log.warning("station2", TIMEOUT=True, queue=str(sock.SOCKET_QUEUE.queue))
break

View file

@ -67,7 +67,7 @@ def test_mm_rx():
for idx in range(3):
datac_freedv.append(
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(
@ -121,11 +121,11 @@ def test_mm_rx():
for idx in range(3):
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:
return
time_datac = [time_datac0, time_datac1, time_datac3]
time_datac = [time_datac13, time_datac1, time_datac3]
datac_rxstatus = ["", "", ""]
for idx in range(3):
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(
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"DATAC3: {rx_bursts_datac[2]}/{rx_total_frames_datac[2]}",
file=sys.stderr,
@ -241,7 +241,7 @@ def parse_arguments():
parser.add_argument(
"--timeout",
dest="TIMEOUT",
default=10,
default=60,
type=int,
help="Timeout (seconds) before test ends",
)

View file

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

View file

@ -221,7 +221,7 @@ def parse_arguments():
"--framesperburst", dest="N_FRAMES_PER_BURST", default=1, type=int
)
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(
"--audiodev",
@ -234,7 +234,7 @@ def parse_arguments():
parser.add_argument(
"--timeout",
dest="TIMEOUT",
default=10,
default=60,
type=int,
help="Timeout (seconds) before test ends",
)

View file

@ -122,18 +122,18 @@ def t_arq_iss(*args):
else:
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))
time.sleep(1.5)
time.sleep(7.5)
data = {"type": "arq", "command": "stop_transmission"}
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))
# Set timeout

View file

@ -198,7 +198,7 @@ def parse_arguments():
help="delay between bursts in ms",
)
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(
"--audiodev",

View file

@ -18,20 +18,38 @@ import structlog
log = structlog.get_logger("codec2")
# Enum for codec2 modes
class FREEDV_MODE(Enum):
"""
Enumeration for codec2 modes and names
"""
sig0 = 14
sig1 = 14
sig0 = 19
sig1 = 19
datac0 = 14
datac1 = 10
datac3 = 12
datac4 = 18
datac13 = 19
fsk_ldpc = 9
fsk_ldpc_0 = 200
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
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.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.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_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
@ -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_NC_MAX = 50 + 1 * 2
MODEM_STATS_NR_MAX = 160 * 2
MODEM_STATS_NR_MAX = 320 * 2
MODEM_STATS_ET_MAX = 8
MODEM_STATS_EYE_IND_MAX = 160
MODEM_STATS_NSPEC = 512

View file

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

View file

@ -26,7 +26,8 @@ import crcengine
import log_handler
import serial.tools.list_ports
import sock
import static
from static import ARQ, AudioParam, Beacon, Channel, Daemon, HamlibParam, ModemParam, Station, Statistics, TCIParam, TNC
import structlog
import ujson as json
import config
@ -82,10 +83,10 @@ class DAEMON:
"""
while True:
try:
if not static.TNCSTARTED:
if not Daemon.tncstarted:
(
static.AUDIO_INPUT_DEVICES,
static.AUDIO_OUTPUT_DEVICES,
AudioParam.audio_input_devices,
AudioParam.audio_output_devices,
) = audio.get_audio_devices()
except Exception as err1:
self.log.error(
@ -112,7 +113,7 @@ class DAEMON:
{"port": str(port), "description": str(description)}
)
static.SERIAL_DEVICES = serial_devices
Daemon.serial_devices = serial_devices
threading.Event().wait(1)
except Exception as err1:
self.log.error(
@ -210,10 +211,10 @@ class DAEMON:
self.log.warning("[DMN] Starting TNC", rig=data[5], port=data[6])
# 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
data[50] = int(static.DAEMONPORT - 1)
data[50] = int(DAEMON.port - 1)
options.append("--mycall")
options.extend((data[1], "--mygrid"))
@ -317,8 +318,8 @@ class DAEMON:
self.log.info("[DMN] TNC started", path="source")
static.TNCPROCESS = proc
static.TNCSTARTED = True
Daemon.tncprocess = proc
Daemon.tncstarted = True
if __name__ == "__main__":
mainlog = structlog.get_logger(__file__)
# we need to run this on Windows for multiprocessing support
@ -335,7 +336,7 @@ if __name__ == "__main__":
)
ARGS = PARSER.parse_args()
static.DAEMONPORT = ARGS.socket_port
DAEMON.port = ARGS.socket_port
try:
if sys.platform == "linux":
@ -363,11 +364,11 @@ if __name__ == "__main__":
config = config.CONFIG("config.ini")
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
socketserver.TCPServer.allow_reuse_address = True
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.daemon = True
@ -375,7 +376,7 @@ if __name__ == "__main__":
except Exception as err:
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)
daemon = DAEMON()
@ -384,7 +385,7 @@ if __name__ == "__main__":
"[DMN] Starting FreeDATA Daemon",
author="DJ2LS",
year="2023",
version=static.VERSION,
version=TNC.version,
)
while True:
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 structlog
import static
from static import ARQ, AudioParam, Beacon, Channel, Daemon, HamlibParam, ModemParam, Station, Statistics, TCIParam, TNC
log = structlog.get_logger("explorer")
@ -32,30 +34,31 @@ class explorer():
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"
callsign = str(static.MYCALLSIGN, "utf-8")
gridsquare = str(static.MYGRID, "utf-8")
version = str(static.VERSION)
bandwidth = str(static.LOW_BANDWIDTH_MODE)
beacon = str(static.BEACON_STATE)
strength = str(static.HAMLIB_STRENGTH)
callsign = str(Station.mycallsign, "utf-8")
gridsquare = str(Station.mygrid, "utf-8")
version = str(TNC.version)
bandwidth = str(TNC.low_bandwidth_mode)
beacon = str(Beacon.beacon_state)
strength = str(HamlibParam.hamlib_strength)
log.info("[EXPLORER] publish", frequency=frequency, band=band, callsign=callsign, gridsquare=gridsquare, version=version, bandwidth=bandwidth)
headers = {"Content-Type": "application/json"}
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:
callsign = str(i[0], "UTF-8")
grid = str(i[1], "UTF-8")
timestamp = i[2]
frequency = i[6]
try:
snr = i[4].split("/")[1]
except AttributeError:
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:
log.debug("[EXPLORER] not publishing station", e=e)

View file

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

View file

@ -29,6 +29,7 @@ import helpers
import log_handler
import modem
import static
from static import ARQ, AudioParam, Beacon, Channel, Daemon, HamlibParam, ModemParam, Station, Statistics, TCIParam, TNC
import structlog
import explorer
import json
@ -255,7 +256,7 @@ if __name__ == "__main__":
ARGS = PARSER.parse_args()
# 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:
@ -266,46 +267,46 @@ if __name__ == "__main__":
try:
mycallsign = bytes(ARGS.mycall.upper(), "utf-8")
mycallsign = helpers.callsign_to_bytes(mycallsign)
static.MYCALLSIGN = helpers.bytes_to_callsign(mycallsign)
static.MYCALLSIGN_CRC = helpers.get_crc_24(static.MYCALLSIGN)
static.SSID_LIST = ARGS.ssid_list
Station.mycallsign = helpers.bytes_to_callsign(mycallsign)
Station.mycallsign_crc = helpers.get_crc_24(Station.mycallsign)
Station.ssid_list = ARGS.ssid_list
# check if own ssid is always part of ssid list
own_ssid = int(static.MYCALLSIGN.split(b"-")[1])
if own_ssid not in static.SSID_LIST:
static.SSID_LIST.append(own_ssid)
own_ssid = int(Station.mycallsign.split(b"-")[1])
if own_ssid not in Station.ssid_list:
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
try:
static.AUDIO_INPUT_DEVICE = int(ARGS.audio_input_device)
AudioParam.audio_input_device = int(ARGS.audio_input_device)
except ValueError:
static.AUDIO_INPUT_DEVICE = ARGS.audio_input_device
AudioParam.audio_input_device = ARGS.audio_input_device
try:
static.AUDIO_OUTPUT_DEVICE = int(ARGS.audio_output_device)
AudioParam.audio_output_device = int(ARGS.audio_output_device)
except ValueError:
static.AUDIO_OUTPUT_DEVICE = ARGS.audio_output_device
AudioParam.audio_output_device = ARGS.audio_output_device
static.PORT = ARGS.socket_port
static.HAMLIB_RADIOCONTROL = ARGS.hamlib_radiocontrol
static.HAMLIB_RIGCTLD_IP = ARGS.rigctld_ip
static.HAMLIB_RIGCTLD_PORT = str(ARGS.rigctld_port)
static.ENABLE_SCATTER = ARGS.send_scatter
static.ENABLE_FFT = ARGS.send_fft
static.ENABLE_FSK = ARGS.enable_fsk
static.LOW_BANDWIDTH_MODE = ARGS.low_bandwidth_mode
static.TUNING_RANGE_FMIN = ARGS.tuning_range_fmin
static.TUNING_RANGE_FMAX = ARGS.tuning_range_fmax
static.TX_AUDIO_LEVEL = ARGS.tx_audio_level
static.RESPOND_TO_CQ = ARGS.enable_respond_to_cq
static.RX_BUFFER_SIZE = ARGS.rx_buffer_size
static.ENABLE_EXPLORER = ARGS.enable_explorer
static.AUDIO_AUTO_TUNE = ARGS.enable_audio_auto_tune
static.ENABLE_STATS = ARGS.enable_stats
static.AUDIO_ENABLE_TCI = ARGS.audio_enable_tci
static.TCI_IP = ARGS.tci_ip
static.TCI_PORT = ARGS.tci_port
static.TX_DELAY = ARGS.tx_delay
TNC.port = ARGS.socket_port
HamlibParam.hamlib_radiocontrol = ARGS.hamlib_radiocontrol
HamlibParam.hamlib_rigctld_ip = ARGS.rigctld_ip
HamlibParam.hamlib_rigctld_port = str(ARGS.rigctld_port)
ModemParam.enable_scatter = ARGS.send_scatter
AudioParam.enable_fft = ARGS.send_fft
TNC.enable_fsk = ARGS.enable_fsk
TNC.low_bandwidth_mode = ARGS.low_bandwidth_mode
ModemParam.tuning_range_fmin = ARGS.tuning_range_fmin
ModemParam.tuning_range_fmax = ARGS.tuning_range_fmax
AudioParam.tx_audio_level = ARGS.tx_audio_level
TNC.respond_to_cq = ARGS.enable_respond_to_cq
ARQ.rx_buffer_size = ARGS.rx_buffer_size
TNC.enable_explorer = ARGS.enable_explorer
AudioParam.audio_auto_tune = ARGS.enable_audio_auto_tune
TNC.enable_stats = ARGS.enable_stats
AudioParam.audio_enable_tci = ARGS.audio_enable_tci
TCIParam.ip = ARGS.tci_ip
TCIParam.port = ARGS.tci_port
ModemParam.tx_delay = ARGS.tx_delay
except Exception as 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
mycallsign = bytes(conf.get('STATION', 'mycall', 'AA0AA'), "utf-8")
mycallsign = helpers.callsign_to_bytes(mycallsign)
static.MYCALLSIGN = helpers.bytes_to_callsign(mycallsign)
static.MYCALLSIGN_CRC = helpers.get_crc_24(static.MYCALLSIGN)
Station.mycallsign = helpers.bytes_to_callsign(mycallsign)
Station.mycallsign_crc = helpers.get_crc_24(Station.mycallsign)
#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
try:
static.AUDIO_INPUT_DEVICE = int(conf.get('AUDIO', 'rx', '0'))
AudioParam.audio_input_device = int(conf.get('AUDIO', 'rx', '0'))
except ValueError:
static.AUDIO_INPUT_DEVICE = conf.get('AUDIO', 'rx', '0')
AudioParam.audio_input_device = conf.get('AUDIO', 'rx', '0')
try:
static.AUDIO_OUTPUT_DEVICE = int(conf.get('AUDIO', 'tx', '0'))
AudioParam.audio_output_device = int(conf.get('AUDIO', 'tx', '0'))
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'))
static.HAMLIB_RADIOCONTROL = conf.get('RADIO', 'radiocontrol', 'rigctld')
static.HAMLIB_RIGCTLD_IP = conf.get('RADIO', 'rigctld_ip', '127.0.0.1')
static.HAMLIB_RIGCTLD_PORT = str(conf.get('RADIO', 'rigctld_port', '4532'))
static.ENABLE_SCATTER = conf.get('TNC', 'scatter', 'True')
static.ENABLE_FFT = conf.get('TNC', 'fft', 'True')
static.ENABLE_FSK = conf.get('TNC', 'fsk', 'False')
static.LOW_BANDWIDTH_MODE = conf.get('TNC', 'narrowband', 'False')
static.TUNING_RANGE_FMIN = float(conf.get('TNC', 'fmin', '-50.0'))
static.TUNING_RANGE_FMAX = float(conf.get('TNC', 'fmax', '50.0'))
static.TX_AUDIO_LEVEL = int(conf.get('AUDIO', 'txaudiolevel', '100'))
static.RESPOND_TO_CQ = conf.get('TNC', 'qrv', 'True')
static.RX_BUFFER_SIZE = int(conf.get('TNC', 'rxbuffersize', '16'))
static.ENABLE_EXPLORER = conf.get('TNC', 'explorer', 'False')
static.AUDIO_AUTO_TUNE = conf.get('AUDIO', 'auto_tune', 'False')
static.ENABLE_STATS = conf.get('TNC', 'stats', 'False')
static.AUDIO_ENABLE_TCI = conf.get('AUDIO', 'enable_tci', 'False')
static.TCI_IP = str(conf.get('AUDIO', 'tci_ip', 'localhost'))
static.TCI_PORT = int(conf.get('AUDIO', 'tci_port', '50001'))
static.TX_DELAY = int(conf.get('TNC', 'tx_delay', '0'))
TNC.port = int(conf.get('NETWORK', 'tncport', '3000'))
HamlibParam.hamlib_radiocontrol = conf.get('RADIO', 'radiocontrol', 'rigctld')
HamlibParam.hamlib_rigctld_ip = conf.get('RADIO', 'rigctld_ip', '127.0.0.1')
HamlibParam.hamlib_rigctld_port = str(conf.get('RADIO', 'rigctld_port', '4532'))
ModemParam.enable_scatter = conf.get('TNC', 'scatter', 'True')
AudioParam.enable_fft = conf.get('TNC', 'fft', 'True')
TNC.enable_fsk = conf.get('TNC', 'fsk', 'False')
TNC.low_bandwidth_mode = conf.get('TNC', 'narrowband', 'False')
ModemParam.tuning_range_fmin = float(conf.get('TNC', 'fmin', '-50.0'))
ModemParam.tuning_range_fmax = float(conf.get('TNC', 'fmax', '50.0'))
AudioParam.tx_audio_level = int(conf.get('AUDIO', 'txaudiolevel', '100'))
TNC.respond_to_cq = conf.get('TNC', 'qrv', 'True')
ARQ.rx_buffer_size = int(conf.get('TNC', 'rxbuffersize', '16'))
TNC.enable_explorer = conf.get('TNC', 'explorer', 'False')
AudioParam.audio_auto_tune = conf.get('AUDIO', 'auto_tune', 'False')
TNC.enable_stats = conf.get('TNC', 'stats', 'False')
AudioParam.audio_enable_tci = conf.get('AUDIO', 'enable_tci', 'False')
TCIParam.ip = str(conf.get('AUDIO', 'tci_ip', 'localhost'))
TCIParam.port = int(conf.get('AUDIO', 'tci_port', '50001'))
ModemParam.tx_delay = int(conf.get('TNC', 'tx_delay', '0'))
except KeyError as e:
log.warning("[CFG] Error reading config file near", key=str(e))
except Exception as e:
log.warning("[CFG] Error", e=e)
# make sure the own ssid is always part of the ssid list
my_ssid = int(static.MYCALLSIGN.split(b'-')[1])
if my_ssid not in static.SSID_LIST:
static.SSID_LIST.append(my_ssid)
my_ssid = int(Station.mycallsign.split(b'-')[1])
if my_ssid not in Station.ssid_list:
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
import sock
@ -394,7 +395,7 @@ if __name__ == "__main__":
log.error("[DMN] logger init error", exception=err)
log.info(
"[TNC] Starting FreeDATA", author="DJ2LS", version=static.VERSION
"[TNC] Starting FreeDATA", author="DJ2LS", version=TNC.version
)
# start data handler
@ -404,17 +405,17 @@ if __name__ == "__main__":
modem = modem.RF()
# optionally start explorer module
if static.ENABLE_EXPLORER:
log.info("[EXPLORER] Publishing to https://explorer.freedata.app", state=static.ENABLE_EXPLORER)
if TNC.enable_explorer:
log.info("[EXPLORER] Publishing to https://explorer.freedata.app", state=TNC.enable_explorer)
explorer = explorer.explorer()
# --------------------------------------------START CMD SERVER
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
socketserver.TCPServer.allow_reuse_address = True
cmdserver = sock.ThreadedTCPServer(
(static.HOST, static.PORT), sock.ThreadedTCPRequestHandler
(TNC.host, TNC.port), sock.ThreadedTCPRequestHandler
)
server_thread = threading.Thread(target=cmdserver.serve_forever)
@ -422,7 +423,7 @@ if __name__ == "__main__":
server_thread.start()
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)
while True:
threading.Event().wait(1)
threading.Event().wait(1)

File diff suppressed because it is too large Load diff

View file

@ -3,6 +3,7 @@ Hold queues used by more than one module to eliminate cyclic imports.
"""
import queue
import static
from static import ARQ, AudioParam, Beacon, Channel, Daemon, HamlibParam, ModemParam, Station, TCIParam, TNC
DATA_QUEUE_TRANSMIT = queue.Queue()
DATA_QUEUE_RECEIVED = queue.Queue()
@ -16,7 +17,7 @@ AUDIO_RECEIVED_QUEUE = queue.Queue()
AUDIO_TRANSMIT_QUEUE = queue.Queue()
# 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
RIGCTLD_COMMAND_QUEUE = queue.Queue()

View file

@ -10,9 +10,7 @@ import time
import structlog
import threading
import static
# set global hamlib version
hamlib_version = 0
from static import ARQ, AudioParam, Beacon, Channel, Daemon, HamlibParam, ModemParam, Station, TCIParam
class radio:
@ -243,7 +241,7 @@ class radio:
if 'RPRT' not in alc:
try:
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:
self.alc = 0.0

View file

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

View file

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

View file

@ -5,13 +5,188 @@ Created on Wed Dec 23 11:13:57 2020
@author: DJ2LS
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
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_STATS = False
@ -87,15 +262,15 @@ AUDIO_ENABLE_TCI: bool = False
TCI_IP: str = '127.0.0.1'
TCI_PORT: int = '9000'
AUDIO_DBFS: int = 0
FFT: list = [0]
ENABLE_FFT: bool = True
CHANNEL_BUSY: bool = False
# 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
SPEED_LIST: list = []
@ -138,33 +313,4 @@ INFO: list = []
# ------- CODEC2 SETTINGS
TUNING_RANGE_FMIN: float = -50.0
TUNING_RANGE_FMAX: float = 50.0
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
IS_CODEC2_TRAFFIC: bool = False # true if we have codec2 signalling mode traffic on channel

View file

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

View file

@ -7,8 +7,9 @@ import websocket
import numpy as np
import time
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):
# websocket.enableTrace(True)
self.log = structlog.get_logger("TCI")