500hz mode, protocol improvements....

...and a lot of different changes. Also deactivated single mode transmission. This needs to be optimised another day...Time is the missing ressource...
This commit is contained in:
dj2ls 2022-02-08 15:27:34 +01:00
parent 92cf30225e
commit 35d95bbb14
12 changed files with 709 additions and 693 deletions

View file

@ -32,7 +32,7 @@ function connectDAEMON() {
//client.setTimeout(5000); //client.setTimeout(5000);
} }
daemon.on('connect', function(data) { daemon.on('connect', function(err) {
console.log('DAEMON connection established') console.log('DAEMON connection established')
let Data = { let Data = {
daemon_connection: daemon.readyState, daemon_connection: daemon.readyState,
@ -41,8 +41,10 @@ daemon.on('connect', function(data) {
}) })
daemon.on('error', function(data) { daemon.on('error', function(err) {
console.log('DAEMON connection error'); console.log('DAEMON connection error');
console.log(err)
daemon.destroy();
setTimeout(connectDAEMON, 2000) setTimeout(connectDAEMON, 2000)
let Data = { let Data = {
daemon_connection: daemon.readyState, daemon_connection: daemon.readyState,
@ -64,7 +66,8 @@ client.on('close', function(data) {
daemon.on('end', function(data) { daemon.on('end', function(data) {
console.log('DAEMON connection ended'); console.log('DAEMON connection ended');
setTimeout(connectDAEMON, 2000) daemon.destroy();
setTimeout(connectDAEMON, 500)
let Data = { let Data = {
daemon_connection: daemon.readyState, daemon_connection: daemon.readyState,
}; };
@ -198,7 +201,7 @@ exports.getDaemonState = function() {
// START TNC // START TNC
// ` `== multi line string // ` `== multi line string
exports.startTNC = function(mycall, mygrid, rx_audio, tx_audio, radiocontrol, devicename, deviceport, pttprotocol, pttport, serialspeed, data_bits, stop_bits, handshake, rigctld_ip, rigctld_port, enable_fft, enable_scatter) { exports.startTNC = function(mycall, mygrid, rx_audio, tx_audio, radiocontrol, devicename, deviceport, pttprotocol, pttport, serialspeed, data_bits, stop_bits, handshake, rigctld_ip, rigctld_port, enable_fft, enable_scatter, low_bandwith_mode) {
var json_command = JSON.stringify({ var json_command = JSON.stringify({
type: 'set', type: 'set',
command: 'start_tnc', command: 'start_tnc',
@ -219,7 +222,8 @@ exports.startTNC = function(mycall, mygrid, rx_audio, tx_audio, radiocontrol, de
rigctld_port: rigctld_port, rigctld_port: rigctld_port,
rigctld_ip: rigctld_ip, rigctld_ip: rigctld_ip,
enable_scatter: enable_scatter, enable_scatter: enable_scatter,
enable_fft: enable_fft enable_fft: enable_fft,
low_bandwith_mode : low_bandwith_mode
}] }]
}) })

View file

@ -2,15 +2,15 @@ const {
app, app,
BrowserWindow, BrowserWindow,
ipcMain ipcMain
} = require('electron') } = require('electron');
const path = require('path') const path = require('path');
const fs = require('fs') const fs = require('fs');
const os = require('os'); const os = require('os');
app.setName("FreeDATA"); app.setName("FreeDATA");
var appDataFolder = process.env.APPDATA || (process.platform == 'darwin' ? process.env.HOME + '/Library/Application Support' : process.env.HOME + "/.config") var appDataFolder = process.env.APPDATA || (process.platform == 'darwin' ? process.env.HOME + '/Library/Application Support' : process.env.HOME + "/.config");
var configFolder = path.join(appDataFolder, "FreeDATA"); var configFolder = path.join(appDataFolder, "FreeDATA");
var configPath = path.join(configFolder, 'config.json') var configPath = path.join(configFolder, 'config.json');
// create config folder if not exists // create config folder if not exists
if (!fs.existsSync(configFolder)) { if (!fs.existsSync(configFolder)) {
@ -44,6 +44,7 @@ var configContent = `
"rigctld_ip" : "127.0.0.1", "rigctld_ip" : "127.0.0.1",
"enable_scatter" : "False", "enable_scatter" : "False",
"enable_fft" : "False", "enable_fft" : "False",
"low_bandwith_mode" : "False"
} }
`; `;
@ -67,7 +68,7 @@ var configContent = `
} }
`; `;
if (!fs.existsSync(chatDB)) { if (!fs.existsSync(chatDB)) {
fs.writeFileSync(chatDB, configContent) fs.writeFileSync(chatDB, configContent);
} }
@ -134,8 +135,8 @@ function createWindow() {
} }
}) })
chat.loadFile('src/chat-module.html') chat.loadFile('src/chat-module.html');
chat.setMenuBarVisibility(false) chat.setMenuBarVisibility(false);
// Emitted when the window is closed. // Emitted when the window is closed.
@ -158,13 +159,13 @@ function createWindow() {
// https://stackoverflow.com/questions/44258831/only-hide-the-window-when-closing-it-electron // https://stackoverflow.com/questions/44258831/only-hide-the-window-when-closing-it-electron
chat.on('close', function(evt) { chat.on('close', function(evt) {
evt.preventDefault(); evt.preventDefault();
chat.hide() chat.hide();
}); });
} }
app.whenReady().then(() => { app.whenReady().then(() => {
createWindow() createWindow();
// start daemon by checking os // start daemon by checking os
// https://stackoverflow.com/a/5775120 // https://stackoverflow.com/a/5775120
@ -173,8 +174,8 @@ app.whenReady().then(() => {
if(os.platform()=='linux' || os.platform()=='darwin'){ if(os.platform()=='linux' || os.platform()=='darwin'){
daemonProcess = exec('./tnc/daemon', function callback(err, stdout, stderr) { daemonProcess = exec('./tnc/daemon', function callback(err, stdout, stderr) {
if (err) { if (err) {
console.log(os.platform()) console.log(os.platform());
console.error(err) console.error(err);
console.error("Can't start daemon binary"); console.error("Can't start daemon binary");
console.error("--> this is only working with the app bundle and a precompiled binaries"); console.error("--> this is only working with the app bundle and a precompiled binaries");
return; return;
@ -186,8 +187,8 @@ app.whenReady().then(() => {
if(os.platform()=='win32' || os.platform()=='win64'){ if(os.platform()=='win32' || os.platform()=='win64'){
daemonProcess = exec('./tnc/daemon.exe', function callback(err, stdout, stderr) { daemonProcess = exec('./tnc/daemon.exe', function callback(err, stdout, stderr) {
if (err) { if (err) {
console.log(os.platform()) console.log(os.platform());
console.error(err) console.error(err);
console.error("Can't start daemon binary"); console.error("Can't start daemon binary");
console.error("--> this is only working with the app bundle and a precompiled binaries"); console.error("--> this is only working with the app bundle and a precompiled binaries");
return; return;
@ -200,7 +201,7 @@ app.whenReady().then(() => {
app.on('activate', () => { app.on('activate', () => {
if (BrowserWindow.getAllWindows().length === 0) { if (BrowserWindow.getAllWindows().length === 0) {
createWindow() createWindow();
} }
}) })
}) })
@ -210,14 +211,14 @@ app.on('window-all-closed', () => {
daemonProcess.kill('SIGINT'); daemonProcess.kill('SIGINT');
if (process.platform !== 'darwin') { if (process.platform !== 'darwin') {
app.quit() app.quit();
} }
}) })
// IPC HANDLER // IPC HANDLER
ipcMain.on('request-show-chat-window', (event, arg) => { ipcMain.on('request-show-chat-window', (event, arg) => {
chat.show() chat.show();
}); });

File diff suppressed because it is too large Load diff

View file

@ -57,8 +57,8 @@ client.on('error', function(data) {
}; };
ipcRenderer.send('request-update-tnc-state', Data); ipcRenderer.send('request-update-tnc-state', Data);
client.destroy();
setTimeout(connectTNC, 2000) setTimeout(connectTNC, 500)
// setTimeout( function() { exports.connectTNC(tnc_host, tnc_port); }, 2000 ); // setTimeout( function() { exports.connectTNC(tnc_host, tnc_port); }, 2000 );
}); });
@ -72,32 +72,26 @@ client.on('close', function(data) {
client.on('end', function(data) { client.on('end', function(data) {
console.log('TNC connection ended'); console.log('TNC connection ended');
//setTimeout(connectTNC, 2000)
setTimeout(connectTNC, 0)
// setTimeout( function() { exports.connectTNC(tnc_host, tnc_port); }, 2000 ); client.destroy();
setTimeout(connectTNC, 500)
}); });
//exports.writeTncCommand = function(command){
writeTncCommand = function(command) { writeTncCommand = function(command) {
//console.log(command) //console.log(command)
// we use the writingCommand function to update our TCPIP state because we are calling this function a lot // we use the writingCommand function to update our TCPIP state because we are calling this function a lot
// if socket openend, we are able to run commands // if socket openend, we are able to run commands
if (client.readyState == 'open') { if (client.readyState == 'open') {
//uiMain.setTNCconnection('open')
client.write(command + '\n'); client.write(command + '\n');
} }
if (client.readyState == 'closed') { if (client.readyState == 'closed') {
//uiMain.setTNCconnection('closed') console.log("CLOSED!")
//console.log("CLOSED!!!!!")
} }
if (client.readyState == 'opening') { if (client.readyState == 'opening') {
//uiMain.setTNCconnection('opening')
//console.log("OPENING!!!!!")
console.log('connecting to TNC...') console.log('connecting to TNC...')
} }
} }
@ -238,12 +232,12 @@ client.on('data', function(socketdata) {
if(splitted_data[0] == 'f'){ if(splitted_data[0] == 'f'){
dataArray.push(data['data-array'][i]) dataArray.push(data['data-array'][i])
//dataArray.push(splitted_data)
} }
if(splitted_data[0] == 'm'){ if(splitted_data[0] == 'm'){
messageArray.push(data['data-array'][i]) messageArray.push(data['data-array'][i])
//messageArray.push(splitted_data)
} }
} catch (e) { } catch (e) {

View file

@ -722,13 +722,16 @@
<label class="form-check-label" for="scatterSwitch">Scatter</label> <label class="form-check-label" for="scatterSwitch">Scatter</label>
</div> </div>
<div class="form-check form-switch form-check-inline">
<input class="form-check-input" type="checkbox" id="500HzModeSwitch">
<label class="form-check-label" for="500HzModeSwitch">500Hz</label>
</div>
<!--<button class="btn btn-secondary btn-sm" id="python_version" type="button" disabled>Python</button>--> <!--<button class="btn btn-secondary btn-sm" id="python_version" type="button" disabled>Python</button>-->
<!--<button class="btn btn-secondary btn-sm" id="node_version" type="button" disabled>Node</button>--> <!--<button class="btn btn-secondary btn-sm" id="node_version" type="button" disabled>Node</button>-->
<!--<button class="btn btn-secondary btn-sm" id="hamlib_version" type="button" disabled>Hamlib</button>--> <!--<button class="btn btn-secondary btn-sm" id="hamlib_version" type="button" disabled>Hamlib</button>-->
<!--<button class="btn btn-secondary btn-sm" id="operating_system" type="button" disabled>OS</button>--> <!--<button class="btn btn-secondary btn-sm" id="operating_system" type="button" disabled>OS</button>-->
<button class="btn btn-secondary btn-sm" id="cpu_load_button" type="button" disabled> <!--<button class="btn btn-secondary btn-sm" id="cpu_load_button" type="button" disabled>
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="currentColor" class="bi bi-cpu" viewBox="0 0 16 16"> <svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="currentColor" class="bi bi-cpu" viewBox="0 0 16 16">
<path d="M5 0a.5.5 0 0 1 .5.5V2h1V.5a.5.5 0 0 1 1 0V2h1V.5a.5.5 0 0 1 1 0V2h1V.5a.5.5 0 0 1 1 0V2A2.5 2.5 0 0 1 14 4.5h1.5a.5.5 0 0 1 0 1H14v1h1.5a.5.5 0 0 1 0 1H14v1h1.5a.5.5 0 0 1 0 1H14v1h1.5a.5.5 0 0 1 0 1H14a2.5 2.5 0 0 1-2.5 2.5v1.5a.5.5 0 0 1-1 0V14h-1v1.5a.5.5 0 0 1-1 0V14h-1v1.5a.5.5 0 0 1-1 0V14h-1v1.5a.5.5 0 0 1-1 0V14A2.5 2.5 0 0 1 2 11.5H.5a.5.5 0 0 1 0-1H2v-1H.5a.5.5 0 0 1 0-1H2v-1H.5a.5.5 0 0 1 0-1H2v-1H.5a.5.5 0 0 1 0-1H2A2.5 2.5 0 0 1 4.5 2V.5A.5.5 0 0 1 5 0zm-.5 3A1.5 1.5 0 0 0 3 4.5v7A1.5 1.5 0 0 0 4.5 13h7a1.5 1.5 0 0 0 1.5-1.5v-7A1.5 1.5 0 0 0 11.5 3h-7zM5 6.5A1.5 1.5 0 0 1 6.5 5h3A1.5 1.5 0 0 1 11 6.5v3A1.5 1.5 0 0 1 9.5 11h-3A1.5 1.5 0 0 1 5 9.5v-3zM6.5 6a.5.5 0 0 0-.5.5v3a.5.5 0 0 0 .5.5h3a.5.5 0 0 0 .5-.5v-3a.5.5 0 0 0-.5-.5h-3z" /> <path d="M5 0a.5.5 0 0 1 .5.5V2h1V.5a.5.5 0 0 1 1 0V2h1V.5a.5.5 0 0 1 1 0V2h1V.5a.5.5 0 0 1 1 0V2A2.5 2.5 0 0 1 14 4.5h1.5a.5.5 0 0 1 0 1H14v1h1.5a.5.5 0 0 1 0 1H14v1h1.5a.5.5 0 0 1 0 1H14v1h1.5a.5.5 0 0 1 0 1H14a2.5 2.5 0 0 1-2.5 2.5v1.5a.5.5 0 0 1-1 0V14h-1v1.5a.5.5 0 0 1-1 0V14h-1v1.5a.5.5 0 0 1-1 0V14h-1v1.5a.5.5 0 0 1-1 0V14A2.5 2.5 0 0 1 2 11.5H.5a.5.5 0 0 1 0-1H2v-1H.5a.5.5 0 0 1 0-1H2v-1H.5a.5.5 0 0 1 0-1H2v-1H.5a.5.5 0 0 1 0-1H2A2.5 2.5 0 0 1 4.5 2V.5A.5.5 0 0 1 5 0zm-.5 3A1.5 1.5 0 0 0 3 4.5v7A1.5 1.5 0 0 0 4.5 13h7a1.5 1.5 0 0 0 1.5-1.5v-7A1.5 1.5 0 0 0 11.5 3h-7zM5 6.5A1.5 1.5 0 0 1 6.5 5h3A1.5 1.5 0 0 1 11 6.5v3A1.5 1.5 0 0 1 9.5 11h-3A1.5 1.5 0 0 1 5 9.5v-3zM6.5 6a.5.5 0 0 0-.5.5v3a.5.5 0 0 0 .5.5h3a.5.5 0 0 0 .5-.5v-3a.5.5 0 0 0-.5-.5h-3z" />
</svg> <span id="cpu_load">---</span> </svg> <span id="cpu_load">---</span>
@ -738,12 +741,13 @@
<path d="M1 3a1 1 0 0 0-1 1v8a1 1 0 0 0 1 1h4.586a1 1 0 0 0 .707-.293l.353-.353a.5.5 0 0 1 .708 0l.353.353a1 1 0 0 0 .707.293H15a1 1 0 0 0 1-1V4a1 1 0 0 0-1-1H1Zm.5 1h3a.5.5 0 0 1 .5.5v4a.5.5 0 0 1-.5.5h-3a.5.5 0 0 1-.5-.5v-4a.5.5 0 0 1 .5-.5Zm5 0h3a.5.5 0 0 1 .5.5v4a.5.5 0 0 1-.5.5h-3a.5.5 0 0 1-.5-.5v-4a.5.5 0 0 1 .5-.5Zm4.5.5a.5.5 0 0 1 .5-.5h3a.5.5 0 0 1 .5.5v4a.5.5 0 0 1-.5.5h-3a.5.5 0 0 1-.5-.5v-4ZM2 10v2H1v-2h1Zm2 0v2H3v-2h1Zm2 0v2H5v-2h1Zm3 0v2H8v-2h1Zm2 0v2h-1v-2h1Zm2 0v2h-1v-2h1Zm2 0v2h-1v-2h1Z" /> <path d="M1 3a1 1 0 0 0-1 1v8a1 1 0 0 0 1 1h4.586a1 1 0 0 0 .707-.293l.353-.353a.5.5 0 0 1 .708 0l.353.353a1 1 0 0 0 .707.293H15a1 1 0 0 0 1-1V4a1 1 0 0 0-1-1H1Zm.5 1h3a.5.5 0 0 1 .5.5v4a.5.5 0 0 1-.5.5h-3a.5.5 0 0 1-.5-.5v-4a.5.5 0 0 1 .5-.5Zm5 0h3a.5.5 0 0 1 .5.5v4a.5.5 0 0 1-.5.5h-3a.5.5 0 0 1-.5-.5v-4a.5.5 0 0 1 .5-.5Zm4.5.5a.5.5 0 0 1 .5-.5h3a.5.5 0 0 1 .5.5v4a.5.5 0 0 1-.5.5h-3a.5.5 0 0 1-.5-.5v-4ZM2 10v2H1v-2h1Zm2 0v2H3v-2h1Zm2 0v2H5v-2h1Zm3 0v2H8v-2h1Zm2 0v2h-1v-2h1Zm2 0v2h-1v-2h1Zm2 0v2h-1v-2h1Z" />
</svg> <span id="ram_load">---</span> </svg> <span id="ram_load">---</span>
</button> </button>
-->
</div> </div>
</div> </div>
</div> </div>
</div> </div>
</div> </div>
<!--<hr class="m-1">-->
<div class="container mt-2 p-0"> <div class="container mt-2 p-0">
<div class="row collapse multi-collapse" id="collapseThirdRow"> <div class="row collapse multi-collapse" id="collapseThirdRow">
<div class="col-5"> <div class="col-5">
@ -965,18 +969,17 @@
<div class="row"> <div class="row">
<div class="col"> <div class="col">
<div class="input-group input-group-sm"> <span class="input-group-text" id="basic-addon1" >Mode</span> <div class="input-group input-group-sm"> <span class="input-group-text" id="basic-addon1" >Mode</span>
<select class="form-select form-select-sm" aria-label=".form-select-sm" id="datamode"> <select class="form-select form-select-sm" aria-label=".form-select-sm" id="datamode" disabled>
<!--<option value="14">low SNR (DC0)</option>-->
<option selected value="255">AUTO</option> <option selected value="255">AUTO</option>
<option value="10">HIGH SNR (DC1)</option> <!--<option value="232">HIGH SNR (DC1)</option>-->
<option value="12">MED SNR (DC3)</option> <!--<option value="231">MED SNR (DC3)</option>-->
<option value="14">LOW SNR (DC0)</option> <!--<option value="230">LOW SNR (DC0)</option>-->
</select> </select>
</div> </div>
</div> </div>
<div class="col-auto"> <div class="col-auto">
<div class="input-group input-group-sm"> <span class="input-group-text" id="basic-addon1">Frames</span> <div class="input-group input-group-sm"> <span class="input-group-text" id="basic-addon1">Frames</span>
<select class="form-select form-select-sm" aria-label=".form-select-sm" id="framesperburst"> <select class="form-select form-select-sm" aria-label=".form-select-sm" id="framesperburst" disabled>
<option selected value="1">1</option> <option selected value="1">1</option>
</select> </select>
</div> </div>

View file

@ -146,7 +146,7 @@ class audio_buffer:
# a buffer of int16 samples, using a fixed length numpy array self.buffer for storage # a buffer of int16 samples, using a fixed length numpy array self.buffer for storage
# self.nbuffer is the current number of samples in the buffer # self.nbuffer is the current number of samples in the buffer
def __init__(self, size): def __init__(self, size):
print("create audio_buffer: ", size) structlog.get_logger("structlog").debug("[C2 ] creating audio buffer", size=size)
self.size = size self.size = size
self.buffer = np.zeros(size, dtype=np.int16) self.buffer = np.zeros(size, dtype=np.int16)
self.nbuffer = 0 self.nbuffer = 0
@ -180,7 +180,7 @@ class resampler:
MEM48 = api.FDMDV_OS_TAPS_48K MEM48 = api.FDMDV_OS_TAPS_48K
def __init__(self): def __init__(self):
print("create 48<->8 kHz resampler") structlog.get_logger("structlog").debug("[C2 ] create 48<->8 kHz resampler")
self.filter_mem8 = np.zeros(self.MEM8, dtype=np.int16) self.filter_mem8 = np.zeros(self.MEM8, dtype=np.int16)
self.filter_mem48 = np.zeros(self.MEM48) self.filter_mem48 = np.zeros(self.MEM48)

View file

@ -26,7 +26,7 @@ import os
import queue import queue
import audio import audio
import sock import sock
import atexit
class DAEMON(): class DAEMON():
def __init__(self): def __init__(self):
@ -89,6 +89,7 @@ class DAEMON():
# data[15] rigctld_port # data[15] rigctld_port
# data[16] send_scatter # data[16] send_scatter
# data[17] send_fft # data[17] send_fft
# data[18] low_bandwith_mode
if data[0] == 'STARTTNC': if data[0] == 'STARTTNC':
structlog.get_logger("structlog").warning("[DMN] Starting TNC", rig=data[5], port=data[6]) structlog.get_logger("structlog").warning("[DMN] Starting TNC", rig=data[5], port=data[6])
@ -150,6 +151,9 @@ class DAEMON():
if data[17] == 'True': if data[17] == 'True':
options.append('--fft') options.append('--fft')
if data[18] == 'True':
options.append('--500hz')
# try running tnc from binary, else run from source # try running tnc from binary, else run from source
# this helps running the tnc in a developer environment # this helps running the tnc in a developer environment
try: try:
@ -161,6 +165,8 @@ class DAEMON():
command += options command += options
p = subprocess.Popen(command) p = subprocess.Popen(command)
atexit.register(p.kill)
structlog.get_logger("structlog").info("[DMN] TNC started", path="binary") structlog.get_logger("structlog").info("[DMN] TNC started", path="binary")
except: except:
command = [] command = []
@ -176,7 +182,14 @@ class DAEMON():
static.TNCPROCESS = p # .pid static.TNCPROCESS = p # .pid
static.TNCSTARTED = True static.TNCSTARTED = True
'''
# WE HAVE THIS PART in SOCKET
if data[0] == 'STOPTNC':
static.TNCPROCESS.kill()
structlog.get_logger("structlog").warning("[DMN] Stopping TNC")
#os.kill(static.TNCPROCESS, signal.SIGKILL)
static.TNCSTARTED = False
'''
# data[1] devicename # data[1] devicename
# data[2] deviceport # data[2] deviceport
# data[3] serialspeed # data[3] serialspeed

View file

@ -32,8 +32,6 @@ class DATA():
def __init__(self): def __init__(self):
print("init DATA handler...")
self.data_queue_transmit = DATA_QUEUE_TRANSMIT self.data_queue_transmit = DATA_QUEUE_TRANSMIT
self.data_queue_received = DATA_QUEUE_RECEIVED self.data_queue_received = DATA_QUEUE_RECEIVED
@ -48,10 +46,30 @@ class DATA():
self.data_frame_bof = b'BOF' # 2 bytes for the BOF End of File indicator in a data frame self.data_frame_bof = b'BOF' # 2 bytes for the BOF End of File indicator in a data frame
self.data_frame_eof = b'EOF' # 2 bytes for the EOF End of File indicator in a data frame self.data_frame_eof = b'EOF' # 2 bytes for the EOF End of File indicator in a data frame
#self.mode_list = [14,14,14,12,10] # mode list of available modes, each mode will be used 2times per speed level self.rx_n_max_retries_per_burst = 10
self.mode_list = [14,12,10] # mode list of available modes, each mode will be used 2times per speed level self.n_retries_per_burst = 0
self.time_list = [3, 6, 7] # list for time to wait for correspinding mode in seconds
self.received_low_bandwith_mode = False # indicator if we recevied a low bandwith mode channel ope ner
self.data_channel_max_retries = 3
self.mode_list_low_bw = [14,12]
self.time_list_low_bw = [3,6]
self.mode_list_high_bw = [14,12,10] # mode list of available modes, each mode will be used 2times per speed level
self.time_list_high_bw = [3, 6, 7] # list for time to wait for correspinding mode in seconds
# mode list for selecting between low bandwith ( 500Hz ) and normal modes with higher bandwith
if static.LOW_BANDWITH_MODE:
self.mode_list = self.mode_list_low_bw # mode list of available modes, each mode will be used 2times per speed level
self.time_list = self.time_list_low_bw # list for time to wait for correspinding mode in seconds
else:
self.mode_list = self.mode_list_high_bw # mode list of available modes, each mode will be used 2times per speed level
self.time_list = self.time_list_high_bw # list for time to wait for correspinding mode in seconds
self.speed_level = len(self.mode_list) - 1 # speed level for selecting mode self.speed_level = len(self.mode_list) - 1 # speed level for selecting mode
self.is_IRS = False self.is_IRS = False
self.burst_nack = False self.burst_nack = False
self.burst_nack_counter = 0 self.burst_nack_counter = 0
@ -60,7 +78,7 @@ class DATA():
self.rx_frame_bof_received = False self.rx_frame_bof_received = False
self.rx_frame_eof_received = False self.rx_frame_eof_received = False
self.transmission_timeout = 120 # transmission timeout in seconds self.transmission_timeout = 30 # transmission timeout in seconds
worker_thread_transmit = threading.Thread(target=self.worker_transmit, name="worker thread transmit") worker_thread_transmit = threading.Thread(target=self.worker_transmit, name="worker thread transmit")
worker_thread_transmit.start() worker_thread_transmit.start()
@ -222,24 +240,31 @@ class DATA():
self.received_ping_ack(bytes_out[:-2]) self.received_ping_ack(bytes_out[:-2])
# ARQ FILE TRANSFER RECEIVED! # ARQ FILE TRANSFER RECEIVED!
elif frametype == 225: elif frametype == 225 or frametype == 227:
structlog.get_logger("structlog").debug("ARQ arq_received_data_channel_opener") structlog.get_logger("structlog").debug("ARQ arq_received_data_channel_opener")
self.arq_received_data_channel_opener(bytes_out[:-2]) self.arq_received_data_channel_opener(bytes_out[:-2])
# ARQ CHANNEL IS OPENED # ARQ CHANNEL IS OPENED
elif frametype == 226: elif frametype == 226 or frametype == 228:
structlog.get_logger("structlog").debug("ARQ arq_received_channel_is_open") structlog.get_logger("structlog").debug("ARQ arq_received_channel_is_open")
self.arq_received_channel_is_open(bytes_out[:-2]) self.arq_received_channel_is_open(bytes_out[:-2])
# ARQ MANUAL MODE TRANSMISSION
elif 230 <= frametype <= 240 :
structlog.get_logger("structlog").debug("ARQ manual mode ")
self.arq_received_data_channel_opener(bytes_out[:-2])
# ARQ STOP TRANSMISSION # ARQ STOP TRANSMISSION
elif frametype == 227: elif frametype == 249:
structlog.get_logger("structlog").debug("ARQ received stop transmission") structlog.get_logger("structlog").debug("ARQ received stop transmission")
self.received_stop_transmission() self.received_stop_transmission()
# ARQ CONNECT ACK / KEEP ALIVE # ARQ CONNECT ACK / KEEP ALIVE
# this is outdated and we may remove it # this is outdated and we may remove it
elif frametype == 230: elif frametype == 250:
structlog.get_logger("structlog").debug("BEACON RECEIVED") structlog.get_logger("structlog").debug("BEACON RECEIVED")
self.received_beacon(bytes_out[:-2]) self.received_beacon(bytes_out[:-2])
@ -283,7 +308,6 @@ class DATA():
if len(static.RX_BURST_BUFFER) != RX_N_FRAMES_PER_BURST: if len(static.RX_BURST_BUFFER) != RX_N_FRAMES_PER_BURST:
static.RX_BURST_BUFFER = [None] * RX_N_FRAMES_PER_BURST static.RX_BURST_BUFFER = [None] * RX_N_FRAMES_PER_BURST
# append data to rx burst buffer # append data to rx burst buffer
static.RX_BURST_BUFFER[RX_N_FRAME_OF_BURST] = data_in[6:] # [frame_type][n_frames_per_burst][CRC16][CRC16] static.RX_BURST_BUFFER[RX_N_FRAME_OF_BURST] = data_in[6:] # [frame_type][n_frames_per_burst][CRC16][CRC16]
@ -320,7 +344,7 @@ class DATA():
self.speed_level = len(self.mode_list) - 1 self.speed_level = len(self.mode_list) - 1
# updated modes we are listening to # updated modes we are listening to
self.set_listening_modes() self.set_listening_modes(self.mode_list[self.speed_level])
# create an ack frame # create an ack frame
@ -381,8 +405,6 @@ class DATA():
structlog.get_logger("structlog").error("we shouldnt reach this point...", frame=RX_N_FRAME_OF_BURST, frames=RX_N_FRAMES_PER_BURST) structlog.get_logger("structlog").error("we shouldnt reach this point...", frame=RX_N_FRAME_OF_BURST, frames=RX_N_FRAMES_PER_BURST)
# We have a BOF and EOF flag in our data. If we received both we received our frame. # We have a BOF and EOF flag in our data. If we received both we received our frame.
# In case of loosing data but we received already a BOF and EOF we need to make sure, we # In case of loosing data but we received already a BOF and EOF we need to make sure, we
# received the complete last burst by checking it for Nones # received the complete last burst by checking it for Nones
@ -391,9 +413,15 @@ class DATA():
# get total bytes per transmission information as soon we recevied a frame with a BOF # get total bytes per transmission information as soon we recevied a frame with a BOF
if bof_position >=0: if bof_position >=0:
crc_position = bof_position+len(self.data_frame_bof)
size_position = crc_position + 4 payload = static.RX_FRAME_BUFFER[bof_position+len(self.data_frame_bof):eof_position]
static.TOTAL_BYTES = int.from_bytes(bytes(static.RX_FRAME_BUFFER[size_position:size_position + 4]), "big") # Bytes frame_length = int.from_bytes(payload[4:8], "big") #4:8 4bytes
static.TOTAL_BYTES = frame_length
compression_factor = int.from_bytes(payload[8:9], "big") #4:8 4bytes
static.ARQ_COMPRESSION_FACTOR = compression_factor / 10
self.calculate_transfer_rate_rx(self.rx_start_of_transmission, len(static.RX_FRAME_BUFFER))
if bof_position >= 0 and eof_position > 0 and not None in static.RX_BURST_BUFFER: if bof_position >= 0 and eof_position > 0 and not None in static.RX_BURST_BUFFER:
print(f"bof_position {bof_position} / eof_position {eof_position}") print(f"bof_position {bof_position} / eof_position {eof_position}")
self.rx_frame_bof_received = True self.rx_frame_bof_received = True
@ -402,11 +430,13 @@ class DATA():
#now extract raw data from buffer #now extract raw data from buffer
payload = static.RX_FRAME_BUFFER[bof_position+len(self.data_frame_bof):eof_position] payload = static.RX_FRAME_BUFFER[bof_position+len(self.data_frame_bof):eof_position]
# get the data frame crc # get the data frame crc
data_frame_crc = payload[:4] #0:4 4bytes
frame_length = int.from_bytes(payload[4:8], "big") #4:8 4bytes
static.TOTAL_BYTES = frame_length
# 8:9 = compression factor
data_frame_crc = payload[:4] data_frame = payload[9:]
frame_length = int.from_bytes(bytes(static.RX_FRAME_BUFFER[size_position:size_position + 4]), "big")
data_frame = payload[8:]
data_frame_crc_received = helpers.get_crc_32(data_frame) data_frame_crc_received = helpers.get_crc_32(data_frame)
# check if data_frame_crc is equal with received crc # check if data_frame_crc is equal with received crc
if data_frame_crc == data_frame_crc_received: if data_frame_crc == data_frame_crc_received:
@ -501,7 +531,6 @@ class DATA():
self.speed_level = len(self.mode_list) - 1 # speed level for selecting mode self.speed_level = len(self.mode_list) - 1 # speed level for selecting mode
TX_N_SENT_BYTES = 0 # already sent bytes per data frame TX_N_SENT_BYTES = 0 # already sent bytes per data frame
self.tx_n_retry_of_burst = 0 # retries we already sent data self.tx_n_retry_of_burst = 0 # retries we already sent data
TX_N_MAX_RETRIES_PER_BURST = 50 # max amount of retries we sent before frame is lost TX_N_MAX_RETRIES_PER_BURST = 50 # max amount of retries we sent before frame is lost
@ -524,7 +553,10 @@ class DATA():
# compression # compression
data_frame_compressed = zlib.compress(data_out) data_frame_compressed = zlib.compress(data_out)
static.ARQ_COMPRESSION_FACTOR = len(data_out) / len(data_frame_compressed) compression_factor = len(data_out) / len(data_frame_compressed)
static.ARQ_COMPRESSION_FACTOR = compression_factor
compression_factor = bytes([int(static.ARQ_COMPRESSION_FACTOR * 10)])
data_out = data_frame_compressed data_out = data_frame_compressed
# reset statistics # reset statistics
@ -534,7 +566,7 @@ class DATA():
# append a crc and beginn and end of file indicators # append a crc and beginn and end of file indicators
frame_payload_crc = helpers.get_crc_32(data_out) frame_payload_crc = helpers.get_crc_32(data_out)
# data_out = self.data_frame_bof + frame_payload_crc + data_out + self.data_frame_eof # data_out = self.data_frame_bof + frame_payload_crc + data_out + self.data_frame_eof
data_out = self.data_frame_bof + frame_payload_crc + frame_total_size + data_out + self.data_frame_eof data_out = self.data_frame_bof + frame_payload_crc + frame_total_size + compression_factor + data_out + self.data_frame_eof
#initial bufferposition is 0 #initial bufferposition is 0
bufferposition = 0 bufferposition = 0
@ -559,17 +591,25 @@ class DATA():
# as soon as we received an ACK for the current burst, speed_level will increase # as soon as we received an ACK for the current burst, speed_level will increase
# by 1. # by 1.
# the can be optimised by checking the optimal speed level for the current conditions # the can be optimised by checking the optimal speed level for the current conditions
'''
if not self.tx_n_retry_of_burst % 2 and self.tx_n_retry_of_burst > 0: if not self.tx_n_retry_of_burst % 2 and self.tx_n_retry_of_burst > 0:
self.speed_level -= 1 self.speed_level -= 1
if self.speed_level < 0: if self.speed_level < 0:
self.speed_level = 0 self.speed_level = 0
'''
#if self.tx_n_retry_of_burst <= 1: #if self.tx_n_retry_of_burst <= 1:
# self.speed_level += 1 # self.speed_level += 1
# if self.speed_level >= len(self.mode_list)-1: # if self.speed_level >= len(self.mode_list)-1:
# self.speed_level = len(self.mode_list)-1 # self.speed_level = len(self.mode_list)-1
# if speed level is greater than our available modes, set speed level to maximum = lenght of mode list -1
print(self.mode_list)
if self.speed_level >= len(self.mode_list):
self.speed_level = len(self.mode_list) - 1
data_mode = self.mode_list[self.speed_level] data_mode = self.mode_list[self.speed_level]
structlog.get_logger("structlog").debug("Speed-level", level=self.speed_level, retry=self.tx_n_retry_of_burst) structlog.get_logger("structlog").debug("Speed-level", level=self.speed_level, retry=self.tx_n_retry_of_burst)
@ -625,11 +665,14 @@ class DATA():
time.sleep(0.01) time.sleep(0.01)
# after transmission finished wait for an ACK or RPT frame # after transmission finished wait for an ACK or RPT frame
'''
burstacktimeout = time.time() + BURST_ACK_TIMEOUT_SECONDS + 100 burstacktimeout = time.time() + BURST_ACK_TIMEOUT_SECONDS + 100
while not self.burst_ack and not self.burst_nack and not self.rpt_request_received and not self.data_frame_ack_received and time.time() < burstacktimeout and static.ARQ_STATE: while not self.burst_ack and not self.burst_nack and not self.rpt_request_received and not self.data_frame_ack_received and time.time() < burstacktimeout and static.ARQ_STATE:
time.sleep(0.01) time.sleep(0.01)
#structlog.get_logger("structlog").debug("[TNC] waiting for ack", burst_ack=self.burst_ack, frame_ack = self.data_frame_ack_received, arq_state = static.ARQ_STATE, overflows=static.BUFFER_OVERFLOW_COUNTER) '''
#burstacktimeout = time.time() + BURST_ACK_TIMEOUT_SECONDS + 100
while not self.burst_ack and not self.burst_nack and not self.rpt_request_received and not self.data_frame_ack_received and static.ARQ_STATE:
time.sleep(0.01)
# once we received a burst ack, reset its state and break the RETRIES loop # once we received a burst ack, reset its state and break the RETRIES loop
if self.burst_ack: if self.burst_ack:
@ -782,38 +825,35 @@ class DATA():
return False return False
def arq_open_data_channel(self, mode:int, n_frames_per_burst:int): def arq_open_data_channel(self, mode:int, n_frames_per_burst:int):
self.is_IRS = False self.is_IRS = False
DATA_CHANNEL_MAX_RETRIES = 5 # N attempts for connecting to another station
self.data_channel_last_received = int(time.time()) self.data_channel_last_received = int(time.time())
# devide by 1024 for getting Bytes -> kBytes if static.LOW_BANDWITH_MODE and mode == 255:
#data_len = int(data_len / 1024) frametype = bytes([227])
structlog.get_logger("structlog").debug("requesting low bandwith mode")
# multiply compression factor for reducing it from float to int else:
compression_factor = int(static.ARQ_COMPRESSION_FACTOR * 10) frametype = bytes([225])
structlog.get_logger("structlog").debug("requesting high bandwith mode")
if 230 <= mode <= 240:
structlog.get_logger("structlog").debug("requesting manual mode --> not yet implemented ")
frametype = bytes([mode])
connection_frame = bytearray(14) connection_frame = bytearray(14)
connection_frame[:1] = bytes([225]) connection_frame[:1] = frametype
connection_frame[1:3] = static.DXCALLSIGN_CRC connection_frame[1:3] = static.DXCALLSIGN_CRC
connection_frame[3:5] = static.MYCALLSIGN_CRC connection_frame[3:5] = static.MYCALLSIGN_CRC
connection_frame[5:11] = static.MYCALLSIGN connection_frame[5:11] = static.MYCALLSIGN
connection_frame[11:12] = bytes([mode])
#connection_frame[10:12] = data_len.to_bytes(2, byteorder='big')
connection_frame[12:13] = bytes([compression_factor])
connection_frame[13:14] = bytes([n_frames_per_burst]) connection_frame[13:14] = bytes([n_frames_per_burst])
while not static.ARQ_STATE: while not static.ARQ_STATE:
time.sleep(0.01) time.sleep(0.01)
for attempt in range(1,DATA_CHANNEL_MAX_RETRIES+1): for attempt in range(1,self.data_channel_max_retries+1):
static.INFO.append("DATACHANNEL;OPENING") static.INFO.append("DATACHANNEL;OPENING")
structlog.get_logger("structlog").info("[TNC] ARQ | DATA | TX | [" + str(static.MYCALLSIGN, 'utf-8') + "]>> <<[" + str(static.DXCALLSIGN, 'utf-8') + "]", attempt=str(attempt) + "/" + str(self.data_channel_max_retries))
structlog.get_logger("structlog").info("[TNC] ARQ | DATA | TX | [" + str(static.MYCALLSIGN, 'utf-8') + "]>> <<[" + str(static.DXCALLSIGN, 'utf-8') + "]", attempt=str(attempt) + "/" + str(DATA_CHANNEL_MAX_RETRIES))
txbuffer = [connection_frame] txbuffer = [connection_frame]
static.TRANSMITTING = True static.TRANSMITTING = True
modem.MODEM_TRANSMIT_QUEUE.put([14,1,0,txbuffer]) modem.MODEM_TRANSMIT_QUEUE.put([14,1,0,txbuffer])
# wait while transmitting # wait while transmitting
@ -829,7 +869,7 @@ class DATA():
if static.ARQ_STATE: if static.ARQ_STATE:
break break
if not static.ARQ_STATE and attempt == DATA_CHANNEL_MAX_RETRIES: if not static.ARQ_STATE and attempt == self.data_channel_max_retries:
static.INFO.append("DATACHANNEL;FAILED") static.INFO.append("DATACHANNEL;FAILED")
structlog.get_logger("structlog").warning("[TNC] ARQ | TX | DATA [" + str(static.MYCALLSIGN, 'utf-8') + "]>>X<<[" + str(static.DXCALLSIGN, 'utf-8') + "]") structlog.get_logger("structlog").warning("[TNC] ARQ | TX | DATA [" + str(static.MYCALLSIGN, 'utf-8') + "]>>X<<[" + str(static.DXCALLSIGN, 'utf-8') + "]")
@ -840,59 +880,60 @@ class DATA():
return False return False
#sys.exit() # close thread and so connection attempts #sys.exit() # close thread and so connection attempts
def arq_received_data_channel_opener(self, data_in:bytes): def arq_received_data_channel_opener(self, data_in:bytes):
self.is_IRS = True self.is_IRS = True
static.INFO.append("DATACHANNEL;RECEIVEDOPENER") static.INFO.append("DATACHANNEL;RECEIVEDOPENER")
static.DXCALLSIGN_CRC = bytes(data_in[3:5]) static.DXCALLSIGN_CRC = bytes(data_in[3:5])
static.DXCALLSIGN = bytes(data_in[5:11]).rstrip(b'\x00') static.DXCALLSIGN = bytes(data_in[5:11]).rstrip(b'\x00')
static.ARQ_COMPRESSION_FACTOR = float(int.from_bytes(bytes(data_in[12:13]), "big") / 10)
n_frames_per_burst = int.from_bytes(bytes(data_in[13:14]), "big") n_frames_per_burst = int.from_bytes(bytes(data_in[13:14]), "big")
mode = int.from_bytes(bytes(data_in[11:12]), "big") frametype = int.from_bytes(bytes(data_in[:1]), "big")
#check if we received low bandwith mode
if frametype == 225:
self.received_low_bandwith_mode = False
self.mode_list = self.mode_list_high_bw
self.time_list = self.time_list_high_bw
self.speed_level = len(self.mode_list) - 1
else:
self.received_low_bandwith_mode = True
self.mode_list = self.mode_list_low_bw
self.time_list = self.time_list_low_bw
self.speed_level = len(self.mode_list) - 1
if 230 <= frametype <= 240:
print("manual mode request")
# updated modes we are listening to # updated modes we are listening to
self.set_listening_modes() self.set_listening_modes(self.mode_list[self.speed_level])
'''
# set modes we want to listening to
mode_name = codec2.freedv_get_mode_name_by_value(mode)
if mode_name == 'datac1':
modem.RECEIVE_DATAC1 = True
elif mode_name == 'datac3':
modem.RECEIVE_DATAC3 = True
elif mode_name == 'allmodes':
pass
#modem.RECEIVE_DATAC1 = True
#modem.RECEIVE_DATAC3 = True
'''
#we need to find a way how to do this. this isn't working anymore since we mode to a class based module
#modem.set_frames_per_burst(n_frames_per_burst)
helpers.add_to_heard_stations(static.DXCALLSIGN,static.DXGRID, 'DATA-CHANNEL', static.SNR, static.FREQ_OFFSET, static.HAMLIB_FREQUENCY) helpers.add_to_heard_stations(static.DXCALLSIGN,static.DXGRID, 'DATA-CHANNEL', static.SNR, static.FREQ_OFFSET, static.HAMLIB_FREQUENCY)
structlog.get_logger("structlog").info("[TNC] ARQ | DATA | RX | [" + str(static.MYCALLSIGN, 'utf-8') + "]>> <<[" + str(static.DXCALLSIGN, 'utf-8') + "]") structlog.get_logger("structlog").info("[TNC] ARQ | DATA | RX | [" + str(static.MYCALLSIGN, 'utf-8') + "]>> <<[" + str(static.DXCALLSIGN, 'utf-8') + "]", bandwith="wide")
static.ARQ_STATE = True static.ARQ_STATE = True
static.TNC_STATE = 'BUSY' static.TNC_STATE = 'BUSY'
# reset ARQ statistics
static.ARQ_BYTES_PER_MINUTE_BURST = 0 self.reset_statistics()
static.ARQ_BYTES_PER_MINUTE = 0
static.ARQ_BITS_PER_SECOND_BURST = 0
static.ARQ_BITS_PER_SECOND = 0
static.ARQ_TRANSMISSION_PERCENT = 0
static.TOTAL_BYTES = 0
self.data_channel_last_received = int(time.time()) self.data_channel_last_received = int(time.time())
# check if we are in low bandwith mode
if static.LOW_BANDWITH_MODE or self.received_low_bandwith_mode:
frametype = bytes([228])
structlog.get_logger("structlog").debug("responding with low bandwith mode")
else:
frametype = bytes([226])
structlog.get_logger("structlog").debug("responding with high bandwith mode")
connection_frame = bytearray(14) connection_frame = bytearray(14)
connection_frame[:1] = bytes([226]) connection_frame[:1] = frametype
connection_frame[1:3] = static.DXCALLSIGN_CRC connection_frame[1:3] = static.DXCALLSIGN_CRC
connection_frame[3:5] = static.MYCALLSIGN_CRC connection_frame[3:5] = static.MYCALLSIGN_CRC
#connection_frame[5:11] = static.MYCALLSIGN connection_frame[13:14] = bytes([static.ARQ_PROTOCOL_VERSION]) #crc8 of version for checking protocol version
txbuffer = [connection_frame] txbuffer = [connection_frame]
@ -901,28 +942,46 @@ class DATA():
# wait while transmitting # wait while transmitting
while static.TRANSMITTING: while static.TRANSMITTING:
time.sleep(0.01) time.sleep(0.01)
structlog.get_logger("structlog").info("[TNC] ARQ | DATA | RX | [" + str(static.MYCALLSIGN, 'utf-8') + "]>>|<<[" + str(static.DXCALLSIGN, 'utf-8') + "]", snr=static.SNR) structlog.get_logger("structlog").info("[TNC] ARQ | DATA | RX | [" + str(static.MYCALLSIGN, 'utf-8') + "]>>|<<[" + str(static.DXCALLSIGN, 'utf-8') + "]", bandwith="wide", snr=static.SNR)
# set start of transmission for our statistics # set start of transmission for our statistics
self.rx_start_of_transmission = time.time() self.rx_start_of_transmission = time.time()
def arq_received_channel_is_open(self, data_in:bytes):
def arq_received_channel_is_open(self, data_in:bytes):
protocol_version = int.from_bytes(bytes(data_in[13:14]), "big")
if protocol_version == static.ARQ_PROTOCOL_VERSION:
static.INFO.append("DATACHANNEL;OPEN") static.INFO.append("DATACHANNEL;OPEN")
static.DXCALLSIGN_CRC = bytes(data_in[3:5]) static.DXCALLSIGN_CRC = bytes(data_in[3:5])
#static.DXCALLSIGN = bytes(data_in[5:11]).rstrip(b'\x00') frametype = int.from_bytes(bytes(data_in[:1]), "big")
helpers.add_to_heard_stations(static.DXCALLSIGN,static.DXGRID, 'DATA-CHANNEL', static.SNR, static.FREQ_OFFSET, static.HAMLIB_FREQUENCY)
self.data_channel_last_received = int(time.time()) if frametype == 228:
self.received_low_bandwith_mode = True
self.mode_list = self.mode_list_low_bw
self.time_list = self.time_list_low_bw
self.speed_level = len(self.mode_list) - 1
structlog.get_logger("structlog").debug("low bandwith mode", modes=self.mode_list)
else:
self.received_low_bandwith_mode = False
self.mode_list = self.mode_list_high_bw
self.time_list = self.time_list_high_bw
self.speed_level = len(self.mode_list) - 1
structlog.get_logger("structlog").debug("high bandwith mode", modes=self.mode_list)
helpers.add_to_heard_stations(static.DXCALLSIGN,static.DXGRID, 'DATA-CHANNEL', static.SNR, static.FREQ_OFFSET, static.HAMLIB_FREQUENCY)
structlog.get_logger("structlog").info("[TNC] ARQ | DATA | TX | [" + str(static.MYCALLSIGN, 'utf-8') + "]>>|<<[" + str(static.DXCALLSIGN, 'utf-8') + "]", snr=static.SNR) structlog.get_logger("structlog").info("[TNC] ARQ | DATA | TX | [" + str(static.MYCALLSIGN, 'utf-8') + "]>>|<<[" + str(static.DXCALLSIGN, 'utf-8') + "]", snr=static.SNR)
# as soon as we set ARQ_STATE to DATA, transmission starts # as soon as we set ARQ_STATE to DATA, transmission starts
static.ARQ_STATE = True static.ARQ_STATE = True
self.data_channel_last_received = int(time.time()) self.data_channel_last_received = int(time.time())
else:
static.TNC_STATE = 'IDLE'
static.ARQ_STATE = False
static.INFO.append("PROTOCOL;VERSION_MISSMATCH")
structlog.get_logger("structlog").warning("protocol version missmatch", received=protocol_version, own=static.ARQ_PROTOCOL_VERSION)
self.arq_cleanup()
# ---------- PING # ---------- PING
@ -986,7 +1045,7 @@ class DATA():
def stop_transmission(self): def stop_transmission(self):
structlog.get_logger("structlog").warning("[TNC] Stopping transmission!") structlog.get_logger("structlog").warning("[TNC] Stopping transmission!")
stop_frame = bytearray(14) stop_frame = bytearray(14)
stop_frame[:1] = bytes([227]) stop_frame[:1] = bytes([249])
stop_frame[1:3] = static.DXCALLSIGN_CRC stop_frame[1:3] = static.DXCALLSIGN_CRC
stop_frame[3:5] = static.MYCALLSIGN_CRC stop_frame[3:5] = static.MYCALLSIGN_CRC
@ -1017,7 +1076,7 @@ class DATA():
while static.BEACON_STATE and not static.ARQ_STATE: while static.BEACON_STATE and not static.ARQ_STATE:
beacon_frame = bytearray(14) beacon_frame = bytearray(14)
beacon_frame[:1] = bytes([230]) beacon_frame[:1] = bytes([250])
beacon_frame[1:2] = b'\x01' beacon_frame[1:2] = b'\x01'
beacon_frame[2:8] = static.MYCALLSIGN beacon_frame[2:8] = static.MYCALLSIGN
beacon_frame[8:14] = static.MYGRID beacon_frame[8:14] = static.MYGRID
@ -1099,6 +1158,14 @@ class DATA():
def reset_statistics(self):
# reset ARQ statistics
static.ARQ_BYTES_PER_MINUTE_BURST = 0
static.ARQ_BYTES_PER_MINUTE = 0
static.ARQ_BITS_PER_SECOND_BURST = 0
static.ARQ_BITS_PER_SECOND = 0
static.ARQ_TRANSMISSION_PERCENT = 0
static.TOTAL_BYTES = 0
def calculate_transfer_rate_tx(self, tx_start_of_transmission:float, sentbytes:int, tx_buffer_length:int) -> list: def calculate_transfer_rate_tx(self, tx_start_of_transmission:float, sentbytes:int, tx_buffer_length:int) -> list:
@ -1156,6 +1223,14 @@ class DATA():
self.frame_received_counter = 0 self.frame_received_counter = 0
self.speed_level = len(self.mode_list) - 1 self.speed_level = len(self.mode_list) - 1
# low bandwith mode indicator
self.received_low_bandwith_mode = False
# reset retry counter for rx channel / burst
self.n_retries_per_burst = 0
def arq_reset_ack(self,state:bool): def arq_reset_ack(self,state:bool):
self.burst_ack = state self.burst_ack = state
@ -1163,16 +1238,21 @@ class DATA():
self.data_frame_ack_received = state self.data_frame_ack_received = state
def set_listening_modes(self): def set_listening_modes(self, mode):
# set modes we want to listening to # set modes we want listening to
mode_name = codec2.freedv_get_mode_name_by_value(self.mode_list[self.speed_level])
mode_name = codec2.freedv_get_mode_name_by_value(mode)
if mode_name == 'datac1': if mode_name == 'datac1':
modem.RECEIVE_DATAC1 = True modem.RECEIVE_DATAC1 = True
structlog.get_logger("structlog").debug("changing listening data mode", mode="datac1")
elif mode_name == 'datac3': elif mode_name == 'datac3':
modem.RECEIVE_DATAC3 = True modem.RECEIVE_DATAC3 = True
structlog.get_logger("structlog").debug("changing listening data mode", mode="datac3")
elif mode_name == 'allmodes': elif mode_name == 'allmodes':
modem.RECEIVE_DATAC1 = True modem.RECEIVE_DATAC1 = True
modem.RECEIVE_DATAC3 = True modem.RECEIVE_DATAC3 = True
structlog.get_logger("structlog").debug("changing listening data mode", mode="datac1/datac3")
# ------------------------- WATCHDOG FUNCTIONS FOR TIMER # ------------------------- WATCHDOG FUNCTIONS FOR TIMER
@ -1188,20 +1268,12 @@ class DATA():
self.burst_watchdog() self.burst_watchdog()
def burst_watchdog(self): def burst_watchdog(self):
'''
# ISS SIDE WE ALSO NEED TO CHECK TIME SO WE ARE SENDING IN CORRECT MODE IF WE MISSED A NACK FRAME
if static.ARQ_STATE and static.TNC_STATE == 'BUSY' and not self.is_IRS:
if self.data_channel_last_received + self.time_list[self.speed_level] < time.time():
self.speed_level -= 1
if self.speed_level <= 0:
self.speed_level = 0
self.data_channel_last_received = time.time()
'''
# IRS SIDE # IRS SIDE
if static.ARQ_STATE and static.TNC_STATE == 'BUSY' and self.is_IRS: if static.ARQ_STATE and static.TNC_STATE == 'BUSY' and self.is_IRS:
if self.data_channel_last_received + self.time_list[self.speed_level] > time.time(): if self.data_channel_last_received + self.time_list[self.speed_level] > time.time():
print((self.data_channel_last_received + self.time_list[self.speed_level])-time.time()) #print((self.data_channel_last_received + self.time_list[self.speed_level])-time.time())
#pass pass
else: else:
print("TIMEOUT") print("TIMEOUT")
self.frame_received_counter = 0 self.frame_received_counter = 0
@ -1210,7 +1282,7 @@ class DATA():
self.speed_level = 0 self.speed_level = 0
# updated modes we are listening to # updated modes we are listening to
self.set_listening_modes() self.set_listening_modes(self.mode_list[self.speed_level])
# BUILDING NACK FRAME FOR DATA FRAME # BUILDING NACK FRAME FOR DATA FRAME
burst_nack_frame = bytearray(14) burst_nack_frame = bytearray(14)
@ -1229,6 +1301,12 @@ class DATA():
# #time.sleep(0.01) # #time.sleep(0.01)
# self.data_channel_last_received = time.time() # self.data_channel_last_received = time.time()
self.data_channel_last_received = time.time() self.data_channel_last_received = time.time()
self.n_retries_per_burst += 1
if self.n_retries_per_burst >= self.rx_n_max_retries_per_burst:
self.arq_cleanup()
print("RX TIMEOUT!!!!")
#print(self.n_retries_per_burst)
def data_channel_keep_alive_watchdog(self): def data_channel_keep_alive_watchdog(self):
""" """
@ -1241,6 +1319,7 @@ class DATA():
time.sleep(0.01) time.sleep(0.01)
if self.data_channel_last_received + self.transmission_timeout > time.time(): if self.data_channel_last_received + self.transmission_timeout > time.time():
time.sleep(0.01) time.sleep(0.01)
#print(self.data_channel_last_received + self.transmission_timeout - time.time())
#pass #pass
else: else:
self.data_channel_last_received = 0 self.data_channel_last_received = 0

View file

@ -42,7 +42,7 @@ if __name__ == '__main__':
PARSER.add_argument('--rigctld_ip', dest="rigctld_ip", default="direct", help="Set rigctld ip") PARSER.add_argument('--rigctld_ip', dest="rigctld_ip", default="direct", help="Set rigctld ip")
PARSER.add_argument('--scatter', dest="send_scatter", action="store_true", help="Send scatter information via network") PARSER.add_argument('--scatter', dest="send_scatter", action="store_true", help="Send scatter information via network")
PARSER.add_argument('--fft', dest="send_fft", action="store_true", help="Send fft information via network") PARSER.add_argument('--fft', dest="send_fft", action="store_true", help="Send fft information via network")
PARSER.add_argument('--500hz', dest="low_bandwith_mode", action="store_true", help="Enable low bandwith mode ( 500 Hz only )")
@ -67,6 +67,11 @@ if __name__ == '__main__':
static.HAMLIB_RGICTLD_PORT = ARGS.rigctld_port static.HAMLIB_RGICTLD_PORT = ARGS.rigctld_port
static.ENABLE_SCATTER = ARGS.send_scatter static.ENABLE_SCATTER = ARGS.send_scatter
static.ENABLE_FFT = ARGS.send_fft static.ENABLE_FFT = ARGS.send_fft
static.LOW_BANDWITH_MODE = ARGS.low_bandwith_mode
# we need to wait until we got all parameters from argparse first before we can load the other modules # we need to wait until we got all parameters from argparse first before we can load the other modules
import sock import sock

View file

@ -106,7 +106,6 @@ class radio:
# get devicenumber by looking for deviceobject in Hamlib module # get devicenumber by looking for deviceobject in Hamlib module
try: try:
self.devicenumber = int(getattr(Hamlib, self.devicename)) self.devicenumber = int(getattr(Hamlib, self.devicename))
print(self.devicenumber)
except: except:
structlog.get_logger("structlog").error("[RIG] Hamlib: rig not supported...") structlog.get_logger("structlog").error("[RIG] Hamlib: rig not supported...")
self.devicenumber = 0 self.devicenumber = 0
@ -122,14 +121,6 @@ class radio:
self.my_rig.set_conf("ptt_pathname", self.pttport) self.my_rig.set_conf("ptt_pathname", self.pttport)
print(self.my_rig.get_conf("rig_pathname"))
print(self.my_rig.get_conf("retry"))
print(self.my_rig.get_conf("serial_speed"))
print(self.my_rig.get_conf("serial_handshake"))
print(self.my_rig.get_conf("stop_bits"))
print(self.my_rig.get_conf("data_bits"))
print(self.my_rig.get_conf("ptt_pathname"))
if self.hamlib_ptt_type == 'RIG': if self.hamlib_ptt_type == 'RIG':
@ -168,6 +159,8 @@ class radio:
else: #self.hamlib_ptt_type == 'RIG_PTT_NONE': else: #self.hamlib_ptt_type == 'RIG_PTT_NONE':
self.hamlib_ptt_type = Hamlib.RIG_PTT_NONE self.hamlib_ptt_type = Hamlib.RIG_PTT_NONE
structlog.get_logger("structlog").info("[RIG] Opening...", device=self.devicenumber, path=self.my_rig.get_conf("rig_pathname"), serial_speed=self.my_rig.get_conf("serial_speed"), serial_handshake=self.my_rig.get_conf("serial_handshake"), stop_bits=self.my_rig.get_conf("stop_bits"), data_bits=self.my_rig.get_conf("data_bits"), ptt_pathname=self.my_rig.get_conf("ptt_pathname"))
self.my_rig.open() self.my_rig.open()
atexit.register(self.my_rig.close) atexit.register(self.my_rig.close)

View file

@ -50,15 +50,20 @@ class ThreadedTCPRequestHandler(socketserver.StreamRequestHandler):
def send_to_client(self): def send_to_client(self):
tempdata = b''
while self.connection_alive: while self.connection_alive:
# send tnc state as network stream # send tnc state as network stream
# check server port against daemon port and send corresponding data # check server port against daemon port and send corresponding data
if self.server.server_address[1] == static.PORT and not static.TNCSTARTED: if self.server.server_address[1] == static.PORT and not static.TNCSTARTED:
data = send_tnc_state() data = send_tnc_state()
if data != tempdata:
tempdata = data
SOCKET_QUEUE.put(data) SOCKET_QUEUE.put(data)
else: else:
data = send_daemon_state() data = send_daemon_state()
if data != tempdata:
tempdata = data
SOCKET_QUEUE.put(data) SOCKET_QUEUE.put(data)
time.sleep(0.5) time.sleep(0.5)
@ -96,14 +101,20 @@ class ThreadedTCPRequestHandler(socketserver.StreamRequestHandler):
#print("connection broken. Closing...") #print("connection broken. Closing...")
self.connection_alive = False self.connection_alive = False
if data.startswith(b'{"type"') and data.endswith(b'}\n'): if data.startswith(b'{') and data.endswith(b'}\n'):
data = data[:-1] # remove b'\n' # split data by \n if we have multiple commands in socket buffer
print(data) data = data.split(b'\n')
if self.server.server_address[1] == static.PORT: # remove empty data
process_tnc_commands(data) data.remove(b'')
else:
process_daemon_commands(data)
# iterate thorugh data list
for commands in data:
if self.server.server_address[1] == static.PORT:
process_tnc_commands(commands)
else:
process_daemon_commands(commands)
# finally delete our rx buffer to be ready for new commands
data = bytes() data = bytes()
@ -128,7 +139,6 @@ class ThreadedTCPRequestHandler(socketserver.StreamRequestHandler):
CONNECTED_CLIENTS.remove(self.request) CONNECTED_CLIENTS.remove(self.request)
except: except:
print("client connection already removed from client list") print("client connection already removed from client list")
print(CONNECTED_CLIENTS)
def process_tnc_commands(data): def process_tnc_commands(data):
@ -189,89 +199,6 @@ def process_tnc_commands(data):
except Exception as e: except Exception as e:
structlog.get_logger("structlog").warning("[SCK] command execution error", e=e, command=received_json) structlog.get_logger("structlog").warning("[SCK] command execution error", e=e, command=received_json)
# TRANSMIT FILE ----------------------------------------------------------
'''
if received_json["type"] == 'arq' and received_json["command"] == "send_file":
try:
static.TNC_STATE = 'BUSY'
# on a new transmission we reset the timer
static.ARQ_START_OF_TRANSMISSION = int(time.time())
dxcallsign = received_json["parameter"][0]["dxcallsign"]
mode = int(received_json["parameter"][0]["mode"])
n_frames = int(received_json["parameter"][0]["n_frames"])
filename = received_json["parameter"][0]["filename"]
filetype = received_json["parameter"][0]["filetype"]
data = received_json["parameter"][0]["data"]
checksum = received_json["parameter"][0]["checksum"]
static.DXCALLSIGN = bytes(dxcallsign, 'utf-8')
static.DXCALLSIGN_CRC = helpers.get_crc_16(static.DXCALLSIGN)
# dt = datatype
# --> f = file
# --> m = message
# fn = filename
# ft = filetype
# d = data
# crc = checksum
#rawdata = {"dt": "f", "fn": filename, "ft": filetype,"d": data, "crc": checksum}
#dataframe = json.dumps(rawdata)
#data_out = bytes(dataframe, 'utf-8')
rawdata = bytes()
rawdata += bytes('f', 'utf-8')
rawdata += bytes(';', 'utf-8')
rawdata += bytes(filename, 'utf-8')
rawdata += bytes(';', 'utf-8')
rawdata += bytes(filetype, 'utf-8')
rawdata += bytes(';', 'utf-8')
rawdata += bytes(checksum, 'utf-8')
rawdata += bytes(';', 'utf-8')
rawdata += bytes(data, 'utf-8')
data_handler.DATA_QUEUE_TRANSMIT.put(['ARQ_FILE', data_out, mode, n_frames])
print(data_handler.DATA_QUEUE_TRANSMIT.qsize())
except Exception as e:
structlog.get_logger("structlog").warning("[SCK] command execution error", e=e, command=received_json)
# TRANSMIT MESSAGE ----------------------------------------------------------
if received_json["type"] == 'arq' and received_json["command"] == "send_message":
try:
static.TNC_STATE = 'BUSY'
print(received_json)
# on a new transmission we reset the timer
static.ARQ_START_OF_TRANSMISSION = int(time.time())
dxcallsign = received_json["parameter"][0]["dxcallsign"]
mode = int(received_json["parameter"][0]["mode"])
n_frames = int(received_json["parameter"][0]["n_frames"])
data = received_json["parameter"][0]["data"] # d = data
checksum = received_json["parameter"][0]["checksum"] # crc = checksum
static.DXCALLSIGN = bytes(dxcallsign, 'utf-8')
static.DXCALLSIGN_CRC = helpers.get_crc_16(static.DXCALLSIGN)
# dt = datatype
# --> f = file
# --> m = message
# fn = filename
# ft = filetype
# d = data
# crc = checksum
rawdata = {"dt": "m","d": data, "crc": checksum}
dataframe = json.dumps(rawdata)
data_out = bytes(dataframe, 'utf-8')
data_handler.DATA_QUEUE_TRANSMIT.put(['ARQ_MESSAGE', data_out, mode, n_frames])
except Exception as e:
structlog.get_logger("structlog").warning("[SCK] command execution error", e=e, command=received_json)
'''
# STOP TRANSMISSION ---------------------------------------------------------- # STOP TRANSMISSION ----------------------------------------------------------
@ -306,31 +233,13 @@ def process_tnc_commands(data):
except Exception as e: except Exception as e:
structlog.get_logger("structlog").warning("[SCK] command execution error", e=e, command=received_json) structlog.get_logger("structlog").warning("[SCK] command execution error", e=e, command=received_json)
'''
if received_json["type"] == 'GET' and received_json["command"] == 'rx_msg_buffer':
output = {
"command": "rx_msg_buffer",
"data-array": [],
}
for i in range(0, len(static.RX_MSG_BUFFER)):
rawdata = json.loads(static.RX_MSG_BUFFER[i][3])
output["data-array"].append({"dxcallsign": str(static.RX_MSG_BUFFER[i][0], 'utf-8'), "dxgrid": str(static.RX_MSG_BUFFER[i][1], 'utf-8'), "timestamp": static.RX_MSG_BUFFER[i][2], "rxdata": [rawdata]})
jsondata = json.dumps(output)
#self.request.sendall(bytes(jsondata, encoding))
SOCKET_QUEUE.put(jsondata)
'''
if received_json["type"] == 'set' and received_json["command"] == 'del_rx_buffer': if received_json["type"] == 'set' and received_json["command"] == 'del_rx_buffer':
try: try:
static.RX_BUFFER = [] static.RX_BUFFER = []
except Exception as e: except Exception as e:
structlog.get_logger("structlog").warning("[SCK] command execution error", e=e, command=received_json) structlog.get_logger("structlog").warning("[SCK] command execution error", e=e, command=received_json)
'''
if received_json["type"] == 'set' and received_json["command"] == 'del_rx_msg_buffer':
static.RX_MSG_BUFFER = []
'''
# exception, if JSON cant be decoded # exception, if JSON cant be decoded
except Exception as e: except Exception as e:
structlog.get_logger("structlog").error("[TNC] JSON decoding error", e=e) structlog.get_logger("structlog").error("[TNC] JSON decoding error", e=e)
@ -427,6 +336,7 @@ def process_daemon_commands(data):
rigctld_port = str(received_json["parameter"][0]["rigctld_port"]) rigctld_port = str(received_json["parameter"][0]["rigctld_port"])
enable_scatter = str(received_json["parameter"][0]["enable_scatter"]) enable_scatter = str(received_json["parameter"][0]["enable_scatter"])
enable_fft = str(received_json["parameter"][0]["enable_fft"]) enable_fft = str(received_json["parameter"][0]["enable_fft"])
low_bandwith_mode = str(received_json["parameter"][0]["low_bandwith_mode"])
DAEMON_QUEUE.put(['STARTTNC', \ DAEMON_QUEUE.put(['STARTTNC', \
mycall, \ mycall, \
@ -445,7 +355,8 @@ def process_daemon_commands(data):
rigctld_ip, \ rigctld_ip, \
rigctld_port, \ rigctld_port, \
enable_scatter, \ enable_scatter, \
enable_fft \ enable_fft, \
low_bandwith_mode \
]) ])
except Exception as e: except Exception as e:
@ -465,6 +376,7 @@ def process_daemon_commands(data):
radiocontrol = str(received_json["parameter"][0]["radiocontrol"]) radiocontrol = str(received_json["parameter"][0]["radiocontrol"])
rigctld_ip = str(received_json["parameter"][0]["rigctld_ip"]) rigctld_ip = str(received_json["parameter"][0]["rigctld_ip"])
rigctld_port = str(received_json["parameter"][0]["rigctld_port"]) rigctld_port = str(received_json["parameter"][0]["rigctld_port"])
DAEMON_QUEUE.put(['TEST_HAMLIB', \ DAEMON_QUEUE.put(['TEST_HAMLIB', \
devicename, \ devicename, \
deviceport, \ deviceport, \
@ -501,8 +413,8 @@ def send_daemon_state():
'input_devices': static.AUDIO_INPUT_DEVICES, 'input_devices': static.AUDIO_INPUT_DEVICES,
'output_devices': static.AUDIO_OUTPUT_DEVICES, 'output_devices': static.AUDIO_OUTPUT_DEVICES,
'serial_devices': static.SERIAL_DEVICES, 'serial_devices': static.SERIAL_DEVICES,
'cpu': str(psutil.cpu_percent()), #'cpu': str(psutil.cpu_percent()),
'ram': str(psutil.virtual_memory().percent), #'ram': str(psutil.virtual_memory().percent),
'version': '0.1' 'version': '0.1'
} }

View file

@ -7,6 +7,9 @@ Created on Wed Dec 23 11:13:57 2020
Here we are saving application wide variables and stats, which have to be accessed everywhere. Here we are saving application wide variables and stats, which have to be accessed everywhere.
Not nice, tipps are appreciated :-) Not nice, tipps are appreciated :-)
""" """
VERSION = '0.1'
# DAEMON # DAEMON
DAEMONPORT = 3001 DAEMONPORT = 3001
TNCSTARTED = False TNCSTARTED = False
@ -23,6 +26,7 @@ DXCALLSIGN_CRC = b'A'
MYGRID = b'' MYGRID = b''
DXGRID = b'' DXGRID = b''
LOW_BANDWITH_MODE = False
# --------------------------------- # ---------------------------------
# Server Defaults # Server Defaults
@ -73,6 +77,9 @@ AUDIO_RMS = 0
FFT = [0] FFT = [0]
ENABLE_FFT = False ENABLE_FFT = False
# ARQ PROTOCOL VERSION
ARQ_PROTOCOL_VERSION = 0
# ARQ statistics # ARQ statistics
ARQ_BYTES_PER_MINUTE_BURST = 0 ARQ_BYTES_PER_MINUTE_BURST = 0
ARQ_BYTES_PER_MINUTE = 0 ARQ_BYTES_PER_MINUTE = 0