protocol/network/gui/..

changed protocol so IRS is now the speed-level master / send ptt state via network / introduced no rig mode / disable scatter and waterfall in gui and tnc/ increased network chunk size / ...
This commit is contained in:
dj2ls 2022-02-02 21:12:16 +01:00
parent 3835e19c4a
commit 8384bf8d12
12 changed files with 542 additions and 138 deletions

View file

@ -198,7 +198,7 @@ exports.getDaemonState = function() {
// START TNC
// ` `== multi line string
exports.startTNC = function(mycall, mygrid, rx_audio, tx_audio, radiocontrol, devicename, deviceport, pttprotocol, pttport, serialspeed, data_bits, stop_bits, handshake, rigctld_ip, rigctld_port) {
exports.startTNC = function(mycall, mygrid, rx_audio, tx_audio, radiocontrol, devicename, deviceport, pttprotocol, pttport, serialspeed, data_bits, stop_bits, handshake, rigctld_ip, rigctld_port, enable_fft, enable_scatter) {
var json_command = JSON.stringify({
type: 'set',
command: 'start_tnc',
@ -217,7 +217,9 @@ exports.startTNC = function(mycall, mygrid, rx_audio, tx_audio, radiocontrol, de
stop_bits: stop_bits,
handshake: handshake,
rigctld_port: rigctld_port,
rigctld_ip: rigctld_ip
rigctld_ip: rigctld_ip,
enable_scatter: enable_scatter,
enable_fft: enable_fft
}]
})

View file

@ -41,7 +41,9 @@ var configContent = `
"serialspeed_rigctl" : "9600",
"pttprotocol_rigctl" : "USB",
"rigctld_port" : "4532",
"rigctld_ip" : "127.0.0.1"
"rigctld_ip" : "127.0.0.1",
"enable_scatter" : "False",
"enable_fft" : "False",
}
`;

View file

@ -13,6 +13,10 @@ const {
} = require('qth-locator');
const os = require('os');
// split character used for appending addiotional data to files
const split_char = '\0;'
// https://stackoverflow.com/a/26227660
var appDataFolder = process.env.APPDATA || (process.platform == 'darwin' ? process.env.HOME + '/Library/Application Support' : process.env.HOME + "/.config")
var configFolder = path.join(appDataFolder, "FreeDATA");
@ -69,6 +73,23 @@ window.addEventListener('DOMContentLoaded', () => {
document.getElementById('hamlib_handshake_advanced').value = config.handshake
document.getElementById("beaconInterval").value = config.beacon_interval
document.getElementById("beaconInterval").value = config.enable_scatter
document.getElementById("beaconInterval").value = config.enable_fft
if(config.enable_scatter == 'True'){
document.getElementById("scatterSwitch").checked = true
} else {
document.getElementById("scatterSwitch").checked = false
}
if(config.enable_fft == 'True'){
document.getElementById("fftSwitch").checked = true
} else {
document.getElementById("fftSwitch").checked = false
}
if (config.spectrum == 'waterfall') {
@ -87,8 +108,26 @@ window.addEventListener('DOMContentLoaded', () => {
}
// radio control element
if (config.radiocontrol == 'rigctl') {
if (config.radiocontrol == 'direct') {
document.getElementById("radio-control-switch0").checked = false
document.getElementById("radio-control-switch1").checked = true
document.getElementById("radio-control-switch2").checked = false
document.getElementById("radio-control-switch3").checked = false
document.getElementById("radio-control-rigctl").style.visibility = 'hidden';
document.getElementById("radio-control-rigctld").style.visibility = 'hidden';
document.getElementById("radio-control-rigctl").style.display = 'none';
document.getElementById("radio-control-rigctld").style.display = 'none';
document.getElementById("radio-control-direct").style.display = 'block';
document.getElementById("radio-control-direct").style.visibility = 'visible';
document.getElementById("radio-control-direct").style.height = '100%';
} else if (config.radiocontrol == 'rigctl') {
document.getElementById("radio-control-switch0").checked = false
document.getElementById("radio-control-switch1").checked = false
document.getElementById("radio-control-switch2").checked = true
document.getElementById("radio-control-switch3").checked = false
@ -104,6 +143,7 @@ window.addEventListener('DOMContentLoaded', () => {
} else if (config.radiocontrol == 'rigctld') {
document.getElementById("radio-control-switch0").checked = false
document.getElementById("radio-control-switch1").checked = false
document.getElementById("radio-control-switch2").checked = false
document.getElementById("radio-control-switch3").checked = true
@ -118,8 +158,9 @@ window.addEventListener('DOMContentLoaded', () => {
document.getElementById("radio-control-rigctld").style.height = '100%';
} else {
document.getElementById("radio-control-switch1").checked = true
document.getElementById("radio-control-switch0").checked = true
document.getElementById("radio-control-switch1").checked = false
document.getElementById("radio-control-switch2").checked = false
document.getElementById("radio-control-switch3").checked = false
@ -154,6 +195,21 @@ window.addEventListener('DOMContentLoaded', () => {
// on click radio control toggle view
// disabled
document.getElementById("radio-control-switch0").addEventListener("click", () => {
document.getElementById("radio-control-rigctl").style.visibility = 'hidden';
document.getElementById("radio-control-rigctld").style.visibility = 'hidden';
document.getElementById("radio-control-rigctl").style.display = 'none';
document.getElementById("radio-control-rigctld").style.display = 'none';
document.getElementById("radio-control-direct").style.display = 'block';
document.getElementById("radio-control-direct").style.visibility = 'visible';
document.getElementById("radio-control-direct").style.height = '100%';
config.radiocontrol = 'disabled'
fs.writeFileSync(configPath, JSON.stringify(config, null, 2));
});
// direct
document.getElementById("radio-control-switch1").addEventListener("click", () => {
document.getElementById("radio-control-rigctl").style.visibility = 'hidden';
@ -304,6 +360,33 @@ window.addEventListener('DOMContentLoaded', () => {
fs.writeFileSync(configPath, JSON.stringify(config, null, 2));
sock.startBeacon(interval)
});
// sendscatter Switch clicked
document.getElementById("scatterSwitch").addEventListener("click", () => {
console.log(document.getElementById("scatterSwitch").checked)
if(document.getElementById("scatterSwitch").checked == true){
config.enable_scatter = "True"
} else {
config.enable_scatter = "False"
}
fs.writeFileSync(configPath, JSON.stringify(config, null, 2));
});
// sendfft Switch clicked
document.getElementById("fftSwitch").addEventListener("click", () => {
if(document.getElementById("fftSwitch").checked == true){
config.enable_fft = "True"
} else {
config.enable_fft = "False"
}
fs.writeFileSync(configPath, JSON.stringify(config, null, 2));
});
// Stop beacon button clicked
document.getElementById("stopBeacon").addEventListener("click", () => {
@ -314,21 +397,22 @@ window.addEventListener('DOMContentLoaded', () => {
document.getElementById("startTNC").addEventListener("click", () => {
var deviceid_rigctl = document.getElementById("hamlib_deviceid_rigctl").value
var deviceport_rigctl = document.getElementById("hamlib_deviceport_rigctl").value
var serialspeed_rigctl = document.getElementById("hamlib_serialspeed_rigctl").value
var pttprotocol_rigctl = document.getElementById("hamlib_ptt_protocol_rigctl").value
var rigctld_ip = document.getElementById("hamlib_rigctld_ip").value
var rigctld_port = document.getElementById("hamlib_rigctld_port").value
var deviceid_rigctl = document.getElementById("hamlib_deviceid_rigctl").value
var deviceport_rigctl = document.getElementById("hamlib_deviceport_rigctl").value
var serialspeed_rigctl = document.getElementById("hamlib_serialspeed_rigctl").value
var pttprotocol_rigctl = document.getElementById("hamlib_ptt_protocol_rigctl").value
var rigctld_ip = document.getElementById("hamlib_rigctld_ip").value
var rigctld_port = document.getElementById("hamlib_rigctld_port").value
var deviceid = document.getElementById("hamlib_deviceid").value
var deviceport = document.getElementById("hamlib_deviceport").value
var serialspeed = document.getElementById("hamlib_serialspeed").value
var pttprotocol = document.getElementById("hamlib_ptt_protocol").value
var deviceid = document.getElementById("hamlib_deviceid").value
var deviceport = document.getElementById("hamlib_deviceport").value
var serialspeed = document.getElementById("hamlib_serialspeed").value
var pttprotocol = document.getElementById("hamlib_ptt_protocol").value
var mycall = document.getElementById("myCall").value
mycall = mycall.toUpperCase()
var mygrid = document.getElementById("myGrid").value
var rx_audio = document.getElementById("audio_input_selectbox").value
var tx_audio = document.getElementById("audio_output_selectbox").value
@ -338,6 +422,20 @@ window.addEventListener('DOMContentLoaded', () => {
var stop_bits = document.getElementById('hamlib_stopbits_advanced').value
var handshake = document.getElementById('hamlib_handshake_advanced').value
if (document.getElementById("scatterSwitch").checked == true){
var enable_scatter = "True"
} else {
var enable_scatter = "False"
}
if (document.getElementById("fftSwitch").checked == true){
var enable_fft = "True"
} else {
var enable_fft = "False"
}
// loop through audio device list and select
for(i = 0; i < document.getElementById("audio_input_selectbox").length; i++) {
@ -376,7 +474,8 @@ window.addEventListener('DOMContentLoaded', () => {
config.rigctld_port = rigctld_port
config.rigctld_ip = rigctld_ip
config.deviceport_rigctl = deviceport_rigctl
config.enable_scatter = enable_scatter
config.enable_fft = enable_fft
fs.writeFileSync(configPath, JSON.stringify(config, null, 2));
@ -405,11 +504,14 @@ window.addEventListener('DOMContentLoaded', () => {
} else if (document.getElementById("radio-control-switch3").checked) {
var radiocontrol = 'rigctld'
} else {
} else if (document.getElementById("radio-control-switch1").checked) {
var radiocontrol = 'direct'
} else {
var radiocontrol = 'disabled'
}
daemon.startTNC(mycall, mygrid, rx_audio, tx_audio, radiocontrol, deviceid, deviceport, pttprotocol, pttport, serialspeed, data_bits, stop_bits, handshake, rigctld_ip, rigctld_port)
daemon.startTNC(mycall, mygrid, rx_audio, tx_audio, radiocontrol, deviceid, deviceport, pttprotocol, pttport, serialspeed, data_bits, stop_bits, handshake, rigctld_ip, rigctld_port, enable_fft, enable_scatter)
})
@ -465,13 +567,13 @@ window.addEventListener('DOMContentLoaded', () => {
var handshake = document.getElementById("hamlib_handshake_advanced").value
var pttport = document.getElementById("hamlib_ptt_port_advanced").value
var rigctld_ip = document.getElementById("hamlib_rigctld_ip").value
var rigctld_port = document.getElementById("hamlib_rigctld_port").value
var rigctld_ip = document.getElementById("hamlib_rigctld_ip").value
var rigctld_port = document.getElementById("hamlib_rigctld_port").value
var deviceid = document.getElementById("hamlib_deviceid").value
var deviceport = document.getElementById("hamlib_deviceport").value
var serialspeed = document.getElementById("hamlib_serialspeed").value
var pttprotocol = document.getElementById("hamlib_ptt_protocol").value
var deviceid = document.getElementById("hamlib_deviceid").value
var deviceport = document.getElementById("hamlib_deviceport").value
var serialspeed = document.getElementById("hamlib_serialspeed").value
var pttprotocol = document.getElementById("hamlib_ptt_protocol").value
// overriding settings for rigctl / direct
@ -485,8 +587,10 @@ window.addEventListener('DOMContentLoaded', () => {
} else if (document.getElementById("radio-control-switch3").checked) {
var radiocontrol = 'rigctld'
} else {
} else if (document.getElementById("radio-control-switch1").checked) {
var radiocontrol = 'direct'
} else {
var radiocontrol = 'disabled'
}
@ -507,14 +611,17 @@ window.addEventListener('DOMContentLoaded', () => {
var fileList = document.getElementById("dataModalFile").files;
var reader = new FileReader();
//reader.readAsBinaryString(fileList[0]);
reader.readAsDataURL(fileList[0]);
reader.readAsBinaryString(fileList[0]);
//reader.readAsDataURL(fileList[0]);
reader.onload = function(e) {
// binary data
var data = e.target.result
console.log(data)
let Data = {
command: "send_file",
@ -1063,19 +1170,21 @@ ipcRenderer.on('action-update-daemon-state', (event, arg) => {
// OPERATING SYSTEM
//document.getElementById("operating_system").innerHTML = "OS " + os.type()
/*
// PYTHON VERSION
document.getElementById("python_version").innerHTML = "Python " + arg.python_version
document.getElementById("python_version").className = "btn btn-sm btn-success";
*/
/*
// HAMLIB VERSION
document.getElementById("hamlib_version").innerHTML = "Hamlib " + arg.hamlib_version
document.getElementById("hamlib_version").className = "btn btn-sm btn-success";
*/
/*
// NODE VERSION
document.getElementById("node_version").innerHTML = "Node " + process.version
document.getElementById("node_version").className = "btn btn-sm btn-success";
*/
// UPDATE AUDIO INPUT
if (arg.tnc_running_state == "stopped") {
@ -1343,9 +1452,20 @@ ipcRenderer.on('action-update-rx-buffer', (event, arg) => {
*/
console.log(arg.data)
var encoded_data = atob(arg.data[i]['data'])
var splitted_data = encoded_data.split(split_char)
console.log(splitted_data)
var fileName = document.createElement("td");
var fileNameText = document.createElement('span');
var fileNameString = arg.data[i]['data'][0]['fn']
//var fileNameString = arg.data[i]['data'][0]['fn']
var fileNameString = splitted_data[1]
fileNameText.innerText = fileNameString
fileName.appendChild(fileNameText);
@ -1373,15 +1493,17 @@ ipcRenderer.on('action-update-rx-buffer', (event, arg) => {
// write file to data folder
var base64String = arg.data[i]['data'][0]['d']
////var base64String = arg.data[i]['data'][0]['d']
// remove header from base64 String
// https://www.codeblocq.com/2016/04/Convert-a-base64-string-to-a-file-in-Node/
var base64Data = base64String.split(';base64,').pop()
////var base64Data = base64String.split(';base64,').pop()
//write data to file
var base64Data = splitted_data[4]
var receivedFile = path.join(receivedFilesFolder, fileNameString);
console.log(receivedFile)
require("fs").writeFile(receivedFile, base64Data, 'base64', function(err) {
require("fs").writeFile(receivedFile, base64Data, 'binary', function(err) {
//require("fs").writeFile(receivedFile, base64Data, 'base64', function(err) {
console.log(err);
});
}

View file

@ -13,6 +13,9 @@ const config = require(configPath);
var client = new net.Socket();
var socketchunk = ''; // Current message, per connection.
// split character
const split_char = '\0;'
// globals for getting new data only if available so we are saving bandwith
var rxBufferLengthTnc = 0
var rxBufferLengthGui = 0
@ -229,15 +232,20 @@ client.on('data', function(socketdata) {
for (i = 0; i < data['data-array'].length; i++) {
try{
if(data['data-array'][i]['data'][0]['dt'] == 'f'){
dataArray.push(data['data-array'][i])
var encoded_data = atob(data['data-array'][i]['data'])
var splitted_data = encoded_data.split(split_char)
if(splitted_data[0] == 'f'){
dataArray.push(data['data-array'][i])
//dataArray.push(splitted_data)
}
if(data['data-array'][i]['data'][0]['dt'] == 'm'){
if(splitted_data[0] == 'm'){
messageArray.push(data['data-array'][i])
//messageArray.push(splitted_data)
}
} catch (e) {
console.log(e)
}
@ -302,7 +310,22 @@ exports.sendCQ = function() {
// Send File
exports.sendFile = function(dxcallsign, mode, frames, filename, filetype, data, checksum) {
command = '{"type" : "arq", "command" : "send_file", "parameter" : [{"dxcallsign" : "' + dxcallsign + '", "mode" : "' + mode + '", "n_frames" : "' + frames + '", "filename" : "' + filename + '", "filetype" : "' + filetype + '", "data" : "' + data + '", "checksum" : "' + checksum + '"}]}'
console.log(data)
console.log(filetype)
console.log(filename)
var datatype = "f"
//data = data.split('base64,')
//data = data[1]
data = datatype + split_char + filename + split_char + filetype + split_char + checksum + split_char + data
console.log(data)
console.log(btoa(data))
data = btoa(data)
//command = '{"type" : "arq", "command" : "send_file", "parameter" : [{"dxcallsign" : "' + dxcallsign + '", "mode" : "' + mode + '", "n_frames" : "' + frames + '", "filename" : "' + filename + '", "filetype" : "' + filetype + '", "data" : "' + data + '", "checksum" : "' + checksum + '"}]}'
command = '{"type" : "arq", "command" : "send_raw", "parameter" : [{"dxcallsign" : "' + dxcallsign + '", "mode" : "' + mode + '", "n_frames" : "' + frames + '", "data" : "' + data + '"}]}'
writeTncCommand(command)
}

View file

@ -217,7 +217,7 @@
<path d="M11.536 14.01A8.473 8.473 0 0 0 14.026 8a8.473 8.473 0 0 0-2.49-6.01l-.708.707A7.476 7.476 0 0 1 13.025 8c0 2.071-.84 3.946-2.197 5.303l.708.707z" />
<path d="M10.121 12.596A6.48 6.48 0 0 0 12.025 8a6.48 6.48 0 0 0-1.904-4.596l-.707.707A5.483 5.483 0 0 1 11.025 8a5.483 5.483 0 0 1-1.61 3.89l.706.706z" />
<path d="M10.025 8a4.486 4.486 0 0 1-1.318 3.182L8 10.475A3.489 3.489 0 0 0 9.025 8c0-.966-.392-1.841-1.025-2.475l.707-.707A4.486 4.486 0 0 1 10.025 8zM7 4a.5.5 0 0 0-.812-.39L3.825 5.5H1.5A.5.5 0 0 0 1 6v4a.5.5 0 0 0 .5.5h2.325l2.363 1.89A.5.5 0 0 0 7 12V4zM4.312 6.39 6 5.04v5.92L4.312 9.61A.5.5 0 0 0 4 9.5H2v-3h2a.5.5 0 0 0 .312-.11z" />
</svg> <strong>AUDIO SETTINGS</strong>
</svg> <strong>AUDIO</strong>
</div>
<div class="card-body p-2 mb-1">
<div class="input-group input-group-sm mb-1"> <span class="input-group-text" id="basic-addon1">
@ -249,8 +249,18 @@
<svg xmlns="http://www.w3.org/2000/svg" width="25" height="25" fill="currentColor" class="bi bi-projector" viewBox="0 0 16 16">
<path d="M14 7.5a1.5 1.5 0 1 1-3 0 1.5 1.5 0 0 1 3 0ZM2.5 6a.5.5 0 0 0 0 1h4a.5.5 0 0 0 0-1h-4Zm0 2a.5.5 0 0 0 0 1h4a.5.5 0 0 0 0-1h-4Z" />
<path fill-rule="evenodd" d="M0 6a2 2 0 0 1 2-2h12a2 2 0 0 1 2 2v3a2 2 0 0 1-2 2 1 1 0 0 1-1 1h-1a1 1 0 0 1-1-1H5a1 1 0 0 1-1 1H3a1 1 0 0 1-1-1 2 2 0 0 1-2-2V6Zm2-1h12a1 1 0 0 1 1 1v3a1 1 0 0 1-1 1H2a1 1 0 0 1-1-1V6a1 1 0 0 1 1-1Z" />
</svg> <strong>RADIO SETTINGS</strong>
</svg> <strong>RADIO</strong>
<div class="btn-group btn-group-sm" role="group" aria-label="waterfall-scatter-switch toggle button group">
<input type="radio" class="btn-check" name="radio-control-switch" id="radio-control-switch0" autocomplete="off" checked>
<label class="btn btn-sm btn-outline-secondary" for="radio-control-switch0">
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="currentColor" class="bi bi-x-circle" viewBox="0 0 16 16">
<path d="M8 15A7 7 0 1 1 8 1a7 7 0 0 1 0 14zm0 1A8 8 0 1 0 8 0a8 8 0 0 0 0 16z"/>
<path d="M4.646 4.646a.5.5 0 0 1 .708 0L8 7.293l2.646-2.647a.5.5 0 0 1 .708.708L8.707 8l2.647 2.646a.5.5 0 0 1-.708.708L8 8.707l-2.646 2.647a.5.5 0 0 1-.708-.708L7.293 8 4.646 5.354a.5.5 0 0 1 0-.708z"/>
</svg>
</label>
<input type="radio" class="btn-check" name="radio-control-switch" id="radio-control-switch1" autocomplete="off" checked>
<label class="btn btn-sm btn-outline-secondary" for="radio-control-switch1"><strong>direct</strong>
</label>
@ -698,12 +708,25 @@
<svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" fill="currentColor" class="bi bi-motherboard" viewBox="0 0 16 16">
<path d="M11.5 2a.5.5 0 0 1 .5.5v7a.5.5 0 0 1-1 0v-7a.5.5 0 0 1 .5-.5Zm2 0a.5.5 0 0 1 .5.5v7a.5.5 0 0 1-1 0v-7a.5.5 0 0 1 .5-.5Zm-10 8a.5.5 0 0 0 0 1h6a.5.5 0 0 0 0-1h-6Zm0 2a.5.5 0 0 0 0 1h6a.5.5 0 0 0 0-1h-6ZM5 3a1 1 0 0 0-1 1h-.5a.5.5 0 0 0 0 1H4v1h-.5a.5.5 0 0 0 0 1H4a1 1 0 0 0 1 1v.5a.5.5 0 0 0 1 0V8h1v.5a.5.5 0 0 0 1 0V8a1 1 0 0 0 1-1h.5a.5.5 0 0 0 0-1H9V5h.5a.5.5 0 0 0 0-1H9a1 1 0 0 0-1-1v-.5a.5.5 0 0 0-1 0V3H6v-.5a.5.5 0 0 0-1 0V3Zm0 1h3v3H5V4Zm6.5 7a.5.5 0 0 0-.5.5v1a.5.5 0 0 0 .5.5h2a.5.5 0 0 0 .5-.5v-1a.5.5 0 0 0-.5-.5h-2Z" />
<path d="M1 2a2 2 0 0 1 2-2h11a2 2 0 0 1 2 2v11a2 2 0 0 1-2 2H3a2 2 0 0 1-2-2v-2H.5a.5.5 0 0 1-.5-.5v-1A.5.5 0 0 1 .5 9H1V8H.5a.5.5 0 0 1-.5-.5v-1A.5.5 0 0 1 .5 6H1V5H.5a.5.5 0 0 1-.5-.5v-2A.5.5 0 0 1 .5 2H1Zm1 11a1 1 0 0 0 1 1h11a1 1 0 0 0 1-1V2a1 1 0 0 0-1-1H3a1 1 0 0 0-1 1v11Z" />
</svg> <strong>SYSTEM STATUS</strong>
</svg> <strong>SETTINGS</strong>
</div>
<div class="card-body p-2 mb-1">
<button class="btn btn-secondary btn-sm" id="python_version" type="button" disabled>Python</button>
<button class="btn btn-secondary btn-sm" id="node_version" type="button" disabled>Node</button>
<button class="btn btn-secondary btn-sm" id="hamlib_version" type="button" disabled>Hamlib</button>
<div class="form-check form-switch form-check-inline">
<input class="form-check-input" type="checkbox" id="fftSwitch">
<label class="form-check-label" for="fftSwitch">Waterfall</label>
</div>
<div class="form-check form-switch form-check-inline">
<input class="form-check-input" type="checkbox" id="scatterSwitch">
<label class="form-check-label" for="scatterSwitch">Scatter</label>
</div>
<!--<button class="btn btn-secondary btn-sm" id="python_version" type="button" disabled>Python</button>-->
<!--<button class="btn btn-secondary btn-sm" id="node_version" type="button" disabled>Node</button>-->
<!--<button class="btn btn-secondary btn-sm" id="hamlib_version" type="button" disabled>Hamlib</button>-->
<!--<button class="btn btn-secondary btn-sm" id="operating_system" type="button" disabled>OS</button>-->
<button class="btn btn-secondary btn-sm" id="cpu_load_button" type="button" disabled>
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="currentColor" class="bi bi-cpu" viewBox="0 0 16 16">

View file

@ -87,11 +87,18 @@ class DAEMON():
# data[13] radiocontrol
# data[14] rigctld_ip
# data[15] rigctld_port
# data[16] send_scatter
# data[17] send_fft
if data[0] == 'STARTTNC':
structlog.get_logger("structlog").warning("[DMN] Starting TNC", rig=data[5], port=data[6])
# list of parameters, necessary for running subprocess command as a list
options = []
options.append('--port')
options.append(str(static.DAEMONPORT - 1))
options.append('--mycall')
options.append(data[1])
@ -136,7 +143,13 @@ class DAEMON():
options.append('--rigctld_port')
options.append(data[15])
if data[16] == 'True':
options.append('--scatter')
if data[17] == 'True':
options.append('--fft')
# try running tnc from binary, else run from source
# this helps running the tnc in a developer environment
try:
@ -198,7 +211,7 @@ class DAEMON():
elif radiocontrol == 'rigctld':
import rigctld as rig
else:
raise NotImplementedError
import rigdummy as rig
hamlib = rig.radio()
hamlib.open_rig(devicename=devicename, deviceport=deviceport, hamlib_ptt_type=pttprotocol, serialspeed=serialspeed, pttport=pttport, data_bits=data_bits, stop_bits=stop_bits, handshake=handshake, rigctld_ip=rigctld_ip, rigctld_port = rigctld_port)

View file

@ -20,6 +20,7 @@ import codec2
import queue
import sock
import uuid
import base64
TESTMODE = False
@ -47,14 +48,19 @@ class DATA():
self.data_frame_bof = b'BOF' # 2 bytes for the BOF End of File indicator in a data frame
self.data_frame_eof = b'EOF' # 2 bytes for the EOF End of File indicator in a data frame
self.mode_list = [14,14,14,12,10] # mode list of available modes, each mode will be used 2times per speed level
#self.mode_list = [14,14,14,12,10] # mode list of available modes, each mode will be used 2times per speed level
self.mode_list = [14,12,10] # mode list of available modes, each mode will be used 2times per speed level
self.time_list = [3, 6, 7] # list for time to wait for correspinding mode in seconds
self.speed_level = len(self.mode_list) - 1 # speed level for selecting mode
self.is_IRS = False
self.burst_nack = False
self.burst_nack_counter = 0
self.frame_received_counter = 0
self.rx_frame_bof_received = False
self.rx_frame_eof_received = False
self.transmission_timeout = 360 # transmission timeout in seco
self.transmission_timeout = 120 # transmission timeout in seconds
worker_thread_transmit = threading.Thread(target=self.worker_transmit, name="worker thread transmit")
worker_thread_transmit.start()
@ -176,9 +182,15 @@ class DATA():
# FRAME NACK
elif frametype == 63:
structlog.get_logger("structlog").debug("FRAME NOT ACK RECEIVED....")
structlog.get_logger("structlog").debug("FRAME NACK RECEIVED....")
self.frame_nack_received(bytes_out[:-2])
# BURST NACK
elif frametype == 64:
structlog.get_logger("structlog").debug("BURST NACK RECEIVED....")
self.burst_nack_received(bytes_out[:-2])
# CQ FRAME
elif frametype == 200:
structlog.get_logger("structlog").debug("CQ RECEIVED....")
@ -222,7 +234,7 @@ class DATA():
# ARQ STOP TRANSMISSION
elif frametype == 227:
structlog.get_logger("structlog").debug("ARQ received stop transmis")
structlog.get_logger("structlog").debug("ARQ received stop transmission")
self.received_stop_transmission()
# ARQ CONNECT ACK / KEEP ALIVE
@ -300,20 +312,33 @@ class DATA():
# lets check if we didnt receive a BOF and EOF yet to avoid sending ack frames if we already received all data
if not self.rx_frame_bof_received and not self.rx_frame_eof_received and data_in.find(self.data_frame_eof) < 0:
self.frame_received_counter += 1
if self.frame_received_counter >= 2:
self.frame_received_counter = 0
self.speed_level += 1
if self.speed_level >= len(self.mode_list):
self.speed_level = len(self.mode_list) - 1
# updated modes we are listening to
self.set_listening_modes()
# create an ack frame
ack_frame = bytearray(14)
ack_frame[:1] = bytes([60])
ack_frame[1:3] = static.DXCALLSIGN_CRC
ack_frame[3:5] = static.MYCALLSIGN_CRC
ack_frame[5:6] = bytes([int(snr)])
ack_frame[6:7] = bytes([int(self.speed_level)])
# and transmit it
txbuffer = [ack_frame]
structlog.get_logger("structlog").info("[TNC] ARQ | RX | ACK")
structlog.get_logger("structlog").info("[TNC] ARQ | RX | SENDING ACK")
static.TRANSMITTING = True
modem.MODEM_TRANSMIT_QUEUE.put([14,1,0,txbuffer])
# wait while transmitting
while static.TRANSMITTING:
time.sleep(0.01)
self.calculate_transfer_rate_rx(self.rx_start_of_transmission, len(static.RX_FRAME_BUFFER))
@ -392,11 +417,13 @@ class DATA():
data_frame_decompressed = zlib.decompress(data_frame)
static.ARQ_COMPRESSION_FACTOR = len(data_frame_decompressed) / len(data_frame)
data_frame = data_frame_decompressed
# decode to utf-8 string
data_frame = data_frame.decode("utf-8")
#data_frame = data_frame.decode("utf-8")
# decode json objects from data frame to inspect if we received a file or message
rawdata = json.loads(data_frame)
#rawdata = json.loads(data_frame)
'''
if datatype is a file, we append to RX_BUFFER, which contains files only
dt = datatype
@ -412,10 +439,12 @@ class DATA():
#else:
# datatype = "raw"
uniqueid = str(uuid.uuid4())
static.RX_BUFFER.append([uniqueid, int(time.time()), static.DXCALLSIGN,static.DXGRID, data_frame])
print(static.RX_BUFFER)
jsondata = {"arq":"received", "uuid" : static.RX_BUFFER[i][0], "timestamp": static.RX_BUFFER[i][1], "dxcallsign": str(static.RX_BUFFER[i][2], 'utf-8'), "dxgrid": str(static.RX_BUFFER[i][3], 'utf-8'), "data": [rawdata]}
uniqueid = str(uuid.uuid4())
base64_data = base64.b64encode(data_frame)
base64_data = base64_data.decode("utf-8")
static.RX_BUFFER.append([uniqueid, int(time.time()), static.DXCALLSIGN,static.DXGRID, base64_data])
jsondata = {"arq":"received", "uuid" : static.RX_BUFFER[i][0], "timestamp": static.RX_BUFFER[i][1], "dxcallsign": str(static.RX_BUFFER[i][2], 'utf-8'), "dxgrid": str(static.RX_BUFFER[i][3], 'utf-8'), "data": base64_data}
data_out = json.dumps(jsondata)
sock.SOCKET_QUEUE.put(data_out)
static.INFO.append("ARQ;RECEIVING;SUCCESS")
@ -425,7 +454,8 @@ class DATA():
ack_frame[:1] = bytes([61])
ack_frame[1:3] = static.DXCALLSIGN_CRC
ack_frame[3:5] = static.MYCALLSIGN_CRC
ack_frame[5:6] = bytes([int(snr)])
ack_frame[6:7] = bytes([int(self.speed_level)])
# TRANSMIT ACK FRAME FOR BURST
structlog.get_logger("structlog").info("[TNC] ARQ | RX | SENDING DATA FRAME ACK", snr=static.SNR, crc=data_frame_crc.hex())
txbuffer = [ack_frame]
@ -448,7 +478,9 @@ class DATA():
nack_frame[:1] = bytes([63])
nack_frame[1:3] = static.DXCALLSIGN_CRC
nack_frame[3:5] = static.MYCALLSIGN_CRC
nack_frame[5:6] = bytes([int(snr)])
nack_frame[6:7] = bytes([int(self.speed_level)])
# TRANSMIT NACK FRAME FOR BURST
txbuffer = [nack_frame]
static.TRANSMITTING = True
@ -593,8 +625,8 @@ class DATA():
time.sleep(0.01)
# after transmission finished wait for an ACK or RPT frame
burstacktimeout = time.time() + BURST_ACK_TIMEOUT_SECONDS
while not self.burst_ack and not self.rpt_request_received and not self.data_frame_ack_received and time.time() < burstacktimeout and static.ARQ_STATE:
burstacktimeout = time.time() + BURST_ACK_TIMEOUT_SECONDS + 100
while not self.burst_ack and not self.burst_nack and not self.rpt_request_received and not self.data_frame_ack_received and time.time() < burstacktimeout and static.ARQ_STATE:
time.sleep(0.01)
#structlog.get_logger("structlog").debug("[TNC] waiting for ack", burst_ack=self.burst_ack, frame_ack = self.data_frame_ack_received, arq_state = static.ARQ_STATE, overflows=static.BUFFER_OVERFLOW_COUNTER)
@ -605,6 +637,10 @@ class DATA():
self.tx_n_retry_of_burst = 0 # reset retries
break #break retry loop
if self.burst_nack:
self.burst_nack = False #reset nack state
# not yet implemented
if self.rpt_request_received:
pass
@ -658,15 +694,34 @@ class DATA():
def burst_ack_received(self, data_in:bytes):
# increase speed level if we received a burst ack
self.speed_level += 1
if self.speed_level >= len(self.mode_list)-1:
self.speed_level = len(self.mode_list)-1
#self.speed_level += 1
#if self.speed_level >= len(self.mode_list)-1:
# self.speed_level = len(self.mode_list)-1
# only process data if we are in ARQ and BUSY state
if static.ARQ_STATE:
self.burst_ack = True # Force data loops of TNC to stop and continue with next frame
self.data_channel_last_received = int(time.time()) # we need to update our timeout timestamp
self.burst_ack_snr= int.from_bytes(bytes(data_in[3:4]), "big")
self.burst_ack_snr= int.from_bytes(bytes(data_in[5:6]), "big")
self.speed_level= int.from_bytes(bytes(data_in[6:7]), "big")
print(self.speed_level)
self.burst_nack_counter = 0
# signalling frames received
def burst_nack_received(self, data_in:bytes):
# increase speed level if we received a burst ack
#self.speed_level += 1
#if self.speed_level >= len(self.mode_list)-1:
# self.speed_level = len(self.mode_list)-1
# only process data if we are in ARQ and BUSY state
if static.ARQ_STATE:
self.burst_nack = True # Force data loops of TNC to stop and continue with next frame
self.data_channel_last_received = int(time.time()) # we need to update our timeout timestamp
self.burst_ack_snr= int.from_bytes(bytes(data_in[5:6]), "big")
self.speed_level= int.from_bytes(bytes(data_in[6:7]), "big")
self.burst_nack_counter += 1
print(self.speed_level)
def frame_ack_received(self):
@ -728,6 +783,8 @@ class DATA():
def arq_open_data_channel(self, mode:int, n_frames_per_burst:int):
self.is_IRS = False
DATA_CHANNEL_MAX_RETRIES = 5 # N attempts for connecting to another station
self.data_channel_last_received = int(time.time())
@ -785,6 +842,8 @@ class DATA():
def arq_received_data_channel_opener(self, data_in:bytes):
self.is_IRS = True
static.INFO.append("DATACHANNEL;RECEIVEDOPENER")
static.DXCALLSIGN_CRC = bytes(data_in[3:5])
static.DXCALLSIGN = bytes(data_in[5:11]).rstrip(b'\x00')
@ -792,6 +851,9 @@ class DATA():
n_frames_per_burst = int.from_bytes(bytes(data_in[13:14]), "big")
mode = int.from_bytes(bytes(data_in[11:12]), "big")
# updated modes we are listening to
self.set_listening_modes()
'''
# set modes we want to listening to
mode_name = codec2.freedv_get_mode_name_by_value(mode)
if mode_name == 'datac1':
@ -799,9 +861,10 @@ class DATA():
elif mode_name == 'datac3':
modem.RECEIVE_DATAC3 = True
elif mode_name == 'allmodes':
modem.RECEIVE_DATAC1 = True
modem.RECEIVE_DATAC3 = True
pass
#modem.RECEIVE_DATAC1 = True
#modem.RECEIVE_DATAC3 = True
'''
@ -1086,7 +1149,13 @@ class DATA():
# reset buffer overflow counter
static.BUFFER_OVERFLOW_COUNTER = [0,0,0]
self.is_IRS = False
self.burst_nack = False
self.burst_nack_counter = 0
self.frame_received_counter = 0
self.speed_level = len(self.mode_list) - 1
def arq_reset_ack(self,state:bool):
self.burst_ack = state
@ -1094,6 +1163,18 @@ class DATA():
self.data_frame_ack_received = state
def set_listening_modes(self):
# set modes we want to listening to
mode_name = codec2.freedv_get_mode_name_by_value(self.mode_list[self.speed_level])
if mode_name == 'datac1':
modem.RECEIVE_DATAC1 = True
elif mode_name == 'datac3':
modem.RECEIVE_DATAC3 = True
elif mode_name == 'allmodes':
modem.RECEIVE_DATAC1 = True
modem.RECEIVE_DATAC3 = True
# ------------------------- WATCHDOG FUNCTIONS FOR TIMER
def watchdog(self):
"""
@ -1102,9 +1183,52 @@ class DATA():
watchdog master function. Frome here we call the watchdogs
"""
while True:
time.sleep(0.5)
time.sleep(0.1)
self.data_channel_keep_alive_watchdog()
self.burst_watchdog()
def burst_watchdog(self):
'''
# ISS SIDE WE ALSO NEED TO CHECK TIME SO WE ARE SENDING IN CORRECT MODE IF WE MISSED A NACK FRAME
if static.ARQ_STATE and static.TNC_STATE == 'BUSY' and not self.is_IRS:
if self.data_channel_last_received + self.time_list[self.speed_level] < time.time():
self.speed_level -= 1
if self.speed_level <= 0:
self.speed_level = 0
self.data_channel_last_received = time.time()
'''
# IRS SIDE
if static.ARQ_STATE and static.TNC_STATE == 'BUSY' and self.is_IRS:
if self.data_channel_last_received + self.time_list[self.speed_level] > time.time():
print((self.data_channel_last_received + self.time_list[self.speed_level])-time.time())
#pass
else:
print("TIMEOUT")
self.frame_received_counter = 0
self.speed_level -= 1
if self.speed_level <= 0:
self.speed_level = 0
# updated modes we are listening to
self.set_listening_modes()
# BUILDING NACK FRAME FOR DATA FRAME
burst_nack_frame = bytearray(14)
burst_nack_frame[:1] = bytes([64])
burst_nack_frame[1:3] = static.DXCALLSIGN_CRC
burst_nack_frame[3:5] = static.MYCALLSIGN_CRC
burst_nack_frame[5:6] = bytes([0])
burst_nack_frame[6:7] = bytes([int(self.speed_level)])
# TRANSMIT NACK FRAME FOR BURST
txbuffer = [burst_nack_frame]
static.TRANSMITTING = True
modem.MODEM_TRANSMIT_QUEUE.put([14,1,0,txbuffer])
# wait while transmitting
#while static.TRANSMITTING:
# #time.sleep(0.01)
# self.data_channel_last_received = time.time()
self.data_channel_last_received = time.time()
def data_channel_keep_alive_watchdog(self):
"""

View file

@ -37,9 +37,11 @@ if __name__ == '__main__':
PARSER.add_argument('--data_bits', dest="hamlib_data_bits", default="8", help="Hamlib data bits", type=str)
PARSER.add_argument('--stop_bits', dest="hamlib_stop_bits", default="1", help="Hamlib stop bits", type=str)
PARSER.add_argument('--handshake', dest="hamlib_handshake", default="None", help="Hamlib handshake", type=str)
PARSER.add_argument('--radiocontrol', dest="hamlib_radiocontrol", default="direct", help="Set how you want to control your radio")
PARSER.add_argument('--radiocontrol', dest="hamlib_radiocontrol", default="disabled", help="Set how you want to control your radio")
PARSER.add_argument('--rigctld_port', dest="rigctld_port", default="direct", help="Set rigctld port")
PARSER.add_argument('--rigctld_ip', dest="rigctld_ip", default="direct", help="Set rigctld ip")
PARSER.add_argument('--scatter', dest="send_scatter", action="store_true", help="Send scatter information via network")
PARSER.add_argument('--fft', dest="send_fft", action="store_true", help="Send fft information via network")
@ -49,7 +51,6 @@ if __name__ == '__main__':
static.MYCALLSIGN = bytes(ARGS.mycall, 'utf-8')
static.MYCALLSIGN_CRC = helpers.get_crc_16(static.MYCALLSIGN)
static.MYGRID = bytes(ARGS.mygrid, 'utf-8')
static.AUDIO_INPUT_DEVICE = ARGS.audio_input_device
static.AUDIO_OUTPUT_DEVICE = ARGS.audio_output_device
static.PORT = ARGS.socket_port
@ -64,6 +65,8 @@ if __name__ == '__main__':
static.HAMLIB_RADIOCONTROL = ARGS.hamlib_radiocontrol
static.HAMLIB_RGICTLD_IP = ARGS.rigctld_ip
static.HAMLIB_RGICTLD_PORT = ARGS.rigctld_port
static.ENABLE_SCATTER = ARGS.send_scatter
static.ENABLE_FFT = ARGS.send_fft
# we need to wait until we got all parameters from argparse first before we can load the other modules
import sock

View file

@ -17,7 +17,8 @@ import numpy as np
import helpers
import static
import data_handler
import ujson as json
import sock
import re
import queue
import codec2
@ -58,6 +59,10 @@ RECEIVE_DATAC3 = False
class RF():
def __init__(self):
self.sampler_avg = 0
self.buffer_avg = 0
self.AUDIO_SAMPLE_RATE_RX = 48000
self.AUDIO_SAMPLE_RATE_TX = 48000
self.MODEM_SAMPLE_RATE = codec2.api.FREEDV_FS_8000
@ -164,17 +169,19 @@ class RF():
import rigctl as rig
elif static.HAMLIB_RADIOCONTROL == 'rigctld':
import rigctld as rig
elif static.HAMLIB_RADIOCONTROL == 'disabled':
import rigdummy as rig
else:
raise NotImplementedError
import rigdummy as rig
self.hamlib = rig.radio()
self.hamlib.open_rig(devicename=static.HAMLIB_DEVICE_NAME, deviceport=static.HAMLIB_DEVICE_PORT, hamlib_ptt_type=static.HAMLIB_PTT_TYPE, serialspeed=static.HAMLIB_SERIAL_SPEED, pttport=static.HAMLIB_PTT_PORT, data_bits=static.HAMLIB_DATA_BITS, stop_bits=static.HAMLIB_STOP_BITS, handshake=static.HAMLIB_HANDSHAKE, rigctld_ip = static.HAMLIB_RGICTLD_IP, rigctld_port = static.HAMLIB_RGICTLD_PORT)
# --------------------------------------------START DECODER THREAD
fft_thread = threading.Thread(target=self.calculate_fft, name="FFT_THREAD")
fft_thread.start()
if static.ENABLE_FFT:
fft_thread = threading.Thread(target=self.calculate_fft, name="FFT_THREAD")
fft_thread.start()
audio_thread_datac0 = threading.Thread(target=self.audio_datac0, name="AUDIO_THREAD DATAC0")
audio_thread_datac0.start()
@ -185,8 +192,9 @@ class RF():
audio_thread_datac3 = threading.Thread(target=self.audio_datac3, name="AUDIO_THREAD DATAC3")
audio_thread_datac3.start()
hamlib_thread = threading.Thread(target=self.update_rig_data, name="HAMLIB_THREAD")
hamlib_thread.start()
#hamlib_thread = threading.Thread(target=self.update_rig_data, name="HAMLIB_THREAD")
#hamlib_thread.start()
worker_received = threading.Thread(target=self.worker_received, name="WORKER_THREAD")
worker_received.start()
@ -196,10 +204,13 @@ class RF():
# --------------------------------------------------------------------------------------------------------
def audio_callback(self, data_in48k, frame_count, time_info, status):
x = np.frombuffer(data_in48k, dtype=np.int16)
time_sampler_start = time.time()
x = self.resampler.resample48_to_8(x)
time_sampler_end = time.time()
time_buffer_start = time.time()
# avoid buffer overflow by filling only if buffer not full
if not self.datac0_buffer.nbuffer+len(x) > self.datac0_buffer.size:
self.datac0_buffer.push(x)
@ -223,10 +234,13 @@ class RF():
if self.modoutqueue.empty() or self.mod_out_locked:
data_out48k = bytes(self.AUDIO_FRAMES_PER_BUFFER_TX*2)
self.fft_data = bytes(x)
else:
data_out48k = self.modoutqueue.get()
self.fft_data = bytes(data_out48k)
time_buffer_end = time.time()
#print(f"SAMPLER {time_sampler_end - time_sampler_start} BUFFER {time_buffer_end - time_buffer_start}")
return (data_out48k, audio.pyaudio.paContinue)
# --------------------------------------------------------------------------------------------------------
@ -234,8 +248,13 @@ class RF():
def transmit(self, mode, repeats, repeat_delay, frames):
static.TRANSMITTING = True
# toggle ptt early to save some time
# toggle ptt early to save some time and send ptt state via socket
static.PTT_STATE = self.hamlib.set_ptt(True)
jsondata = {"ptt":"True"}
data_out = json.dumps(jsondata)
sock.SOCKET_QUEUE.put(data_out)
# open codec2 instance
self.MODE = mode
freedv = cast(codec2.api.freedv_open(self.MODE), c_void_p)
@ -300,6 +319,8 @@ class RF():
mod_out_silence = create_string_buffer(samples_delay*2)
txbuffer += bytes(mod_out_silence)
#time.sleep(0.05)
# resample up to 48k (resampler works on np.int16)
x = np.frombuffer(txbuffer, dtype=np.int16)
@ -311,6 +332,7 @@ class RF():
# split modualted audio to chunks
#https://newbedev.com/how-to-split-a-byte-string-into-separate-bytes-in-python
txbuffer_48k = bytes(txbuffer_48k)
chunk = [txbuffer_48k[i:i+self.AUDIO_FRAMES_PER_BUFFER_RX*2] for i in range(0, len(txbuffer_48k), self.AUDIO_FRAMES_PER_BUFFER_RX*2)]
# add modulated chunks to fifo buffer
for c in chunk:
@ -322,14 +344,18 @@ class RF():
structlog.get_logger("structlog").debug("[TNC] mod out shorter than audio buffer", delta=len(delta))
self.modoutqueue.put(c)
# Release our mod_out_lock so we can use the queue
self.mod_out_locked = False
# maybe we need to toggle PTT before craeting modulation because of queue processing
#static.PTT_STATE = self.hamlib.set_ptt(True)
while not self.modoutqueue.empty():
pass
static.PTT_STATE = self.hamlib.set_ptt(False)
jsondata = {"ptt":"False"}
data_out = json.dumps(jsondata)
sock.SOCKET_QUEUE.put(data_out)
# after processing we want to set the locking state back to true to be prepared for next transmission
self.mod_out_locked = True
@ -413,29 +439,30 @@ class RF():
def get_scatter(self, freedv):
modemStats = MODEMSTATS()
self.c_lib.freedv_get_modem_extended_stats.restype = None
self.c_lib.freedv_get_modem_extended_stats(freedv, ctypes.byref(modemStats))
if static.ENABLE_SCATTER:
modemStats = MODEMSTATS()
self.c_lib.freedv_get_modem_extended_stats.restype = None
self.c_lib.freedv_get_modem_extended_stats(freedv, ctypes.byref(modemStats))
scatterdata = []
scatterdata_small = []
for i in range(MODEM_STATS_NC_MAX):
for j in range(MODEM_STATS_NR_MAX):
# check if odd or not to get every 2nd item for x
if (j % 2) == 0:
xsymbols = round(modemStats.rx_symbols[i][j]/1000)
ysymbols = round(modemStats.rx_symbols[i][j+1]/1000)
# check if value 0.0 or has real data
if xsymbols != 0.0 and ysymbols != 0.0:
scatterdata.append({"x": xsymbols, "y": ysymbols})
scatterdata = []
scatterdata_small = []
for i in range(MODEM_STATS_NC_MAX):
for j in range(MODEM_STATS_NR_MAX):
# check if odd or not to get every 2nd item for x
if (j % 2) == 0:
xsymbols = round(modemStats.rx_symbols[i][j]/1000)
ysymbols = round(modemStats.rx_symbols[i][j+1]/1000)
# check if value 0.0 or has real data
if xsymbols != 0.0 and ysymbols != 0.0:
scatterdata.append({"x": xsymbols, "y": ysymbols})
# only append scatter data if new data arrived
if 150 > len(scatterdata) > 0:
static.SCATTER = scatterdata
else:
# only take every tenth data point
scatterdata_small = scatterdata[::10]
static.SCATTER = scatterdata_small
# only append scatter data if new data arrived
if 150 > len(scatterdata) > 0:
static.SCATTER = scatterdata
else:
# only take every tenth data point
scatterdata_small = scatterdata[::10]
static.SCATTER = scatterdata_small
def calculate_snr(self, freedv):
@ -456,7 +483,7 @@ class RF():
def update_rig_data(self):
while True:
#time.sleep(0.5)
#time.sleep(1.5)
threading.Event().wait(0.5)
#(static.HAMLIB_FREQUENCY, static.HAMLIB_MODE, static.HAMLIB_BANDWITH, static.PTT_STATE) = self.hamlib.get_rig_data()
static.HAMLIB_FREQUENCY = self.hamlib.get_frequency()
@ -495,7 +522,7 @@ class RF():
structlog.get_logger("structlog").debug("[TNC] Setting fft=0")
# else 0
static.FFT = [0] * 320
static.FFT = [0]
else:
pass

35
tnc/rigdummy.py Normal file
View file

@ -0,0 +1,35 @@
#!/usr/bin/env python3
import structlog
hamlib_version = 0
class radio:
def __init__(self):
pass
def open_rig(self, **kwargs):
return True
def get_frequency(self):
return None
def get_mode(self):
return None
def get_bandwith(self):
return None
def set_mode(self, mode):
return None
def get_ptt(self):
return None
def set_ptt(self, state):
return state
def close_rig(self):
return

View file

@ -33,6 +33,7 @@ import logging, structlog, log_handler
import queue
import psutil
import audio
import base64
SOCKET_QUEUE = queue.Queue()
DAEMON_QUEUE = queue.Queue()
@ -88,7 +89,7 @@ class ThreadedTCPRequestHandler(socketserver.StreamRequestHandler):
data = bytes()
while self.connection_alive:
# BrokenPipeError: [Errno 32] Broken pipe
chunk = self.request.recv(2)
chunk = self.request.recv(1024)
data += chunk
if chunk == b'':
@ -174,18 +175,22 @@ def process_tnc_commands(data):
dxcallsign = received_json["parameter"][0]["dxcallsign"]
mode = int(received_json["parameter"][0]["mode"])
n_frames = int(received_json["parameter"][0]["n_frames"])
data = received_json["parameter"][0]["data"]
base64data = received_json["parameter"][0]["data"]
if not len(base64data) % 4:
binarydata = base64.b64decode(base64data)
static.DXCALLSIGN = bytes(dxcallsign, 'utf-8')
static.DXCALLSIGN_CRC = helpers.get_crc_16(static.DXCALLSIGN)
rawdata = {"raw": data}
dataframe = json.dumps(rawdata)
data_out = bytes(dataframe, 'utf-8')
data_handler.DATA_QUEUE_TRANSMIT.put(['ARQ_RAW', data_out, mode, n_frames])
print(data_handler.DATA_QUEUE_TRANSMIT.qsize())
static.DXCALLSIGN = bytes(dxcallsign, 'utf-8')
static.DXCALLSIGN_CRC = helpers.get_crc_16(static.DXCALLSIGN)
data_handler.DATA_QUEUE_TRANSMIT.put(['ARQ_RAW', binarydata, mode, n_frames])
else:
raise TypeError
except Exception as e:
structlog.get_logger("structlog").warning("[SCK] command execution error", e=e, command=received_json)
structlog.get_logger("structlog").warning("[SCK] command execution error", e=e, command=received_json)
# TRANSMIT FILE ----------------------------------------------------------
'''
if received_json["type"] == 'arq' and received_json["command"] == "send_file":
try:
static.TNC_STATE = 'BUSY'
@ -212,9 +217,24 @@ def process_tnc_commands(data):
# ft = filetype
# d = data
# crc = checksum
rawdata = {"dt": "f", "fn": filename, "ft": filetype,"d": data, "crc": checksum}
dataframe = json.dumps(rawdata)
data_out = bytes(dataframe, 'utf-8')
#rawdata = {"dt": "f", "fn": filename, "ft": filetype,"d": data, "crc": checksum}
#dataframe = json.dumps(rawdata)
#data_out = bytes(dataframe, 'utf-8')
rawdata = bytes()
rawdata += bytes('f', 'utf-8')
rawdata += bytes(';', 'utf-8')
rawdata += bytes(filename, 'utf-8')
rawdata += bytes(';', 'utf-8')
rawdata += bytes(filetype, 'utf-8')
rawdata += bytes(';', 'utf-8')
rawdata += bytes(checksum, 'utf-8')
rawdata += bytes(';', 'utf-8')
rawdata += bytes(data, 'utf-8')
data_handler.DATA_QUEUE_TRANSMIT.put(['ARQ_FILE', data_out, mode, n_frames])
print(data_handler.DATA_QUEUE_TRANSMIT.qsize())
except Exception as e:
@ -251,7 +271,9 @@ def process_tnc_commands(data):
data_handler.DATA_QUEUE_TRANSMIT.put(['ARQ_MESSAGE', data_out, mode, n_frames])
except Exception as e:
structlog.get_logger("structlog").warning("[SCK] command execution error", e=e, command=received_json)
'''
# STOP TRANSMISSION ----------------------------------------------------------
if received_json["type"] == 'arq' and received_json["command"] == "stop_transmission":
try:
@ -272,12 +294,13 @@ def process_tnc_commands(data):
}
for i in range(0, len(static.RX_BUFFER)):
rawdata = json.loads(static.RX_BUFFER[i][4])
output["data-array"].append({"uuid": static.RX_BUFFER[i][0],"timestamp": static.RX_BUFFER[i][1], "dxcallsign": str(static.RX_BUFFER[i][2], 'utf-8'), "dxgrid": str(static.RX_BUFFER[i][3], 'utf-8'), "data": [rawdata]})
print(static.RX_BUFFER[i][4])
#rawdata = json.loads(static.RX_BUFFER[i][4])
base64_data = static.RX_BUFFER[i][4]
output["data-array"].append({"uuid": static.RX_BUFFER[i][0],"timestamp": static.RX_BUFFER[i][1], "dxcallsign": str(static.RX_BUFFER[i][2], 'utf-8'), "dxgrid": str(static.RX_BUFFER[i][3], 'utf-8'), "data": base64_data})
jsondata = json.dumps(output)
#self.request.sendall(bytes(jsondata, encoding))
print(jsondata)
SOCKET_QUEUE.put(jsondata)
except Exception as e:
@ -402,6 +425,9 @@ def process_daemon_commands(data):
radiocontrol = str(received_json["parameter"][0]["radiocontrol"])
rigctld_ip = str(received_json["parameter"][0]["rigctld_ip"])
rigctld_port = str(received_json["parameter"][0]["rigctld_port"])
enable_scatter = str(received_json["parameter"][0]["enable_scatter"])
enable_fft = str(received_json["parameter"][0]["enable_fft"])
DAEMON_QUEUE.put(['STARTTNC', \
mycall, \
mygrid, \
@ -417,8 +443,11 @@ def process_daemon_commands(data):
handshake, \
radiocontrol, \
rigctld_ip, \
rigctld_port \
rigctld_port, \
enable_scatter, \
enable_fft \
])
except Exception as e:
structlog.get_logger("structlog").warning("[SCK] command execution error", e=e, command=received_json)

View file

@ -47,8 +47,8 @@ HAMLIB_STOP_BITS = '1'
HAMLIB_DATA_BITS = '8'
HAMLIB_HANDSHAKE = 'None'
HAMLIB_RADIOCONTROL = 'direct'
HAMLIB_RGICTLD_IP = '127.0.0.1'
HAMLIB_RGICTLD_PORT = '4532'
HAMLIB_RIGCTLD_IP = '127.0.0.1'
HAMLIB_RIGCTLD_PORT = '4532'
HAMLIB_FREQUENCY = 0
HAMLIB_MODE = ''
@ -59,6 +59,7 @@ HAMLIB_BANDWITH = 0
SNR = 0
FREQ_OFFSET = 0
SCATTER = []
ENABLE_SCATTER = False
# ---------------------------------
# Audio Defaults
@ -69,8 +70,8 @@ AUDIO_OUTPUT_DEVICE = -2
BUFFER_OVERFLOW_COUNTER = [0,0,0]
AUDIO_RMS = 0
FFT = []
FFT = [0]
ENABLE_FFT = False
# ARQ statistics
ARQ_BYTES_PER_MINUTE_BURST = 0