mirror of
https://github.com/DJ2LS/FreeDATA
synced 2024-05-14 08:04:33 +00:00
Merge pull request #323 from DJ2LS/ls-arq
Further ARQ improvements + speed chart
This commit is contained in:
commit
3c0484cf46
|
@ -32,8 +32,8 @@
|
||||||
"bootstrap": "^5.2.1",
|
"bootstrap": "^5.2.1",
|
||||||
"bootstrap-icons": "^1.9.1",
|
"bootstrap-icons": "^1.9.1",
|
||||||
"bootswatch": "^5.2.0",
|
"bootswatch": "^5.2.0",
|
||||||
"chart.js": "^3.9.1",
|
"chart.js": "^4.0.0",
|
||||||
"chartjs-plugin-annotation": "^2.0.1",
|
"chartjs-plugin-annotation": "^2.1.2",
|
||||||
"electron-log": "^4.4.8",
|
"electron-log": "^4.4.8",
|
||||||
"electron-updater": "^5.2.1",
|
"electron-updater": "^5.2.1",
|
||||||
"emoji-picker-element": "^1.12.1",
|
"emoji-picker-element": "^1.12.1",
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
const path = require('path');
|
const path = require('path');
|
||||||
const {ipcRenderer} = require('electron');
|
const {ipcRenderer, shell} = require('electron');
|
||||||
const exec = require('child_process').spawn;
|
const exec = require('child_process').spawn;
|
||||||
const sock = require('./sock.js');
|
const sock = require('./sock.js');
|
||||||
const daemon = require('./daemon.js');
|
const daemon = require('./daemon.js');
|
||||||
|
@ -37,8 +37,76 @@ var dbfs_level_raw = 0
|
||||||
// WINDOW LISTENER
|
// WINDOW LISTENER
|
||||||
window.addEventListener('DOMContentLoaded', () => {
|
window.addEventListener('DOMContentLoaded', () => {
|
||||||
|
|
||||||
|
// save frequency event listener
|
||||||
|
document.getElementById("saveFrequency").addEventListener("click", () => {
|
||||||
|
var freq = document.getElementById("newFrequency").value;
|
||||||
|
console.log(freq)
|
||||||
|
let Data = {
|
||||||
|
type: "set",
|
||||||
|
command: "frequency",
|
||||||
|
frequency: freq,
|
||||||
|
};
|
||||||
|
ipcRenderer.send('run-tnc-command', Data);
|
||||||
|
|
||||||
|
});
|
||||||
|
|
||||||
|
// enter button for input field
|
||||||
|
document.getElementById("newFrequency").addEventListener("keypress", function(event) {
|
||||||
|
if (event.key === "Enter") {
|
||||||
|
event.preventDefault();
|
||||||
|
document.getElementById("saveFrequency").click();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// save mode event listener
|
||||||
|
document.getElementById("saveModePKTUSB").addEventListener("click", () => {
|
||||||
|
let Data = {
|
||||||
|
type: "set",
|
||||||
|
command: "mode",
|
||||||
|
mode: "PKTUSB",
|
||||||
|
};
|
||||||
|
ipcRenderer.send('run-tnc-command', Data);
|
||||||
|
});
|
||||||
|
|
||||||
|
// save mode event listener
|
||||||
|
document.getElementById("saveModeUSB").addEventListener("click", () => {
|
||||||
|
let Data = {
|
||||||
|
type: "set",
|
||||||
|
command: "mode",
|
||||||
|
mode: "USB",
|
||||||
|
};
|
||||||
|
ipcRenderer.send('run-tnc-command', Data);
|
||||||
|
});
|
||||||
|
|
||||||
|
// save mode event listener
|
||||||
|
document.getElementById("saveModeLSB").addEventListener("click", () => {
|
||||||
|
let Data = {
|
||||||
|
type: "set",
|
||||||
|
command: "mode",
|
||||||
|
mode: "LSB",
|
||||||
|
};
|
||||||
|
ipcRenderer.send('run-tnc-command', Data);
|
||||||
|
});
|
||||||
|
|
||||||
|
// save mode event listener
|
||||||
|
document.getElementById("saveModeAM").addEventListener("click", () => {
|
||||||
|
let Data = {
|
||||||
|
type: "set",
|
||||||
|
command: "mode",
|
||||||
|
mode: "AM",
|
||||||
|
};
|
||||||
|
ipcRenderer.send('run-tnc-command', Data);
|
||||||
|
});
|
||||||
|
|
||||||
|
// save mode event listener
|
||||||
|
document.getElementById("saveModeFM").addEventListener("click", () => {
|
||||||
|
let Data = {
|
||||||
|
type: "set",
|
||||||
|
command: "mode",
|
||||||
|
mode: "FM",
|
||||||
|
};
|
||||||
|
ipcRenderer.send('run-tnc-command', Data);
|
||||||
|
});
|
||||||
|
|
||||||
// start stop audio recording event listener
|
// start stop audio recording event listener
|
||||||
document.getElementById("startStopRecording").addEventListener("click", () => {
|
document.getElementById("startStopRecording").addEventListener("click", () => {
|
||||||
|
@ -221,16 +289,57 @@ document.getElementById('openReceivedFilesFolder').addEventListener('click', ()
|
||||||
if (config.spectrum == 'waterfall') {
|
if (config.spectrum == 'waterfall') {
|
||||||
document.getElementById("waterfall-scatter-switch1").checked = true;
|
document.getElementById("waterfall-scatter-switch1").checked = true;
|
||||||
document.getElementById("waterfall-scatter-switch2").checked = false;
|
document.getElementById("waterfall-scatter-switch2").checked = false;
|
||||||
document.getElementById("scatter").style.visibility = 'hidden';
|
document.getElementById("waterfall-scatter-switch3").checked = false;
|
||||||
|
|
||||||
document.getElementById("waterfall").style.visibility = 'visible';
|
document.getElementById("waterfall").style.visibility = 'visible';
|
||||||
document.getElementById("waterfall").style.height = '100%';
|
document.getElementById("waterfall").style.height = '100%';
|
||||||
} else {
|
document.getElementById("waterfall").style.display = 'block';
|
||||||
|
|
||||||
|
document.getElementById("scatter").style.height = '0px';
|
||||||
|
document.getElementById("scatter").style.visibility = 'hidden';
|
||||||
|
document.getElementById("scatter").style.display = 'none';
|
||||||
|
|
||||||
|
document.getElementById("chart").style.height = '0px';
|
||||||
|
document.getElementById("chart").style.visibility = 'hidden';
|
||||||
|
document.getElementById("chart").style.display = 'none';
|
||||||
|
|
||||||
|
} else if (config.spectrum == 'scatter'){
|
||||||
|
|
||||||
document.getElementById("waterfall-scatter-switch1").checked = false;
|
document.getElementById("waterfall-scatter-switch1").checked = false;
|
||||||
document.getElementById("waterfall-scatter-switch2").checked = true;
|
document.getElementById("waterfall-scatter-switch2").checked = true;
|
||||||
document.getElementById("scatter").style.visibility = 'visible';
|
document.getElementById("waterfall-scatter-switch3").checked = false;
|
||||||
|
|
||||||
document.getElementById("waterfall").style.visibility = 'hidden';
|
document.getElementById("waterfall").style.visibility = 'hidden';
|
||||||
document.getElementById("waterfall").style.height = '0px';
|
document.getElementById("waterfall").style.height = '0px';
|
||||||
|
document.getElementById("waterfall").style.display = 'none';
|
||||||
|
|
||||||
|
|
||||||
|
document.getElementById("scatter").style.height = '100%';
|
||||||
|
document.getElementById("scatter").style.visibility = 'visible';
|
||||||
|
document.getElementById("scatter").style.display = 'block';
|
||||||
|
|
||||||
|
document.getElementById("chart").style.visibility = 'hidden';
|
||||||
|
document.getElementById("chart").style.height = '0px';
|
||||||
|
document.getElementById("chart").style.display = 'none';
|
||||||
|
|
||||||
|
} else {
|
||||||
|
|
||||||
|
document.getElementById("waterfall-scatter-switch1").checked = false;
|
||||||
|
document.getElementById("waterfall-scatter-switch2").checked = false;
|
||||||
|
document.getElementById("waterfall-scatter-switch3").checked = true;
|
||||||
|
|
||||||
|
document.getElementById("waterfall").style.visibility = 'hidden';
|
||||||
|
document.getElementById("waterfall").style.height = '0px';
|
||||||
|
document.getElementById("waterfall").style.display = 'none';
|
||||||
|
|
||||||
|
document.getElementById("scatter").style.height = '0px';
|
||||||
|
document.getElementById("scatter").style.visibility = 'hidden';
|
||||||
|
document.getElementById("scatter").style.display = 'none';
|
||||||
|
|
||||||
|
document.getElementById("chart").style.visibility = 'visible';
|
||||||
|
document.getElementById("chart").style.height = '100%';
|
||||||
|
document.getElementById("chart").style.display = 'block';
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// radio control element
|
// radio control element
|
||||||
|
@ -703,21 +812,55 @@ document.getElementById('hamlib_rigctld_stop').addEventListener('click', () => {
|
||||||
// on click waterfall scatter toggle view
|
// on click waterfall scatter toggle view
|
||||||
// waterfall
|
// waterfall
|
||||||
document.getElementById("waterfall-scatter-switch1").addEventListener("click", () => {
|
document.getElementById("waterfall-scatter-switch1").addEventListener("click", () => {
|
||||||
|
document.getElementById("chart").style.visibility = 'hidden';
|
||||||
|
document.getElementById("chart").style.display = 'none';
|
||||||
|
document.getElementById("chart").style.height = '0px';
|
||||||
|
|
||||||
|
document.getElementById("scatter").style.height = '0px';
|
||||||
|
document.getElementById("scatter").style.display = 'none';
|
||||||
document.getElementById("scatter").style.visibility = 'hidden';
|
document.getElementById("scatter").style.visibility = 'hidden';
|
||||||
|
|
||||||
|
document.getElementById("waterfall").style.display = 'block';
|
||||||
document.getElementById("waterfall").style.visibility = 'visible';
|
document.getElementById("waterfall").style.visibility = 'visible';
|
||||||
document.getElementById("waterfall").style.height = '100%';
|
document.getElementById("waterfall").style.height = '100%';
|
||||||
|
|
||||||
config.spectrum = 'waterfall';
|
config.spectrum = 'waterfall';
|
||||||
fs.writeFileSync(configPath, JSON.stringify(config, null, 2));
|
fs.writeFileSync(configPath, JSON.stringify(config, null, 2));
|
||||||
});
|
});
|
||||||
// scatter
|
// scatter
|
||||||
document.getElementById("waterfall-scatter-switch2").addEventListener("click", () => {
|
document.getElementById("waterfall-scatter-switch2").addEventListener("click", () => {
|
||||||
|
document.getElementById("scatter").style.display = 'block';
|
||||||
document.getElementById("scatter").style.visibility = 'visible';
|
document.getElementById("scatter").style.visibility = 'visible';
|
||||||
|
document.getElementById("scatter").style.height = '100%';
|
||||||
|
|
||||||
document.getElementById("waterfall").style.visibility = 'hidden';
|
document.getElementById("waterfall").style.visibility = 'hidden';
|
||||||
document.getElementById("waterfall").style.height = '0px';
|
document.getElementById("waterfall").style.height = '0px';
|
||||||
|
document.getElementById("waterfall").style.display = 'none';
|
||||||
|
|
||||||
|
document.getElementById("chart").style.visibility = 'hidden';
|
||||||
|
document.getElementById("chart").style.height = '0px';
|
||||||
|
document.getElementById("chart").style.display = 'none';
|
||||||
|
|
||||||
config.spectrum = 'scatter';
|
config.spectrum = 'scatter';
|
||||||
fs.writeFileSync(configPath, JSON.stringify(config, null, 2));
|
fs.writeFileSync(configPath, JSON.stringify(config, null, 2));
|
||||||
});
|
});
|
||||||
|
// chart
|
||||||
|
document.getElementById("waterfall-scatter-switch3").addEventListener("click", () => {
|
||||||
|
document.getElementById("waterfall").style.visibility = 'hidden';
|
||||||
|
document.getElementById("waterfall").style.height = '0px';
|
||||||
|
document.getElementById("waterfall").style.display = 'none';
|
||||||
|
|
||||||
|
document.getElementById("scatter").style.height = '0px';
|
||||||
|
document.getElementById("scatter").style.visibility = 'hidden';
|
||||||
|
document.getElementById("scatter").style.display = 'none';
|
||||||
|
|
||||||
|
document.getElementById("chart").style.height = '100%';
|
||||||
|
document.getElementById("chart").style.display = 'block';
|
||||||
|
document.getElementById("chart").style.visibility = 'visible';
|
||||||
|
|
||||||
|
config.spectrum = 'chart';
|
||||||
|
fs.writeFileSync(configPath, JSON.stringify(config, null, 2));
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
// on click remote tnc toggle view
|
// on click remote tnc toggle view
|
||||||
|
@ -1008,6 +1151,11 @@ document.getElementById('hamlib_rigctld_stop').addEventListener('click', () => {
|
||||||
sock.stopBeacon();
|
sock.stopBeacon();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// Explorer button clicked
|
||||||
|
document.getElementById("openExplorer").addEventListener("click", () => {
|
||||||
|
shell.openExternal('https://explorer.freedata.app/?myCall=' + document.getElementById("myCall").value);
|
||||||
|
});
|
||||||
|
|
||||||
// startTNC button clicked
|
// startTNC button clicked
|
||||||
document.getElementById("startTNC").addEventListener("click", () => {
|
document.getElementById("startTNC").addEventListener("click", () => {
|
||||||
|
|
||||||
|
@ -1332,28 +1480,18 @@ ipcRenderer.on('action-update-tnc-state', (event, arg) => {
|
||||||
document.title = documentTitle[0] + 'Call: ' + arg.mycallsign;
|
document.title = documentTitle[0] + 'Call: ' + arg.mycallsign;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// update mygrid information with data from tnc
|
||||||
// TOE TIME OF EXECUTION --> How many time needs a command to be executed until data arrives
|
if (typeof(arg.mygrid) !== 'undefined') {
|
||||||
// deactivated this feature, beacuse its useless at this time. maybe it is getting more interesting, if we are working via network
|
document.getElementById("myGrid").value = arg.mygrid;
|
||||||
// but for this we need to find a nice place for this on the screen
|
|
||||||
/*
|
|
||||||
if (typeof(arg.toe) == 'undefined') {
|
|
||||||
var toe = 0
|
|
||||||
} else {
|
|
||||||
var toe = arg.toe
|
|
||||||
}
|
}
|
||||||
document.getElementById("toe").innerHTML = toe + ' ms'
|
|
||||||
*/
|
|
||||||
|
|
||||||
// DATA STATE
|
// DATA STATE
|
||||||
global.rxBufferLengthTnc = arg.rx_buffer_length
|
global.rxBufferLengthTnc = arg.rx_buffer_length
|
||||||
|
|
||||||
// SCATTER DIAGRAM PLOTTING
|
|
||||||
//global.myChart.destroy();
|
|
||||||
|
|
||||||
//console.log(arg.scatter.length)
|
// START OF SCATTER CHART
|
||||||
|
|
||||||
const config = {
|
const scatterConfig = {
|
||||||
plugins: {
|
plugins: {
|
||||||
legend: {
|
legend: {
|
||||||
display: false,
|
display: false,
|
||||||
|
@ -1407,16 +1545,12 @@ ipcRenderer.on('action-update-tnc-state', (event, arg) => {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
var scatterData = arg.scatter
|
||||||
|
var newScatterData = {
|
||||||
|
|
||||||
|
|
||||||
var data = arg.scatter
|
|
||||||
var newdata = {
|
|
||||||
datasets: [{
|
datasets: [{
|
||||||
//label: 'constellation diagram',
|
//label: 'constellation diagram',
|
||||||
data: data,
|
data: scatterData,
|
||||||
options: config,
|
options: scatterConfig,
|
||||||
backgroundColor: 'rgb(255, 99, 132)'
|
backgroundColor: 'rgb(255, 99, 132)'
|
||||||
}],
|
}],
|
||||||
};
|
};
|
||||||
|
@ -1426,25 +1560,122 @@ ipcRenderer.on('action-update-tnc-state', (event, arg) => {
|
||||||
} else {
|
} else {
|
||||||
var scatterSize = arg.scatter.length;
|
var scatterSize = arg.scatter.length;
|
||||||
}
|
}
|
||||||
if (global.data != newdata && scatterSize > 0) {
|
|
||||||
try {
|
if (global.scatterData != newScatterData && scatterSize > 0) {
|
||||||
global.myChart.destroy();
|
global.scatterData = newScatterData;
|
||||||
} catch (e) {
|
|
||||||
// myChart not yet created
|
if (typeof(global.scatterChart) == 'undefined') {
|
||||||
console.log(e);
|
var scatterCtx = document.getElementById('scatter').getContext('2d');
|
||||||
|
global.scatterChart = new Chart(scatterCtx, {
|
||||||
|
type: 'scatter',
|
||||||
|
data: global.scatterData,
|
||||||
|
options: scatterConfig
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
global.scatterChart.data = global.scatterData;
|
||||||
|
global.scatterChart.update();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// END OF SCATTER CHART
|
||||||
|
|
||||||
|
// START OF SPEED CHART
|
||||||
|
|
||||||
|
var speedDataTime = []
|
||||||
|
|
||||||
|
if (typeof(arg.speed_list) == 'undefined') {
|
||||||
|
var speed_listSize = 0;
|
||||||
|
} else {
|
||||||
|
var speed_listSize = arg.speed_list.length;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (var i=0; i < speed_listSize; i++) {
|
||||||
|
var timestamp = arg.speed_list[i].timestamp * 1000
|
||||||
|
var h = new Date(timestamp).getHours();
|
||||||
|
var m = new Date(timestamp).getMinutes();
|
||||||
|
var s = new Date(timestamp).getSeconds();
|
||||||
|
var time = h + ':' + m + ':' + s;
|
||||||
|
speedDataTime.push(time)
|
||||||
|
}
|
||||||
|
|
||||||
|
var speedDataBpm = []
|
||||||
|
for (var i=0; i < speed_listSize; i++) {
|
||||||
|
speedDataBpm.push(arg.speed_list[i].bpm)
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
var speedDataSnr = []
|
||||||
|
for (var i=0; i < speed_listSize; i++) {
|
||||||
|
speedDataSnr.push(arg.speed_list[i].snr)
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
var speedChartConfig = {
|
||||||
|
type: 'line',
|
||||||
|
};
|
||||||
|
|
||||||
|
var newSpeedData = {
|
||||||
|
labels: speedDataTime,
|
||||||
|
datasets: [
|
||||||
|
{
|
||||||
|
type: 'line',
|
||||||
|
label: 'SNR[dB]',
|
||||||
|
data: speedDataSnr,
|
||||||
|
borderColor: 'rgb(255, 99, 132, 1.0)',
|
||||||
|
backgroundColor: 'rgba(255, 99, 132, 0.2)',
|
||||||
|
order: 1,
|
||||||
|
yAxisID: 'SNR',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
type: 'bar',
|
||||||
|
label: 'Speed[bpm]',
|
||||||
|
data: speedDataBpm,
|
||||||
|
borderColor: 'rgb(120, 100, 120, 1.0)',
|
||||||
|
backgroundColor: 'rgba(120, 100, 120, 0.2)',
|
||||||
|
order: 0,
|
||||||
|
yAxisID: 'SPEED',
|
||||||
|
}
|
||||||
|
],
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
var speedChartOptions = {
|
||||||
|
responsive: true,
|
||||||
|
animations: true,
|
||||||
|
cubicInterpolationMode: 'monotone',
|
||||||
|
tension: 0.4,
|
||||||
|
scales: {
|
||||||
|
SNR:{
|
||||||
|
type: 'linear',
|
||||||
|
ticks: { beginAtZero: true, color: 'rgb(255, 99, 132)' },
|
||||||
|
position: 'right',
|
||||||
|
},
|
||||||
|
SPEED :{
|
||||||
|
type: 'linear',
|
||||||
|
ticks: { beginAtZero: true, color: 'rgb(120, 100, 120)' },
|
||||||
|
position: 'left',
|
||||||
|
grid: {
|
||||||
|
drawOnChartArea: false, // only want the grid lines for one axis to show up
|
||||||
|
},
|
||||||
|
},
|
||||||
|
x: { ticks: { beginAtZero: true } },
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
global.data = newdata;
|
if (typeof(global.speedChart) == 'undefined') {
|
||||||
|
var speedCtx = document.getElementById('chart').getContext('2d');
|
||||||
|
global.speedChart = new Chart(speedCtx, {
|
||||||
var ctx = document.getElementById('scatter').getContext('2d');
|
data: newSpeedData,
|
||||||
global.myChart = new Chart(ctx, {
|
options: speedChartOptions
|
||||||
type: 'scatter',
|
|
||||||
data: global.data,
|
|
||||||
options: config
|
|
||||||
});
|
});
|
||||||
|
} else {
|
||||||
|
if(speedDataSnr.length > 0){
|
||||||
|
global.speedChart.data = newSpeedData;
|
||||||
|
global.speedChart.update();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// END OF SPEED CHART
|
||||||
|
|
||||||
// PTT STATE
|
// PTT STATE
|
||||||
if (arg.ptt_state == 'True') {
|
if (arg.ptt_state == 'True') {
|
||||||
document.getElementById("ptt_state").className = "btn btn-sm btn-danger";
|
document.getElementById("ptt_state").className = "btn btn-sm btn-danger";
|
||||||
|
@ -1566,7 +1797,10 @@ ipcRenderer.on('action-update-tnc-state', (event, arg) => {
|
||||||
}
|
}
|
||||||
|
|
||||||
// SET FREQUENCY
|
// SET FREQUENCY
|
||||||
document.getElementById("frequency").innerHTML = arg.frequency;
|
// https://stackoverflow.com/a/2901298
|
||||||
|
var freq = arg.frequency.toString().replace(/\B(?=(\d{3})+(?!\d))/g, ".");
|
||||||
|
document.getElementById("frequency").innerHTML = freq;
|
||||||
|
//document.getElementById("newFrequency").value = arg.frequency;
|
||||||
|
|
||||||
// SET MODE
|
// SET MODE
|
||||||
document.getElementById("mode").innerHTML = arg.mode;
|
document.getElementById("mode").innerHTML = arg.mode;
|
||||||
|
@ -1589,7 +1823,30 @@ ipcRenderer.on('action-update-tnc-state', (event, arg) => {
|
||||||
var arq_bytes_per_minute_compressed = Math.round(arg.arq_bytes_per_minute * arg.arq_compression_factor);
|
var arq_bytes_per_minute_compressed = Math.round(arg.arq_bytes_per_minute * arg.arq_compression_factor);
|
||||||
}
|
}
|
||||||
document.getElementById("bytes_per_min_compressed").innerHTML = arq_bytes_per_minute_compressed;
|
document.getElementById("bytes_per_min_compressed").innerHTML = arq_bytes_per_minute_compressed;
|
||||||
|
|
||||||
|
// SET TIME LEFT UNTIL FINIHED
|
||||||
|
if (typeof(arg.arq_seconds_until_finish) == 'undefined') {
|
||||||
|
var time_left = 0;
|
||||||
|
} else {
|
||||||
|
var arq_seconds_until_finish = arg.arq_seconds_until_finish
|
||||||
|
var hours = Math.floor(arq_seconds_until_finish / 3600);
|
||||||
|
var minutes = Math.floor((arq_seconds_until_finish % 3600) / 60 );
|
||||||
|
var seconds = arq_seconds_until_finish % 60;
|
||||||
|
|
||||||
|
if(hours < 0) {
|
||||||
|
hours = 0;
|
||||||
|
}
|
||||||
|
if(minutes < 0) {
|
||||||
|
minutes = 0;
|
||||||
|
}
|
||||||
|
if(seconds < 0) {
|
||||||
|
seconds = 0;
|
||||||
|
}
|
||||||
|
var time_left = "time left: ~ "+ minutes + "min" + " " + seconds + "s";
|
||||||
|
}
|
||||||
|
document.getElementById("transmission_timeleft").innerHTML = time_left;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
// SET SPEED LEVEL
|
// SET SPEED LEVEL
|
||||||
|
|
||||||
|
@ -1608,6 +1865,7 @@ ipcRenderer.on('action-update-tnc-state', (event, arg) => {
|
||||||
if(arg.speed_level >= 4) {
|
if(arg.speed_level >= 4) {
|
||||||
document.getElementById("speed_level").className = "bi bi-reception-4";
|
document.getElementById("speed_level").className = "bi bi-reception-4";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
@ -2109,8 +2367,15 @@ ipcRenderer.on('run-tnc-command', (event, arg) => {
|
||||||
}
|
}
|
||||||
if (arg.command == 'send_test_frame') {
|
if (arg.command == 'send_test_frame') {
|
||||||
sock.sendTestFrame();
|
sock.sendTestFrame();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (arg.command == 'frequency') {
|
||||||
|
sock.set_frequency(arg.frequency);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (arg.command == 'mode') {
|
||||||
|
sock.set_mode(arg.mode);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
});
|
});
|
||||||
|
|
17
gui/sock.js
17
gui/sock.js
|
@ -196,6 +196,7 @@ client.on('data', function(socketdata) {
|
||||||
|
|
||||||
let Data = {
|
let Data = {
|
||||||
mycallsign: data['mycallsign'],
|
mycallsign: data['mycallsign'],
|
||||||
|
mygrid: data['mygrid'],
|
||||||
ptt_state: data['ptt_state'],
|
ptt_state: data['ptt_state'],
|
||||||
busy_state: data['tnc_state'],
|
busy_state: data['tnc_state'],
|
||||||
arq_state: data['arq_state'],
|
arq_state: data['arq_state'],
|
||||||
|
@ -221,6 +222,7 @@ client.on('data', function(socketdata) {
|
||||||
arq_rx_n_current_arq_frame: data['arq_rx_n_current_arq_frame'],
|
arq_rx_n_current_arq_frame: data['arq_rx_n_current_arq_frame'],
|
||||||
arq_n_arq_frames_per_data_frame: data['arq_n_arq_frames_per_data_frame'],
|
arq_n_arq_frames_per_data_frame: data['arq_n_arq_frames_per_data_frame'],
|
||||||
arq_bytes_per_minute: data['arq_bytes_per_minute'],
|
arq_bytes_per_minute: data['arq_bytes_per_minute'],
|
||||||
|
arq_seconds_until_finish: data['arq_seconds_until_finish'],
|
||||||
arq_compression_factor: data['arq_compression_factor'],
|
arq_compression_factor: data['arq_compression_factor'],
|
||||||
total_bytes: data['total_bytes'],
|
total_bytes: data['total_bytes'],
|
||||||
arq_transmission_percent: data['arq_transmission_percent'],
|
arq_transmission_percent: data['arq_transmission_percent'],
|
||||||
|
@ -229,6 +231,8 @@ client.on('data', function(socketdata) {
|
||||||
hamlib_status: data['hamlib_status'],
|
hamlib_status: data['hamlib_status'],
|
||||||
listen: data['listen'],
|
listen: data['listen'],
|
||||||
audio_recording: data['audio_recording'],
|
audio_recording: data['audio_recording'],
|
||||||
|
speed_list: data['speed_list'],
|
||||||
|
//speed_table: [{"bpm" : 5200, "snr": -3, "timestamp":1673555399},{"bpm" : 2315, "snr": 12, "timestamp":1673555500}],
|
||||||
};
|
};
|
||||||
|
|
||||||
ipcRenderer.send('request-update-tnc-state', Data);
|
ipcRenderer.send('request-update-tnc-state', Data);
|
||||||
|
@ -597,6 +601,19 @@ exports.record_audio = function() {
|
||||||
writeTncCommand(command)
|
writeTncCommand(command)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// SET FREQUENCY
|
||||||
|
exports.set_frequency = function(frequency) {
|
||||||
|
command = '{"type" : "set", "command" : "frequency", "frequency": '+ frequency +'}'
|
||||||
|
writeTncCommand(command)
|
||||||
|
}
|
||||||
|
|
||||||
|
// SET MODE
|
||||||
|
exports.set_mode = function(mode) {
|
||||||
|
command = '{"type" : "set", "command" : "mode", "mode": "'+ mode +'"}'
|
||||||
|
console.log(command)
|
||||||
|
writeTncCommand(command)
|
||||||
|
}
|
||||||
|
|
||||||
ipcRenderer.on('action-update-tnc-ip', (event, arg) => {
|
ipcRenderer.on('action-update-tnc-ip', (event, arg) => {
|
||||||
client.destroy();
|
client.destroy();
|
||||||
let Data = {
|
let Data = {
|
||||||
|
|
|
@ -44,6 +44,7 @@
|
||||||
<button class="btn btn-sm btn-danger" id="stop_transmission_connection" type="button"> <i class="bi bi-x-octagon-fill" style="font-size: 1rem; color: white;"></i> STOP </button>
|
<button class="btn btn-sm btn-danger" id="stop_transmission_connection" type="button"> <i class="bi bi-x-octagon-fill" style="font-size: 1rem; color: white;"></i> STOP </button>
|
||||||
</div>
|
</div>
|
||||||
<div class="btn-toolbar" role="toolbar">
|
<div class="btn-toolbar" role="toolbar">
|
||||||
|
<button class="btn btn-sm btn-primary me-4 position-relative" id="openExplorer" type="button" data-bs-placement="bottom" data-bs-toggle="tooltip" data-bs-html="false" title="View explorer map"> <strong>Explorer</strong> <i class="bi bi-pin-map-fill" style="font-size: 1rem; color: white;"></i></button>
|
||||||
<button class="btn btn-sm btn-primary me-4 position-relative" id="openRFChat" type="button" data-bs-placement="bottom" data-bs-toggle="tooltip" data-bs-html="false" title="Open the HF chat module. This is currently just a test and not finished, yet!"> <strong>RF Chat</strong> <i class="bi bi-chat-left-text-fill" style="font-size: 1rem; color: white;"></i> <span class="position-absolute top-0 start-100 translate-middle badge rounded-pill bg-danger">.</span> </button> <span data-bs-placement="bottom" data-bs-toggle="tooltip" data-bs-html="false" title="View the received files. This is currently under development!">
|
<button class="btn btn-sm btn-primary me-4 position-relative" id="openRFChat" type="button" data-bs-placement="bottom" data-bs-toggle="tooltip" data-bs-html="false" title="Open the HF chat module. This is currently just a test and not finished, yet!"> <strong>RF Chat</strong> <i class="bi bi-chat-left-text-fill" style="font-size: 1rem; color: white;"></i> <span class="position-absolute top-0 start-100 translate-middle badge rounded-pill bg-danger">.</span> </button> <span data-bs-placement="bottom" data-bs-toggle="tooltip" data-bs-html="false" title="View the received files. This is currently under development!">
|
||||||
<!--
|
<!--
|
||||||
|
|
||||||
|
@ -837,37 +838,19 @@
|
||||||
<div class="card-header p-1">
|
<div class="card-header p-1">
|
||||||
<div class="btn-group btn-group-sm" role="group" aria-label="waterfall-scatter-switch toggle button group">
|
<div class="btn-group btn-group-sm" role="group" aria-label="waterfall-scatter-switch toggle button group">
|
||||||
<input type="radio" class="btn-check" name="waterfall-scatter-switch" id="waterfall-scatter-switch1" autocomplete="off" checked>
|
<input type="radio" class="btn-check" name="waterfall-scatter-switch" id="waterfall-scatter-switch1" autocomplete="off" checked>
|
||||||
<label class="btn btn-sm btn-outline-secondary" for="waterfall-scatter-switch1"><strong>WATERFALL</strong> </label>
|
<label class="btn btn-sm btn-outline-secondary" for="waterfall-scatter-switch1"><strong><i class="bi bi-water"></i></strong> </label>
|
||||||
<input type="radio" class="btn-check" name="waterfall-scatter-switch" id="waterfall-scatter-switch2" autocomplete="off">
|
<input type="radio" class="btn-check" name="waterfall-scatter-switch" id="waterfall-scatter-switch2" autocomplete="off">
|
||||||
<label class="btn btn-sm btn-outline-secondary" for="waterfall-scatter-switch2"><strong>SCATTER</strong> </label>
|
<label class="btn btn-sm btn-outline-secondary" for="waterfall-scatter-switch2"><strong><i class="bi bi-border-outer"></i></strong> </label>
|
||||||
|
<input type="radio" class="btn-check" name="waterfall-scatter-switch" id="waterfall-scatter-switch3" autocomplete="off">
|
||||||
|
<label class="btn btn-sm btn-outline-secondary" for="waterfall-scatter-switch3"><strong><i class="bi bi-graph-up-arrow"></i></strong> </label>
|
||||||
</div>
|
</div>
|
||||||
<button class="btn btn-sm btn-secondary" id="channel_busy" type="button" data-bs-placement="top" data-bs-toggle="tooltip" data-bs-html="true" title="Channel busy state: <strong class='text-success'>not busy</strong> / <strong class='text-danger'>busy </strong>">busy</button>
|
<button class="btn btn-sm btn-secondary" id="channel_busy" type="button" data-bs-placement="top" data-bs-toggle="tooltip" data-bs-html="true" title="Channel busy state: <strong class='text-success'>not busy</strong> / <strong class='text-danger'>busy </strong>">busy</button>
|
||||||
</div>
|
</div>
|
||||||
<div class="card-body p-1" style="height: 200px">
|
<div class="card-body p-1" style="height: 200px">
|
||||||
<!-- TEST FOR WATERFALL OVERLAY
|
|
||||||
<div class="opacity-100 w-100 h-100 p-0 m-0 position-absolute" style="height: 190px;z-index: 10">
|
|
||||||
<div class="row m-0 p-0 w-100 h-100">
|
|
||||||
<div class="col m-0 p-0 col-3 ">
|
|
||||||
-
|
|
||||||
</div>
|
|
||||||
<div class="col border border-danger m-0 p-0 col-2">
|
|
||||||
1800Hz
|
|
||||||
</div>
|
|
||||||
<div class="col border border-danger m-0 p-0" style="width: 190px;">
|
|
||||||
500Hz
|
|
||||||
</div>
|
|
||||||
<div class="col border border-danger m-0 p-0 col-2">
|
|
||||||
-
|
|
||||||
</div>
|
|
||||||
<div class="col m-0 p-0 col-3">
|
|
||||||
-
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
-->
|
|
||||||
<!--278px-->
|
<!--278px-->
|
||||||
<canvas id="waterfall" style="position: relative; z-index: 2;"></canvas>
|
<canvas id="waterfall" style="position: relative; z-index: 2; transform: translateZ(0);"></canvas>
|
||||||
<canvas id="scatter" style="position: relative; z-index: 1;"></canvas>
|
<canvas id="scatter" style="position: relative; z-index: 1; transform: translateZ(0);"></canvas>
|
||||||
|
<canvas id="chart" style="position: relative; z-index: 1; transform: translateZ(0);"></canvas>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
@ -1066,28 +1049,64 @@
|
||||||
<nav class="navbar fixed-bottom navbar-light bg-light">
|
<nav class="navbar fixed-bottom navbar-light bg-light">
|
||||||
<div class="container-fluid">
|
<div class="container-fluid">
|
||||||
<div class="btn-toolbar" role="toolbar">
|
<div class="btn-toolbar" role="toolbar">
|
||||||
<div class="btn-group btn-group-sm me-2" role="group">
|
<div class="btn-group btn-group-sm me-1" role="group">
|
||||||
<button class="btn btn-sm btn-secondary" id="ptt_state" type="button" data-bs-placement="top" data-bs-toggle="tooltip" data-bs-html="true" title="PTT state:<strong class='text-success'>RECEIVING</strong> / <strong class='text-danger'>TRANSMITTING</strong>"> <i class="bi bi-broadcast-pin" style="font-size: 0.8rem; color: white;"></i> </button>
|
<button class="btn btn-sm btn-secondary" id="ptt_state" type="button" data-bs-placement="top" data-bs-toggle="tooltip" data-bs-html="true" title="PTT state:<strong class='text-success'>RECEIVING</strong> / <strong class='text-danger'>TRANSMITTING</strong>"> <i class="bi bi-broadcast-pin" style="font-size: 0.8rem; color: white;"></i> </button>
|
||||||
</div>
|
</div>
|
||||||
<div class="btn-group btn-group-sm me-2" role="group">
|
<div class="btn-group btn-group-sm me-1" role="group">
|
||||||
<button class="btn btn-sm btn-secondary" id="busy_state" type="button" data-bs-placement="top" data-bs-toggle="tooltip" data-bs-html="true" title="TNC busy state: <strong class='text-success'>IDLE</strong> / <strong class='text-danger'>BUSY</strong>"> <i class="bi bi-cpu" style="font-size: 0.8rem; color: white;"></i> </button>
|
<button class="btn btn-sm btn-secondary" id="busy_state" type="button" data-bs-placement="top" data-bs-toggle="tooltip" data-bs-html="true" title="TNC busy state: <strong class='text-success'>IDLE</strong> / <strong class='text-danger'>BUSY</strong>"> <i class="bi bi-cpu" style="font-size: 0.8rem; color: white;"></i> </button>
|
||||||
</div>
|
</div>
|
||||||
<div class="btn-group btn-group-sm me-2" role="group">
|
<div class="btn-group btn-group-sm me-1" role="group">
|
||||||
<button class="btn btn-sm btn-secondary" id="arq_session" type="button" data-bs-placement="top" data-bs-toggle="tooltip" data-bs-html="true" title="ARQ SESSION state: <strong class='text-warning'>OPEN</strong>"> <i class="bi bi-arrow-left-right" style="font-size: 0.8rem; color: white;"></i> </button>
|
<button class="btn btn-sm btn-secondary" id="arq_session" type="button" data-bs-placement="top" data-bs-toggle="tooltip" data-bs-html="true" title="ARQ SESSION state: <strong class='text-warning'>OPEN</strong>"> <i class="bi bi-arrow-left-right" style="font-size: 0.8rem; color: white;"></i> </button>
|
||||||
</div>
|
</div>
|
||||||
<div class="btn-group btn-group-sm me-2" role="group">
|
<div class="btn-group btn-group-sm me-1" role="group">
|
||||||
<button class="btn btn-sm btn-secondary" id="arq_state" type="button" data-bs-placement="top" data-bs-toggle="tooltip" data-bs-html="true" title="DATA-CHANNEL state: <strong class='text-warning'>OPEN</strong>"> <i class="bi bi-file-earmark-binary" style="font-size: 0.8rem; color: white;"></i> </button>
|
<button class="btn btn-sm btn-secondary" id="arq_state" type="button" data-bs-placement="top" data-bs-toggle="tooltip" data-bs-html="true" title="DATA-CHANNEL state: <strong class='text-warning'>OPEN</strong>"> <i class="bi bi-file-earmark-binary" style="font-size: 0.8rem; color: white;"></i> </button>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="btn-group btn-group-sm me-2" role="group">
|
<div class="btn-group btn-group-sm me-1" role="group">
|
||||||
<button class="btn btn-sm btn-secondary" id="rigctld_state" type="button" data-bs-placement="top" data-bs-toggle="tooltip" data-bs-html="true" title="rigctld state: <strong class='text-success'>CONNECTED</strong> / <strong class='text-secondary'>UNKNOWN</strong>"> <i class="bi bi-usb-symbol" style="font-size: 0.8rem; color: white;"></i> </button>
|
<button class="btn btn-sm btn-secondary" id="rigctld_state" type="button" data-bs-placement="top" data-bs-toggle="tooltip" data-bs-html="true" title="rigctld state: <strong class='text-success'>CONNECTED</strong> / <strong class='text-secondary'>UNKNOWN</strong>"> <i class="bi bi-usb-symbol" style="font-size: 0.8rem; color: white;"></i> </button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="container-fluid p-0" style="width:15rem">
|
<div class="container-fluid p-0" style="width:20rem">
|
||||||
<div class="input-group input-group-sm">
|
<div class="input-group input-group-sm">
|
||||||
<!--<span class="input-group-text" id="basic-addon1"><strong>Freq</strong></span>--><span class="input-group-text" id="frequency">---</span>
|
|
||||||
<!--<span class="input-group-text" id="basic-addon1"><strong>Mode</strong></span>--><span class="input-group-text" id="mode">---</span>
|
<div class="btn-group dropup me-1">
|
||||||
<!--<span class="input-group-text" id="basic-addon1"><strong>BW</strong></span>--><span class="input-group-text" id="bandwidth">---</span> </div>
|
<button type="button" class="btn btn-sm btn-secondary dropdown-toggle" data-bs-toggle="dropdown" aria-expanded="false" id="frequency">
|
||||||
|
---
|
||||||
|
</button>
|
||||||
|
<form class="dropdown-menu p-2">
|
||||||
|
<div class="input-group input-group-sm">
|
||||||
|
<input type="text" class="form-control" style="max-width: 6rem;" placeholder="7063000" pattern="[0-9]*" id="newFrequency" maxlength="11" aria-label="Input group" aria-describedby="btnGroupAddon">
|
||||||
|
<span class="input-group-text">Hz</span>
|
||||||
|
<button class="btn btn-sm btn-success" id="saveFrequency" type="button" data-bs-placement="bottom" data-bs-toggle="tooltip" data-bs-html="false" title="save frequency"> <i class="bi bi-check-lg" style="font-size: 0.8rem; color: white;"></i> </button>
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="btn-group dropup me-1">
|
||||||
|
<button type="button" class="btn btn-sm btn-secondary dropdown-toggle" data-bs-toggle="dropdown" aria-expanded="false" id="mode">
|
||||||
|
---
|
||||||
|
</button>
|
||||||
|
<form class="dropdown-menu p-2">
|
||||||
|
<button type="button" class="btn btn-sm btn-secondary" data-bs-placement="bottom" data-bs-toggle="tooltip" data-bs-html="false" title="set FM" id="saveModeFM">FM</button>
|
||||||
|
<button type="button" class="btn btn-sm btn-secondary" data-bs-placement="bottom" data-bs-toggle="tooltip" data-bs-html="false" title="set AM" type="button" id="saveModeAM">AM</button>
|
||||||
|
<button type="button" class="btn btn-sm btn-secondary" data-bs-placement="bottom" data-bs-toggle="tooltip" data-bs-html="false" title="set LSB" type="button" id="saveModeLSB">LSB</button>
|
||||||
|
<hr>
|
||||||
|
<button type="button" class="btn btn-sm btn-secondary" data-bs-placement="bottom" data-bs-toggle="tooltip" data-bs-html="false" title="set USB" type="button" id="saveModeUSB">USB</button>
|
||||||
|
<button type="button" class="btn btn-sm btn-secondary" data-bs-placement="bottom" data-bs-toggle="tooltip" data-bs-html="false" title="set PKTUSB" type="button" id="saveModePKTUSB">PKTUSB</button>
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="btn-group dropup">
|
||||||
|
<button type="button" class="btn btn-sm btn-secondary dropdown-toggle" data-bs-toggle="dropdown" aria-expanded="false" id="bandwidth">
|
||||||
|
---
|
||||||
|
</button>
|
||||||
|
<form class="dropdown-menu p-2">
|
||||||
|
<div class="input-group input-group-sm">
|
||||||
|
...soon...
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="container-fluid p-0" style="width:12rem">
|
<div class="container-fluid p-0" style="width:12rem">
|
||||||
<div class="input-group input-group-sm"> <span class="input-group-text" id="basic-addon1"><i class="bi bi-speedometer2" style="font-size: 1rem; color: black;"></i></span> <span class="input-group-text" data-bs-placement="bottom" data-bs-toggle="tooltip" data-bs-html="false" title="actual speed level">
|
<div class="input-group input-group-sm"> <span class="input-group-text" id="basic-addon1"><i class="bi bi-speedometer2" style="font-size: 1rem; color: black;"></i></span> <span class="input-group-text" data-bs-placement="bottom" data-bs-toggle="tooltip" data-bs-html="false" title="actual speed level">
|
||||||
|
@ -1100,6 +1119,7 @@
|
||||||
<div class="progress" style="height: 30px;">
|
<div class="progress" style="height: 30px;">
|
||||||
<div class="progress-bar progress-bar-striped bg-primary" id="transmission_progress" role="progressbar" style="width: 0%" aria-valuenow="0" aria-valuemin="0" aria-valuemax="100"></div>
|
<div class="progress-bar progress-bar-striped bg-primary" id="transmission_progress" role="progressbar" style="width: 0%" aria-valuenow="0" aria-valuemin="0" aria-valuemax="100"></div>
|
||||||
<!--<p class="justify-content-center d-flex position-absolute w-100">PROGRESS</p>-->
|
<!--<p class="justify-content-center d-flex position-absolute w-100">PROGRESS</p>-->
|
||||||
|
<p class="justify-content-center mt-2 d-flex position-absolute w-100" id="transmission_timeleft">---</p>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
@ -1107,8 +1127,8 @@
|
||||||
<!-- bootstrap -->
|
<!-- bootstrap -->
|
||||||
<script src="../node_modules/bootstrap/dist/js/bootstrap.bundle.min.js"></script>
|
<script src="../node_modules/bootstrap/dist/js/bootstrap.bundle.min.js"></script>
|
||||||
<!-- chart.js -->
|
<!-- chart.js -->
|
||||||
<script src="../node_modules/chart.js/dist/chart.min.js"></script>
|
<script src="../node_modules/chart.js/dist/chart.umd.js"></script>
|
||||||
<script src="../node_modules/chartjs-plugin-annotation/dist/chartjs-plugin-annotation.min.js"></script>
|
<!--<script src="../node_modules/chartjs-plugin-annotation/dist/chartjs-plugin-annotation.min.js"></script>-->
|
||||||
<!--<script src="../ui.js"></script>-->
|
<!--<script src="../ui.js"></script>-->
|
||||||
<!-- WATERFALL -->
|
<!-- WATERFALL -->
|
||||||
<script src="waterfall/colormap.js"></script>
|
<script src="waterfall/colormap.js"></script>
|
||||||
|
@ -1295,4 +1315,4 @@
|
||||||
</div>
|
</div>
|
||||||
</body>
|
</body>
|
||||||
|
|
||||||
</html>
|
</html>
|
||||||
|
|
|
@ -8,6 +8,7 @@ body {
|
||||||
/*Progress bars with centered text*/
|
/*Progress bars with centered text*/
|
||||||
.progress {
|
.progress {
|
||||||
position: relative;
|
position: relative;
|
||||||
|
transform: translateZ(0);
|
||||||
}
|
}
|
||||||
|
|
||||||
.progress span {
|
.progress span {
|
||||||
|
|
|
@ -64,6 +64,7 @@ class DATA:
|
||||||
|
|
||||||
self.transmission_uuid = ""
|
self.transmission_uuid = ""
|
||||||
|
|
||||||
|
self.burst_last_received = 0.0 # time of last "live sign" of a burst
|
||||||
self.data_channel_last_received = 0.0 # time of last "live sign" of a frame
|
self.data_channel_last_received = 0.0 # time of last "live sign" of a frame
|
||||||
self.burst_ack_snr = 0 # SNR from received burst ack frames
|
self.burst_ack_snr = 0 # SNR from received burst ack frames
|
||||||
|
|
||||||
|
@ -397,7 +398,8 @@ class DATA:
|
||||||
:param repeat_delay: Delay time before sending repeat frame, defaults to 0
|
:param repeat_delay: Delay time before sending repeat frame, defaults to 0
|
||||||
:type repeat_delay: int, optional
|
:type repeat_delay: int, optional
|
||||||
"""
|
"""
|
||||||
self.log.debug("[TNC] enqueue_frame_for_tx", c2_mode=FREEDV_MODE(c2_mode).name)
|
frame_type = FR_TYPE(int.from_bytes(frame_to_tx[0][:1], byteorder="big")).name
|
||||||
|
self.log.debug("[TNC] enqueue_frame_for_tx", c2_mode=FREEDV_MODE(c2_mode).name, data=frame_to_tx, type=frame_type)
|
||||||
|
|
||||||
# Set the TRANSMITTING flag before adding an object to the transmit queue
|
# Set the TRANSMITTING flag before adding an object to the transmit queue
|
||||||
# TODO: This is not that nice, we could improve this somehow
|
# TODO: This is not that nice, we could improve this somehow
|
||||||
|
@ -470,9 +472,17 @@ class DATA:
|
||||||
ack_frame[2:3] = helpers.snr_to_bytes(snr)
|
ack_frame[2:3] = helpers.snr_to_bytes(snr)
|
||||||
ack_frame[3:4] = bytes([int(self.speed_level)])
|
ack_frame[3:4] = bytes([int(self.speed_level)])
|
||||||
|
|
||||||
|
# wait while timeout not reached and our busy state is busy
|
||||||
|
channel_busy_timeout = time.time() + 5
|
||||||
|
while static.CHANNEL_BUSY and time.time() < channel_busy_timeout:
|
||||||
|
threading.Event().wait(0.01)
|
||||||
|
|
||||||
|
|
||||||
# Transmit frame
|
# Transmit frame
|
||||||
self.enqueue_frame_for_tx([ack_frame], c2_mode=FREEDV_MODE.sig1.value)
|
self.enqueue_frame_for_tx([ack_frame], c2_mode=FREEDV_MODE.sig1.value)
|
||||||
|
|
||||||
|
# reset burst timeout in case we had to wait too long
|
||||||
|
self.burst_last_received = time.time()
|
||||||
def send_data_ack_frame(self, snr) -> None:
|
def send_data_ack_frame(self, snr) -> None:
|
||||||
"""Build and send ACK frame for received DATA frame"""
|
"""Build and send ACK frame for received DATA frame"""
|
||||||
|
|
||||||
|
@ -485,11 +495,19 @@ class DATA:
|
||||||
# ack_frame[7:8] = bytes([int(snr)])
|
# ack_frame[7:8] = bytes([int(snr)])
|
||||||
# ack_frame[8:9] = bytes([int(self.speed_level)])
|
# ack_frame[8:9] = bytes([int(self.speed_level)])
|
||||||
|
|
||||||
|
# wait while timeout not reached and our busy state is busy
|
||||||
|
channel_busy_timeout = time.time() + 5
|
||||||
|
while static.CHANNEL_BUSY and time.time() < channel_busy_timeout:
|
||||||
|
threading.Event().wait(0.01)
|
||||||
|
|
||||||
# Transmit frame
|
# Transmit frame
|
||||||
# TODO: Do we have to send , self.send_ident_frame(False) ?
|
# TODO: Do we have to send , self.send_ident_frame(False) ?
|
||||||
# self.enqueue_frame_for_tx([ack_frame, self.send_ident_frame(False)], c2_mode=FREEDV_MODE.sig1.value, copies=3, repeat_delay=0)
|
# self.enqueue_frame_for_tx([ack_frame, self.send_ident_frame(False)], c2_mode=FREEDV_MODE.sig1.value, copies=3, repeat_delay=0)
|
||||||
self.enqueue_frame_for_tx([ack_frame], c2_mode=FREEDV_MODE.sig1.value, copies=6, repeat_delay=0)
|
self.enqueue_frame_for_tx([ack_frame], c2_mode=FREEDV_MODE.sig1.value, copies=6, repeat_delay=0)
|
||||||
|
|
||||||
|
# reset burst timeout in case we had to wait too long
|
||||||
|
self.burst_last_received = time.time()
|
||||||
|
|
||||||
def send_retransmit_request_frame(self, freedv) -> None:
|
def send_retransmit_request_frame(self, freedv) -> None:
|
||||||
# check where a None is in our burst buffer and do frame+1, because lists start at 0
|
# check where a None is in our burst buffer and do frame+1, because lists start at 0
|
||||||
# FIXME: Check to see if there's a `frame - 1` in the receive portion. Remove both if there is.
|
# FIXME: Check to see if there's a `frame - 1` in the receive portion. Remove both if there is.
|
||||||
|
@ -532,7 +550,15 @@ class DATA:
|
||||||
# TRANSMIT NACK FRAME FOR BURST
|
# TRANSMIT NACK FRAME FOR BURST
|
||||||
# TODO: Do we have to send ident frame?
|
# TODO: Do we have to send ident frame?
|
||||||
# self.enqueue_frame_for_tx([ack_frame, self.send_ident_frame(False)], c2_mode=FREEDV_MODE.sig1.value, copies=3, repeat_delay=0)
|
# self.enqueue_frame_for_tx([ack_frame, self.send_ident_frame(False)], c2_mode=FREEDV_MODE.sig1.value, copies=3, repeat_delay=0)
|
||||||
|
|
||||||
|
# wait while timeout not reached and our busy state is busy
|
||||||
|
channel_busy_timeout = time.time() + 5
|
||||||
|
while static.CHANNEL_BUSY and time.time() < channel_busy_timeout:
|
||||||
|
threading.Event().wait(0.01)
|
||||||
|
|
||||||
self.enqueue_frame_for_tx([nack_frame], c2_mode=FREEDV_MODE.sig1.value, copies=6, repeat_delay=0)
|
self.enqueue_frame_for_tx([nack_frame], c2_mode=FREEDV_MODE.sig1.value, copies=6, repeat_delay=0)
|
||||||
|
# reset burst timeout in case we had to wait too long
|
||||||
|
self.burst_last_received = time.time()
|
||||||
def send_burst_nack_frame_watchdog(self, snr: bytes) -> None:
|
def send_burst_nack_frame_watchdog(self, snr: bytes) -> None:
|
||||||
"""Build and send NACK frame for watchdog timeout"""
|
"""Build and send NACK frame for watchdog timeout"""
|
||||||
|
|
||||||
|
@ -549,8 +575,15 @@ class DATA:
|
||||||
nack_frame[2:3] = helpers.snr_to_bytes(snr)
|
nack_frame[2:3] = helpers.snr_to_bytes(snr)
|
||||||
nack_frame[3:4] = bytes([int(self.speed_level)])
|
nack_frame[3:4] = bytes([int(self.speed_level)])
|
||||||
|
|
||||||
|
# wait while timeout not reached and our busy state is busy
|
||||||
|
channel_busy_timeout = time.time() + 5
|
||||||
|
while static.CHANNEL_BUSY and time.time() < channel_busy_timeout:
|
||||||
|
threading.Event().wait(0.01)
|
||||||
|
|
||||||
# TRANSMIT NACK FRAME FOR BURST
|
# TRANSMIT NACK FRAME FOR BURST
|
||||||
self.enqueue_frame_for_tx([nack_frame], c2_mode=FREEDV_MODE.sig1.value, copies=1, repeat_delay=0)
|
self.enqueue_frame_for_tx([nack_frame], c2_mode=FREEDV_MODE.sig1.value, copies=1, repeat_delay=0)
|
||||||
|
# reset burst timeout in case we had to wait too long
|
||||||
|
self.burst_last_received = time.time()
|
||||||
|
|
||||||
def send_disconnect_frame(self) -> None:
|
def send_disconnect_frame(self) -> None:
|
||||||
"""Build and send a disconnect frame"""
|
"""Build and send a disconnect frame"""
|
||||||
|
@ -563,6 +596,12 @@ class DATA:
|
||||||
# TODO: We need to add the ident frame feature with a seperate PR after publishing latest protocol
|
# TODO: We need to add the ident frame feature with a seperate PR after publishing latest protocol
|
||||||
# TODO: We need to wait some time between last arq related signalling frame and ident frame
|
# TODO: We need to wait some time between last arq related signalling frame and ident frame
|
||||||
# TODO: Maybe about 500ms - 1500ms to avoid confusion and too much PTT toggles
|
# TODO: Maybe about 500ms - 1500ms to avoid confusion and too much PTT toggles
|
||||||
|
|
||||||
|
# wait while timeout not reached and our busy state is busy
|
||||||
|
channel_busy_timeout = time.time() + 5
|
||||||
|
while static.CHANNEL_BUSY and time.time() < channel_busy_timeout:
|
||||||
|
threading.Event().wait(0.01)
|
||||||
|
|
||||||
self.enqueue_frame_for_tx([disconnection_frame], c2_mode=FREEDV_MODE.sig0.value, copies=6, repeat_delay=0)
|
self.enqueue_frame_for_tx([disconnection_frame], c2_mode=FREEDV_MODE.sig0.value, copies=6, repeat_delay=0)
|
||||||
|
|
||||||
def arq_data_received(
|
def arq_data_received(
|
||||||
|
@ -602,6 +641,7 @@ class DATA:
|
||||||
|
|
||||||
# Update data_channel timestamp
|
# Update data_channel timestamp
|
||||||
self.data_channel_last_received = int(time.time())
|
self.data_channel_last_received = int(time.time())
|
||||||
|
self.burst_last_received = int(time.time())
|
||||||
|
|
||||||
# Extract some important data from the frame
|
# Extract some important data from the frame
|
||||||
# Get sequence number of burst frame
|
# Get sequence number of burst frame
|
||||||
|
@ -641,6 +681,26 @@ class DATA:
|
||||||
# static.RX_FRAME_BUFFER += static.RX_BURST_BUFFER[i]
|
# static.RX_FRAME_BUFFER += static.RX_BURST_BUFFER[i]
|
||||||
temp_burst_buffer += bytes(value) # type: ignore
|
temp_burst_buffer += bytes(value) # type: ignore
|
||||||
|
|
||||||
|
# TODO: Needs to be removed as soon as mode error is fixed
|
||||||
|
# catch possible modem error which leads into false byteorder
|
||||||
|
# modem possibly decodes too late - data then is pushed to buffer
|
||||||
|
# which leads into wrong byteorder
|
||||||
|
# Lets put this in try/except so we are not crashing tnc as its hihgly experimental
|
||||||
|
# This might only work for datac1 and datac3
|
||||||
|
try:
|
||||||
|
#area_of_interest = (modem.get_bytes_per_frame(self.mode_list[speed_level] - 1) -3) * 2
|
||||||
|
if static.RX_FRAME_BUFFER.endswith(temp_burst_buffer[:246]) and len(temp_burst_buffer) >= 246:
|
||||||
|
self.log.warning(
|
||||||
|
"[TNC] ARQ | RX | wrong byteorder received - dropping data"
|
||||||
|
)
|
||||||
|
# we need to run a return here, so we are not sending an ACK
|
||||||
|
return
|
||||||
|
except Exception as e:
|
||||||
|
self.log.warning(
|
||||||
|
"[TNC] ARQ | RX | wrong byteorder check failed", e=e
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
# if frame buffer ends not with the current frame, we are going to append new data
|
# if frame buffer ends not with the current frame, we are going to append new data
|
||||||
# if data already exists, we received the frame correctly,
|
# if data already exists, we received the frame correctly,
|
||||||
# but the ACK frame didn't receive its destination (ISS)
|
# but the ACK frame didn't receive its destination (ISS)
|
||||||
|
@ -657,6 +717,11 @@ class DATA:
|
||||||
# static.RX_FRAME_BUFFER --> existing data
|
# static.RX_FRAME_BUFFER --> existing data
|
||||||
# temp_burst_buffer --> new data
|
# temp_burst_buffer --> new data
|
||||||
# search_area --> area where we want to search
|
# search_area --> area where we want to search
|
||||||
|
|
||||||
|
|
||||||
|
#data_mode = self.mode_list[self.speed_level]
|
||||||
|
#payload_per_frame = modem.get_bytes_per_frame(data_mode) - 2
|
||||||
|
#search_area = payload_per_frame - 3 # (3 bytes arq frame header)
|
||||||
search_area = 510 - 3 # (3 bytes arq frame header)
|
search_area = 510 - 3 # (3 bytes arq frame header)
|
||||||
|
|
||||||
search_position = len(static.RX_FRAME_BUFFER) - search_area
|
search_position = len(static.RX_FRAME_BUFFER) - search_area
|
||||||
|
@ -711,7 +776,7 @@ class DATA:
|
||||||
self.set_listening_modes(False, True, self.mode_list[self.speed_level])
|
self.set_listening_modes(False, True, self.mode_list[self.speed_level])
|
||||||
|
|
||||||
# Create and send ACK frame
|
# Create and send ACK frame
|
||||||
self.log.info("[TNC] ARQ | RX | SENDING ACK")
|
self.log.info("[TNC] ARQ | RX | SENDING ACK", finished=static.ARQ_SECONDS_UNTIL_FINISH, bytesperminute=static.ARQ_BYTES_PER_MINUTE)
|
||||||
self.send_burst_ack_frame(snr)
|
self.send_burst_ack_frame(snr)
|
||||||
|
|
||||||
# Reset n retries per burst counter
|
# Reset n retries per burst counter
|
||||||
|
@ -732,6 +797,7 @@ class DATA:
|
||||||
bytesperminute=static.ARQ_BYTES_PER_MINUTE,
|
bytesperminute=static.ARQ_BYTES_PER_MINUTE,
|
||||||
mycallsign=str(self.mycallsign, 'UTF-8'),
|
mycallsign=str(self.mycallsign, 'UTF-8'),
|
||||||
dxcallsign=str(self.dxcallsign, 'UTF-8'),
|
dxcallsign=str(self.dxcallsign, 'UTF-8'),
|
||||||
|
finished=static.ARQ_SECONDS_UNTIL_FINISH,
|
||||||
)
|
)
|
||||||
|
|
||||||
elif rx_n_frame_of_burst == rx_n_frames_per_burst - 1:
|
elif rx_n_frame_of_burst == rx_n_frames_per_burst - 1:
|
||||||
|
@ -812,8 +878,10 @@ class DATA:
|
||||||
|
|
||||||
# transmittion duration
|
# transmittion duration
|
||||||
duration = time.time() - self.rx_start_of_transmission
|
duration = time.time() - self.rx_start_of_transmission
|
||||||
self.log.info("[TNC] ARQ | RX | DATA FRAME SUCCESSFULLY RECEIVED", nacks=self.frame_nack_counter,bytesperminute=static.ARQ_BYTES_PER_MINUTE, total_bytes=static.TOTAL_BYTES, duration=duration
|
self.calculate_transfer_rate_rx(
|
||||||
)
|
self.rx_start_of_transmission, len(static.RX_FRAME_BUFFER)
|
||||||
|
)
|
||||||
|
self.log.info("[TNC] ARQ | RX | DATA FRAME SUCCESSFULLY RECEIVED", nacks=self.frame_nack_counter,bytesperminute=static.ARQ_BYTES_PER_MINUTE, total_bytes=static.TOTAL_BYTES, duration=duration)
|
||||||
|
|
||||||
# Decompress the data frame
|
# Decompress the data frame
|
||||||
data_frame_decompressed = lzma.decompress(data_frame)
|
data_frame_decompressed = lzma.decompress(data_frame)
|
||||||
|
@ -945,11 +1013,12 @@ class DATA:
|
||||||
overflows=static.BUFFER_OVERFLOW_COUNTER,
|
overflows=static.BUFFER_OVERFLOW_COUNTER,
|
||||||
nacks=self.frame_nack_counter,
|
nacks=self.frame_nack_counter,
|
||||||
duration=duration,
|
duration=duration,
|
||||||
bytesperminute=static.ARQ_BYTES_PER_MINUTE
|
bytesperminute=static.ARQ_BYTES_PER_MINUTE,
|
||||||
|
data=data_frame,
|
||||||
|
|
||||||
)
|
)
|
||||||
|
|
||||||
self.log.info("[TNC] ARQ | RX | Sending NACK")
|
self.log.info("[TNC] ARQ | RX | Sending NACK", finished=static.ARQ_SECONDS_UNTIL_FINISH, bytesperminute=static.ARQ_BYTES_PER_MINUTE)
|
||||||
self.send_burst_nack_frame(snr)
|
self.send_burst_nack_frame(snr)
|
||||||
|
|
||||||
# Update arq_session timestamp
|
# Update arq_session timestamp
|
||||||
|
@ -990,6 +1059,7 @@ class DATA:
|
||||||
uuid=self.transmission_uuid,
|
uuid=self.transmission_uuid,
|
||||||
percent=static.ARQ_TRANSMISSION_PERCENT,
|
percent=static.ARQ_TRANSMISSION_PERCENT,
|
||||||
bytesperminute=static.ARQ_BYTES_PER_MINUTE,
|
bytesperminute=static.ARQ_BYTES_PER_MINUTE,
|
||||||
|
finished=static.ARQ_SECONDS_UNTIL_FINISH,
|
||||||
mycallsign=str(self.mycallsign, 'UTF-8'),
|
mycallsign=str(self.mycallsign, 'UTF-8'),
|
||||||
dxcallsign=str(self.dxcallsign, 'UTF-8'),
|
dxcallsign=str(self.dxcallsign, 'UTF-8'),
|
||||||
)
|
)
|
||||||
|
@ -1024,7 +1094,7 @@ class DATA:
|
||||||
+ data_out
|
+ data_out
|
||||||
+ self.data_frame_eof
|
+ self.data_frame_eof
|
||||||
)
|
)
|
||||||
|
self.log.debug("[TNC] frame raw data:", data=data_out)
|
||||||
# Initial bufferposition is 0
|
# Initial bufferposition is 0
|
||||||
bufferposition = bufferposition_end = 0
|
bufferposition = bufferposition_end = 0
|
||||||
|
|
||||||
|
@ -1184,6 +1254,7 @@ class DATA:
|
||||||
uuid=self.transmission_uuid,
|
uuid=self.transmission_uuid,
|
||||||
percent=static.ARQ_TRANSMISSION_PERCENT,
|
percent=static.ARQ_TRANSMISSION_PERCENT,
|
||||||
bytesperminute=static.ARQ_BYTES_PER_MINUTE,
|
bytesperminute=static.ARQ_BYTES_PER_MINUTE,
|
||||||
|
finished=static.ARQ_SECONDS_UNTIL_FINISH,
|
||||||
irs_snr=self.burst_ack_snr,
|
irs_snr=self.burst_ack_snr,
|
||||||
mycallsign=str(self.mycallsign, 'UTF-8'),
|
mycallsign=str(self.mycallsign, 'UTF-8'),
|
||||||
dxcallsign=str(self.dxcallsign, 'UTF-8'),
|
dxcallsign=str(self.dxcallsign, 'UTF-8'),
|
||||||
|
@ -1209,6 +1280,7 @@ class DATA:
|
||||||
uuid=self.transmission_uuid,
|
uuid=self.transmission_uuid,
|
||||||
percent=static.ARQ_TRANSMISSION_PERCENT,
|
percent=static.ARQ_TRANSMISSION_PERCENT,
|
||||||
bytesperminute=static.ARQ_BYTES_PER_MINUTE,
|
bytesperminute=static.ARQ_BYTES_PER_MINUTE,
|
||||||
|
finished=static.ARQ_SECONDS_UNTIL_FINISH,
|
||||||
mycallsign=str(self.mycallsign, 'UTF-8'),
|
mycallsign=str(self.mycallsign, 'UTF-8'),
|
||||||
dxcallsign=str(self.dxcallsign, 'UTF-8'),
|
dxcallsign=str(self.dxcallsign, 'UTF-8'),
|
||||||
)
|
)
|
||||||
|
@ -1441,7 +1513,7 @@ class DATA:
|
||||||
)
|
)
|
||||||
|
|
||||||
# wait while timeout not reached and our busy state is busy
|
# wait while timeout not reached and our busy state is busy
|
||||||
channel_busy_timeout = time.time() + 30
|
channel_busy_timeout = time.time() + 15
|
||||||
while static.CHANNEL_BUSY and time.time() < channel_busy_timeout:
|
while static.CHANNEL_BUSY and time.time() < channel_busy_timeout:
|
||||||
threading.Event().wait(0.01)
|
threading.Event().wait(0.01)
|
||||||
|
|
||||||
|
@ -1594,6 +1666,14 @@ class DATA:
|
||||||
if not static.RESPOND_TO_CALL:
|
if not static.RESPOND_TO_CALL:
|
||||||
return False
|
return False
|
||||||
|
|
||||||
|
# ignore channel opener if already in ARQ STATE
|
||||||
|
# use case: Station A is connecting to Station B while
|
||||||
|
# Station B already tries connecting to Station A.
|
||||||
|
# For avoiding ignoring repeated connect request in case of packet loss
|
||||||
|
# we are only ignoring packets in case we are ISS
|
||||||
|
if static.ARQ_SESSION and self.IS_ARQ_SESSION_MASTER:
|
||||||
|
return False
|
||||||
|
|
||||||
self.IS_ARQ_SESSION_MASTER = False
|
self.IS_ARQ_SESSION_MASTER = False
|
||||||
static.ARQ_SESSION_STATE = "connecting"
|
static.ARQ_SESSION_STATE = "connecting"
|
||||||
|
|
||||||
|
@ -1927,25 +2007,10 @@ class DATA:
|
||||||
)
|
)
|
||||||
|
|
||||||
# wait while timeout not reached and our busy state is busy
|
# wait while timeout not reached and our busy state is busy
|
||||||
channel_busy_timeout = time.time() + 30
|
channel_busy_timeout = time.time() + 10
|
||||||
while static.CHANNEL_BUSY and time.time() < channel_busy_timeout:
|
while static.CHANNEL_BUSY and time.time() < channel_busy_timeout:
|
||||||
threading.Event().wait(0.01)
|
threading.Event().wait(0.01)
|
||||||
|
|
||||||
# if channel busy timeout reached, stop connecting
|
|
||||||
if time.time() > channel_busy_timeout:
|
|
||||||
self.log.warning("[TNC] Channel busy, try again later...")
|
|
||||||
static.ARQ_SESSION_STATE = "failed"
|
|
||||||
self.send_data_to_socket_queue(
|
|
||||||
freedata="tnc-message",
|
|
||||||
arq="transmission",
|
|
||||||
status="failed",
|
|
||||||
reason="busy",
|
|
||||||
mycallsign=str(self.mycallsign, 'UTF-8'),
|
|
||||||
dxcallsign=str(self.dxcallsign, 'UTF-8'),
|
|
||||||
)
|
|
||||||
static.ARQ_SESSION_STATE = "disconnected"
|
|
||||||
return False
|
|
||||||
|
|
||||||
self.enqueue_frame_for_tx([connection_frame], c2_mode=FREEDV_MODE.datac0.value, copies=1, repeat_delay=0)
|
self.enqueue_frame_for_tx([connection_frame], c2_mode=FREEDV_MODE.datac0.value, copies=1, repeat_delay=0)
|
||||||
|
|
||||||
timeout = time.time() + 3
|
timeout = time.time() + 3
|
||||||
|
@ -2015,6 +2080,14 @@ class DATA:
|
||||||
# check if callsign ssid override
|
# check if callsign ssid override
|
||||||
_, self.mycallsign = helpers.check_callsign(self.mycallsign, data_in[1:4])
|
_, self.mycallsign = helpers.check_callsign(self.mycallsign, data_in[1:4])
|
||||||
|
|
||||||
|
# ignore channel opener if already in ARQ STATE
|
||||||
|
# use case: Station A is connecting to Station B while
|
||||||
|
# Station B already tries connecting to Station A.
|
||||||
|
# For avoiding ignoring repeated connect request in case of packet loss
|
||||||
|
# we are only ignoring packets in case we are ISS
|
||||||
|
if static.ARQ_STATE and not self.is_IRS:
|
||||||
|
return False
|
||||||
|
|
||||||
static.DXCALLSIGN_CRC = bytes(data_in[4:7])
|
static.DXCALLSIGN_CRC = bytes(data_in[4:7])
|
||||||
self.dxcallsign = helpers.bytes_to_callsign(bytes(data_in[7:13]))
|
self.dxcallsign = helpers.bytes_to_callsign(bytes(data_in[7:13]))
|
||||||
static.DXCALLSIGN = self.dxcallsign
|
static.DXCALLSIGN = self.dxcallsign
|
||||||
|
@ -2100,7 +2173,6 @@ class DATA:
|
||||||
)
|
)
|
||||||
|
|
||||||
self.session_id = data_in[13:14]
|
self.session_id = data_in[13:14]
|
||||||
print(self.session_id)
|
|
||||||
|
|
||||||
# check again if callsign ssid override
|
# check again if callsign ssid override
|
||||||
_, self.mycallsign = helpers.check_callsign(self.mycallsign, data_in[1:4])
|
_, self.mycallsign = helpers.check_callsign(self.mycallsign, data_in[1:4])
|
||||||
|
@ -2114,14 +2186,17 @@ class DATA:
|
||||||
channel_constellation=constellation,
|
channel_constellation=constellation,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
# Reset data_channel/burst timestamps
|
||||||
|
self.data_channel_last_received = int(time.time())
|
||||||
|
self.burst_last_received = int(time.time())
|
||||||
|
|
||||||
|
# Set ARQ State AFTER resetting timeouts
|
||||||
|
# this avoids timeouts starting too early
|
||||||
static.ARQ_STATE = True
|
static.ARQ_STATE = True
|
||||||
static.TNC_STATE = "BUSY"
|
static.TNC_STATE = "BUSY"
|
||||||
|
|
||||||
self.reset_statistics()
|
self.reset_statistics()
|
||||||
|
|
||||||
# Update data_channel timestamp
|
|
||||||
self.data_channel_last_received = int(time.time())
|
|
||||||
|
|
||||||
# Select the frame type based on the current TNC mode
|
# Select the frame type based on the current TNC mode
|
||||||
if static.LOW_BANDWIDTH_MODE or self.received_LOW_BANDWIDTH_MODE:
|
if static.LOW_BANDWIDTH_MODE or self.received_LOW_BANDWIDTH_MODE:
|
||||||
frametype = bytes([FR_TYPE.ARQ_DC_OPEN_ACK_N.value])
|
frametype = bytes([FR_TYPE.ARQ_DC_OPEN_ACK_N.value])
|
||||||
|
@ -2163,8 +2238,9 @@ class DATA:
|
||||||
# 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()
|
||||||
|
|
||||||
# Update data_channel timestamp
|
# Reset data_channel/burst timestamps once again for avoiding running into timeout
|
||||||
self.data_channel_last_received = int(time.time())
|
self.data_channel_last_received = int(time.time())
|
||||||
|
self.burst_last_received = int(time.time())
|
||||||
|
|
||||||
def arq_received_channel_is_open(self, data_in: bytes) -> None:
|
def arq_received_channel_is_open(self, data_in: bytes) -> None:
|
||||||
"""
|
"""
|
||||||
|
@ -2240,7 +2316,6 @@ class DATA:
|
||||||
own=static.ARQ_PROTOCOL_VERSION,
|
own=static.ARQ_PROTOCOL_VERSION,
|
||||||
)
|
)
|
||||||
self.stop_transmission()
|
self.stop_transmission()
|
||||||
self.arq_cleanup()
|
|
||||||
|
|
||||||
# ---------- PING
|
# ---------- PING
|
||||||
def transmit_ping(self, mycallsign: bytes, dxcallsign: bytes) -> None:
|
def transmit_ping(self, mycallsign: bytes, dxcallsign: bytes) -> None:
|
||||||
|
@ -2313,6 +2388,8 @@ class DATA:
|
||||||
snr=static.SNR,
|
snr=static.SNR,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
static.DXGRID = b'------'
|
||||||
|
|
||||||
helpers.add_to_heard_stations(
|
helpers.add_to_heard_stations(
|
||||||
dxcallsign,
|
dxcallsign,
|
||||||
static.DXGRID,
|
static.DXGRID,
|
||||||
|
@ -2409,7 +2486,6 @@ class DATA:
|
||||||
"""
|
"""
|
||||||
self.log.warning("[TNC] Stopping transmission!")
|
self.log.warning("[TNC] Stopping transmission!")
|
||||||
|
|
||||||
|
|
||||||
static.TNC_STATE = "IDLE"
|
static.TNC_STATE = "IDLE"
|
||||||
static.ARQ_STATE = False
|
static.ARQ_STATE = False
|
||||||
self.send_data_to_socket_queue(
|
self.send_data_to_socket_queue(
|
||||||
|
@ -2728,10 +2804,16 @@ class DATA:
|
||||||
static.ARQ_BYTES_PER_MINUTE = int(
|
static.ARQ_BYTES_PER_MINUTE = int(
|
||||||
receivedbytes / (transmissiontime / 60)
|
receivedbytes / (transmissiontime / 60)
|
||||||
)
|
)
|
||||||
|
static.ARQ_SECONDS_UNTIL_FINISH = int(((static.TOTAL_BYTES - receivedbytes) / (static.ARQ_BYTES_PER_MINUTE * static.ARQ_COMPRESSION_FACTOR)) * 60) -20 # offset because of frame ack/nack
|
||||||
|
|
||||||
|
speed_chart = {"snr": static.SNR, "bpm": static.ARQ_BYTES_PER_MINUTE, "timestamp": int(time.time())}
|
||||||
|
# check if data already in list
|
||||||
|
if speed_chart not in static.SPEED_LIST:
|
||||||
|
static.SPEED_LIST.append(speed_chart)
|
||||||
else:
|
else:
|
||||||
static.ARQ_BITS_PER_SECOND = 0
|
static.ARQ_BITS_PER_SECOND = 0
|
||||||
static.ARQ_BYTES_PER_MINUTE = 0
|
static.ARQ_BYTES_PER_MINUTE = 0
|
||||||
|
static.ARQ_SECONDS_UNTIL_FINISH = 0
|
||||||
except Exception as err:
|
except Exception as err:
|
||||||
self.log.error(f"[TNC] calculate_transfer_rate_rx: Exception: {err}")
|
self.log.error(f"[TNC] calculate_transfer_rate_rx: Exception: {err}")
|
||||||
static.ARQ_TRANSMISSION_PERCENT = 0.0
|
static.ARQ_TRANSMISSION_PERCENT = 0.0
|
||||||
|
@ -2755,6 +2837,7 @@ class DATA:
|
||||||
static.ARQ_BITS_PER_SECOND = 0
|
static.ARQ_BITS_PER_SECOND = 0
|
||||||
static.ARQ_TRANSMISSION_PERCENT = 0
|
static.ARQ_TRANSMISSION_PERCENT = 0
|
||||||
static.TOTAL_BYTES = 0
|
static.TOTAL_BYTES = 0
|
||||||
|
static.ARQ_SECONDS_UNTIL_FINISH = 0
|
||||||
|
|
||||||
def calculate_transfer_rate_tx(
|
def calculate_transfer_rate_tx(
|
||||||
self, tx_start_of_transmission: float, sentbytes: int, tx_buffer_length: int
|
self, tx_start_of_transmission: float, sentbytes: int, tx_buffer_length: int
|
||||||
|
@ -2781,10 +2864,18 @@ class DATA:
|
||||||
if sentbytes > 0:
|
if sentbytes > 0:
|
||||||
static.ARQ_BITS_PER_SECOND = int((sentbytes * 8) / transmissiontime)
|
static.ARQ_BITS_PER_SECOND = int((sentbytes * 8) / transmissiontime)
|
||||||
static.ARQ_BYTES_PER_MINUTE = int(sentbytes / (transmissiontime / 60))
|
static.ARQ_BYTES_PER_MINUTE = int(sentbytes / (transmissiontime / 60))
|
||||||
|
static.ARQ_SECONDS_UNTIL_FINISH = int(((tx_buffer_length - sentbytes) / (static.ARQ_BYTES_PER_MINUTE* static.ARQ_COMPRESSION_FACTOR)) * 60 )
|
||||||
|
|
||||||
|
|
||||||
|
speed_chart = {"snr": self.burst_ack_snr, "bpm": static.ARQ_BYTES_PER_MINUTE, "timestamp": int(time.time())}
|
||||||
|
# check if data already in list
|
||||||
|
if speed_chart not in static.SPEED_LIST:
|
||||||
|
static.SPEED_LIST.append(speed_chart)
|
||||||
|
|
||||||
else:
|
else:
|
||||||
static.ARQ_BITS_PER_SECOND = 0
|
static.ARQ_BITS_PER_SECOND = 0
|
||||||
static.ARQ_BYTES_PER_MINUTE = 0
|
static.ARQ_BYTES_PER_MINUTE = 0
|
||||||
|
static.ARQ_SECONDS_UNTIL_FINISH = 0
|
||||||
|
|
||||||
except Exception as err:
|
except Exception as err:
|
||||||
self.log.error(f"[TNC] calculate_transfer_rate_tx: Exception: {err}")
|
self.log.error(f"[TNC] calculate_transfer_rate_tx: Exception: {err}")
|
||||||
|
@ -2809,7 +2900,7 @@ class DATA:
|
||||||
|
|
||||||
self.log.debug("[TNC] arq_cleanup")
|
self.log.debug("[TNC] arq_cleanup")
|
||||||
|
|
||||||
self.session_id = bytes(1)
|
|
||||||
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.burst_ack = False
|
self.burst_ack = False
|
||||||
|
@ -2817,7 +2908,7 @@ class DATA:
|
||||||
self.data_frame_ack_received = False
|
self.data_frame_ack_received = False
|
||||||
static.RX_BURST_BUFFER = []
|
static.RX_BURST_BUFFER = []
|
||||||
static.RX_FRAME_BUFFER = b""
|
static.RX_FRAME_BUFFER = b""
|
||||||
self.burst_ack_snr = 255
|
self.burst_ack_snr = 0
|
||||||
|
|
||||||
# reset modem receiving state to reduce cpu load
|
# reset modem receiving state to reduce cpu load
|
||||||
modem.RECEIVE_SIG0 = True
|
modem.RECEIVE_SIG0 = True
|
||||||
|
@ -2848,11 +2939,14 @@ class DATA:
|
||||||
self.session_connect_max_retries = 10
|
self.session_connect_max_retries = 10
|
||||||
self.data_channel_max_retries = 10
|
self.data_channel_max_retries = 10
|
||||||
|
|
||||||
|
# we need to keep these values if in ARQ_SESSION
|
||||||
if not static.ARQ_SESSION:
|
if not static.ARQ_SESSION:
|
||||||
static.TNC_STATE = "IDLE"
|
static.TNC_STATE = "IDLE"
|
||||||
self.dxcallsign = b"AA0AA-0"
|
self.dxcallsign = b"AA0AA-0"
|
||||||
self.mycallsign = static.MYCALLSIGN
|
self.mycallsign = static.MYCALLSIGN
|
||||||
|
self.session_id = bytes(1)
|
||||||
|
|
||||||
|
static.SPEED_LIST = []
|
||||||
static.ARQ_STATE = False
|
static.ARQ_STATE = False
|
||||||
self.arq_file_transfer = False
|
self.arq_file_transfer = False
|
||||||
|
|
||||||
|
@ -2938,10 +3032,13 @@ class DATA:
|
||||||
modem_error_state = modem.get_modem_error_state()
|
modem_error_state = modem.get_modem_error_state()
|
||||||
|
|
||||||
# We want to reach this state only if connected ( == return above not called )
|
# We want to reach this state only if connected ( == return above not called )
|
||||||
if (
|
timeout = self.burst_last_received + self.time_list[self.speed_level]
|
||||||
self.data_channel_last_received + self.time_list[self.speed_level]
|
if timeout <= time.time() or modem_error_state:
|
||||||
<= time.time() or modem_error_state
|
print("timeout----------------")
|
||||||
):
|
print(time.time() - timeout)
|
||||||
|
print(time.time() - (self.burst_last_received + self.time_list[self.speed_level]))
|
||||||
|
|
||||||
|
print("-----------------------")
|
||||||
if modem_error_state:
|
if modem_error_state:
|
||||||
self.log.warning(
|
self.log.warning(
|
||||||
"[TNC] Decoding Error",
|
"[TNC] Decoding Error",
|
||||||
|
@ -2951,11 +3048,15 @@ class DATA:
|
||||||
)
|
)
|
||||||
else:
|
else:
|
||||||
self.log.warning(
|
self.log.warning(
|
||||||
"[TNC] Frame timeout",
|
"[TNC] Burst timeout",
|
||||||
attempt=self.n_retries_per_burst,
|
attempt=self.n_retries_per_burst,
|
||||||
max_attempts=self.rx_n_max_retries_per_burst,
|
max_attempts=self.rx_n_max_retries_per_burst,
|
||||||
speed_level=self.speed_level,
|
speed_level=self.speed_level,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
# reset self.burst_last_received
|
||||||
|
self.burst_last_received = time.time() + self.time_list[self.speed_level]
|
||||||
|
|
||||||
# reduce speed level if nack counter increased
|
# reduce speed level if nack counter increased
|
||||||
self.frame_received_counter = 0
|
self.frame_received_counter = 0
|
||||||
self.burst_nack_counter += 1
|
self.burst_nack_counter += 1
|
||||||
|
@ -2980,7 +3081,6 @@ class DATA:
|
||||||
|
|
||||||
if self.n_retries_per_burst >= self.rx_n_max_retries_per_burst:
|
if self.n_retries_per_burst >= self.rx_n_max_retries_per_burst:
|
||||||
self.stop_transmission()
|
self.stop_transmission()
|
||||||
self.arq_cleanup()
|
|
||||||
|
|
||||||
def data_channel_keep_alive_watchdog(self) -> None:
|
def data_channel_keep_alive_watchdog(self) -> None:
|
||||||
"""
|
"""
|
||||||
|
@ -2995,9 +3095,11 @@ class DATA:
|
||||||
> time.time()
|
> time.time()
|
||||||
):
|
):
|
||||||
|
|
||||||
timeleft = (self.data_channel_last_received + self.transmission_timeout) - time.time()
|
timeleft = int((self.data_channel_last_received + self.transmission_timeout) - time.time())
|
||||||
self.log.debug("Time left until timeout", seconds=timeleft)
|
if timeleft % 10 == 0:
|
||||||
threading.Event().wait(5)
|
self.log.debug("Time left until timeout", seconds=timeleft)
|
||||||
|
|
||||||
|
# threading.Event().wait(5)
|
||||||
# print(self.data_channel_last_received + self.transmission_timeout - time.time())
|
# print(self.data_channel_last_received + self.transmission_timeout - time.time())
|
||||||
# pass
|
# pass
|
||||||
else:
|
else:
|
||||||
|
@ -3071,8 +3173,10 @@ class DATA:
|
||||||
|
|
||||||
def send_test_frame(self) -> None:
|
def send_test_frame(self) -> None:
|
||||||
"""Send an empty test frame"""
|
"""Send an empty test frame"""
|
||||||
|
test_frame = bytearray(126)
|
||||||
|
test_frame[:1] = bytes([FR_TYPE.TEST_FRAME.value])
|
||||||
self.enqueue_frame_for_tx(
|
self.enqueue_frame_for_tx(
|
||||||
frame_to_tx=[bytearray(126)], c2_mode=FREEDV_MODE.datac3.value
|
frame_to_tx=[test_frame], c2_mode=FREEDV_MODE.datac3.value
|
||||||
)
|
)
|
||||||
|
|
||||||
def save_data_to_folder(self,
|
def save_data_to_folder(self,
|
||||||
|
|
|
@ -49,11 +49,12 @@ class explorer():
|
||||||
try:
|
try:
|
||||||
callsign = str(i[0], "UTF-8")
|
callsign = str(i[0], "UTF-8")
|
||||||
grid = str(i[1], "UTF-8")
|
grid = str(i[1], "UTF-8")
|
||||||
|
timestamp = i[2]
|
||||||
try:
|
try:
|
||||||
snr = i[4].split("/")[1]
|
snr = i[4].split("/")[1]
|
||||||
except AttributeError:
|
except AttributeError:
|
||||||
snr = str(i[4])
|
snr = str(i[4])
|
||||||
station_data["lastheard"].append({"callsign": callsign, "grid": grid, "snr": snr})
|
station_data["lastheard"].append({"callsign": callsign, "grid": grid, "snr": snr, "timestamp": timestamp})
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
log.debug("[EXPLORER] not publishing station", e=e)
|
log.debug("[EXPLORER] not publishing station", e=e)
|
||||||
|
|
||||||
|
|
|
@ -5,7 +5,7 @@ Created on Fri Dec 25 21:25:14 2020
|
||||||
@author: DJ2LS
|
@author: DJ2LS
|
||||||
"""
|
"""
|
||||||
import time
|
import time
|
||||||
|
from datetime import datetime,timezone
|
||||||
import crcengine
|
import crcengine
|
||||||
import static
|
import static
|
||||||
import structlog
|
import structlog
|
||||||
|
@ -132,7 +132,7 @@ def add_to_heard_stations(dxcallsign, dxgrid, datatype, snr, offset, frequency):
|
||||||
# check if buffer empty
|
# check if buffer empty
|
||||||
if len(static.HEARD_STATIONS) == 0:
|
if len(static.HEARD_STATIONS) == 0:
|
||||||
static.HEARD_STATIONS.append(
|
static.HEARD_STATIONS.append(
|
||||||
[dxcallsign, dxgrid, int(time.time()), datatype, snr, offset, frequency]
|
[dxcallsign, dxgrid, int(datetime.now(timezone.utc).timestamp()), datatype, snr, offset, frequency]
|
||||||
)
|
)
|
||||||
# if not, we search and update
|
# if not, we search and update
|
||||||
else:
|
else:
|
||||||
|
@ -316,7 +316,7 @@ def check_callsign(callsign: bytes, crc_to_check: bytes):
|
||||||
log.debug("[HLP] check_callsign matched:", call_with_ssid=call_with_ssid)
|
log.debug("[HLP] check_callsign matched:", call_with_ssid=call_with_ssid)
|
||||||
return [True, bytes(call_with_ssid)]
|
return [True, bytes(call_with_ssid)]
|
||||||
|
|
||||||
return [False, ""]
|
return [False, b'']
|
||||||
|
|
||||||
|
|
||||||
def check_session_id(id: bytes, id_to_check: bytes):
|
def check_session_id(id: bytes, id_to_check: bytes):
|
||||||
|
|
|
@ -14,7 +14,7 @@ def setup_logging(filename: str = "", level: str = "DEBUG"):
|
||||||
|
|
||||||
"""
|
"""
|
||||||
|
|
||||||
timestamper = structlog.processors.TimeStamper(fmt="%Y-%m-%d %H:%M:%S")
|
timestamper = structlog.processors.TimeStamper(fmt="iso")
|
||||||
pre_chain = [
|
pre_chain = [
|
||||||
# Add the log level and a timestamp to the event_dict if the log entry
|
# Add the log level and a timestamp to the event_dict if the log entry
|
||||||
# is not from structlog.
|
# is not from structlog.
|
||||||
|
|
10
tnc/main.py
10
tnc/main.py
|
@ -31,6 +31,7 @@ import modem
|
||||||
import static
|
import static
|
||||||
import structlog
|
import structlog
|
||||||
import explorer
|
import explorer
|
||||||
|
import json
|
||||||
|
|
||||||
log = structlog.get_logger("main")
|
log = structlog.get_logger("main")
|
||||||
|
|
||||||
|
@ -348,7 +349,9 @@ if __name__ == "__main__":
|
||||||
static.MYCALLSIGN = helpers.bytes_to_callsign(mycallsign)
|
static.MYCALLSIGN = helpers.bytes_to_callsign(mycallsign)
|
||||||
static.MYCALLSIGN_CRC = helpers.get_crc_24(static.MYCALLSIGN)
|
static.MYCALLSIGN_CRC = helpers.get_crc_24(static.MYCALLSIGN)
|
||||||
|
|
||||||
static.SSID_LIST = config['STATION']['ssid_list']
|
#json.loads = for converting str list to list
|
||||||
|
static.SSID_LIST = json.loads(config['STATION']['ssid_list'])
|
||||||
|
|
||||||
static.MYGRID = bytes(config['STATION']['mygrid'], "utf-8")
|
static.MYGRID = bytes(config['STATION']['mygrid'], "utf-8")
|
||||||
# check if we have an int or str as device name
|
# check if we have an int or str as device name
|
||||||
try:
|
try:
|
||||||
|
@ -389,6 +392,11 @@ if __name__ == "__main__":
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
log.warning("[CFG] Error", e=e)
|
log.warning("[CFG] Error", e=e)
|
||||||
|
|
||||||
|
# make sure the own ssid is always part of the ssid list
|
||||||
|
my_ssid = int(static.MYCALLSIGN.split(b'-')[1])
|
||||||
|
if my_ssid not in static.SSID_LIST:
|
||||||
|
static.SSID_LIST.append(my_ssid)
|
||||||
|
|
||||||
# we need to wait until we got all parameters from argparse first before we can load the other modules
|
# we need to wait until we got all parameters from argparse first before we can load the other modules
|
||||||
import sock
|
import sock
|
||||||
|
|
||||||
|
|
67
tnc/modem.py
67
tnc/modem.py
|
@ -25,7 +25,7 @@ import sounddevice as sd
|
||||||
import static
|
import static
|
||||||
import structlog
|
import structlog
|
||||||
import ujson as json
|
import ujson as json
|
||||||
from queues import DATA_QUEUE_RECEIVED, MODEM_RECEIVED_QUEUE, MODEM_TRANSMIT_QUEUE
|
from queues import DATA_QUEUE_RECEIVED, MODEM_RECEIVED_QUEUE, MODEM_TRANSMIT_QUEUE, RIGCTLD_COMMAND_QUEUE
|
||||||
|
|
||||||
TESTMODE = False
|
TESTMODE = False
|
||||||
RXCHANNEL = ""
|
RXCHANNEL = ""
|
||||||
|
@ -280,6 +280,13 @@ class RF:
|
||||||
)
|
)
|
||||||
hamlib_thread.start()
|
hamlib_thread.start()
|
||||||
|
|
||||||
|
hamlib_set_thread = threading.Thread(
|
||||||
|
target=self.set_rig_data, name="HAMLIB_SET_THREAD", daemon=True
|
||||||
|
)
|
||||||
|
hamlib_set_thread.start()
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
# self.log.debug("[MDM] Starting worker_receive")
|
# self.log.debug("[MDM] Starting worker_receive")
|
||||||
worker_received = threading.Thread(
|
worker_received = threading.Thread(
|
||||||
target=self.worker_received, name="WORKER_THREAD", daemon=True
|
target=self.worker_received, name="WORKER_THREAD", daemon=True
|
||||||
|
@ -321,7 +328,7 @@ class RF:
|
||||||
# (self.fsk_ldpc_buffer_1, static.ENABLE_FSK),
|
# (self.fsk_ldpc_buffer_1, static.ENABLE_FSK),
|
||||||
]:
|
]:
|
||||||
if (
|
if (
|
||||||
not data_buffer.nbuffer + length_x > data_buffer.size
|
not (data_buffer.nbuffer + length_x) > data_buffer.size
|
||||||
and receive
|
and receive
|
||||||
):
|
):
|
||||||
data_buffer.push(x)
|
data_buffer.push(x)
|
||||||
|
@ -378,7 +385,7 @@ class RF:
|
||||||
(self.fsk_ldpc_buffer_0, static.ENABLE_FSK, 4),
|
(self.fsk_ldpc_buffer_0, static.ENABLE_FSK, 4),
|
||||||
(self.fsk_ldpc_buffer_1, static.ENABLE_FSK, 5),
|
(self.fsk_ldpc_buffer_1, static.ENABLE_FSK, 5),
|
||||||
]:
|
]:
|
||||||
if audiobuffer.nbuffer + length_x > audiobuffer.size:
|
if (audiobuffer.nbuffer + length_x) > audiobuffer.size:
|
||||||
static.BUFFER_OVERFLOW_COUNTER[index] += 1
|
static.BUFFER_OVERFLOW_COUNTER[index] += 1
|
||||||
elif receive:
|
elif receive:
|
||||||
audiobuffer.push(x)
|
audiobuffer.push(x)
|
||||||
|
@ -596,6 +603,7 @@ class RF:
|
||||||
bytes_out,
|
bytes_out,
|
||||||
bytes_per_frame,
|
bytes_per_frame,
|
||||||
state_buffer,
|
state_buffer,
|
||||||
|
mode_name,
|
||||||
) -> int:
|
) -> int:
|
||||||
"""
|
"""
|
||||||
De-modulate supplied audio stream with supplied codec2 instance.
|
De-modulate supplied audio stream with supplied codec2 instance.
|
||||||
|
@ -613,6 +621,8 @@ class RF:
|
||||||
:type bytes_per_frame: int
|
:type bytes_per_frame: int
|
||||||
:param state_buffer: modem states
|
:param state_buffer: modem states
|
||||||
:type state_buffer: int
|
:type state_buffer: int
|
||||||
|
:param mode_name: mode name
|
||||||
|
:type mode_name: str
|
||||||
:return: NIN from freedv instance
|
:return: NIN from freedv instance
|
||||||
:rtype: int
|
:rtype: int
|
||||||
"""
|
"""
|
||||||
|
@ -631,9 +641,20 @@ class RF:
|
||||||
# 3 trial sync
|
# 3 trial sync
|
||||||
# 6 decoded
|
# 6 decoded
|
||||||
# 10 error decoding == NACK
|
# 10 error decoding == NACK
|
||||||
state = codec2.api.freedv_get_rx_status(freedv)
|
rx_status = codec2.api.freedv_get_rx_status(freedv)
|
||||||
if state == 10:
|
|
||||||
state_buffer.append(state)
|
if rx_status != 0:
|
||||||
|
# if we're receiving FreeDATA signals, reset channel busy state
|
||||||
|
static.CHANNEL_BUSY = False
|
||||||
|
|
||||||
|
self.log.debug(
|
||||||
|
"[MDM] [demod_audio] modem state", mode=mode_name, rx_status=rx_status, sync_flag=codec2.api.rx_sync_flags_to_text[rx_status]
|
||||||
|
)
|
||||||
|
|
||||||
|
if rx_status == 10:
|
||||||
|
state_buffer.append(rx_status)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
audiobuffer.pop(nin)
|
audiobuffer.pop(nin)
|
||||||
nin = codec2.api.freedv_nin(freedv)
|
nin = codec2.api.freedv_nin(freedv)
|
||||||
|
@ -734,7 +755,8 @@ class RF:
|
||||||
self.sig0_datac0_freedv,
|
self.sig0_datac0_freedv,
|
||||||
self.sig0_datac0_bytes_out,
|
self.sig0_datac0_bytes_out,
|
||||||
self.sig0_datac0_bytes_per_frame,
|
self.sig0_datac0_bytes_per_frame,
|
||||||
SIG0_DATAC0_STATE
|
SIG0_DATAC0_STATE,
|
||||||
|
"sig0-datac0"
|
||||||
)
|
)
|
||||||
|
|
||||||
def audio_sig1_datac0(self) -> None:
|
def audio_sig1_datac0(self) -> None:
|
||||||
|
@ -745,7 +767,8 @@ class RF:
|
||||||
self.sig1_datac0_freedv,
|
self.sig1_datac0_freedv,
|
||||||
self.sig1_datac0_bytes_out,
|
self.sig1_datac0_bytes_out,
|
||||||
self.sig1_datac0_bytes_per_frame,
|
self.sig1_datac0_bytes_per_frame,
|
||||||
SIG1_DATAC0_STATE
|
SIG1_DATAC0_STATE,
|
||||||
|
"sig1-datac0"
|
||||||
)
|
)
|
||||||
|
|
||||||
def audio_dat0_datac1(self) -> None:
|
def audio_dat0_datac1(self) -> None:
|
||||||
|
@ -756,7 +779,8 @@ class RF:
|
||||||
self.dat0_datac1_freedv,
|
self.dat0_datac1_freedv,
|
||||||
self.dat0_datac1_bytes_out,
|
self.dat0_datac1_bytes_out,
|
||||||
self.dat0_datac1_bytes_per_frame,
|
self.dat0_datac1_bytes_per_frame,
|
||||||
DAT0_DATAC1_STATE
|
DAT0_DATAC1_STATE,
|
||||||
|
"dat0-datac1"
|
||||||
)
|
)
|
||||||
|
|
||||||
def audio_dat0_datac3(self) -> None:
|
def audio_dat0_datac3(self) -> None:
|
||||||
|
@ -767,7 +791,8 @@ class RF:
|
||||||
self.dat0_datac3_freedv,
|
self.dat0_datac3_freedv,
|
||||||
self.dat0_datac3_bytes_out,
|
self.dat0_datac3_bytes_out,
|
||||||
self.dat0_datac3_bytes_per_frame,
|
self.dat0_datac3_bytes_per_frame,
|
||||||
DAT0_DATAC3_STATE
|
DAT0_DATAC3_STATE,
|
||||||
|
"dat0-datac3"
|
||||||
)
|
)
|
||||||
|
|
||||||
def audio_fsk_ldpc_0(self) -> None:
|
def audio_fsk_ldpc_0(self) -> None:
|
||||||
|
@ -874,7 +899,6 @@ class RF:
|
||||||
# only take every tenth data point
|
# only take every tenth data point
|
||||||
static.SCATTER = scatterdata[::10]
|
static.SCATTER = scatterdata[::10]
|
||||||
|
|
||||||
|
|
||||||
def calculate_snr(self, freedv: ctypes.c_void_p) -> float:
|
def calculate_snr(self, freedv: ctypes.c_void_p) -> float:
|
||||||
"""
|
"""
|
||||||
Ask codec2 for data about the received signal and calculate
|
Ask codec2 for data about the received signal and calculate
|
||||||
|
@ -908,6 +932,20 @@ class RF:
|
||||||
static.SNR = 0
|
static.SNR = 0
|
||||||
return static.SNR
|
return static.SNR
|
||||||
|
|
||||||
|
def set_rig_data(self) -> None:
|
||||||
|
"""
|
||||||
|
Set rigctld parameters like frequency, mode
|
||||||
|
THis needs to be processed in a queue
|
||||||
|
"""
|
||||||
|
while True:
|
||||||
|
cmd = RIGCTLD_COMMAND_QUEUE.get()
|
||||||
|
if cmd[0] == "set_frequency":
|
||||||
|
# [1] = Frequency
|
||||||
|
self.hamlib.set_frequency(cmd[1])
|
||||||
|
if cmd[0] == "set_mode":
|
||||||
|
# [1] = Mode
|
||||||
|
self.hamlib.set_mode(cmd[1])
|
||||||
|
|
||||||
def update_rig_data(self) -> None:
|
def update_rig_data(self) -> None:
|
||||||
"""
|
"""
|
||||||
Request information about the current state of the radio via hamlib
|
Request information about the current state of the radio via hamlib
|
||||||
|
@ -922,6 +960,7 @@ class RF:
|
||||||
static.HAMLIB_MODE = self.hamlib.get_mode()
|
static.HAMLIB_MODE = self.hamlib.get_mode()
|
||||||
static.HAMLIB_BANDWIDTH = self.hamlib.get_bandwidth()
|
static.HAMLIB_BANDWIDTH = self.hamlib.get_bandwidth()
|
||||||
static.HAMLIB_STATUS = self.hamlib.get_status()
|
static.HAMLIB_STATUS = self.hamlib.get_status()
|
||||||
|
|
||||||
def calculate_fft(self) -> None:
|
def calculate_fft(self) -> None:
|
||||||
"""
|
"""
|
||||||
Calculate an average signal strength of the channel to assess
|
Calculate an average signal strength of the channel to assess
|
||||||
|
@ -974,6 +1013,8 @@ class RF:
|
||||||
# try except for avoiding runtime errors by division/0
|
# try except for avoiding runtime errors by division/0
|
||||||
try:
|
try:
|
||||||
rms = int(np.sqrt(np.max(d ** 2)))
|
rms = int(np.sqrt(np.max(d ** 2)))
|
||||||
|
if rms == 0:
|
||||||
|
raise ZeroDivisionError
|
||||||
static.AUDIO_DBFS = 20 * np.log10(rms / 32768)
|
static.AUDIO_DBFS = 20 * np.log10(rms / 32768)
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
self.log.warning(
|
self.log.warning(
|
||||||
|
@ -1009,9 +1050,9 @@ class RF:
|
||||||
# so we have a smoother state toggle
|
# so we have a smoother state toggle
|
||||||
if np.sum(dfft[dfft > avg + 15]) >= 400 and not static.TRANSMITTING:
|
if np.sum(dfft[dfft > avg + 15]) >= 400 and not static.TRANSMITTING:
|
||||||
static.CHANNEL_BUSY = True
|
static.CHANNEL_BUSY = True
|
||||||
# Limit delay counter to a maximum of 250. The higher this value,
|
# Limit delay counter to a maximum of 200. The higher this value,
|
||||||
# the longer we will wait until releasing state
|
# the longer we will wait until releasing state
|
||||||
channel_busy_delay = min(channel_busy_delay + 10, 250)
|
channel_busy_delay = min(channel_busy_delay + 10, 200)
|
||||||
else:
|
else:
|
||||||
# Decrement channel busy counter if no signal has been detected.
|
# Decrement channel busy counter if no signal has been detected.
|
||||||
channel_busy_delay = max(channel_busy_delay - 1, 0)
|
channel_busy_delay = max(channel_busy_delay - 1, 0)
|
||||||
|
|
|
@ -13,3 +13,6 @@ MODEM_TRANSMIT_QUEUE = queue.Queue()
|
||||||
|
|
||||||
# Initialize FIFO queue to finally store received data
|
# Initialize FIFO queue to finally store received data
|
||||||
RX_BUFFER = queue.Queue(maxsize=static.RX_BUFFER_SIZE)
|
RX_BUFFER = queue.Queue(maxsize=static.RX_BUFFER_SIZE)
|
||||||
|
|
||||||
|
# Commands we want to send to rigctld
|
||||||
|
RIGCTLD_COMMAND_QUEUE = queue.Queue()
|
173
tnc/rigctld.py
173
tnc/rigctld.py
|
@ -21,9 +21,11 @@ class radio:
|
||||||
|
|
||||||
def __init__(self, hostname="localhost", port=4532, poll_rate=5, timeout=5):
|
def __init__(self, hostname="localhost", port=4532, poll_rate=5, timeout=5):
|
||||||
"""Open a connection to rigctld, and test it for validity"""
|
"""Open a connection to rigctld, and test it for validity"""
|
||||||
self.sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
|
self.ptt_sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
|
||||||
|
self.data_sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
|
||||||
|
|
||||||
self.connected = False
|
self.ptt_connected = False
|
||||||
|
self.data_connected = False
|
||||||
self.hostname = hostname
|
self.hostname = hostname
|
||||||
self.port = port
|
self.port = port
|
||||||
self.connection_attempts = 5
|
self.connection_attempts = 5
|
||||||
|
@ -33,7 +35,6 @@ class radio:
|
||||||
self.frequency = ''
|
self.frequency = ''
|
||||||
self.mode = ''
|
self.mode = ''
|
||||||
|
|
||||||
|
|
||||||
def open_rig(
|
def open_rig(
|
||||||
self,
|
self,
|
||||||
devicename,
|
devicename,
|
||||||
|
@ -67,8 +68,20 @@ class radio:
|
||||||
self.hostname = rigctld_ip
|
self.hostname = rigctld_ip
|
||||||
self.port = int(rigctld_port)
|
self.port = int(rigctld_port)
|
||||||
|
|
||||||
if self.connect():
|
#_ptt_connect = self.ptt_connect()
|
||||||
self.log.debug("Rigctl initialized")
|
#_data_connect = self.data_connect()
|
||||||
|
|
||||||
|
ptt_thread = threading.Thread(target=self.ptt_connect, args=[], daemon=True)
|
||||||
|
ptt_thread.start()
|
||||||
|
|
||||||
|
data_thread = threading.Thread(target=self.data_connect, args=[], daemon=True)
|
||||||
|
data_thread.start()
|
||||||
|
|
||||||
|
# wait some time
|
||||||
|
threading.Event().wait(0.5)
|
||||||
|
|
||||||
|
if self.ptt_connected and self.data_connected:
|
||||||
|
self.log.debug("Rigctl DATA/PTT initialized")
|
||||||
return True
|
return True
|
||||||
|
|
||||||
self.log.error(
|
self.log.error(
|
||||||
|
@ -76,33 +89,58 @@ class radio:
|
||||||
)
|
)
|
||||||
return False
|
return False
|
||||||
|
|
||||||
def connect(self):
|
def ptt_connect(self):
|
||||||
"""Connect to rigctld instance"""
|
"""Connect to rigctld instance"""
|
||||||
if not self.connected:
|
while True:
|
||||||
try:
|
|
||||||
self.connection = socket.create_connection((self.hostname, self.port))
|
if not self.ptt_connected:
|
||||||
self.connected = True
|
try:
|
||||||
self.log.info(
|
self.ptt_connection = socket.create_connection((self.hostname, self.port))
|
||||||
"[RIGCTLD] Connected to rigctld!", ip=self.hostname, port=self.port
|
self.ptt_connected = True
|
||||||
)
|
self.log.info(
|
||||||
return True
|
"[RIGCTLD] Connected PTT instance to rigctld!", ip=self.hostname, port=self.port
|
||||||
except Exception as err:
|
)
|
||||||
# ConnectionRefusedError: [Errno 111] Connection refused
|
except Exception as err:
|
||||||
self.close_rig()
|
# ConnectionRefusedError: [Errno 111] Connection refused
|
||||||
self.log.warning(
|
self.close_rig()
|
||||||
"[RIGCTLD] Reconnect...",
|
self.log.warning(
|
||||||
ip=self.hostname,
|
"[RIGCTLD] PTT Reconnect...",
|
||||||
port=self.port,
|
ip=self.hostname,
|
||||||
e=err,
|
port=self.port,
|
||||||
)
|
e=err,
|
||||||
return False
|
)
|
||||||
|
|
||||||
|
threading.Event().wait(0.5)
|
||||||
|
|
||||||
|
def data_connect(self):
|
||||||
|
"""Connect to rigctld instance"""
|
||||||
|
while True:
|
||||||
|
if not self.data_connected:
|
||||||
|
try:
|
||||||
|
self.data_connection = socket.create_connection((self.hostname, self.port))
|
||||||
|
self.data_connected = True
|
||||||
|
self.log.info(
|
||||||
|
"[RIGCTLD] Connected DATA instance to rigctld!", ip=self.hostname, port=self.port
|
||||||
|
)
|
||||||
|
except Exception as err:
|
||||||
|
# ConnectionRefusedError: [Errno 111] Connection refused
|
||||||
|
self.close_rig()
|
||||||
|
self.log.warning(
|
||||||
|
"[RIGCTLD] DATA Reconnect...",
|
||||||
|
ip=self.hostname,
|
||||||
|
port=self.port,
|
||||||
|
e=err,
|
||||||
|
)
|
||||||
|
threading.Event().wait(0.5)
|
||||||
|
|
||||||
def close_rig(self):
|
def close_rig(self):
|
||||||
""" """
|
""" """
|
||||||
self.sock.close()
|
self.ptt_sock.close()
|
||||||
self.connected = False
|
self.data_sock.close()
|
||||||
|
self.ptt_connected = False
|
||||||
|
self.data_connected = False
|
||||||
|
|
||||||
def send_command(self, command, expect_answer) -> bytes:
|
def send_ptt_command(self, command, expect_answer) -> bytes:
|
||||||
"""Send a command to the connected rotctld instance,
|
"""Send a command to the connected rotctld instance,
|
||||||
and return the return value.
|
and return the return value.
|
||||||
|
|
||||||
|
@ -110,9 +148,9 @@ class radio:
|
||||||
command:
|
command:
|
||||||
|
|
||||||
"""
|
"""
|
||||||
if self.connected:
|
if self.ptt_connected:
|
||||||
try:
|
try:
|
||||||
self.connection.sendall(command + b"\n")
|
self.ptt_connection.sendall(command + b"\n")
|
||||||
except Exception:
|
except Exception:
|
||||||
self.log.warning(
|
self.log.warning(
|
||||||
"[RIGCTLD] Command not executed!",
|
"[RIGCTLD] Command not executed!",
|
||||||
|
@ -120,12 +158,33 @@ class radio:
|
||||||
ip=self.hostname,
|
ip=self.hostname,
|
||||||
port=self.port,
|
port=self.port,
|
||||||
)
|
)
|
||||||
self.connected = False
|
self.ptt_connected = False
|
||||||
|
return b""
|
||||||
|
|
||||||
|
def send_data_command(self, command, expect_answer) -> bytes:
|
||||||
|
"""Send a command to the connected rotctld instance,
|
||||||
|
and return the return value.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
command:
|
||||||
|
|
||||||
|
"""
|
||||||
|
if self.data_connected:
|
||||||
|
try:
|
||||||
|
self.data_connection.sendall(command + b"\n")
|
||||||
|
except Exception:
|
||||||
|
self.log.warning(
|
||||||
|
"[RIGCTLD] Command not executed!",
|
||||||
|
command=command,
|
||||||
|
ip=self.hostname,
|
||||||
|
port=self.port,
|
||||||
|
)
|
||||||
|
self.data_connected = False
|
||||||
|
|
||||||
try:
|
try:
|
||||||
# recv seems to be blocking so in case of ptt we dont need the response
|
# recv seems to be blocking so in case of ptt we don't need the response
|
||||||
# maybe this speeds things up and avoids blocking states
|
# maybe this speeds things up and avoids blocking states
|
||||||
return self.connection.recv(16) if expect_answer else True
|
return self.data_connection.recv(64) if expect_answer else True
|
||||||
except Exception:
|
except Exception:
|
||||||
self.log.warning(
|
self.log.warning(
|
||||||
"[RIGCTLD] No command response!",
|
"[RIGCTLD] No command response!",
|
||||||
|
@ -133,23 +192,17 @@ class radio:
|
||||||
ip=self.hostname,
|
ip=self.hostname,
|
||||||
port=self.port,
|
port=self.port,
|
||||||
)
|
)
|
||||||
self.connected = False
|
self.data_connected = False
|
||||||
else:
|
|
||||||
|
|
||||||
# reconnecting....
|
|
||||||
threading.Event().wait(0.5)
|
|
||||||
self.connect()
|
|
||||||
|
|
||||||
return b""
|
return b""
|
||||||
|
|
||||||
def get_status(self):
|
def get_status(self):
|
||||||
""" """
|
""" """
|
||||||
return "connected" if self.connected else "unknown/disconnected"
|
return "connected" if self.data_connected and self.ptt_connected else "unknown/disconnected"
|
||||||
|
|
||||||
def get_mode(self):
|
def get_mode(self):
|
||||||
""" """
|
""" """
|
||||||
try:
|
try:
|
||||||
data = self.send_command(b"m", True)
|
data = self.send_data_command(b"m", True)
|
||||||
data = data.split(b"\n")
|
data = data.split(b"\n")
|
||||||
data = data[0].decode("utf-8")
|
data = data[0].decode("utf-8")
|
||||||
if 'RPRT' not in data:
|
if 'RPRT' not in data:
|
||||||
|
@ -165,7 +218,7 @@ class radio:
|
||||||
def get_bandwidth(self):
|
def get_bandwidth(self):
|
||||||
""" """
|
""" """
|
||||||
try:
|
try:
|
||||||
data = self.send_command(b"m", True)
|
data = self.send_data_command(b"m", True)
|
||||||
data = data.split(b"\n")
|
data = data.split(b"\n")
|
||||||
data = data[1].decode("utf-8")
|
data = data[1].decode("utf-8")
|
||||||
|
|
||||||
|
@ -179,7 +232,7 @@ class radio:
|
||||||
def get_frequency(self):
|
def get_frequency(self):
|
||||||
""" """
|
""" """
|
||||||
try:
|
try:
|
||||||
data = self.send_command(b"f", True)
|
data = self.send_data_command(b"f", True)
|
||||||
data = data.decode("utf-8")
|
data = data.decode("utf-8")
|
||||||
if 'RPRT' not in data and data not in [0, '0', '']:
|
if 'RPRT' not in data and data not in [0, '0', '']:
|
||||||
with contextlib.suppress(ValueError):
|
with contextlib.suppress(ValueError):
|
||||||
|
@ -209,9 +262,39 @@ class radio:
|
||||||
"""
|
"""
|
||||||
try:
|
try:
|
||||||
if state:
|
if state:
|
||||||
self.send_command(b"T 1", False)
|
self.send_ptt_command(b"T 1", False)
|
||||||
else:
|
else:
|
||||||
self.send_command(b"T 0", False)
|
self.send_ptt_command(b"T 0", False)
|
||||||
return state
|
return state
|
||||||
except Exception:
|
except Exception:
|
||||||
return False
|
return False
|
||||||
|
|
||||||
|
def set_frequency(self, frequency):
|
||||||
|
"""
|
||||||
|
|
||||||
|
Args:
|
||||||
|
frequency:
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
|
||||||
|
"""
|
||||||
|
try:
|
||||||
|
command = bytes(f"F {frequency}", "utf-8")
|
||||||
|
self.send_data_command(command, False)
|
||||||
|
except Exception:
|
||||||
|
return False
|
||||||
|
|
||||||
|
def set_mode(self, mode):
|
||||||
|
"""
|
||||||
|
|
||||||
|
Args:
|
||||||
|
mode:
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
|
||||||
|
"""
|
||||||
|
try:
|
||||||
|
command = bytes(f"M {mode} {self.bandwidth}", "utf-8")
|
||||||
|
self.send_data_command(command, False)
|
||||||
|
except Exception:
|
||||||
|
return False
|
|
@ -30,6 +30,9 @@ class radio:
|
||||||
""" """
|
""" """
|
||||||
return None
|
return None
|
||||||
|
|
||||||
|
def set_bandwidth(self):
|
||||||
|
""" """
|
||||||
|
return None
|
||||||
def set_mode(self, mode):
|
def set_mode(self, mode):
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
@ -41,6 +44,16 @@ class radio:
|
||||||
"""
|
"""
|
||||||
return None
|
return None
|
||||||
|
|
||||||
|
def set_frequency(self, frequency):
|
||||||
|
"""
|
||||||
|
|
||||||
|
Args:
|
||||||
|
mode:
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
|
||||||
|
"""
|
||||||
|
return None
|
||||||
def get_status(self):
|
def get_status(self):
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
|
34
tnc/sock.py
34
tnc/sock.py
|
@ -31,7 +31,7 @@ import static
|
||||||
import structlog
|
import structlog
|
||||||
import ujson as json
|
import ujson as json
|
||||||
from exceptions import NoCallsign
|
from exceptions import NoCallsign
|
||||||
from queues import DATA_QUEUE_TRANSMIT, RX_BUFFER
|
from queues import DATA_QUEUE_TRANSMIT, RX_BUFFER, RIGCTLD_COMMAND_QUEUE
|
||||||
|
|
||||||
SOCKET_QUEUE = queue.Queue()
|
SOCKET_QUEUE = queue.Queue()
|
||||||
DAEMON_QUEUE = queue.Queue()
|
DAEMON_QUEUE = queue.Queue()
|
||||||
|
@ -136,7 +136,7 @@ class ThreadedTCPRequestHandler(socketserver.StreamRequestHandler):
|
||||||
# we might improve this by only processing one command or
|
# we might improve this by only processing one command or
|
||||||
# doing some kind of selection to determin which commands need to be dropped
|
# doing some kind of selection to determin which commands need to be dropped
|
||||||
# and which one can be processed during a running transmission
|
# and which one can be processed during a running transmission
|
||||||
threading.Event().wait(3)
|
threading.Event().wait(0.5)
|
||||||
|
|
||||||
# finally delete our rx buffer to be ready for new commands
|
# finally delete our rx buffer to be ready for new commands
|
||||||
data = bytes()
|
data = bytes()
|
||||||
|
@ -594,6 +594,32 @@ def process_tnc_commands(data):
|
||||||
command=received_json,
|
command=received_json,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
# SET FREQUENCY -----------------------------------------------------
|
||||||
|
if received_json["command"] == "frequency" and received_json["type"] == "set":
|
||||||
|
try:
|
||||||
|
RIGCTLD_COMMAND_QUEUE.put(["set_frequency", received_json["frequency"]])
|
||||||
|
command_response("set_frequency", True)
|
||||||
|
except Exception as err:
|
||||||
|
command_response("set_frequency", False)
|
||||||
|
log.warning(
|
||||||
|
"[SCK] Set frequency command execution error",
|
||||||
|
e=err,
|
||||||
|
command=received_json,
|
||||||
|
)
|
||||||
|
|
||||||
|
# SET MODE -----------------------------------------------------
|
||||||
|
if received_json["command"] == "mode" and received_json["type"] == "set":
|
||||||
|
try:
|
||||||
|
RIGCTLD_COMMAND_QUEUE.put(["set_mode", received_json["mode"]])
|
||||||
|
command_response("set_mode", True)
|
||||||
|
except Exception as err:
|
||||||
|
command_response("set_mode", False)
|
||||||
|
log.warning(
|
||||||
|
"[SCK] Set mode command execution error",
|
||||||
|
e=err,
|
||||||
|
command=received_json,
|
||||||
|
)
|
||||||
|
|
||||||
# exception, if JSON cant be decoded
|
# exception, if JSON cant be decoded
|
||||||
except Exception as err:
|
except Exception as err:
|
||||||
log.error("[SCK] JSON decoding error", e=err)
|
log.error("[SCK] JSON decoding error", e=err)
|
||||||
|
@ -625,12 +651,15 @@ def send_tnc_state():
|
||||||
"rx_msg_buffer_length": str(len(static.RX_MSG_BUFFER)),
|
"rx_msg_buffer_length": str(len(static.RX_MSG_BUFFER)),
|
||||||
"arq_bytes_per_minute": str(static.ARQ_BYTES_PER_MINUTE),
|
"arq_bytes_per_minute": str(static.ARQ_BYTES_PER_MINUTE),
|
||||||
"arq_bytes_per_minute_burst": str(static.ARQ_BYTES_PER_MINUTE_BURST),
|
"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_compression_factor": str(static.ARQ_COMPRESSION_FACTOR),
|
||||||
"arq_transmission_percent": str(static.ARQ_TRANSMISSION_PERCENT),
|
"arq_transmission_percent": str(static.ARQ_TRANSMISSION_PERCENT),
|
||||||
|
"speed_list": static.SPEED_LIST,
|
||||||
"total_bytes": str(static.TOTAL_BYTES),
|
"total_bytes": str(static.TOTAL_BYTES),
|
||||||
"beacon_state": str(static.BEACON_STATE),
|
"beacon_state": str(static.BEACON_STATE),
|
||||||
"stations": [],
|
"stations": [],
|
||||||
"mycallsign": str(static.MYCALLSIGN, encoding),
|
"mycallsign": str(static.MYCALLSIGN, encoding),
|
||||||
|
"mygrid": str(static.MYGRID, encoding),
|
||||||
"dxcallsign": str(static.DXCALLSIGN, encoding),
|
"dxcallsign": str(static.DXCALLSIGN, encoding),
|
||||||
"dxgrid": str(static.DXGRID, encoding),
|
"dxgrid": str(static.DXGRID, encoding),
|
||||||
"hamlib_status": static.HAMLIB_STATUS,
|
"hamlib_status": static.HAMLIB_STATUS,
|
||||||
|
@ -651,7 +680,6 @@ def send_tnc_state():
|
||||||
"frequency": heard[6],
|
"frequency": heard[6],
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
return json.dumps(output)
|
return json.dumps(output)
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -11,7 +11,7 @@ Not nice, suggestions are appreciated :-)
|
||||||
import subprocess
|
import subprocess
|
||||||
from enum import Enum
|
from enum import Enum
|
||||||
|
|
||||||
VERSION = "0.6.9-alpha.1"
|
VERSION = "0.6.11-alpha.1-exp"
|
||||||
|
|
||||||
ENABLE_EXPLORER = False
|
ENABLE_EXPLORER = False
|
||||||
|
|
||||||
|
@ -95,12 +95,14 @@ CHANNEL_BUSY: bool = False
|
||||||
ARQ_PROTOCOL_VERSION: int = 5
|
ARQ_PROTOCOL_VERSION: int = 5
|
||||||
|
|
||||||
# ARQ statistics
|
# ARQ statistics
|
||||||
|
SPEED_LIST: list = []
|
||||||
ARQ_BYTES_PER_MINUTE_BURST: int = 0
|
ARQ_BYTES_PER_MINUTE_BURST: int = 0
|
||||||
ARQ_BYTES_PER_MINUTE: int = 0
|
ARQ_BYTES_PER_MINUTE: int = 0
|
||||||
ARQ_BITS_PER_SECOND_BURST: int = 0
|
ARQ_BITS_PER_SECOND_BURST: int = 0
|
||||||
ARQ_BITS_PER_SECOND: int = 0
|
ARQ_BITS_PER_SECOND: int = 0
|
||||||
ARQ_COMPRESSION_FACTOR: int = 0
|
ARQ_COMPRESSION_FACTOR: int = 0
|
||||||
ARQ_TRANSMISSION_PERCENT: int = 0
|
ARQ_TRANSMISSION_PERCENT: int = 0
|
||||||
|
ARQ_SECONDS_UNTIL_FINISH: int = 0
|
||||||
ARQ_SPEED_LEVEL: int = 0
|
ARQ_SPEED_LEVEL: int = 0
|
||||||
TOTAL_BYTES: int = 0
|
TOTAL_BYTES: int = 0
|
||||||
# set save to folder state for allowing downloading files to local file system
|
# set save to folder state for allowing downloading files to local file system
|
||||||
|
|
Loading…
Reference in a new issue