diff --git a/gui/daemon.js b/gui/daemon.js
new file mode 100644
index 00000000..2205326d
--- /dev/null
+++ b/gui/daemon.js
@@ -0,0 +1,201 @@
+var net = require('net');
+
+var daemon = new net.Socket();
+var msg = ''; // Current message, per connection.
+
+
+
+const { ipcRenderer } = require('electron');
+
+
+
+
+
+setTimeout(connectDAEMON, 500)
+
+function connectDAEMON(){
+
+ console.log('connecting to DAEMON...')
+
+ //clear message buffer after reconnecting or inital connection
+ msg = '';
+
+ //daemon_host = document.getElementById("daemon_adress").value
+ //daemon_port = document.getElementById("daemon_port").value
+ daemon_host = '192.168.178.163'
+ daemon_port = 3001
+
+ daemon.connect(daemon_port, daemon_host)
+ //client.setTimeout(5000);
+}
+
+daemon.on('connect', function(data) {
+ console.log('DAEMON connection established')
+})
+
+daemon.on('error', function(data) {
+ console.log('DAEMON connection error');
+ setTimeout(connectDAEMON, 2000)
+});
+
+/*
+client.on('close', function(data) {
+ console.log(' TNC connection closed');
+ setTimeout(connectTNC, 2000)
+});
+*/
+
+daemon.on('end', function(data) {
+ console.log('DAEMON connection ended');
+ setTimeout(connectDAEMON, 2000)
+});
+
+
+//exports.writeCommand = function(command){
+writeDaemonCommand = function(command){
+
+
+ // we use the writingCommand function to update our TCPIP state because we are calling this function a lot
+ // if socket openend, we are able to run commands
+ if(daemon.readyState == 'open'){
+ //uiMain.setDAEMONconnection('open')
+
+ daemon.write(command + '\n');
+ }
+
+ if(daemon.readyState == 'closed'){
+ //uiMain.setDAEMONconnection('closed')
+ }
+
+ if(daemon.readyState == 'opening'){
+ //uiMain.setDAEMONconnection('opening')
+ }
+
+
+ let Data = {
+ daemon_connection: daemon.readyState,
+ };
+ ipcRenderer.send('request-update-daemon-connection', Data);
+}
+
+
+
+ // "https://stackoverflow.com/questions/9070700/nodejs-net-createserver-large-amount-of-data-coming-in"
+
+
+daemon.on('data', function(data) {
+
+ data = data.toString('utf8'); /* convert data to string */
+ msg += data.toString('utf8'); /*append data to buffer so we can stick long data together */
+
+
+
+ /* check if we reached an EOF, if true, clear buffer and parse JSON data */
+ if (data.endsWith('}')) {
+ /*console.log(msg)*/
+ try {
+ /*console.log(msg)*/
+ data = JSON.parse(msg)
+ } catch (e) {
+ console.log(e); /* "SyntaxError */
+ }
+ msg = '';
+ /*console.log("EOF detected!")*/
+
+ if(data['COMMAND'] == 'DAEMON_STATE'){
+
+
+
+ let Data = {
+ input_devices: data['INPUT_DEVICES'],
+ output_devices: data['OUTPUT_DEVICES'],
+ tnc_running_state: data['DAEMON_STATE'][0]['STATUS']
+
+ };
+ ipcRenderer.send('request-update-daemon-state', Data);
+
+
+ //input_devices = data['INPUT_DEVICES']
+ //uiMain.updateAudioInput(input_devices)
+
+ //output_devices = data['OUTPUT_DEVICES']
+ //uiMain.updateAudioOutput(output_devices)
+
+ //daemon_state = data['DAEMON_STATE'][0]['STATUS']
+ //uiMain.updateTncRunningState(daemon_state)
+ }
+
+
+
+
+
+////// check if EOF ...
+ }
+
+
+
+});
+
+function hexToBytes(hex) {
+ for (var bytes = [], c = 0; c < hex.length; c += 2)
+ bytes.push(parseInt(hex.substr(c, 2), 16));
+ return bytes;
+}
+
+
+
+
+
+
+ exports.getDaemonState = function(){
+ command = '{"type" : "GET", "command": "DAEMON_STATE"}'
+ writeDaemonCommand(command)
+}
+
+
+
+
+
+
+
+
+
+
+
+
+// START TNC
+// ` `== multi line string
+
+
+exports.startTNC = function(rx_audio, tx_audio, deviceid, deviceport, ptt){
+ var json_command = JSON.stringify({
+ type: 'SET',
+ command: 'STARTTNC',
+ parameter: [{
+ rx_audio: rx_audio,
+ tx_audio: tx_audio,
+ deviceid: deviceid,
+ deviceport: deviceport,
+ ptt: ptt
+ }]
+ })
+
+
+
+ //console.log(json_command)
+ writeDaemonCommand(json_command)
+
+
+}
+
+// STOP TNC
+exports.stopTNC = function(){
+ command = '{"type" : "SET", "command": "STOPTNC" , "parameter": "---" }'
+ writeDaemonCommand(command)
+}
+
+
+
+
+
+
diff --git a/gui/hamlib-list.html b/gui/hamlib-list.html
new file mode 100644
index 00000000..ff293ccc
--- /dev/null
+++ b/gui/hamlib-list.html
@@ -0,0 +1,261 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/gui/package.json b/gui/package.json
new file mode 100644
index 00000000..aed1bbb9
--- /dev/null
+++ b/gui/package.json
@@ -0,0 +1,30 @@
+{
+ "name": "freedv-jate-gui",
+ "version": "0.0.1",
+ "description": "GUI for FreeDV JATE ",
+ "main": "main.js",
+ "scripts": {
+ "start": "electron .",
+ "test": "echo \"Error: no test specified\" && exit 1"
+ },
+ "repository": {
+ "type": "git",
+ "url": "git+https://github.com/DJ2LS/FreeDV-JATE-GUI.git"
+ },
+ "keywords": [
+ "TNC",
+ "GUI",
+ "FreeDV",
+ "codec2"
+ ],
+ "author": "DJ2LS",
+ "license": "LGPL-2.1",
+ "bugs": {
+ "url": "https://github.com/DJ2LS/FreeDV-JATE-GUI/issues"
+ },
+ "homepage": "https://github.com/DJ2LS/FreeDV-JATE-GUI#readme",
+ "dependencies": {
+ "@electron/remote": "^1.2.0",
+ "bootstrap": "^5.0.1"
+ }
+}
diff --git a/gui/preload-data.js b/gui/preload-data.js
new file mode 100644
index 00000000..1e0df1c3
--- /dev/null
+++ b/gui/preload-data.js
@@ -0,0 +1,37 @@
+
+const { ipcRenderer } = require('electron');
+
+
+
+window.addEventListener('DOMContentLoaded', () => {
+ const replaceText = (selector, text) => {
+ const element = document.getElementById(selector)
+ if (element) element.innerText = text
+ }
+
+
+
+ document.getElementById("startTransmission").addEventListener("click", () => {
+ alert("HALLO ")
+ })
+
+
+
+
+})
+
+
+
+
+ ipcRenderer.on('action-update-tnc-state', (event, arg) => {
+
+
+
+ if(arg.ptt_state == 'True'){
+ document.getElementById("ptt_state").className = "btn btn-danger";
+ } else if(arg.ptt_state == 'False'){
+ document.getElementById("ptt_state").className = "btn btn-success";
+ } else {
+ document.getElementById("ptt_state").className = "btn btn-secondary"
+ }
+ });
\ No newline at end of file
diff --git a/gui/preload-main.js b/gui/preload-main.js
new file mode 100644
index 00000000..e0bf55e5
--- /dev/null
+++ b/gui/preload-main.js
@@ -0,0 +1,218 @@
+const sock = require('./sock.js')
+const daemon = require('./daemon.js')
+const { ipcRenderer } = require('electron');
+
+
+
+
+
+window.addEventListener('DOMContentLoaded', () => {
+
+setInterval(sock.getTncState, 1000)
+setInterval(daemon.getDaemonState, 250)
+//setInterval(uiMain.updateFFT, 250)
+
+
+
+// Create spectrum object on canvas with ID "waterfall"
+ global.spectrum = new Spectrum(
+ "waterfall", {
+ spectrumPercent: 20
+ });
+
+
+
+
+ // saveMyCall button clicked
+ document.getElementById("saveMyCall").addEventListener("click", () => {
+ callsign = document.getElementById("myCall").value
+ sock.saveMyCall(callsign)
+ //uiMain.getTncState()
+ })
+
+ // saveMyGrid button clicked
+ document.getElementById("saveMyGrid").addEventListener("click", () => {
+ grid = document.getElementById("myGrid").value
+ sock.saveMyGrid(grid)
+ //uiMain.getTncState()
+ })
+
+
+ // startTNC button clicked
+ document.getElementById("startTNC").addEventListener("click", () => {
+ var rx_audio = document.getElementById("audio_input_selectbox").value
+ var tx_audio = document.getElementById("audio_output_selectbox").value
+ var deviceid = document.getElementById("hamlib_deviceid").value
+ var deviceport = document.getElementById("hamlib_deviceport").value
+ var ptt = document.getElementById("hamlib_ptt").value
+
+ daemon.startTNC(rx_audio, tx_audio, deviceid, deviceport, ptt)
+ })
+
+ // stopTNC button clicked
+ document.getElementById("stopTNC").addEventListener("click", () => {
+ daemon.stopTNC()
+ })
+
+ // openDataModule button clicked
+ document.getElementById("openDataModule").addEventListener("click", () => {
+ //data.show()
+ let Data = {
+ message: "Hello World !"
+ };
+ ipcRenderer.send('show-data-window', Data);
+ })
+
+
+
+
+
+
+
+})
+
+
+
+
+
+
+ipcRenderer.on('action-update-tnc-state', (event, arg) => {
+
+// PTT STATE
+ if(arg.ptt_state == 'True'){
+ document.getElementById("ptt_state").className = "btn btn-danger";
+ } else if(arg.ptt_state == 'False'){
+ document.getElementById("ptt_state").className = "btn btn-success";
+ } else {
+ document.getElementById("ptt_state").className = "btn btn-secondary"
+ }
+
+// BUSY STATE
+ if(arg.busy_state == 'BUSY'){
+ document.getElementById("busy_state").className = "btn btn-danger";
+ } else if(arg.busy_state == 'IDLE'){
+ document.getElementById("busy_state").className = "btn btn-success";
+ } else {
+ document.getElementById("busy_state").className = "btn btn-secondary"
+ }
+
+// ARQ STATE
+ if(arg.arq_state == 'DATA'){
+ document.getElementById("arq_state").className = "btn btn-warning";
+ } else if(arg.arq_state == 'IDLE'){
+ document.getElementById("arq_state").className = "btn btn-secondary";
+ } else {
+ document.getElementById("arq_state").className = "btn btn-secondary"
+ }
+
+// RMS
+ document.getElementById("rms_level").setAttribute("aria-valuenow", arg.rms_level)
+ document.getElementById("rms_level").setAttribute("style", "width:" + arg.rms_level + "%;")
+
+// CHANNEL STATE
+ if(arg.channel_state == 'RECEIVING_SIGNALLING'){
+ document.getElementById("signalling_state").className = "btn btn-success";
+ document.getElementById("data_state").className = "btn btn-secondary";
+
+ } else if(arg.channel_state == 'SENDING_SIGNALLING'){
+ document.getElementById("signalling_state").className = "btn btn-danger";
+ document.getElementById("data_state").className = "btn btn-secondary";
+
+ } else if(arg.channel_state == 'RECEIVING_DATA'){
+ document.getElementById("signalling_state").className = "btn btn-secondary";
+ document.getElementById("data_state").className = "btn btn-success";
+
+ } else if(arg.channel_state == 'SENDING_DATA'){
+ document.getElementById("signalling_state").className = "btn btn-secondary";
+ document.getElementById("data_state").className = "btn btn-danger";
+ } else {
+ document.getElementById("signalling_state").className = "btn btn-secondary"
+ document.getElementById("busy_state").className = "btn btn-secondary"
+
+ }
+
+// SET FREQUENCY
+ document.getElementById("frequency").value = arg.frequency
+
+// SET MODE
+ document.getElementById("mode").value = arg.mode
+
+// SET BANDWITH
+ document.getElementById("bandwith").value = arg.bandwith
+ });
+
+
+
+
+ipcRenderer.on('action-update-daemon-state', (event, arg) => {
+// UPDATE AUDIO INPUT
+
+ if(document.getElementById("audio_input_selectbox").length != arg.input_devices.length){
+ document.getElementById("audio_input_selectbox").innerHTML = ""
+ for (i = 0; i < arg.input_devices.length; i++) {
+ var option = document.createElement("option");
+ option.text = arg.input_devices[i]['NAME'];
+ option.value = arg.input_devices[i]['ID'];
+
+ document.getElementById("audio_input_selectbox").add(option);
+ }
+ }
+ // UPDATE AUDIO OUTPUT
+
+ if(document.getElementById("audio_output_selectbox").length != arg.output_devices.length){
+ document.getElementById("audio_output_selectbox").innerHTML = ""
+ for (i = 0; i < arg.output_devices.length; i++) {
+ var option = document.createElement("option");
+ option.text = arg.output_devices[i]['NAME'];
+ option.value = arg.output_devices[i]['ID'];
+ document.getElementById("audio_output_selectbox").add(option);
+ }
+ }
+
+// TNC RUNNING STATE
+document.getElementById("tnc_running_state").innerHTML = arg.tnc_running_state;
+ if(arg.tnc_running_state == "running"){
+ document.getElementById('hamlib_deviceid').disabled = true
+ document.getElementById('hamlib_deviceport').disabled = true
+ document.getElementById('hamlib_ptt').disabled = true
+ document.getElementById('audio_input_selectbox').disabled = true
+ document.getElementById('audio_output_selectbox').disabled = true
+ document.getElementById('stopTNC').disabled = false
+ document.getElementById('startTNC').disabled = true
+ document.getElementById('myCall').disabled = false
+ document.getElementById('saveMyCall').disabled = false
+ document.getElementById('myGrid').disabled = false
+ document.getElementById('saveMyGrid').disabled = false
+
+ } else {
+ document.getElementById('hamlib_deviceid').disabled = false
+ document.getElementById('hamlib_deviceport').disabled = false
+ document.getElementById('hamlib_ptt').disabled = false
+ document.getElementById('audio_input_selectbox').disabled = false
+ document.getElementById('audio_output_selectbox').disabled = false
+ document.getElementById('stopTNC').disabled = true
+ document.getElementById('startTNC').disabled = false
+ document.getElementById('myCall').disabled = true
+ document.getElementById('saveMyCall').disabled = true
+ document.getElementById('myGrid').disabled = true
+ document.getElementById('saveMyGrid').disabled = true
+ }
+
+
+ });
+
+
+ipcRenderer.on('action-update-daemon-connection', (event, arg) => {
+
+
+ if(arg.daemon_connection == 'open'){
+ document.getElementById("daemon_connection_state").className = "btn btn-success";
+ }
+ if(arg.daemon_connection == 'opening'){
+ document.getElementById("daemon_connection_state").className = "btn btn-warning";
+ }
+ if(arg.daemon_connection == 'closed'){
+ document.getElementById("daemon_connection_state").className = "btn btn-danger";
+ }
+
+ });
\ No newline at end of file
diff --git a/gui/sock.js b/gui/sock.js
new file mode 100644
index 00000000..13d38afa
--- /dev/null
+++ b/gui/sock.js
@@ -0,0 +1,193 @@
+var net = require('net');
+
+const { ipcRenderer } = require('electron');
+
+var client = new net.Socket();
+var msg = ''; // Current message, per connection.
+
+
+
+setTimeout(connectTNC, 500)
+
+function connectTNC(){
+
+ console.log('connecting to TNC...')
+
+ //clear message buffer after reconnecting or inital connection
+ msg = '';
+
+ tnc_host = document.getElementById("tnc_adress").value
+ tnc_port = document.getElementById("tnc_port").value
+ client.connect(tnc_port, tnc_host)
+ //client.setTimeout(5000);
+}
+
+client.on('connect', function(data) {
+ console.log('TNC connection established')
+})
+
+client.on('error', function(data) {
+ console.log('TNC connection error');
+
+
+ let Data = {
+ busy_state: "-",
+ arq_state: "-",
+ channel_state: "-",
+ frequency : "-",
+ mode: "-",
+ bandwith: "-",
+ rms_level : 0
+
+ };
+ ipcRenderer.send('request-update-tnc-state', Data);
+
+ setTimeout(connectTNC, 2000)
+});
+
+/*
+client.on('close', function(data) {
+ console.log(' TNC connection closed');
+ setTimeout(connectTNC, 2000)
+});
+*/
+
+client.on('end', function(data) {
+ console.log('TNC connection ended');
+ setTimeout(connectTNC, 2000)
+});
+
+
+//exports.writeTncCommand = function(command){
+ writeTncCommand = function(command){
+
+ console.log(command)
+ // we use the writingCommand function to update our TCPIP state because we are calling this function a lot
+ // if socket openend, we are able to run commands
+ if(client.readyState == 'open'){
+ //uiMain.setTNCconnection('open')
+ client.write(command + '\n');
+ }
+
+ if(client.readyState == 'closed'){
+ //uiMain.setTNCconnection('closed')
+ }
+
+ if(client.readyState == 'opening'){
+ //uiMain.setTNCconnection('opening')
+ }
+}
+
+
+
+client.on('data', function(data) {
+
+/*
+stackoverflow.com questions 9070700 nodejs-net-createserver-large-amount-of-data-coming-in
+*/
+
+ data = data.toString('utf8'); // convert data to string
+ msg += data.toString('utf8'); // append data to buffer so we can stick long data together
+
+/*
+ if (msg.charCodeAt(msg.length - 1) == 0) {
+ client.emit('message', msg.substring(0, msg.length - 1));
+ msg = '';
+ console.log("END OF FILE")
+ }
+ */
+
+ /*
+ if (msg.startsWith('{"COMMAND":')) {
+ msg = '';
+ msg += data.toString('utf8');
+ console.log("BOF detected!")
+ }
+ */
+
+ // check if we reached an EOF, if true, clear buffer and parse JSON data
+ if (data.endsWith('}')) {
+ //console.log(msg)
+ try {
+ //console.log(msg)
+ data = JSON.parse(msg)
+ } catch (e) {
+ console.log(e); /* "SyntaxError*/
+ }
+ msg = '';
+ /* console.log("EOF detected!") */
+
+
+
+
+
+ if(data['COMMAND'] == 'TNC_STATE'){
+
+
+
+
+ // FFT
+ //fft_raw = data['FFT']
+ //var fft = Buffer.from(fft_raw, "hex")
+ //var fft = hexToBytes(fft_raw)
+ //var fft = Array.from(fft_raw)
+ //console.log(typeof(fft))
+
+
+
+ let Data = {
+ ptt_state: data['PTT_STATE'],
+ busy_state: data['TNC_STATE'],
+ arq_state: data['ARQ_STATE'],
+ channel_state: data['CHANNEL_STATE'],
+ frequency : data['FREQUENCY'],
+ mode: data['MODE'],
+ bandwith: data['BANDWITH'],
+ rms_level : (data['AUDIO_RMS']/1000)*100
+
+ };
+ ipcRenderer.send('request-update-tnc-state', Data);
+
+
+ var fft = Array.from({length: 2048}, () => Math.floor(Math.random() * 10));
+ //console.log(fft)
+ //uiMain.updateFFT(fft)
+
+
+ }
+
+ // check if EOF ...
+ }
+
+
+
+});
+
+function hexToBytes(hex) {
+ for (var bytes = [], c = 0; c < hex.length; c += 2)
+ bytes.push(parseInt(hex.substr(c, 2), 16));
+ return bytes;
+}
+
+
+
+
+
+
+
+
+//Save callsign
+ exports.saveMyCall = function(callsign){
+ command = '{"type" : "SET", "command": "MYCALLSIGN" , "parameter": "'+ callsign +'" }'
+ writeTncCommand(command)
+}
+
+ exports.saveMyGrid = function(grid){
+ command = '{"type" : "SET", "command": "MYGRID" , "parameter": "'+ grid +'" }'
+ writeTncCommand(command)
+}
+
+ exports.getTncState = function(){
+ command = '{"type" : "GET", "command": "TNC_STATE"}';
+ writeTncCommand(command)
+}