mirror of
https://github.com/DJ2LS/FreeDATA
synced 2024-05-14 10:04:33 +02:00
renamed gui_vue to gui
This commit is contained in:
parent
2684d3be39
commit
ff82b0e0f4
4
.github/dependabot.yml
vendored
4
.github/dependabot.yml
vendored
|
@ -16,13 +16,11 @@ updates:
|
||||||
|
|
||||||
# Maintain dependencies for npm
|
# Maintain dependencies for npm
|
||||||
- package-ecosystem: "npm"
|
- package-ecosystem: "npm"
|
||||||
directory: "/gui_vue"
|
directory: "/gui"
|
||||||
schedule:
|
schedule:
|
||||||
interval: "daily"
|
interval: "daily"
|
||||||
target-branch: "ls-gui-single-pager"
|
target-branch: "ls-gui-single-pager"
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
# Maintain dependencies for pip
|
# Maintain dependencies for pip
|
||||||
- package-ecosystem: "pip"
|
- package-ecosystem: "pip"
|
||||||
directory: "/"
|
directory: "/"
|
||||||
|
|
10
.gitignore
vendored
10
.gitignore
vendored
|
@ -24,9 +24,9 @@ coverage.sh
|
||||||
coverage.xml
|
coverage.xml
|
||||||
|
|
||||||
#ignore node_modules
|
#ignore node_modules
|
||||||
/gui_vue/node_modules/
|
/gui/node_modules/
|
||||||
|
|
||||||
#Ignore gui_vue build items
|
#Ignore gui build items
|
||||||
/gui_vue/dist
|
/gui/dist
|
||||||
/gui_vue/release
|
/gui/release
|
||||||
/gui_vue/dist-electron
|
/gui/dist-electron
|
104
gui/.gitignore
vendored
104
gui/.gitignore
vendored
|
@ -1,104 +0,0 @@
|
||||||
# Logs
|
|
||||||
logs
|
|
||||||
*.log
|
|
||||||
npm-debug.log*
|
|
||||||
yarn-debug.log*
|
|
||||||
yarn-error.log*
|
|
||||||
lerna-debug.log*
|
|
||||||
|
|
||||||
# Diagnostic reports (https://nodejs.org/api/report.html)
|
|
||||||
report.[0-9]*.[0-9]*.[0-9]*.[0-9]*.json
|
|
||||||
|
|
||||||
# Runtime data
|
|
||||||
pids
|
|
||||||
*.pid
|
|
||||||
*.seed
|
|
||||||
*.pid.lock
|
|
||||||
|
|
||||||
# Directory for instrumented libs generated by jscoverage/JSCover
|
|
||||||
lib-cov
|
|
||||||
|
|
||||||
# Coverage directory used by tools like istanbul
|
|
||||||
coverage
|
|
||||||
*.lcov
|
|
||||||
|
|
||||||
# nyc test coverage
|
|
||||||
.nyc_output
|
|
||||||
|
|
||||||
# Grunt intermediate storage (https://gruntjs.com/creating-plugins#storing-task-files)
|
|
||||||
.grunt
|
|
||||||
|
|
||||||
# Bower dependency directory (https://bower.io/)
|
|
||||||
bower_components
|
|
||||||
|
|
||||||
# node-waf configuration
|
|
||||||
.lock-wscript
|
|
||||||
|
|
||||||
# Compiled binary addons (https://nodejs.org/api/addons.html)
|
|
||||||
build/Release
|
|
||||||
|
|
||||||
# Dependency directories
|
|
||||||
node_modules/
|
|
||||||
jspm_packages/
|
|
||||||
|
|
||||||
# TypeScript v1 declaration files
|
|
||||||
typings/
|
|
||||||
|
|
||||||
# TypeScript cache
|
|
||||||
*.tsbuildinfo
|
|
||||||
|
|
||||||
# Optional npm cache directory
|
|
||||||
.npm
|
|
||||||
|
|
||||||
# Optional eslint cache
|
|
||||||
.eslintcache
|
|
||||||
|
|
||||||
# Microbundle cache
|
|
||||||
.rpt2_cache/
|
|
||||||
.rts2_cache_cjs/
|
|
||||||
.rts2_cache_es/
|
|
||||||
.rts2_cache_umd/
|
|
||||||
|
|
||||||
# Optional REPL history
|
|
||||||
.node_repl_history
|
|
||||||
|
|
||||||
# Output of 'npm pack'
|
|
||||||
*.tgz
|
|
||||||
|
|
||||||
# Yarn Integrity file
|
|
||||||
.yarn-integrity
|
|
||||||
|
|
||||||
# dotenv environment variables file
|
|
||||||
.env
|
|
||||||
.env.test
|
|
||||||
|
|
||||||
# parcel-bundler cache (https://parceljs.org/)
|
|
||||||
.cache
|
|
||||||
|
|
||||||
# Next.js build output
|
|
||||||
.next
|
|
||||||
|
|
||||||
# Nuxt.js build / generate output
|
|
||||||
.nuxt
|
|
||||||
dist
|
|
||||||
|
|
||||||
# Gatsby files
|
|
||||||
.cache/
|
|
||||||
# Comment in the public line in if your project uses Gatsby and *not* Next.js
|
|
||||||
# https://nextjs.org/blog/next-9-1#public-directory-support
|
|
||||||
# public
|
|
||||||
|
|
||||||
# vuepress build output
|
|
||||||
.vuepress/dist
|
|
||||||
|
|
||||||
# Serverless directories
|
|
||||||
.serverless/
|
|
||||||
|
|
||||||
# FuseBox cache
|
|
||||||
.fusebox/
|
|
||||||
|
|
||||||
# DynamoDB Local files
|
|
||||||
.dynamodb/
|
|
||||||
|
|
||||||
# TernJS port file
|
|
||||||
.tern-port
|
|
347
gui/daemon.js
347
gui/daemon.js
|
@ -1,347 +0,0 @@
|
||||||
var net = require("net");
|
|
||||||
const path = require("path");
|
|
||||||
const { ipcRenderer } = require("electron");
|
|
||||||
const log = require("electron-log");
|
|
||||||
const daemonLog = log.scope("daemon");
|
|
||||||
|
|
||||||
// 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");
|
|
||||||
var configPath = path.join(configFolder, "config.json");
|
|
||||||
const config = require(configPath);
|
|
||||||
|
|
||||||
var daemon = new net.Socket();
|
|
||||||
var socketchunk = ""; // Current message, per connection.
|
|
||||||
|
|
||||||
// global to keep track of daemon connection error emissions
|
|
||||||
var daemonShowConnectStateError = 1;
|
|
||||||
|
|
||||||
// global for storing ip information
|
|
||||||
var daemon_port = config.daemon_port;
|
|
||||||
var daemon_host = config.daemon_host;
|
|
||||||
|
|
||||||
setTimeout(connectDAEMON, 500);
|
|
||||||
|
|
||||||
function connectDAEMON() {
|
|
||||||
if (daemonShowConnectStateError == 1) {
|
|
||||||
daemonLog.info("connecting to daemon");
|
|
||||||
}
|
|
||||||
|
|
||||||
//clear message buffer after reconnecting or initial connection
|
|
||||||
socketchunk = "";
|
|
||||||
|
|
||||||
daemon.connect(config.daemon_port, config.daemon_host);
|
|
||||||
|
|
||||||
//client.setTimeout(5000);
|
|
||||||
}
|
|
||||||
|
|
||||||
daemon.on("connect", function (err) {
|
|
||||||
daemonLog.info("daemon connection established");
|
|
||||||
let Data = {
|
|
||||||
daemon_connection: daemon.readyState,
|
|
||||||
};
|
|
||||||
ipcRenderer.send("request-update-daemon-connection", Data);
|
|
||||||
|
|
||||||
daemonShowConnectStateError = 1;
|
|
||||||
});
|
|
||||||
|
|
||||||
daemon.on("error", function (err) {
|
|
||||||
if (daemonShowConnectStateError == 1) {
|
|
||||||
daemonLog.error("daemon connection error");
|
|
||||||
daemonLog.info("Make sure the daemon is started.");
|
|
||||||
daemonLog.info('Run "python daemon.py" in the tnc directory.');
|
|
||||||
|
|
||||||
daemonShowConnectStateError = 0;
|
|
||||||
}
|
|
||||||
setTimeout(connectDAEMON, 500);
|
|
||||||
daemon.destroy();
|
|
||||||
let Data = {
|
|
||||||
daemon_connection: daemon.readyState,
|
|
||||||
};
|
|
||||||
ipcRenderer.send("request-update-daemon-connection", Data);
|
|
||||||
});
|
|
||||||
|
|
||||||
/*
|
|
||||||
client.on('close', function(data) {
|
|
||||||
console.log(' TNC connection closed');
|
|
||||||
setTimeout(connectTNC, 2000)
|
|
||||||
let Data = {
|
|
||||||
daemon_connection: daemon.readyState,
|
|
||||||
};
|
|
||||||
ipcRenderer.send('request-update-daemon-connection', Data);
|
|
||||||
});
|
|
||||||
*/
|
|
||||||
|
|
||||||
daemon.on("end", function (data) {
|
|
||||||
daemonLog.warn("daemon connection ended");
|
|
||||||
daemon.destroy();
|
|
||||||
setTimeout(connectDAEMON, 500);
|
|
||||||
let Data = {
|
|
||||||
daemon_connection: daemon.readyState,
|
|
||||||
};
|
|
||||||
ipcRenderer.send("request-update-daemon-connection", Data);
|
|
||||||
});
|
|
||||||
|
|
||||||
//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 opened, 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 (socketdata) {
|
|
||||||
/*
|
|
||||||
inspired by:
|
|
||||||
stackoverflow.com questions 9070700 nodejs-net-createserver-large-amount-of-data-coming-in
|
|
||||||
*/
|
|
||||||
|
|
||||||
socketdata = socketdata.toString("utf8"); // convert data to string
|
|
||||||
socketchunk += socketdata; // append data to buffer so we can stick long data together
|
|
||||||
|
|
||||||
// check if we received begin and end of json data
|
|
||||||
if (socketchunk.startsWith('{"') && socketchunk.endsWith('"}\n')) {
|
|
||||||
var data = "";
|
|
||||||
|
|
||||||
// split data into chunks if we received multiple commands
|
|
||||||
socketchunk = socketchunk.split("\n");
|
|
||||||
data = JSON.parse(socketchunk[0]);
|
|
||||||
|
|
||||||
// search for empty entries in socketchunk and remove them
|
|
||||||
for (i = 0; i < socketchunk.length; i++) {
|
|
||||||
if (socketchunk[i] === "") {
|
|
||||||
socketchunk.splice(i, 1);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
//iterate through socketchunks array to execute multiple commands in row
|
|
||||||
for (i = 0; i < socketchunk.length; i++) {
|
|
||||||
//check if data is not empty
|
|
||||||
if (socketchunk[i].length > 0) {
|
|
||||||
//try to parse JSON
|
|
||||||
try {
|
|
||||||
data = JSON.parse(socketchunk[i]);
|
|
||||||
} catch (e) {
|
|
||||||
console.log(e); // "SyntaxError
|
|
||||||
daemonLog.error(e);
|
|
||||||
daemonLog.debug(socketchunk[i]);
|
|
||||||
socketchunk = "";
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (data["command"] == "daemon_state") {
|
|
||||||
let Data = {
|
|
||||||
input_devices: data["input_devices"],
|
|
||||||
output_devices: data["output_devices"],
|
|
||||||
python_version: data["python_version"],
|
|
||||||
hamlib_version: data["hamlib_version"],
|
|
||||||
serial_devices: data["serial_devices"],
|
|
||||||
tnc_running_state: data["daemon_state"][0]["status"],
|
|
||||||
ram_usage: data["ram"],
|
|
||||||
cpu_usage: data["cpu"],
|
|
||||||
version: data["version"],
|
|
||||||
};
|
|
||||||
ipcRenderer.send("request-update-daemon-state", Data);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (data["command"] == "test_hamlib") {
|
|
||||||
let Data = {
|
|
||||||
hamlib_result: data["result"],
|
|
||||||
};
|
|
||||||
ipcRenderer.send("request-update-hamlib-test", Data);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
//finally delete message buffer
|
|
||||||
socketchunk = "";
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
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 () {
|
|
||||||
//function getDaemonState(){
|
|
||||||
command = '{"type" : "get", "command" : "daemon_state"}';
|
|
||||||
writeDaemonCommand(command);
|
|
||||||
};
|
|
||||||
|
|
||||||
// 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,
|
|
||||||
enable_fft,
|
|
||||||
enable_scatter,
|
|
||||||
low_bandwidth_mode,
|
|
||||||
tuning_range_fmin,
|
|
||||||
tuning_range_fmax,
|
|
||||||
enable_fsk,
|
|
||||||
tx_audio_level,
|
|
||||||
respond_to_cq,
|
|
||||||
rx_buffer_size,
|
|
||||||
enable_explorer,
|
|
||||||
explorer_stats,
|
|
||||||
auto_tune,
|
|
||||||
tx_delay,
|
|
||||||
tci_ip,
|
|
||||||
tci_port,
|
|
||||||
enable_mesh,
|
|
||||||
) {
|
|
||||||
var json_command = JSON.stringify({
|
|
||||||
type: "set",
|
|
||||||
command: "start_tnc",
|
|
||||||
parameter: [
|
|
||||||
{
|
|
||||||
mycall: mycall,
|
|
||||||
mygrid: mygrid,
|
|
||||||
rx_audio: rx_audio,
|
|
||||||
tx_audio: tx_audio,
|
|
||||||
radiocontrol: radiocontrol,
|
|
||||||
devicename: devicename,
|
|
||||||
deviceport: deviceport,
|
|
||||||
pttprotocol: pttprotocol,
|
|
||||||
pttport: pttport,
|
|
||||||
serialspeed: serialspeed,
|
|
||||||
data_bits: data_bits,
|
|
||||||
stop_bits: stop_bits,
|
|
||||||
handshake: handshake,
|
|
||||||
rigctld_port: rigctld_port,
|
|
||||||
rigctld_ip: rigctld_ip,
|
|
||||||
enable_scatter: enable_scatter,
|
|
||||||
enable_fft: enable_fft,
|
|
||||||
enable_fsk: enable_fsk,
|
|
||||||
low_bandwidth_mode: low_bandwidth_mode,
|
|
||||||
tuning_range_fmin: tuning_range_fmin,
|
|
||||||
tuning_range_fmax: tuning_range_fmax,
|
|
||||||
tx_audio_level: tx_audio_level,
|
|
||||||
respond_to_cq: respond_to_cq,
|
|
||||||
rx_buffer_size: rx_buffer_size,
|
|
||||||
enable_explorer: enable_explorer,
|
|
||||||
enable_stats: explorer_stats,
|
|
||||||
enable_auto_tune: auto_tune,
|
|
||||||
tx_delay: tx_delay,
|
|
||||||
tci_ip: tci_ip,
|
|
||||||
tci_port: tci_port,
|
|
||||||
enable_mesh: enable_mesh,
|
|
||||||
},
|
|
||||||
],
|
|
||||||
});
|
|
||||||
|
|
||||||
daemonLog.debug(json_command);
|
|
||||||
writeDaemonCommand(json_command);
|
|
||||||
};
|
|
||||||
|
|
||||||
// STOP TNC
|
|
||||||
exports.stopTNC = function () {
|
|
||||||
command = '{"type" : "set", "command": "stop_tnc" , "parameter": "---" }';
|
|
||||||
writeDaemonCommand(command);
|
|
||||||
};
|
|
||||||
|
|
||||||
// TEST HAMLIB
|
|
||||||
exports.testHamlib = function (
|
|
||||||
radiocontrol,
|
|
||||||
devicename,
|
|
||||||
deviceport,
|
|
||||||
serialspeed,
|
|
||||||
pttprotocol,
|
|
||||||
pttport,
|
|
||||||
data_bits,
|
|
||||||
stop_bits,
|
|
||||||
handshake,
|
|
||||||
rigctld_ip,
|
|
||||||
rigctld_port,
|
|
||||||
) {
|
|
||||||
var json_command = JSON.stringify({
|
|
||||||
type: "get",
|
|
||||||
command: "test_hamlib",
|
|
||||||
parameter: [
|
|
||||||
{
|
|
||||||
radiocontrol: radiocontrol,
|
|
||||||
devicename: devicename,
|
|
||||||
deviceport: deviceport,
|
|
||||||
pttprotocol: pttprotocol,
|
|
||||||
pttport: pttport,
|
|
||||||
serialspeed: serialspeed,
|
|
||||||
data_bits: data_bits,
|
|
||||||
stop_bits: stop_bits,
|
|
||||||
handshake: handshake,
|
|
||||||
rigctld_port: rigctld_port,
|
|
||||||
rigctld_ip: rigctld_ip,
|
|
||||||
},
|
|
||||||
],
|
|
||||||
});
|
|
||||||
daemonLog.debug(json_command);
|
|
||||||
writeDaemonCommand(json_command);
|
|
||||||
};
|
|
||||||
|
|
||||||
//Save myCall
|
|
||||||
exports.saveMyCall = function (callsign) {
|
|
||||||
command =
|
|
||||||
'{"type" : "set", "command": "mycallsign" , "parameter": "' +
|
|
||||||
callsign +
|
|
||||||
'"}';
|
|
||||||
writeDaemonCommand(command);
|
|
||||||
};
|
|
||||||
|
|
||||||
// Save myGrid
|
|
||||||
exports.saveMyGrid = function (grid) {
|
|
||||||
command =
|
|
||||||
'{"type" : "set", "command": "mygrid" , "parameter": "' + grid + '"}';
|
|
||||||
writeDaemonCommand(command);
|
|
||||||
};
|
|
||||||
|
|
||||||
ipcRenderer.on("action-update-daemon-ip", (event, arg) => {
|
|
||||||
daemon.destroy();
|
|
||||||
let Data = {
|
|
||||||
busy_state: "-",
|
|
||||||
arq_state: "-",
|
|
||||||
//channel_state: "-",
|
|
||||||
frequency: "-",
|
|
||||||
mode: "-",
|
|
||||||
bandwidth: "-",
|
|
||||||
dbfs_level: 0,
|
|
||||||
};
|
|
||||||
ipcRenderer.send("request-update-tnc-state", Data);
|
|
||||||
daemon_port = arg.port;
|
|
||||||
daemon_host = arg.adress;
|
|
||||||
connectDAEMON();
|
|
||||||
});
|
|
|
@ -1,37 +0,0 @@
|
||||||
const fs = require("fs");
|
|
||||||
const { ipcRenderer } = require("electron");
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Save config and update config setting globally
|
|
||||||
* @param {string} config - config data
|
|
||||||
* @param {string} configPath
|
|
||||||
*/
|
|
||||||
exports.saveConfig = function (config, configPath) {
|
|
||||||
fs.writeFileSync(configPath, JSON.stringify(config, null, 2));
|
|
||||||
ipcRenderer.send("set-config-global", config);
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Binary to ASCII replacement
|
|
||||||
* @param {string} data in normal/usual utf-8 format
|
|
||||||
* @returns base64 encoded string
|
|
||||||
*/
|
|
||||||
exports.btoa_FD = function (data) {
|
|
||||||
return Buffer.from(data, "utf-8").toString("base64");
|
|
||||||
};
|
|
||||||
/**
|
|
||||||
* ASCII to Binary replacement
|
|
||||||
* @param {string} data in base64 encoding
|
|
||||||
* @returns utf-8 normal/usual string
|
|
||||||
*/
|
|
||||||
exports.atob_FD = function (data) {
|
|
||||||
return Buffer.from(data, "base64").toString("utf-8");
|
|
||||||
};
|
|
||||||
/**
|
|
||||||
* UTF8 to ASCII btoa
|
|
||||||
* @param {string} data in base64 encoding
|
|
||||||
* @returns base64 bota compatible data for use in browser
|
|
||||||
*/
|
|
||||||
exports.atob = function (data) {
|
|
||||||
return window.btoa(Buffer.from(data, "base64").toString("utf8"));
|
|
||||||
};
|
|
998
gui/main.js
998
gui/main.js
|
@ -1,998 +0,0 @@
|
||||||
const { app, BrowserWindow, ipcMain, dialog, shell } = require("electron");
|
|
||||||
const https = require("https");
|
|
||||||
const { autoUpdater } = require("electron-updater");
|
|
||||||
const path = require("path");
|
|
||||||
const fs = require("fs");
|
|
||||||
const os = require("os");
|
|
||||||
const spawn = require("child_process").spawn;
|
|
||||||
|
|
||||||
const log = require("electron-log");
|
|
||||||
const mainLog = log.scope("main");
|
|
||||||
const daemonProcessLog = log.scope("freedata-daemon");
|
|
||||||
const mime = require("mime");
|
|
||||||
const net = require("net");
|
|
||||||
const FD = require("./freedata");
|
|
||||||
|
|
||||||
//Useful for debugging event emitter memory leaks
|
|
||||||
//require('events').EventEmitter.defaultMaxListeners = 10;
|
|
||||||
//process.traceProcessWarnings=true;
|
|
||||||
|
|
||||||
const sysInfo = log.scope("system information");
|
|
||||||
sysInfo.info("SYSTEM INFORMATION ----------------------------- ");
|
|
||||||
sysInfo.info("APP VERSION : " + app.getVersion());
|
|
||||||
sysInfo.info("PLATFORM : " + os.platform());
|
|
||||||
sysInfo.info("ARCHITECTURE: " + os.arch());
|
|
||||||
sysInfo.info("FREE MEMORY: " + os.freemem());
|
|
||||||
sysInfo.info("TOTAL MEMORY: " + os.totalmem());
|
|
||||||
sysInfo.info("LOAD AVG : " + os.loadavg());
|
|
||||||
sysInfo.info("RELEASE : " + os.release());
|
|
||||||
sysInfo.info("TYPE : " + os.type());
|
|
||||||
sysInfo.info("VERSION : " + os.version());
|
|
||||||
sysInfo.info("UPTIME : " + os.uptime());
|
|
||||||
|
|
||||||
app.setName("FreeDATA");
|
|
||||||
|
|
||||||
var appDataFolder =
|
|
||||||
process.env.APPDATA ||
|
|
||||||
(process.platform == "darwin"
|
|
||||||
? process.env.HOME + "/Library/Application Support"
|
|
||||||
: process.env.HOME + "/.config");
|
|
||||||
var configFolder = path.join(appDataFolder, "FreeDATA");
|
|
||||||
var configPath = path.join(configFolder, "config.json");
|
|
||||||
|
|
||||||
// create config folder if not exists
|
|
||||||
if (!fs.existsSync(configFolder)) {
|
|
||||||
fs.mkdirSync(configFolder);
|
|
||||||
}
|
|
||||||
|
|
||||||
// create config file if not exists with defaults
|
|
||||||
const configDefaultSettings =
|
|
||||||
'{\
|
|
||||||
"tnc_host": "127.0.0.1",\
|
|
||||||
"tnc_port": "3000",\
|
|
||||||
"daemon_host": "127.0.0.1",\
|
|
||||||
"daemon_port": "3001",\
|
|
||||||
"mycall": "AA0AA-0",\
|
|
||||||
"mygrid": "JN40aa",\
|
|
||||||
"radiocontrol" : "disabled",\
|
|
||||||
"hamlib_deviceid": "RIG_MODEL_DUMMY_NOVFO",\
|
|
||||||
"hamlib_deviceport": "ignore",\
|
|
||||||
"hamlib_stop_bits": "ignore",\
|
|
||||||
"hamlib_data_bits": "ignore",\
|
|
||||||
"hamlib_handshake": "ignore",\
|
|
||||||
"hamlib_serialspeed": "ignore",\
|
|
||||||
"hamlib_dtrstate": "ignore",\
|
|
||||||
"hamlib_pttprotocol": "ignore",\
|
|
||||||
"hamlib_ptt_port": "ignore",\
|
|
||||||
"hamlib_dcd": "ignore",\
|
|
||||||
"hamlbib_serialspeed_ptt": "9600",\
|
|
||||||
"hamlib_rigctld_port" : "4532",\
|
|
||||||
"hamlib_rigctld_ip" : "127.0.0.1",\
|
|
||||||
"hamlib_rigctld_path" : "",\
|
|
||||||
"hamlib_rigctld_server_port" : "4532",\
|
|
||||||
"hamlib_rigctld_custom_args": "",\
|
|
||||||
"tci_port" : "50001",\
|
|
||||||
"tci_ip" : "127.0.0.1",\
|
|
||||||
"spectrum": "waterfall",\
|
|
||||||
"tnclocation": "localhost",\
|
|
||||||
"enable_scatter" : "False",\
|
|
||||||
"enable_fft" : "False",\
|
|
||||||
"enable_fsk" : "False",\
|
|
||||||
"low_bandwidth_mode" : "False",\
|
|
||||||
"theme" : "default",\
|
|
||||||
"screen_height" : 430,\
|
|
||||||
"screen_width" : 1050,\
|
|
||||||
"update_channel" : "latest",\
|
|
||||||
"beacon_interval" : 300,\
|
|
||||||
"received_files_folder" : "None",\
|
|
||||||
"tuning_range_fmin" : "-50.0",\
|
|
||||||
"tuning_range_fmax" : "50.0",\
|
|
||||||
"respond_to_cq" : "True",\
|
|
||||||
"rx_buffer_size" : "16", \
|
|
||||||
"enable_explorer" : "False", \
|
|
||||||
"wftheme": 2, \
|
|
||||||
"high_graphics" : "True",\
|
|
||||||
"explorer_stats" : "False", \
|
|
||||||
"auto_tune" : "False", \
|
|
||||||
"enable_is_writing" : "True", \
|
|
||||||
"shared_folder_path" : ".", \
|
|
||||||
"enable_request_profile" : "True", \
|
|
||||||
"enable_request_shared_folder" : "False", \
|
|
||||||
"max_retry_attempts" : 5, \
|
|
||||||
"enable_auto_retry" : "False", \
|
|
||||||
"tx_delay" : 0, \
|
|
||||||
"auto_start": 0, \
|
|
||||||
"enable_sys_notification": 1, \
|
|
||||||
"enable_mesh_features": "False" \
|
|
||||||
}';
|
|
||||||
|
|
||||||
if (!fs.existsSync(configPath)) {
|
|
||||||
fs.writeFileSync(configPath, configDefaultSettings);
|
|
||||||
}
|
|
||||||
|
|
||||||
// load settings
|
|
||||||
var config = require(configPath);
|
|
||||||
|
|
||||||
//config validation
|
|
||||||
// check running config against default config.
|
|
||||||
// if parameter not exists, add it to running config to prevent errors
|
|
||||||
sysInfo.info("CONFIG VALIDATION ----------------------------- ");
|
|
||||||
|
|
||||||
var parsedConfig = JSON.parse(configDefaultSettings);
|
|
||||||
for (key in parsedConfig) {
|
|
||||||
if (config.hasOwnProperty(key)) {
|
|
||||||
sysInfo.info("FOUND SETTTING [" + key + "]: " + config[key]);
|
|
||||||
} else {
|
|
||||||
sysInfo.error("MISSING SETTTING [" + key + "] : " + parsedConfig[key]);
|
|
||||||
config[key] = parsedConfig[key];
|
|
||||||
fs.writeFileSync(configPath, JSON.stringify(config, null, 2));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
sysInfo.info("------------------------------------------ ");
|
|
||||||
/*
|
|
||||||
var chatDB = path.join(configFolder, 'chatDB.json')
|
|
||||||
// create chat database file if not exists
|
|
||||||
const configContentChatDB = `
|
|
||||||
{ "chatDB" : [{
|
|
||||||
"id" : "00000000",
|
|
||||||
"timestamp" : 1234566,
|
|
||||||
"mycall" : "AA0AA",
|
|
||||||
"dxcall" : "AB0AB",
|
|
||||||
"dxgrid" : "JN1200",
|
|
||||||
"message" : "hallowelt"
|
|
||||||
}]
|
|
||||||
}
|
|
||||||
`;
|
|
||||||
if (!fs.existsSync(chatDB)) {
|
|
||||||
fs.writeFileSync(chatDB, configContentChatDB);
|
|
||||||
}
|
|
||||||
*/
|
|
||||||
|
|
||||||
/*
|
|
||||||
// Creates receivedFiles folder if not exists
|
|
||||||
// https://stackoverflow.com/a/26227660
|
|
||||||
var appDataFolder = process.env.HOME
|
|
||||||
var applicationFolder = path.join(appDataFolder, "FreeDATA");
|
|
||||||
var receivedFilesFolder = path.join(applicationFolder, "receivedFiles");
|
|
||||||
|
|
||||||
// https://stackoverflow.com/a/13544465
|
|
||||||
fs.mkdir(receivedFilesFolder, {
|
|
||||||
recursive: true
|
|
||||||
}, function(err) {
|
|
||||||
console.log(err);
|
|
||||||
});
|
|
||||||
|
|
||||||
*/
|
|
||||||
|
|
||||||
let win = null;
|
|
||||||
let logViewer = null;
|
|
||||||
var daemonProcess = null;
|
|
||||||
|
|
||||||
// create a splash screen
|
|
||||||
function createSplashScreen() {
|
|
||||||
splashScreen = new BrowserWindow({
|
|
||||||
height: 250,
|
|
||||||
width: 250,
|
|
||||||
transparent: true,
|
|
||||||
frame: false,
|
|
||||||
alwaysOnTop: true,
|
|
||||||
});
|
|
||||||
splashScreen.loadFile("src/splash.html");
|
|
||||||
splashScreen.center();
|
|
||||||
}
|
|
||||||
|
|
||||||
function createWindow() {
|
|
||||||
win = new BrowserWindow({
|
|
||||||
width: config.screen_width,
|
|
||||||
height: config.screen_height,
|
|
||||||
show: false,
|
|
||||||
autoHideMenuBar: true,
|
|
||||||
icon: "src/img/icon.png",
|
|
||||||
webPreferences: {
|
|
||||||
//preload: path.join(__dirname, 'preload-main.js'),
|
|
||||||
backgroundThrottle: false,
|
|
||||||
preload: require.resolve("./preload-main.js"),
|
|
||||||
nodeIntegration: true,
|
|
||||||
contextIsolation: false,
|
|
||||||
enableRemoteModule: false,
|
|
||||||
sandbox: false,
|
|
||||||
//https://stackoverflow.com/questions/53390798/opening-new-window-electron/53393655
|
|
||||||
//https://github.com/electron/remote
|
|
||||||
},
|
|
||||||
});
|
|
||||||
// hide menu bar
|
|
||||||
win.setMenuBarVisibility(false);
|
|
||||||
|
|
||||||
//open dev tools
|
|
||||||
/*win.webContents.openDevTools({
|
|
||||||
mode: 'undocked',
|
|
||||||
activate: true,
|
|
||||||
})
|
|
||||||
*/
|
|
||||||
win.loadFile("src/index.html");
|
|
||||||
|
|
||||||
|
|
||||||
// Emitted when the window is closed.
|
|
||||||
win.on("closed", function () {
|
|
||||||
console.log("closing all windows.....");
|
|
||||||
close_all();
|
|
||||||
});
|
|
||||||
|
|
||||||
win.once("ready-to-show", () => {
|
|
||||||
log.transports.file.level = "debug";
|
|
||||||
autoUpdater.logger = log.scope("updater");
|
|
||||||
|
|
||||||
autoUpdater.channel = config.update_channel;
|
|
||||||
|
|
||||||
autoUpdater.autoInstallOnAppQuit = false;
|
|
||||||
autoUpdater.autoDownload = true;
|
|
||||||
autoUpdater.checkForUpdatesAndNotify();
|
|
||||||
//autoUpdater.quitAndInstall();
|
|
||||||
});
|
|
||||||
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
app.whenReady().then(() => {
|
|
||||||
// show splash screen
|
|
||||||
createSplashScreen();
|
|
||||||
|
|
||||||
// create main window
|
|
||||||
createWindow();
|
|
||||||
|
|
||||||
// wait some time, then close splash screen and show main windows
|
|
||||||
setTimeout(function () {
|
|
||||||
splashScreen.close();
|
|
||||||
win.show();
|
|
||||||
}, 3000);
|
|
||||||
|
|
||||||
//Generate daemon binary path
|
|
||||||
var daemonPath = "";
|
|
||||||
switch (os.platform().toLowerCase()) {
|
|
||||||
case "darwin":
|
|
||||||
case "linux":
|
|
||||||
daemonPath = path.join(process.resourcesPath, "tnc", "freedata-daemon");
|
|
||||||
|
|
||||||
break;
|
|
||||||
case "win32":
|
|
||||||
case "win64":
|
|
||||||
daemonPath = path.join(
|
|
||||||
process.resourcesPath,
|
|
||||||
"tnc",
|
|
||||||
"freedata-daemon.exe",
|
|
||||||
);
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
console.log("Unhandled OS Platform: ", os.platform());
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
//Start daemon binary if it exists
|
|
||||||
if (fs.existsSync(daemonPath)) {
|
|
||||||
mainLog.info("Starting freedata-daemon binary");
|
|
||||||
daemonProcess = spawn(daemonPath, [], {
|
|
||||||
cwd: path.join(daemonPath, ".."),
|
|
||||||
});
|
|
||||||
// return process messages
|
|
||||||
daemonProcess.on("error", (err) => {
|
|
||||||
daemonProcessLog.error(`error when starting daemon: ${err}`);
|
|
||||||
});
|
|
||||||
daemonProcess.on("message", (data) => {
|
|
||||||
daemonProcessLog.info(`${data}`);
|
|
||||||
});
|
|
||||||
daemonProcess.stdout.on("data", (data) => {
|
|
||||||
daemonProcessLog.info(`${data}`);
|
|
||||||
});
|
|
||||||
daemonProcess.stderr.on("data", (data) => {
|
|
||||||
daemonProcessLog.info(`${data}`);
|
|
||||||
let arg = {
|
|
||||||
entry: `${data}`,
|
|
||||||
};
|
|
||||||
// send info to log only if log screen available
|
|
||||||
// it seems an error occurs when updating
|
|
||||||
if (logViewer !== null && logViewer !== "") {
|
|
||||||
try {
|
|
||||||
//logViewer.webContents.send("action-update-log", arg);
|
|
||||||
} catch (e) {
|
|
||||||
// empty for keeping error stuff silent
|
|
||||||
// this is important to avoid error messages if we are going to close the app while
|
|
||||||
// an logging information will be pushed to the logger
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
daemonProcess.on("close", (code) => {
|
|
||||||
daemonProcessLog.warn(`daemonProcess exited with code ${code}`);
|
|
||||||
});
|
|
||||||
} else {
|
|
||||||
daemonProcess = null;
|
|
||||||
daemonPath = null;
|
|
||||||
mainLog.info("Daemon binary doesn't exist--normal for dev environments.");
|
|
||||||
}
|
|
||||||
win.send("action-set-app-version", app.getVersion());
|
|
||||||
});
|
|
||||||
|
|
||||||
app.on("activate", () => {
|
|
||||||
if (BrowserWindow.getAllWindows().length === 0) {
|
|
||||||
createWindow();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
app.on("window-all-closed", () => {
|
|
||||||
close_all();
|
|
||||||
});
|
|
||||||
|
|
||||||
// IPC HANDLER
|
|
||||||
//Update configuration globally
|
|
||||||
ipcMain.on("set-config-global", (event, data) => {
|
|
||||||
config = data;
|
|
||||||
win.webContents.send("update-config", config);
|
|
||||||
|
|
||||||
});
|
|
||||||
|
|
||||||
//Show/update task bar/button progressbar
|
|
||||||
ipcMain.on("request-show-electron-progressbar", (event, data) => {
|
|
||||||
win.setProgressBar(data / 100);
|
|
||||||
});
|
|
||||||
|
|
||||||
|
|
||||||
ipcMain.on("request-update-dbclean-spinner", () => {
|
|
||||||
//Turn off dbclean spinner
|
|
||||||
win.webContents.send("action-update-dbclean-spinner");
|
|
||||||
});
|
|
||||||
|
|
||||||
// UPDATE TNC CONNECTION
|
|
||||||
ipcMain.on("request-update-tnc-ip", (event, data) => {
|
|
||||||
win.webContents.send("action-update-tnc-ip", data);
|
|
||||||
});
|
|
||||||
|
|
||||||
// UPDATE DAEMON CONNECTION
|
|
||||||
ipcMain.on("request-update-daemon-ip", (event, data) => {
|
|
||||||
win.webContents.send("action-update-daemon-ip", data);
|
|
||||||
});
|
|
||||||
|
|
||||||
ipcMain.on("request-update-tnc-state", (event, arg) => {
|
|
||||||
win.webContents.send("action-update-tnc-state", arg);
|
|
||||||
//meshViewer.send("action-update-mesh-table", arg);
|
|
||||||
//data.webContents.send('action-update-tnc-state', arg);
|
|
||||||
});
|
|
||||||
|
|
||||||
/*
|
|
||||||
ipcMain.on('request-update-data-state', (event, arg) => {
|
|
||||||
//win.webContents.send('action-update-data-state', arg);
|
|
||||||
//data.webContents.send('action-update-data-state', arg);
|
|
||||||
});
|
|
||||||
|
|
||||||
ipcMain.on('request-update-heard-stations', (event, arg) => {
|
|
||||||
win.webContents.send('action-update-heard-stations', arg);
|
|
||||||
});
|
|
||||||
*/
|
|
||||||
ipcMain.on("request-update-daemon-state", (event, arg) => {
|
|
||||||
win.webContents.send("action-update-daemon-state", arg);
|
|
||||||
});
|
|
||||||
|
|
||||||
ipcMain.on("request-update-hamlib-test", (event, arg) => {
|
|
||||||
win.webContents.send("action-update-hamlib-test", arg);
|
|
||||||
});
|
|
||||||
|
|
||||||
ipcMain.on("request-update-tnc-connection", (event, arg) => {
|
|
||||||
win.webContents.send("action-update-tnc-connection", arg);
|
|
||||||
});
|
|
||||||
|
|
||||||
ipcMain.on("request-update-daemon-connection", (event, arg) => {
|
|
||||||
win.webContents.send("action-update-daemon-connection", arg);
|
|
||||||
});
|
|
||||||
|
|
||||||
ipcMain.on("run-tnc-command", (event, arg) => {
|
|
||||||
win.webContents.send("run-tnc-command", arg);
|
|
||||||
});
|
|
||||||
|
|
||||||
ipcMain.on("tnc-fec-iswriting", (event, arg) => {
|
|
||||||
win.webContents.send("run-tnc-command-fec-iswriting");
|
|
||||||
});
|
|
||||||
|
|
||||||
ipcMain.on("request-update-rx-buffer", (event, arg) => {
|
|
||||||
win.webContents.send("action-update-rx-buffer", arg);
|
|
||||||
});
|
|
||||||
|
|
||||||
/*
|
|
||||||
ipcMain.on('request-update-rx-msg-buffer', (event, arg) => {
|
|
||||||
win.webContents.send('action-update-rx-msg-buffer', arg);
|
|
||||||
});
|
|
||||||
*/
|
|
||||||
ipcMain.on("request-new-msg-received", (event, arg) => {
|
|
||||||
win.webContents.send("action-new-msg-received", arg);
|
|
||||||
});
|
|
||||||
ipcMain.on("request-update-transmission-status", (event, arg) => {
|
|
||||||
//chat.webContents.send("action-update-transmission-status", arg);
|
|
||||||
win.webContents.send("action-update-transmission-status", arg);
|
|
||||||
});
|
|
||||||
|
|
||||||
ipcMain.on("request-update-reception-status", (event, arg) => {
|
|
||||||
win.webContents.send("action-update-reception-status", arg);
|
|
||||||
//chat.webContents.send("action-update-reception-status", arg);
|
|
||||||
});
|
|
||||||
|
|
||||||
//Called by main to query chat if there are new messages
|
|
||||||
ipcMain.on("request-update-unread-messages", () => {
|
|
||||||
//mainLog.info("Got request to check if chat has new messages")
|
|
||||||
win.webContents.send("action-update-unread-messages");
|
|
||||||
});
|
|
||||||
//Called by chat to notify main if there are new messages
|
|
||||||
ipcMain.on("request-update-unread-messages-main", (event, arg) => {
|
|
||||||
win.webContents.send("action-update-unread-messages-main", arg);
|
|
||||||
//mainLog.info("Received reply from chat and ?new messages = " +arg);
|
|
||||||
});
|
|
||||||
|
|
||||||
//Called by main to notify chat we should clean the DB
|
|
||||||
ipcMain.on("request-clean-db", () => {
|
|
||||||
win.webContents.send("action-clean-db");
|
|
||||||
});
|
|
||||||
|
|
||||||
|
|
||||||
//file selector
|
|
||||||
ipcMain.on("get-file-path", (event, data) => {
|
|
||||||
dialog
|
|
||||||
.showOpenDialog({
|
|
||||||
defaultPath: path.join(__dirname, "../"),
|
|
||||||
buttonLabel: "Select File",
|
|
||||||
properties: ["openFile"],
|
|
||||||
})
|
|
||||||
.then((filePaths) => {
|
|
||||||
if (filePaths.canceled == false) {
|
|
||||||
win.webContents.send(data.action, { path: filePaths });
|
|
||||||
}
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
//folder selector
|
|
||||||
ipcMain.on("get-folder-path", (event, data) => {
|
|
||||||
dialog
|
|
||||||
.showOpenDialog({
|
|
||||||
defaultPath: path.join(__dirname, "../"),
|
|
||||||
buttonLabel: "Select folder",
|
|
||||||
properties: ["openDirectory"],
|
|
||||||
})
|
|
||||||
.then((folderPaths) => {
|
|
||||||
if (folderPaths.canceled == false) {
|
|
||||||
win.webContents.send(data.action, { path: folderPaths });
|
|
||||||
//win.webContents.send(data.action, { path: filePaths });
|
|
||||||
}
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
//open folder
|
|
||||||
ipcMain.on("open-folder", (event, data) => {
|
|
||||||
shell.showItemInFolder(data.path);
|
|
||||||
});
|
|
||||||
|
|
||||||
//select file
|
|
||||||
ipcMain.on("select-file", (event, data) => {
|
|
||||||
dialog
|
|
||||||
.showOpenDialog({
|
|
||||||
defaultPath: path.join(__dirname, "../"),
|
|
||||||
buttonLabel: "Select file",
|
|
||||||
properties: ["openFile"],
|
|
||||||
})
|
|
||||||
.then((filepath) => {
|
|
||||||
console.log(filepath.filePaths[0]);
|
|
||||||
|
|
||||||
try {
|
|
||||||
//fs.readFile(filepath.filePaths[0], 'utf8', function (err, data) {
|
|
||||||
//Has to be binary
|
|
||||||
fs.readFile(filepath.filePaths[0], "binary", function (err, data) {
|
|
||||||
console.log(data.length);
|
|
||||||
|
|
||||||
console.log(data);
|
|
||||||
|
|
||||||
var filename = path.basename(filepath.filePaths[0]);
|
|
||||||
var mimeType = mime.getType(filename);
|
|
||||||
console.log(mimeType);
|
|
||||||
if (mimeType == "" || mimeType == null) {
|
|
||||||
mimeType = "plain/text";
|
|
||||||
}
|
|
||||||
|
|
||||||
win.webContents.send("return-selected-files", {
|
|
||||||
data: data,
|
|
||||||
mime: mimeType,
|
|
||||||
filename: filename,
|
|
||||||
});
|
|
||||||
|
|
||||||
});
|
|
||||||
} catch (err) {
|
|
||||||
console.log(err);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
//select image file
|
|
||||||
ipcMain.on("select-user-image", (event, data) => {
|
|
||||||
dialog
|
|
||||||
.showOpenDialog({
|
|
||||||
defaultPath: path.join(__dirname, "../"),
|
|
||||||
buttonLabel: "Select file",
|
|
||||||
properties: ["openFile"],
|
|
||||||
})
|
|
||||||
.then((filepath) => {
|
|
||||||
console.log(filepath.filePaths[0]);
|
|
||||||
|
|
||||||
try {
|
|
||||||
// read data as base64 which makes conversion to blob easier
|
|
||||||
fs.readFile(filepath.filePaths[0], "base64", function (err, data) {
|
|
||||||
var filename = path.basename(filepath.filePaths[0]);
|
|
||||||
var mimeType = mime.getType(filename);
|
|
||||||
|
|
||||||
if (mimeType == "" || mimeType == null) {
|
|
||||||
mimeType = "plain/text";
|
|
||||||
}
|
|
||||||
|
|
||||||
win.webContents.send("return-select-user-image", {
|
|
||||||
data: data,
|
|
||||||
mime: mimeType,
|
|
||||||
filename: filename,
|
|
||||||
});
|
|
||||||
|
|
||||||
});
|
|
||||||
} catch (err) {
|
|
||||||
console.log(err);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
// read files in folder - use case "shared folder"
|
|
||||||
ipcMain.on("read-files-in-folder", (event, data) => {
|
|
||||||
let fileList = [];
|
|
||||||
if (config["enable_request_shared_folder"].toLowerCase() == "false") {
|
|
||||||
//mainLog.info("Shared file folder is disable, not populating fileList");
|
|
||||||
|
|
||||||
win.webContents.send("return-shared-folder-files", {
|
|
||||||
files: fileList,
|
|
||||||
});
|
|
||||||
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
let folder = data.folder;
|
|
||||||
let files = fs.readdirSync(folder);
|
|
||||||
console.log(folder);
|
|
||||||
console.log(files);
|
|
||||||
files.forEach((file) => {
|
|
||||||
try {
|
|
||||||
let filePath = folder + "/" + file;
|
|
||||||
if (fs.lstatSync(filePath).isFile()) {
|
|
||||||
let fileSizeInBytes = fs.statSync(filePath).size;
|
|
||||||
let extension = path.extname(filePath);
|
|
||||||
fileList.push({
|
|
||||||
name: file,
|
|
||||||
extension: extension.substring(1),
|
|
||||||
size: fileSizeInBytes,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
} catch (err) {
|
|
||||||
console.log(err);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
win.webContents.send("return-shared-folder-files", {
|
|
||||||
files: fileList,
|
|
||||||
});
|
|
||||||
|
|
||||||
});
|
|
||||||
|
|
||||||
//save file to folder
|
|
||||||
ipcMain.on("save-file-to-folder", (event, data) => {
|
|
||||||
console.log(data.file);
|
|
||||||
|
|
||||||
dialog.showSaveDialog({ defaultPath: data.filename }).then((filepath) => {
|
|
||||||
console.log(filepath.filePath);
|
|
||||||
console.log(data.file);
|
|
||||||
|
|
||||||
try {
|
|
||||||
let arraybuffer = Buffer.from(data.file, "base64").toString("utf-8");
|
|
||||||
console.log(arraybuffer);
|
|
||||||
//Has to be binary
|
|
||||||
fs.writeFile(
|
|
||||||
filepath.filePath,
|
|
||||||
arraybuffer,
|
|
||||||
"binary",
|
|
||||||
function (err, data) {},
|
|
||||||
);
|
|
||||||
} catch (err) {
|
|
||||||
console.log(err);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
//tnc messages START --------------------------------------
|
|
||||||
|
|
||||||
// FEC iswriting received
|
|
||||||
ipcMain.on("request-show-fec-toast-iswriting", (event, data) => {
|
|
||||||
win.webContents.send("action-show-fec-toast-iswriting", data);
|
|
||||||
win.webContents.send("action-show-feciswriting", data);
|
|
||||||
});
|
|
||||||
|
|
||||||
// CQ TRANSMITTING
|
|
||||||
ipcMain.on("request-show-cq-toast-transmitting", (event, data) => {
|
|
||||||
win.webContents.send("action-show-cq-toast-transmitting", data);
|
|
||||||
});
|
|
||||||
|
|
||||||
// CQ RECEIVED
|
|
||||||
ipcMain.on("request-show-cq-toast-received", (event, data) => {
|
|
||||||
win.webContents.send("action-show-cq-toast-received", data);
|
|
||||||
});
|
|
||||||
|
|
||||||
// QRV TRANSMITTING
|
|
||||||
ipcMain.on("request-show-qrv-toast-transmitting", (event, data) => {
|
|
||||||
win.webContents.send("action-show-qrv-toast-transmitting", data);
|
|
||||||
});
|
|
||||||
|
|
||||||
// QRV RECEIVED
|
|
||||||
ipcMain.on("request-show-qrv-toast-received", (event, data) => {
|
|
||||||
win.webContents.send("action-show-qrv-toast-received", data);
|
|
||||||
});
|
|
||||||
|
|
||||||
// BEACON TRANSMITTING
|
|
||||||
ipcMain.on("request-show-beacon-toast-transmitting", (event, data) => {
|
|
||||||
win.webContents.send("action-show-beacon-toast-transmitting", data);
|
|
||||||
});
|
|
||||||
|
|
||||||
// BEACON RECEIVED
|
|
||||||
ipcMain.on("request-show-beacon-toast-received", (event, data) => {
|
|
||||||
win.webContents.send("action-show-beacon-toast-received", data);
|
|
||||||
});
|
|
||||||
|
|
||||||
// PING TRANSMITTING
|
|
||||||
ipcMain.on("request-show-ping-toast-transmitting", (event, data) => {
|
|
||||||
win.webContents.send("action-show-ping-toast-transmitting", data);
|
|
||||||
});
|
|
||||||
|
|
||||||
// PING RECEIVED
|
|
||||||
ipcMain.on("request-show-ping-toast-received", (event, data) => {
|
|
||||||
win.webContents.send("action-show-ping-toast-received", data);
|
|
||||||
});
|
|
||||||
|
|
||||||
// PING RECEIVED ACK
|
|
||||||
ipcMain.on("request-show-ping-toast-received-ack", (event, data) => {
|
|
||||||
win.webContents.send("action-show-ping-toast-received-ack", data);
|
|
||||||
});
|
|
||||||
|
|
||||||
// ARQ DATA CHANNEL OPENING
|
|
||||||
ipcMain.on("request-show-arq-toast-datachannel-opening", (event, data) => {
|
|
||||||
win.webContents.send("action-show-arq-toast-datachannel-opening", data);
|
|
||||||
});
|
|
||||||
|
|
||||||
// ARQ DATA CHANNEL WAITING
|
|
||||||
ipcMain.on("request-show-arq-toast-datachannel-waiting", (event, data) => {
|
|
||||||
win.webContents.send("action-show-arq-toast-datachannel-waiting", data);
|
|
||||||
});
|
|
||||||
|
|
||||||
// ARQ DATA CHANNEL OPEN
|
|
||||||
ipcMain.on("request-show-arq-toast-datachannel-opened", (event, data) => {
|
|
||||||
win.webContents.send("action-show-arq-toast-datachannel-opened", data);
|
|
||||||
});
|
|
||||||
|
|
||||||
// ARQ DATA RECEIVED OPENER
|
|
||||||
ipcMain.on(
|
|
||||||
"request-show-arq-toast-datachannel-received-opener",
|
|
||||||
(event, data) => {
|
|
||||||
win.webContents.send(
|
|
||||||
"action-show-arq-toast-datachannel-received-opener",
|
|
||||||
data,
|
|
||||||
);
|
|
||||||
},
|
|
||||||
);
|
|
||||||
|
|
||||||
// ARQ TRANSMISSION FAILED
|
|
||||||
ipcMain.on("request-show-arq-toast-transmission-failed", (event, data) => {
|
|
||||||
win.webContents.send("action-show-arq-toast-transmission-failed", data);
|
|
||||||
});
|
|
||||||
|
|
||||||
// ARQ TRANSMISSION FAILED
|
|
||||||
ipcMain.on("request-show-arq-toast-transmission-failed-ver", (event, data) => {
|
|
||||||
win.webContents.send("action-show-arq-toast-transmission-failed-ver", data);
|
|
||||||
});
|
|
||||||
|
|
||||||
// ARQ TRANSMISSION RECEIVING
|
|
||||||
ipcMain.on("request-show-arq-toast-transmission-receiving", (event, data) => {
|
|
||||||
win.webContents.send("action-show-arq-toast-transmission-receiving", data);
|
|
||||||
});
|
|
||||||
|
|
||||||
// ARQ TRANSMISSION RECEIVED
|
|
||||||
ipcMain.on("request-show-arq-toast-transmission-received", (event, data) => {
|
|
||||||
win.webContents.send("action-show-arq-toast-transmission-received", data);
|
|
||||||
});
|
|
||||||
|
|
||||||
// ARQ TRANSMISSION TRANSMITTING
|
|
||||||
ipcMain.on(
|
|
||||||
"request-show-arq-toast-transmission-transmitting",
|
|
||||||
(event, data) => {
|
|
||||||
win.webContents.send(
|
|
||||||
"action-show-arq-toast-transmission-transmitting",
|
|
||||||
data,
|
|
||||||
);
|
|
||||||
},
|
|
||||||
);
|
|
||||||
|
|
||||||
// ARQ TRANSMISSION TRANSMITTED
|
|
||||||
ipcMain.on("request-show-arq-toast-transmission-transmitted", (event, data) => {
|
|
||||||
win.webContents.send("action-show-arq-toast-transmission-transmitted", data);
|
|
||||||
});
|
|
||||||
|
|
||||||
// ARQ SESSION CONNECTING
|
|
||||||
ipcMain.on("request-show-arq-toast-session-connecting", (event, data) => {
|
|
||||||
win.webContents.send("action-show-arq-toast-session-connecting", data);
|
|
||||||
});
|
|
||||||
|
|
||||||
// ARQ SESSION WAITING
|
|
||||||
ipcMain.on("request-show-arq-toast-session-waiting", (event, data) => {
|
|
||||||
win.webContents.send("action-show-arq-toast-session-waiting", data);
|
|
||||||
});
|
|
||||||
|
|
||||||
// ARQ SESSION CONNECTED
|
|
||||||
ipcMain.on("request-show-arq-toast-session-connected", (event, data) => {
|
|
||||||
win.webContents.send("action-show-arq-toast-session-connected", data);
|
|
||||||
});
|
|
||||||
|
|
||||||
// ARQ SESSION CLOSE
|
|
||||||
ipcMain.on("request-show-arq-toast-session-close", (event, data) => {
|
|
||||||
win.webContents.send("action-show-arq-toast-session-close", data);
|
|
||||||
});
|
|
||||||
|
|
||||||
// ARQ SESSION FAILED
|
|
||||||
ipcMain.on("request-show-arq-toast-session-failed", (event, data) => {
|
|
||||||
win.webContents.send("action-show-arq-toast-session-failed", data);
|
|
||||||
});
|
|
||||||
|
|
||||||
//tnc messages END --------------------------------------
|
|
||||||
|
|
||||||
//restart and install udpate
|
|
||||||
ipcMain.on("request-restart-and-install", (event, data) => {
|
|
||||||
close_sub_processes();
|
|
||||||
autoUpdater.quitAndInstall();
|
|
||||||
});
|
|
||||||
|
|
||||||
// LISTENER FOR UPDATER EVENTS
|
|
||||||
autoUpdater.on("update-available", (info) => {
|
|
||||||
mainLog.info("update available");
|
|
||||||
|
|
||||||
let arg = {
|
|
||||||
status: "update-available",
|
|
||||||
info: info,
|
|
||||||
};
|
|
||||||
win.webContents.send("action-updater", arg);
|
|
||||||
});
|
|
||||||
|
|
||||||
autoUpdater.on("update-not-available", (info) => {
|
|
||||||
mainLog.info("update not available");
|
|
||||||
let arg = {
|
|
||||||
status: "update-not-available",
|
|
||||||
info: info,
|
|
||||||
};
|
|
||||||
win.webContents.send("action-updater", arg);
|
|
||||||
});
|
|
||||||
|
|
||||||
autoUpdater.on("update-downloaded", (info) => {
|
|
||||||
mainLog.info("update downloaded");
|
|
||||||
let arg = {
|
|
||||||
status: "update-downloaded",
|
|
||||||
info: info,
|
|
||||||
};
|
|
||||||
win.webContents.send("action-updater", arg);
|
|
||||||
// we need to call this at this point.
|
|
||||||
// if an update is available and we are force closing the app
|
|
||||||
// the entire screen crashes...
|
|
||||||
//mainLog.info('quit application and install update');
|
|
||||||
//autoUpdater.quitAndInstall();
|
|
||||||
});
|
|
||||||
|
|
||||||
autoUpdater.on("checking-for-update", () => {
|
|
||||||
mainLog.info("checking for update");
|
|
||||||
let arg = {
|
|
||||||
status: "checking-for-update",
|
|
||||||
version: app.getVersion(),
|
|
||||||
};
|
|
||||||
win.webContents.send("action-updater", arg);
|
|
||||||
});
|
|
||||||
|
|
||||||
autoUpdater.on("download-progress", (progress) => {
|
|
||||||
let arg = {
|
|
||||||
status: "download-progress",
|
|
||||||
progress: progress,
|
|
||||||
};
|
|
||||||
win.webContents.send("action-updater", arg);
|
|
||||||
});
|
|
||||||
|
|
||||||
autoUpdater.on("error", (error) => {
|
|
||||||
mainLog.info("update error");
|
|
||||||
let arg = {
|
|
||||||
status: "error",
|
|
||||||
progress: error,
|
|
||||||
};
|
|
||||||
win.webContents.send("action-updater", arg);
|
|
||||||
mainLog.error("AUTO UPDATER : " + error);
|
|
||||||
});
|
|
||||||
|
|
||||||
function close_sub_processes() {
|
|
||||||
mainLog.warn("closing sub processes");
|
|
||||||
|
|
||||||
// closing the tnc binary if not closed when closing application and also our daemon which has been started by the gui
|
|
||||||
try {
|
|
||||||
if (daemonProcess != null) {
|
|
||||||
daemonProcess.kill();
|
|
||||||
}
|
|
||||||
} catch (e) {
|
|
||||||
mainLog.error(e);
|
|
||||||
}
|
|
||||||
|
|
||||||
mainLog.warn("closing tnc and daemon");
|
|
||||||
try {
|
|
||||||
if (os.platform() == "win32" || os.platform() == "win64") {
|
|
||||||
spawn("Taskkill", ["/IM", "freedata-tnc.exe", "/F"]);
|
|
||||||
spawn("Taskkill", ["/IM", "freedata-daemon.exe", "/F"]);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (os.platform() == "linux") {
|
|
||||||
spawn("pkill", ["-9", "freedata-tnc"]);
|
|
||||||
spawn("pkill", ["-9", "freedata-daemon"]);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (os.platform() == "darwin") {
|
|
||||||
spawn("pkill", ["-9", "freedata-tnc"]);
|
|
||||||
spawn("pkill", ["-9", "freedata-daemon"]);
|
|
||||||
}
|
|
||||||
} catch (e) {
|
|
||||||
mainLog.error(e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function close_all() {
|
|
||||||
// function for closing the application with closing all used processes
|
|
||||||
|
|
||||||
close_sub_processes();
|
|
||||||
|
|
||||||
mainLog.warn("quitting app");
|
|
||||||
|
|
||||||
win.destroy();
|
|
||||||
|
|
||||||
|
|
||||||
app.quit();
|
|
||||||
}
|
|
||||||
|
|
||||||
// RUN RIGCTLD
|
|
||||||
ipcMain.on("request-start-rigctld", (event, data) => {
|
|
||||||
try {
|
|
||||||
let rigctld_proc = spawn(data.path, data.parameters, {
|
|
||||||
windowsVerbatimArguments: true,
|
|
||||||
});
|
|
||||||
|
|
||||||
rigctld_proc.on("exit", function (code) {
|
|
||||||
console.log("rigctld process exited with code " + code);
|
|
||||||
|
|
||||||
// if rigctld crashes, error code is -2
|
|
||||||
// then we are going to restart rigctld
|
|
||||||
// this "fixes" a problem with latest rigctld on raspberry pi
|
|
||||||
//if (code == -2){
|
|
||||||
// setTimeout(ipcRenderer.send('request-start-rigctld', data), 500);
|
|
||||||
//}
|
|
||||||
//let rigctld_proc = spawn(data.path, data.parameters);
|
|
||||||
});
|
|
||||||
} catch (e) {
|
|
||||||
console.log(e);
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
const rigctld = exec(data.path, data.parameters);
|
|
||||||
rigctld.stdout.on("data", data => {
|
|
||||||
console.log(`stdout: ${data}`);
|
|
||||||
});
|
|
||||||
*/
|
|
||||||
});
|
|
||||||
|
|
||||||
// STOP RIGCTLD
|
|
||||||
ipcMain.on("request-stop-rigctld", (event, data) => {
|
|
||||||
mainLog.warn("closing rigctld");
|
|
||||||
try {
|
|
||||||
if (os.platform() == "win32" || os.platform() == "win64") {
|
|
||||||
spawn("Taskkill", ["/IM", "rigctld.exe", "/F"]);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (os.platform() == "linux") {
|
|
||||||
spawn("pkill", ["-9", "rigctld"]);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (os.platform() == "darwin") {
|
|
||||||
spawn("pkill", ["-9", "rigctld"]);
|
|
||||||
}
|
|
||||||
} catch (e) {
|
|
||||||
mainLog.error(e);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
// CHECK RIGCTLD CONNECTION
|
|
||||||
// create new socket so we are not reopening every time a new one
|
|
||||||
var rigctld_connection = new net.Socket();
|
|
||||||
var rigctld_connection_state = false;
|
|
||||||
var rigctld_events_wired = false;
|
|
||||||
|
|
||||||
ipcMain.on("request-check-rigctld", (event, data) => {
|
|
||||||
try {
|
|
||||||
let Data = {
|
|
||||||
state: "unknown",
|
|
||||||
};
|
|
||||||
|
|
||||||
if (!rigctld_connection_state) {
|
|
||||||
rigctld_connection = new net.Socket();
|
|
||||||
rigctld_events_wired = false;
|
|
||||||
rigctld_connection.connect(data.port, data.ip);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Check if we have created a new socket object and attach listeners if not already created
|
|
||||||
if (typeof rigctld_connection != "undefined" && !rigctld_events_wired) {
|
|
||||||
rigctld_connection.on("connect", function () {
|
|
||||||
rigctld_events_wired = true;
|
|
||||||
mainLog.info("Starting rigctld event listeners");
|
|
||||||
rigctld_connection_state = true;
|
|
||||||
Data["state"] = "Connected";
|
|
||||||
Data["active"] = true;
|
|
||||||
if (win !== null && win !== "" && typeof win != "undefined") {
|
|
||||||
// try catch for being sure we have a clean app close
|
|
||||||
try {
|
|
||||||
win.webContents.send("action-check-rigctld", Data);
|
|
||||||
} catch (e) {
|
|
||||||
console.log(e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
rigctld_connection.on("error", function () {
|
|
||||||
rigctld_connection_state = false;
|
|
||||||
Data["state"] = "Not Connected";
|
|
||||||
Data["active"] = false;
|
|
||||||
if (win !== null && win !== "" && typeof win != "undefined") {
|
|
||||||
// try catch for being sure we have a clean app close
|
|
||||||
try {
|
|
||||||
win.webContents.send("action-check-rigctld", Data);
|
|
||||||
} catch (e) {
|
|
||||||
console.log(e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
rigctld_connection.on("end", function () {
|
|
||||||
rigctld_connection_state = false;
|
|
||||||
});
|
|
||||||
}
|
|
||||||
} catch (e) {
|
|
||||||
console.log(e);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
function downloadJsonUrlToFile(url, callsignPath) {
|
|
||||||
// https://nodejs.org/api/https.html#httpsgetoptions-callback
|
|
||||||
|
|
||||||
https
|
|
||||||
.get(url, (res) => {
|
|
||||||
//console.log("statusCode:", res.statusCode);
|
|
||||||
//console.log("headers:", res.headers);
|
|
||||||
|
|
||||||
res.on("data", (d) => {
|
|
||||||
//console.log(d);
|
|
||||||
let json = JSON.parse(d);
|
|
||||||
fs.writeFileSync(callsignPath, JSON.stringify(json, null, 2));
|
|
||||||
sysInfo.info(
|
|
||||||
"Download " + url + " return statuscode: " + res.statusCode,
|
|
||||||
);
|
|
||||||
});
|
|
||||||
})
|
|
||||||
.on("error", (e) => {
|
|
||||||
console.error(e);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
function downloadCallsignReverseLookupData() {
|
|
||||||
sysInfo.info("Downloading callsigns.json");
|
|
||||||
var callsignPath = path.join(configFolder, "callsigns.json");
|
|
||||||
downloadJsonUrlToFile(
|
|
||||||
"https://api.freedata.app/callsign_lookup.php",
|
|
||||||
callsignPath,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
downloadCallsignReverseLookupData();
|
|
104
gui/package.json
104
gui/package.json
|
@ -1,15 +1,17 @@
|
||||||
{
|
{
|
||||||
"name": "FreeDATA",
|
"name": "FreeDATA",
|
||||||
"version": "0.10.2-alpha.1",
|
|
||||||
"description": "FreeDATA ",
|
"description": "FreeDATA ",
|
||||||
"main": "main.js",
|
"private": true,
|
||||||
|
"version": "0.11.0-alpha",
|
||||||
|
"main": "dist-electron/main/index.js",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"start": "electron .",
|
"start": "git pull && npm i && vite",
|
||||||
"test": "echo \"Error: no test specified\" && exit 1"
|
"dev": "vite",
|
||||||
},
|
"check" : "vue-tsc --noEmit",
|
||||||
"engines": {
|
"build": "vue-tsc --noEmit && vite build && electron-builder",
|
||||||
"node": ">=18.17.0",
|
"preview": "vite preview",
|
||||||
"npm": ">=9.0.0"
|
"lint": "eslint --ext .js,.vue src",
|
||||||
|
"lint-fix": "eslint --ext .js,.vue --fix src"
|
||||||
},
|
},
|
||||||
"repository": {
|
"repository": {
|
||||||
"type": "git",
|
"type": "git",
|
||||||
|
@ -28,9 +30,8 @@
|
||||||
},
|
},
|
||||||
"homepage": "https://freedata.app",
|
"homepage": "https://freedata.app",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@electron/asar": "^3.2.4",
|
"@electron/notarize": "^2.1.0",
|
||||||
"@electron/osx-sign": "^1.0.4",
|
"@vueuse/electron": "^10.4.1",
|
||||||
"@popperjs/core": "^2.11.8",
|
|
||||||
"blob-util": "^2.0.2",
|
"blob-util": "^2.0.2",
|
||||||
"bootstrap": "^5.3.1",
|
"bootstrap": "^5.3.1",
|
||||||
"bootstrap-icons": "^1.10.5",
|
"bootstrap-icons": "^1.10.5",
|
||||||
|
@ -38,73 +39,44 @@
|
||||||
"browser-image-compression": "^2.0.2",
|
"browser-image-compression": "^2.0.2",
|
||||||
"chart.js": "^4.3.3",
|
"chart.js": "^4.3.3",
|
||||||
"chartjs-plugin-annotation": "^3.0.1",
|
"chartjs-plugin-annotation": "^3.0.1",
|
||||||
|
"electron-builder-notarize": "^1.5.1",
|
||||||
"electron-log": "^4.4.8",
|
"electron-log": "^4.4.8",
|
||||||
"electron-updater": "^6.1.1",
|
"electron-updater": "^6.1.4",
|
||||||
"emoji-picker-element": "^1.18.3",
|
"emoji-picker-element": "^1.18.3",
|
||||||
"emoji-picker-element-data": "^1.4.0",
|
"emoji-picker-element-data": "^1.4.0",
|
||||||
"express-pouchdb": "^4.2.0",
|
"file-saver": "^2.0.5",
|
||||||
"mime": "^3.0.0",
|
"mime": "^3.0.0",
|
||||||
|
"pinia": "^2.1.6",
|
||||||
"pouchdb": "^8.0.1",
|
"pouchdb": "^8.0.1",
|
||||||
"pouchdb-browser": "^8.0.1",
|
"pouchdb-browser": "^8.0.1",
|
||||||
"pouchdb-express-router": "^0.0.11",
|
|
||||||
"pouchdb-find": "^8.0.1",
|
"pouchdb-find": "^8.0.1",
|
||||||
"pouchdb-replication": "^8.0.1",
|
|
||||||
"pouchdb-upsert": "^2.2.0",
|
"pouchdb-upsert": "^2.2.0",
|
||||||
"qth-locator": "^2.1.0",
|
"qth-locator": "^2.1.0",
|
||||||
"utf8": "^3.0.0",
|
"sass": "^1.66.1",
|
||||||
"uuid": "^9.0.0"
|
"socket.io": "^4.7.2",
|
||||||
|
"uuid": "^9.0.0",
|
||||||
|
"vue": "^3.3.4",
|
||||||
|
"vue-chartjs": "^5.2.0",
|
||||||
|
"vuemoji-picker": "^0.2.0"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@electron/notarize": "^2.1.0",
|
"@typescript-eslint/eslint-plugin": "^6.7.4",
|
||||||
|
"@vitejs/plugin-vue": "^4.4.0",
|
||||||
"electron": "^27.0.0",
|
"electron": "^27.0.0",
|
||||||
"electron-builder": "^24.6.3",
|
"electron-builder": "^24.6.3",
|
||||||
"electron-builder-notarize": "^1.5.1"
|
"eslint": "^8.50.0",
|
||||||
},
|
"eslint-config-prettier": "^9.0.0",
|
||||||
"build": {
|
"eslint-config-standard-with-typescript": "^39.1.0",
|
||||||
"productName": "FreeDATA",
|
"eslint-plugin-import": "^2.28.1",
|
||||||
"appId": "app.freedata",
|
"eslint-plugin-n": "^16.1.0",
|
||||||
"afterSign": "electron-builder-notarize",
|
"eslint-plugin-prettier": "^5.0.0",
|
||||||
"npmRebuild": "false",
|
"eslint-plugin-promise": "^6.1.1",
|
||||||
"directories": {
|
"eslint-plugin-vue": "^9.17.0",
|
||||||
"buildResources": "build",
|
"typescript": "^5.2.2",
|
||||||
"output": "dist"
|
"vite": "^4.3.2",
|
||||||
},
|
"vite-plugin-electron": "^0.14.0",
|
||||||
"mac": {
|
"vite-plugin-electron-renderer": "^0.14.5",
|
||||||
"target": [
|
"vue": "^3.3.4",
|
||||||
"default"
|
"vue-tsc": "^1.4.2"
|
||||||
],
|
|
||||||
"icon": "build/icon.png",
|
|
||||||
"hardenedRuntime": true,
|
|
||||||
"entitlements": "build/entitlements.plist",
|
|
||||||
"entitlementsInherit": "build/entitlements.plist",
|
|
||||||
"gatekeeperAssess": false
|
|
||||||
},
|
|
||||||
"win": {
|
|
||||||
"icon": "build/icon.png",
|
|
||||||
"target": [
|
|
||||||
"nsis"
|
|
||||||
]
|
|
||||||
},
|
|
||||||
"linux": {
|
|
||||||
"icon": "build/icon.png",
|
|
||||||
"target": [
|
|
||||||
"AppImage"
|
|
||||||
],
|
|
||||||
"category": "Development"
|
|
||||||
},
|
|
||||||
"publish": {
|
|
||||||
"provider": "github",
|
|
||||||
"releaseType": "release"
|
|
||||||
},
|
|
||||||
"extraResources": [
|
|
||||||
{
|
|
||||||
"from": "../tnc/dist/tnc/",
|
|
||||||
"to": "tnc",
|
|
||||||
"filter": [
|
|
||||||
"**/*",
|
|
||||||
"!**/.git"
|
|
||||||
]
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
2967
gui/preload-chat.js
2967
gui/preload-chat.js
File diff suppressed because it is too large
Load diff
|
@ -1,186 +0,0 @@
|
||||||
const path = require("path");
|
|
||||||
const { ipcRenderer } = require("electron");
|
|
||||||
|
|
||||||
// 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");
|
|
||||||
var configPath = path.join(configFolder, "config.json");
|
|
||||||
const config = require(configPath);
|
|
||||||
|
|
||||||
// WINDOW LISTENER
|
|
||||||
window.addEventListener("DOMContentLoaded", () => {
|
|
||||||
document
|
|
||||||
.getElementById("enable_filter_info")
|
|
||||||
.addEventListener("click", () => {
|
|
||||||
if (document.getElementById("enable_filter_info").checked) {
|
|
||||||
display_class("table-info", true);
|
|
||||||
} else {
|
|
||||||
display_class("table-info", false);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
document
|
|
||||||
.getElementById("enable_filter_debug")
|
|
||||||
.addEventListener("click", () => {
|
|
||||||
if (document.getElementById("enable_filter_debug").checked) {
|
|
||||||
display_class("table-debug", true);
|
|
||||||
} else {
|
|
||||||
display_class("table-debug", false);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
document
|
|
||||||
.getElementById("enable_filter_warning")
|
|
||||||
.addEventListener("click", () => {
|
|
||||||
if (document.getElementById("enable_filter_warning").checked) {
|
|
||||||
display_class("table-warning", true);
|
|
||||||
} else {
|
|
||||||
display_class("table-warning", false);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
document
|
|
||||||
.getElementById("enable_filter_error")
|
|
||||||
.addEventListener("click", () => {
|
|
||||||
if (document.getElementById("enable_filter_error").checked) {
|
|
||||||
display_class("table-danger", true);
|
|
||||||
} else {
|
|
||||||
display_class("table-danger", false);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
function display_class(class_name, state) {
|
|
||||||
var collection = document.getElementsByClassName(class_name);
|
|
||||||
console.log(collection);
|
|
||||||
for (let i = 0; i < collection.length; i++) {
|
|
||||||
if (state == true) {
|
|
||||||
collection[i].style.display = "table-row";
|
|
||||||
} else {
|
|
||||||
collection[i].style.display = "None";
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
ipcRenderer.on("action-update-log", (event, arg) => {
|
|
||||||
var entry = arg.entry;
|
|
||||||
|
|
||||||
// remove ANSI characters from string, caused by color logging
|
|
||||||
// https://stackoverflow.com/a/29497680
|
|
||||||
entry = entry.replace(
|
|
||||||
/[\u001b\u009b][[()#;?]*(?:[0-9]{1,4}(?:;[0-9]{0,4})*)?[0-9A-ORZcf-nqry=><]/g,
|
|
||||||
"",
|
|
||||||
);
|
|
||||||
|
|
||||||
var tbl = document.getElementById("log");
|
|
||||||
var row = document.createElement("tr");
|
|
||||||
|
|
||||||
var timestamp = document.createElement("td");
|
|
||||||
var timestampText = document.createElement("span");
|
|
||||||
|
|
||||||
//datetime = new Date();
|
|
||||||
//timestampText.innerText = datetime.toISOString();
|
|
||||||
timestampText.innerText = entry.slice(0, 19);
|
|
||||||
timestamp.appendChild(timestampText);
|
|
||||||
|
|
||||||
var type = document.createElement("td");
|
|
||||||
var typeText = document.createElement("span");
|
|
||||||
// typeText.innerText = entry.slice(10, 30).match(/[\[](.*)[^\]]/g);
|
|
||||||
console.log(entry.match(/\[[^\]]+\]/g));
|
|
||||||
|
|
||||||
try {
|
|
||||||
typeText.innerText = entry.match(/\[[^\]]+\]/g)[0];
|
|
||||||
} catch (e) {
|
|
||||||
typeText.innerText = "-";
|
|
||||||
}
|
|
||||||
|
|
||||||
// let res = str.match(/[\[](.*)[^\]]/g);
|
|
||||||
|
|
||||||
type.appendChild(typeText);
|
|
||||||
|
|
||||||
var area = document.createElement("td");
|
|
||||||
var areaText = document.createElement("span");
|
|
||||||
//areaText.innerText = entry.slice(10, 50).match(/[\] \[](.*)[^\]]/g);
|
|
||||||
//areaText.innerText = entry.match(/\[[^\]]+\]/g)[1];
|
|
||||||
|
|
||||||
try {
|
|
||||||
areaText.innerText = entry.match(/\[[^\]]+\]/g)[1];
|
|
||||||
} catch (e) {
|
|
||||||
areaText.innerText = "-";
|
|
||||||
}
|
|
||||||
area.appendChild(areaText);
|
|
||||||
|
|
||||||
var logEntry = document.createElement("td");
|
|
||||||
var logEntryText = document.createElement("span");
|
|
||||||
try {
|
|
||||||
logEntryText.innerText = entry.split("]")[2];
|
|
||||||
} catch (e) {
|
|
||||||
logEntryText.innerText = "-";
|
|
||||||
}
|
|
||||||
logEntry.appendChild(logEntryText);
|
|
||||||
|
|
||||||
row.appendChild(timestamp);
|
|
||||||
row.appendChild(type);
|
|
||||||
row.appendChild(area);
|
|
||||||
row.appendChild(logEntry);
|
|
||||||
|
|
||||||
//row.classList.add("table-blablubb");
|
|
||||||
/*
|
|
||||||
if (logEntryText.innerText.includes('ALSA lib pcm')) {
|
|
||||||
row.classList.add("table-secondary");
|
|
||||||
}
|
|
||||||
*/
|
|
||||||
if (typeText.innerText.includes("info")) {
|
|
||||||
row.classList.add("table-info");
|
|
||||||
}
|
|
||||||
if (typeText.innerText.includes("debug")) {
|
|
||||||
row.classList.add("table-secondary");
|
|
||||||
}
|
|
||||||
|
|
||||||
if (typeText.innerText.includes("warning")) {
|
|
||||||
row.classList.add("table-warning");
|
|
||||||
}
|
|
||||||
|
|
||||||
if (typeText.innerText.includes("error")) {
|
|
||||||
row.classList.add("table-danger");
|
|
||||||
}
|
|
||||||
|
|
||||||
if (document.getElementById("enable_filter_info").checked) {
|
|
||||||
row.style.display = "table-row";
|
|
||||||
display_class("table-info", true);
|
|
||||||
} else {
|
|
||||||
row.style.display = "None";
|
|
||||||
display_class("table-info", false);
|
|
||||||
}
|
|
||||||
if (document.getElementById("enable_filter_debug").checked) {
|
|
||||||
row.style.display = "table-row";
|
|
||||||
display_class("table-secondary", true);
|
|
||||||
} else {
|
|
||||||
row.style.display = "None";
|
|
||||||
display_class("table-secondary", false);
|
|
||||||
}
|
|
||||||
if (document.getElementById("enable_filter_warning").checked) {
|
|
||||||
row.style.display = "table-row";
|
|
||||||
display_class("table-warning", true);
|
|
||||||
} else {
|
|
||||||
row.style.display = "None";
|
|
||||||
display_class("table-warning", false);
|
|
||||||
}
|
|
||||||
if (document.getElementById("enable_filter_error").checked) {
|
|
||||||
row.style.display = "table-row";
|
|
||||||
display_class("table-danger", true);
|
|
||||||
} else {
|
|
||||||
row.style.display = "None";
|
|
||||||
display_class("table-danger", false);
|
|
||||||
}
|
|
||||||
|
|
||||||
tbl.appendChild(row);
|
|
||||||
|
|
||||||
// scroll to bottom of page
|
|
||||||
// https://stackoverflow.com/a/11715670
|
|
||||||
window.scrollTo(0, document.body.scrollHeight);
|
|
||||||
});
|
|
3861
gui/preload-main.js
3861
gui/preload-main.js
File diff suppressed because it is too large
Load diff
|
@ -1,231 +0,0 @@
|
||||||
const path = require("path");
|
|
||||||
const { ipcRenderer } = require("electron");
|
|
||||||
|
|
||||||
// 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");
|
|
||||||
var configPath = path.join(configFolder, "config.json");
|
|
||||||
const config = require(configPath);
|
|
||||||
|
|
||||||
var callsignPath = path.join(configFolder, "callsigns.json");
|
|
||||||
const callsigns = require(callsignPath);
|
|
||||||
|
|
||||||
// WINDOW LISTENER
|
|
||||||
window.addEventListener("DOMContentLoaded", () => {
|
|
||||||
// startPing button clicked
|
|
||||||
document
|
|
||||||
.getElementById("transmit_mesh_ping")
|
|
||||||
.addEventListener("click", () => {
|
|
||||||
var dxcallsign = document
|
|
||||||
.getElementById("dxCallMesh")
|
|
||||||
.value.toUpperCase();
|
|
||||||
if (dxcallsign == "" || dxcallsign == null || dxcallsign == undefined)
|
|
||||||
return;
|
|
||||||
//pauseButton(document.getElementById("transmit_mesh_ping"), 2000);
|
|
||||||
ipcRenderer.send("run-tnc-command", {
|
|
||||||
command: "mesh_ping",
|
|
||||||
dxcallsign: dxcallsign,
|
|
||||||
});
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
ipcRenderer.on("action-update-mesh-table", (event, arg) => {
|
|
||||||
var routes = arg.routing_table;
|
|
||||||
|
|
||||||
if (typeof routes == "undefined") {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
var tbl = document.getElementById("mesh-table");
|
|
||||||
if (tbl !== null) {
|
|
||||||
tbl.innerHTML = "";
|
|
||||||
}
|
|
||||||
|
|
||||||
for (i = 0; i < routes.length; i++) {
|
|
||||||
var row = document.createElement("tr");
|
|
||||||
var datetime = new Date(routes[i]["timestamp"] * 1000).toLocaleString(
|
|
||||||
navigator.language,
|
|
||||||
{
|
|
||||||
hourCycle: "h23",
|
|
||||||
year: "numeric",
|
|
||||||
month: "2-digit",
|
|
||||||
day: "2-digit",
|
|
||||||
hour: "2-digit",
|
|
||||||
minute: "2-digit",
|
|
||||||
second: "2-digit",
|
|
||||||
},
|
|
||||||
);
|
|
||||||
var timestamp = document.createElement("td");
|
|
||||||
var timestampText = document.createElement("span");
|
|
||||||
timestampText.innerText = datetime;
|
|
||||||
timestamp.appendChild(timestampText);
|
|
||||||
|
|
||||||
var dxcall = document.createElement("td");
|
|
||||||
var dxcallText = document.createElement("span");
|
|
||||||
dxcallText.innerText = routes[i]["dxcall"];
|
|
||||||
|
|
||||||
// check for callsign in callsign list, else use checksum
|
|
||||||
for (let call in callsigns) {
|
|
||||||
if (callsigns[call] == routes[i]["dxcall"]) {
|
|
||||||
dxcallText.innerText += " (" + call + ")";
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
dxcall.appendChild(dxcallText);
|
|
||||||
|
|
||||||
var router = document.createElement("td");
|
|
||||||
var routerText = document.createElement("span");
|
|
||||||
routerText.innerText = routes[i]["router"];
|
|
||||||
|
|
||||||
// check for callsign in callsign list, else use checksum
|
|
||||||
for (let call in callsigns) {
|
|
||||||
if (callsigns[call] == routes[i]["router"]) {
|
|
||||||
routerText.innerHTML += `<span class="badge ms-2 bg-secondary">${call}</span>`;
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
router.appendChild(routerText);
|
|
||||||
|
|
||||||
var hops = document.createElement("td");
|
|
||||||
var hopsText = document.createElement("span");
|
|
||||||
hopsText.innerText = routes[i]["hops"];
|
|
||||||
hops.appendChild(hopsText);
|
|
||||||
|
|
||||||
var score = document.createElement("td");
|
|
||||||
var scoreText = document.createElement("span");
|
|
||||||
scoreText.innerText = routes[i]["score"];
|
|
||||||
score.appendChild(scoreText);
|
|
||||||
|
|
||||||
var snr = document.createElement("td");
|
|
||||||
var snrText = document.createElement("span");
|
|
||||||
snrText.innerText = routes[i]["snr"];
|
|
||||||
snr.appendChild(snrText);
|
|
||||||
|
|
||||||
row.appendChild(timestamp);
|
|
||||||
row.appendChild(dxcall);
|
|
||||||
row.appendChild(router);
|
|
||||||
row.appendChild(hops);
|
|
||||||
row.appendChild(score);
|
|
||||||
row.appendChild(snr);
|
|
||||||
|
|
||||||
tbl.appendChild(row);
|
|
||||||
}
|
|
||||||
/*-------------------------------------------*/
|
|
||||||
var routes = arg.mesh_signalling_table;
|
|
||||||
|
|
||||||
//console.log(routes);
|
|
||||||
if (typeof routes == "undefined") {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
var tbl = document.getElementById("mesh-signalling-table");
|
|
||||||
if (tbl !== null) {
|
|
||||||
tbl.innerHTML = "";
|
|
||||||
}
|
|
||||||
|
|
||||||
for (i = 0; i < routes.length; i++) {
|
|
||||||
var row = document.createElement("tr");
|
|
||||||
var datetime = new Date(routes[i]["timestamp"] * 1000).toLocaleString(
|
|
||||||
navigator.language,
|
|
||||||
{
|
|
||||||
hourCycle: "h23",
|
|
||||||
year: "numeric",
|
|
||||||
month: "2-digit",
|
|
||||||
day: "2-digit",
|
|
||||||
hour: "2-digit",
|
|
||||||
minute: "2-digit",
|
|
||||||
second: "2-digit",
|
|
||||||
},
|
|
||||||
);
|
|
||||||
var timestamp = document.createElement("td");
|
|
||||||
var timestampText = document.createElement("span");
|
|
||||||
timestampText.innerText = datetime;
|
|
||||||
timestamp.appendChild(timestampText);
|
|
||||||
|
|
||||||
var destination = document.createElement("td");
|
|
||||||
var destinationText = document.createElement("span");
|
|
||||||
destinationText.innerText = routes[i]["destination"];
|
|
||||||
// check for callsign in callsign list, else use checksum
|
|
||||||
for (let call in callsigns) {
|
|
||||||
if (callsigns[call] == routes[i]["destination"]) {
|
|
||||||
destinationText.innerHTML += `<span class="badge ms-2 bg-secondary">${call}</span>`;
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
destination.appendChild(destinationText);
|
|
||||||
|
|
||||||
var origin = document.createElement("td");
|
|
||||||
var originText = document.createElement("span");
|
|
||||||
originText.innerText = routes[i]["origin"];
|
|
||||||
// check for callsign in callsign list, else use checksum
|
|
||||||
for (let call in callsigns) {
|
|
||||||
if (callsigns[call] == routes[i]["origin"]) {
|
|
||||||
originText.innerHTML += `<span class="badge ms-2 bg-secondary">${call}</span>`;
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
origin.appendChild(originText);
|
|
||||||
|
|
||||||
var frametype = document.createElement("td");
|
|
||||||
var frametypeText = document.createElement("span");
|
|
||||||
frametypeText.innerText = routes[i]["frametype"];
|
|
||||||
frametype.appendChild(frametypeText);
|
|
||||||
|
|
||||||
var payload = document.createElement("td");
|
|
||||||
var payloadText = document.createElement("span");
|
|
||||||
payloadText.innerText = routes[i]["payload"];
|
|
||||||
payload.appendChild(payloadText);
|
|
||||||
|
|
||||||
var attempt = document.createElement("td");
|
|
||||||
var attemptText = document.createElement("span");
|
|
||||||
attemptText.innerText = routes[i]["attempt"];
|
|
||||||
attempt.appendChild(attemptText);
|
|
||||||
|
|
||||||
var status = document.createElement("td");
|
|
||||||
var statusText = document.createElement("span");
|
|
||||||
//statusText.innerText = routes[i]["status"];
|
|
||||||
switch (routes[i]["status"]) {
|
|
||||||
case "acknowledged":
|
|
||||||
var status_icon = '<i class="bi bi-check-circle-fill"></i>';
|
|
||||||
var status_color = "bg-success";
|
|
||||||
break;
|
|
||||||
case "acknowledging":
|
|
||||||
var status_icon = '<i class="bi bi-check-circle"></i>';
|
|
||||||
var status_color = "bg-warning";
|
|
||||||
break;
|
|
||||||
case "forwarding":
|
|
||||||
var status_icon = '<i class="bi bi-arrow-left-right"></i>';
|
|
||||||
var status_color = "bg-secondary";
|
|
||||||
break;
|
|
||||||
case "awaiting_ack":
|
|
||||||
var status_icon = '<i class="bi bi-clock-history"></i>';
|
|
||||||
var status_color = "bg-info";
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
var status_icon = '<i class="bi bi-question-circle-fill"></i>';
|
|
||||||
var status_color = "bg-primary";
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
statusText.innerHTML = `
|
|
||||||
<span class="badge ${status_color}">${status_icon}</span>
|
|
||||||
<span class="badge ${status_color}">${routes[i]["status"]}</span>
|
|
||||||
`;
|
|
||||||
status.appendChild(statusText);
|
|
||||||
|
|
||||||
row.appendChild(timestamp);
|
|
||||||
row.appendChild(destination);
|
|
||||||
row.appendChild(origin);
|
|
||||||
row.appendChild(frametype);
|
|
||||||
row.appendChild(payload);
|
|
||||||
row.appendChild(attempt);
|
|
||||||
row.appendChild(status);
|
|
||||||
|
|
||||||
tbl.appendChild(row);
|
|
||||||
}
|
|
||||||
});
|
|
Before Width: | Height: | Size: 590 KiB After Width: | Height: | Size: 590 KiB |
939
gui/sock.js
939
gui/sock.js
|
@ -1,939 +0,0 @@
|
||||||
var net = require("net");
|
|
||||||
const path = require("path");
|
|
||||||
const { ipcRenderer } = require("electron");
|
|
||||||
const FD = require("./freedata");
|
|
||||||
const log = require("electron-log");
|
|
||||||
const socketLog = log.scope("tnc");
|
|
||||||
//const utf8 = require("utf8");
|
|
||||||
|
|
||||||
// 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");
|
|
||||||
var configPath = path.join(configFolder, "config.json");
|
|
||||||
const config = require(configPath);
|
|
||||||
|
|
||||||
var client = new net.Socket();
|
|
||||||
var socketchunk = ""; // Current message, per connection.
|
|
||||||
|
|
||||||
// split character
|
|
||||||
const split_char = "\0;\1;";
|
|
||||||
|
|
||||||
// globals for getting new data only if available so we are saving bandwidth
|
|
||||||
var rxBufferLengthTnc = 0;
|
|
||||||
var rxBufferLengthGui = 0;
|
|
||||||
//var rxMsgBufferLengthTnc = 0;
|
|
||||||
//var rxMsgBufferLengthGui = 0;
|
|
||||||
|
|
||||||
// global to keep track of TNC connection error emissions
|
|
||||||
var tncShowConnectStateError = 1;
|
|
||||||
|
|
||||||
// global for storing ip information
|
|
||||||
var tnc_port = config.tnc_port;
|
|
||||||
var tnc_host = config.tnc_host;
|
|
||||||
|
|
||||||
// network connection Timeout
|
|
||||||
setTimeout(connectTNC, 2000);
|
|
||||||
|
|
||||||
function connectTNC() {
|
|
||||||
//exports.connectTNC = function(){
|
|
||||||
//socketLog.info('connecting to TNC...')
|
|
||||||
|
|
||||||
//clear message buffer after reconnecting or initial connection
|
|
||||||
socketchunk = "";
|
|
||||||
|
|
||||||
if (config.tnclocation == "localhost") {
|
|
||||||
client.connect(3000, "127.0.0.1");
|
|
||||||
} else {
|
|
||||||
client.connect(tnc_port, tnc_host);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
client.on("connect", function (data) {
|
|
||||||
socketLog.info("TNC connection established");
|
|
||||||
let Data = {
|
|
||||||
busy_state: "-",
|
|
||||||
arq_state: "-",
|
|
||||||
//channel_state: "-",
|
|
||||||
frequency: "-",
|
|
||||||
mode: "-",
|
|
||||||
bandwidth: "-",
|
|
||||||
dbfs_level: 0,
|
|
||||||
};
|
|
||||||
ipcRenderer.send("request-update-tnc-state", Data);
|
|
||||||
|
|
||||||
// also update tnc connection state
|
|
||||||
ipcRenderer.send("request-update-tnc-connection", {
|
|
||||||
tnc_connection: client.readyState,
|
|
||||||
});
|
|
||||||
|
|
||||||
tncShowConnectStateError = 1;
|
|
||||||
});
|
|
||||||
|
|
||||||
client.on("error", function (data) {
|
|
||||||
if (tncShowConnectStateError == 1) {
|
|
||||||
socketLog.error("TNC connection error");
|
|
||||||
tncShowConnectStateError = 0;
|
|
||||||
}
|
|
||||||
setTimeout(connectTNC, 500);
|
|
||||||
client.destroy();
|
|
||||||
let Data = {
|
|
||||||
tnc_connection: client.readyState,
|
|
||||||
busy_state: "-",
|
|
||||||
arq_state: "-",
|
|
||||||
//channel_state: "-",
|
|
||||||
frequency: "-",
|
|
||||||
mode: "-",
|
|
||||||
bandwidth: "-",
|
|
||||||
dbfs_level: 0,
|
|
||||||
};
|
|
||||||
ipcRenderer.send("request-update-tnc-state", Data);
|
|
||||||
ipcRenderer.send("request-update-tnc-connection", {
|
|
||||||
tnc_connection: client.readyState,
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
/*
|
|
||||||
client.on('close', function(data) {
|
|
||||||
socketLog.info(' TNC connection closed');
|
|
||||||
setTimeout(connectTNC, 2000)
|
|
||||||
});
|
|
||||||
*/
|
|
||||||
|
|
||||||
client.on("end", function (data) {
|
|
||||||
socketLog.info("TNC connection ended");
|
|
||||||
ipcRenderer.send("request-update-tnc-connection", {
|
|
||||||
tnc_connection: client.readyState,
|
|
||||||
});
|
|
||||||
client.destroy();
|
|
||||||
|
|
||||||
setTimeout(connectTNC, 500);
|
|
||||||
});
|
|
||||||
|
|
||||||
writeTncCommand = function (command) {
|
|
||||||
//socketLog.info(command)
|
|
||||||
// we use the writingCommand function to update our TCPIP state because we are calling this function a lot
|
|
||||||
// if socket opened, we are able to run commands
|
|
||||||
|
|
||||||
if (client.readyState == "open") {
|
|
||||||
client.write(command + "\n");
|
|
||||||
}
|
|
||||||
|
|
||||||
if (client.readyState == "closed") {
|
|
||||||
socketLog.info("CLOSED!");
|
|
||||||
}
|
|
||||||
|
|
||||||
if (client.readyState == "opening") {
|
|
||||||
socketLog.info("connecting to TNC...");
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
client.on("data", function (socketdata) {
|
|
||||||
ipcRenderer.send("request-update-tnc-connection", {
|
|
||||||
tnc_connection: client.readyState,
|
|
||||||
});
|
|
||||||
|
|
||||||
/*
|
|
||||||
inspired by:
|
|
||||||
stackoverflow.com questions 9070700 nodejs-net-createserver-large-amount-of-data-coming-in
|
|
||||||
*/
|
|
||||||
|
|
||||||
socketdata = socketdata.toString("utf8"); // convert data to string
|
|
||||||
socketchunk += socketdata; // append data to buffer so we can stick long data together
|
|
||||||
|
|
||||||
// check if we received begin and end of json data
|
|
||||||
if (socketchunk.startsWith('{"') && socketchunk.endsWith('"}\n')) {
|
|
||||||
var data = "";
|
|
||||||
|
|
||||||
// split data into chunks if we received multiple commands
|
|
||||||
socketchunk = socketchunk.split("\n");
|
|
||||||
//don't think this is needed anymore
|
|
||||||
//data = JSON.parse(socketchunk[0])
|
|
||||||
|
|
||||||
// search for empty entries in socketchunk and remove them
|
|
||||||
for (i = 0; i < socketchunk.length; i++) {
|
|
||||||
if (socketchunk[i] === "") {
|
|
||||||
socketchunk.splice(i, 1);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
//iterate through socketchunks array to execute multiple commands in row
|
|
||||||
for (i = 0; i < socketchunk.length; i++) {
|
|
||||||
//check if data is not empty
|
|
||||||
if (socketchunk[i].length > 0) {
|
|
||||||
//try to parse JSON
|
|
||||||
try {
|
|
||||||
data = JSON.parse(socketchunk[i]);
|
|
||||||
} catch (e) {
|
|
||||||
socketLog.info("Throwing away data!!!!\n" + e); // "SyntaxError
|
|
||||||
//socketLog.info(e); // "SyntaxError
|
|
||||||
socketLog.info(socketchunk[i]);
|
|
||||||
socketchunk = "";
|
|
||||||
//If we're here, I don't think we want to process any data that may be in data variable
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (data["command"] == "tnc_state") {
|
|
||||||
//socketLog.info(data)
|
|
||||||
// set length of RX Buffer to global variable
|
|
||||||
rxBufferLengthTnc = data["rx_buffer_length"];
|
|
||||||
//rxMsgBufferLengthTnc = data["rx_msg_buffer_length"];
|
|
||||||
|
|
||||||
let Data = {
|
|
||||||
mycallsign: data["mycallsign"],
|
|
||||||
mygrid: data["mygrid"],
|
|
||||||
ptt_state: data["ptt_state"],
|
|
||||||
busy_state: data["tnc_state"],
|
|
||||||
arq_state: data["arq_state"],
|
|
||||||
arq_session: data["arq_session"],
|
|
||||||
//channel_state: data['CHANNEL_STATE'],
|
|
||||||
frequency: data["frequency"],
|
|
||||||
speed_level: data["speed_level"],
|
|
||||||
mode: data["mode"],
|
|
||||||
bandwidth: data["bandwidth"],
|
|
||||||
dbfs_level: data["audio_dbfs"],
|
|
||||||
fft: data["fft"],
|
|
||||||
channel_busy: data["channel_busy"],
|
|
||||||
channel_busy_slot: data["channel_busy_slot"],
|
|
||||||
scatter: data["scatter"],
|
|
||||||
info: data["info"],
|
|
||||||
rx_buffer_length: data["rx_buffer_length"],
|
|
||||||
rx_msg_buffer_length: data["rx_msg_buffer_length"],
|
|
||||||
tx_n_max_retries: data["tx_n_max_retries"],
|
|
||||||
arq_tx_n_frames_per_burst: data["arq_tx_n_frames_per_burst"],
|
|
||||||
arq_tx_n_bursts: data["arq_tx_n_bursts"],
|
|
||||||
arq_tx_n_current_arq_frame: data["arq_tx_n_current_arq_frame"],
|
|
||||||
arq_tx_n_total_arq_frames: data["arq_tx_n_total_arq_frames"],
|
|
||||||
arq_rx_frame_n_bursts: data["arq_rx_frame_n_bursts"],
|
|
||||||
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_bytes_per_minute: data["arq_bytes_per_minute"],
|
|
||||||
arq_seconds_until_finish: data["arq_seconds_until_finish"],
|
|
||||||
arq_compression_factor: data["arq_compression_factor"],
|
|
||||||
total_bytes: data["total_bytes"],
|
|
||||||
arq_transmission_percent: data["arq_transmission_percent"],
|
|
||||||
stations: data["stations"],
|
|
||||||
routing_table: data["routing_table"],
|
|
||||||
mesh_signalling_table: data["mesh_signalling_table"],
|
|
||||||
beacon_state: data["beacon_state"],
|
|
||||||
hamlib_status: data["hamlib_status"],
|
|
||||||
listen: data["listen"],
|
|
||||||
audio_recording: data["audio_recording"],
|
|
||||||
speed_list: data["speed_list"],
|
|
||||||
strength: data["strength"],
|
|
||||||
is_codec2_traffic: data["is_codec2_traffic"],
|
|
||||||
//speed_table: [{"bpm" : 5200, "snr": -3, "timestamp":1673555399},{"bpm" : 2315, "snr": 12, "timestamp":1673555500}],
|
|
||||||
};
|
|
||||||
|
|
||||||
ipcRenderer.send("request-update-tnc-state", Data);
|
|
||||||
//continue to next for loop iteration, nothing else needs to be done here
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
// ----------- catch tnc messages START -----------
|
|
||||||
if (data["freedata"] == "tnc-message") {
|
|
||||||
switch (data["fec"]) {
|
|
||||||
case "is_writing":
|
|
||||||
// RX'd FECiswriting
|
|
||||||
ipcRenderer.send("request-show-fec-toast-iswriting", {
|
|
||||||
data: [data],
|
|
||||||
});
|
|
||||||
break;
|
|
||||||
|
|
||||||
case "broadcast":
|
|
||||||
// RX'd FEC BROADCAST
|
|
||||||
var encoded_data = FD.atob_FD(data["data"]);
|
|
||||||
var splitted_data = encoded_data.split(split_char);
|
|
||||||
var messageArray = [];
|
|
||||||
if (splitted_data[0] == "m") {
|
|
||||||
messageArray.push(data);
|
|
||||||
console.log(data);
|
|
||||||
}
|
|
||||||
|
|
||||||
let Messages = {
|
|
||||||
data: messageArray,
|
|
||||||
};
|
|
||||||
ipcRenderer.send("request-new-msg-received", Messages);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
switch (data["cq"]) {
|
|
||||||
case "transmitting":
|
|
||||||
// CQ TRANSMITTING
|
|
||||||
ipcRenderer.send("request-show-cq-toast-transmitting", {
|
|
||||||
data: [data],
|
|
||||||
});
|
|
||||||
break;
|
|
||||||
|
|
||||||
case "received":
|
|
||||||
// CQ RECEIVED
|
|
||||||
ipcRenderer.send("request-show-cq-toast-received", {
|
|
||||||
data: [data],
|
|
||||||
});
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
switch (data["qrv"]) {
|
|
||||||
case "transmitting":
|
|
||||||
// QRV TRANSMITTING
|
|
||||||
ipcRenderer.send("request-show-qrv-toast-transmitting", {
|
|
||||||
data: [data],
|
|
||||||
});
|
|
||||||
break;
|
|
||||||
|
|
||||||
case "received":
|
|
||||||
// QRV RECEIVED
|
|
||||||
ipcRenderer.send("request-show-qrv-toast-received", {
|
|
||||||
data: [data],
|
|
||||||
});
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
switch (data["beacon"]) {
|
|
||||||
case "transmitting":
|
|
||||||
// BEACON TRANSMITTING
|
|
||||||
ipcRenderer.send("request-show-beacon-toast-transmitting", {
|
|
||||||
data: [data],
|
|
||||||
});
|
|
||||||
break;
|
|
||||||
|
|
||||||
case "received":
|
|
||||||
// BEACON RECEIVED
|
|
||||||
ipcRenderer.send("request-show-beacon-toast-received", {
|
|
||||||
data: [data],
|
|
||||||
});
|
|
||||||
ipcRenderer.send("request-new-msg-received", { data: [data] });
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
switch (data["ping"]) {
|
|
||||||
case "transmitting":
|
|
||||||
// PING TRANSMITTING
|
|
||||||
ipcRenderer.send("request-show-ping-toast-transmitting", {
|
|
||||||
data: [data],
|
|
||||||
});
|
|
||||||
break;
|
|
||||||
|
|
||||||
case "received":
|
|
||||||
// PING RECEIVED
|
|
||||||
ipcRenderer.send("request-show-ping-toast-received", {
|
|
||||||
data: [data],
|
|
||||||
});
|
|
||||||
ipcRenderer.send("request-new-msg-received", { data: [data] });
|
|
||||||
break;
|
|
||||||
|
|
||||||
case "acknowledge":
|
|
||||||
// PING ACKNOWLEDGE
|
|
||||||
ipcRenderer.send("request-show-ping-toast-received-ack", {
|
|
||||||
data: [data],
|
|
||||||
});
|
|
||||||
ipcRenderer.send("request-new-msg-received", { data: [data] });
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
// ARQ SESSION && freedata == tnc-message
|
|
||||||
if (data["arq"] == "session") {
|
|
||||||
switch (data["status"]) {
|
|
||||||
case "connecting":
|
|
||||||
// ARQ Open
|
|
||||||
ipcRenderer.send("request-show-arq-toast-session-connecting", {
|
|
||||||
data: [data],
|
|
||||||
});
|
|
||||||
break;
|
|
||||||
|
|
||||||
case "connected":
|
|
||||||
// ARQ Opening
|
|
||||||
ipcRenderer.send("request-show-arq-toast-session-connected", {
|
|
||||||
data: [data],
|
|
||||||
});
|
|
||||||
break;
|
|
||||||
|
|
||||||
case "waiting":
|
|
||||||
// ARQ Opening
|
|
||||||
ipcRenderer.send("request-show-arq-toast-session-waiting", {
|
|
||||||
data: [data],
|
|
||||||
});
|
|
||||||
break;
|
|
||||||
|
|
||||||
case "close":
|
|
||||||
// ARQ Closing
|
|
||||||
ipcRenderer.send("request-show-arq-toast-session-close", {
|
|
||||||
data: [data],
|
|
||||||
});
|
|
||||||
break;
|
|
||||||
|
|
||||||
case "failed":
|
|
||||||
// ARQ Failed
|
|
||||||
ipcRenderer.send("request-show-arq-toast-session-failed", {
|
|
||||||
data: [data],
|
|
||||||
});
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// ARQ TRANSMISSION && freedata == tnc-message
|
|
||||||
if (data["arq"] == "transmission") {
|
|
||||||
switch (data["status"]) {
|
|
||||||
case "opened":
|
|
||||||
// ARQ Open
|
|
||||||
ipcRenderer.send("request-show-arq-toast-datachannel-opened", {
|
|
||||||
data: [data],
|
|
||||||
});
|
|
||||||
break;
|
|
||||||
|
|
||||||
case "opening":
|
|
||||||
// ARQ Opening IRS/ISS
|
|
||||||
if (data["irs"] == "False") {
|
|
||||||
ipcRenderer.send("request-show-arq-toast-datachannel-opening", {
|
|
||||||
data: [data],
|
|
||||||
});
|
|
||||||
ipcRenderer.send("request-update-transmission-status", {
|
|
||||||
data: [data],
|
|
||||||
});
|
|
||||||
} else {
|
|
||||||
ipcRenderer.send(
|
|
||||||
"request-show-arq-toast-datachannel-received-opener",
|
|
||||||
{ data: [data] },
|
|
||||||
);
|
|
||||||
ipcRenderer.send("request-update-reception-status", {
|
|
||||||
data: [data],
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
break;
|
|
||||||
|
|
||||||
case "waiting":
|
|
||||||
// ARQ waiting
|
|
||||||
ipcRenderer.send("request-show-arq-toast-datachannel-waiting", {
|
|
||||||
data: [data],
|
|
||||||
});
|
|
||||||
break;
|
|
||||||
|
|
||||||
case "receiving":
|
|
||||||
// ARQ RX
|
|
||||||
ipcRenderer.send("request-update-reception-status", {
|
|
||||||
data: [data],
|
|
||||||
});
|
|
||||||
break;
|
|
||||||
|
|
||||||
case "failed":
|
|
||||||
// ARQ TX Failed
|
|
||||||
if (data["reason"] == "protocol version missmatch") {
|
|
||||||
ipcRenderer.send(
|
|
||||||
"request-show-arq-toast-transmission-failed-ver",
|
|
||||||
{ data: [data] },
|
|
||||||
);
|
|
||||||
} else {
|
|
||||||
ipcRenderer.send("request-show-arq-toast-transmission-failed", {
|
|
||||||
data: [data],
|
|
||||||
});
|
|
||||||
}
|
|
||||||
switch (data["irs"]) {
|
|
||||||
case "True":
|
|
||||||
ipcRenderer.send("request-update-reception-status", {
|
|
||||||
data: [data],
|
|
||||||
});
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
ipcRenderer.send("request-update-transmission-status", {
|
|
||||||
data: [data],
|
|
||||||
});
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
|
|
||||||
case "received":
|
|
||||||
// ARQ Received
|
|
||||||
ipcRenderer.send("request-show-arq-toast-transmission-received", {
|
|
||||||
data: [data],
|
|
||||||
});
|
|
||||||
|
|
||||||
ipcRenderer.send("request-update-reception-status", {
|
|
||||||
data: [data],
|
|
||||||
});
|
|
||||||
|
|
||||||
dataArray = [];
|
|
||||||
messageArray = [];
|
|
||||||
|
|
||||||
socketLog.info(data);
|
|
||||||
// we need to encode here to do a deep check for checking if file or message
|
|
||||||
//var encoded_data = atob(data['data'])
|
|
||||||
var encoded_data = FD.atob_FD(data["data"]);
|
|
||||||
var splitted_data = encoded_data.split(split_char);
|
|
||||||
|
|
||||||
if (splitted_data[0] == "f") {
|
|
||||||
dataArray.push(data);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (splitted_data[0] == "m") {
|
|
||||||
messageArray.push(data);
|
|
||||||
console.log(data);
|
|
||||||
}
|
|
||||||
|
|
||||||
rxBufferLengthGui = dataArray.length;
|
|
||||||
let Files = {
|
|
||||||
data: dataArray,
|
|
||||||
};
|
|
||||||
ipcRenderer.send("request-update-rx-buffer", Files);
|
|
||||||
ipcRenderer.send("request-new-msg-received", Files);
|
|
||||||
|
|
||||||
//rxMsgBufferLengthGui = messageArray.length;
|
|
||||||
let Messages = {
|
|
||||||
data: messageArray,
|
|
||||||
};
|
|
||||||
ipcRenderer.send("request-new-msg-received", Messages);
|
|
||||||
break;
|
|
||||||
|
|
||||||
case "transmitting":
|
|
||||||
// ARQ transmitting
|
|
||||||
ipcRenderer.send(
|
|
||||||
"request-show-arq-toast-transmission-transmitting",
|
|
||||||
{ data: [data] },
|
|
||||||
);
|
|
||||||
ipcRenderer.send("request-update-transmission-status", {
|
|
||||||
data: [data],
|
|
||||||
});
|
|
||||||
break;
|
|
||||||
|
|
||||||
case "transmitted":
|
|
||||||
// ARQ transmitted
|
|
||||||
ipcRenderer.send(
|
|
||||||
"request-show-arq-toast-transmission-transmitted",
|
|
||||||
{ data: [data] },
|
|
||||||
);
|
|
||||||
ipcRenderer.send("request-update-transmission-status", {
|
|
||||||
data: [data],
|
|
||||||
});
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// ----------- catch tnc info messages END -----------
|
|
||||||
|
|
||||||
// if we manually checking for the rx buffer we are getting an array of multiple data
|
|
||||||
if (data["command"] == "rx_buffer") {
|
|
||||||
socketLog.info(data);
|
|
||||||
// iterate through buffer list and sort it to file or message array
|
|
||||||
dataArray = [];
|
|
||||||
messageArray = [];
|
|
||||||
|
|
||||||
for (i = 0; i < data["data-array"].length; i++) {
|
|
||||||
try {
|
|
||||||
// we need to encode here to do a deep check for checking if file or message
|
|
||||||
//var encoded_data = atob(data['data-array'][i]['data'])
|
|
||||||
var encoded_data = FD.atob_FD(data["data-array"][i]["data"]);
|
|
||||||
var splitted_data = encoded_data.split(split_char);
|
|
||||||
|
|
||||||
if (splitted_data[0] == "f") {
|
|
||||||
dataArray.push(data["data-array"][i]);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (splitted_data[0] == "m") {
|
|
||||||
messageArray.push(data["data-array"][i]);
|
|
||||||
}
|
|
||||||
} catch (e) {
|
|
||||||
socketLog.info(e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
rxBufferLengthGui = dataArray.length;
|
|
||||||
let Files = {
|
|
||||||
data: dataArray,
|
|
||||||
};
|
|
||||||
ipcRenderer.send("request-update-rx-buffer", Files);
|
|
||||||
|
|
||||||
//rxMsgBufferLengthGui = messageArray.length;
|
|
||||||
let Messages = {
|
|
||||||
data: messageArray,
|
|
||||||
};
|
|
||||||
//ipcRenderer.send('request-update-rx-msg-buffer', Messages);
|
|
||||||
ipcRenderer.send("request-new-msg-received", Messages);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
//finally delete message buffer
|
|
||||||
socketchunk = "";
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
function hexToBytes(hex) {
|
|
||||||
for (var bytes = [], c = 0; c < hex.length; c += 2)
|
|
||||||
bytes.push(parseInt(hex.substr(c, 2), 16));
|
|
||||||
return bytes;
|
|
||||||
}
|
|
||||||
|
|
||||||
//Get TNC State
|
|
||||||
exports.getTncState = function () {
|
|
||||||
command = '{"type" : "get", "command" : "tnc_state"}';
|
|
||||||
writeTncCommand(command);
|
|
||||||
};
|
|
||||||
|
|
||||||
//Get DATA State
|
|
||||||
exports.getDataState = function () {
|
|
||||||
command = '{"type" : "get", "command" : "data_state"}';
|
|
||||||
//writeTncCommand(command)
|
|
||||||
};
|
|
||||||
|
|
||||||
// Send Ping
|
|
||||||
exports.sendPing = function (dxcallsign) {
|
|
||||||
command =
|
|
||||||
'{"type" : "ping", "command" : "ping", "dxcallsign" : "' +
|
|
||||||
dxcallsign +
|
|
||||||
'"}';
|
|
||||||
writeTncCommand(command);
|
|
||||||
};
|
|
||||||
|
|
||||||
// Send Mesh Ping
|
|
||||||
exports.sendMeshPing = function (dxcallsign) {
|
|
||||||
command =
|
|
||||||
'{"type" : "mesh", "command" : "ping", "dxcallsign" : "' +
|
|
||||||
dxcallsign +
|
|
||||||
'"}';
|
|
||||||
writeTncCommand(command);
|
|
||||||
};
|
|
||||||
|
|
||||||
// Send CQ
|
|
||||||
exports.sendCQ = function () {
|
|
||||||
command = '{"type" : "broadcast", "command" : "cqcqcq"}';
|
|
||||||
writeTncCommand(command);
|
|
||||||
};
|
|
||||||
|
|
||||||
// Set AUDIO Level
|
|
||||||
exports.setTxAudioLevel = function (value) {
|
|
||||||
command =
|
|
||||||
'{"type" : "set", "command" : "tx_audio_level", "value" : "' + value + '"}';
|
|
||||||
writeTncCommand(command);
|
|
||||||
};
|
|
||||||
|
|
||||||
// Send File
|
|
||||||
exports.sendFile = function (
|
|
||||||
dxcallsign,
|
|
||||||
mode,
|
|
||||||
frames,
|
|
||||||
filename,
|
|
||||||
filetype,
|
|
||||||
data,
|
|
||||||
checksum,
|
|
||||||
) {
|
|
||||||
socketLog.info(data);
|
|
||||||
socketLog.info(filetype);
|
|
||||||
socketLog.info(filename);
|
|
||||||
|
|
||||||
var datatype = "f";
|
|
||||||
|
|
||||||
data =
|
|
||||||
datatype +
|
|
||||||
split_char +
|
|
||||||
filename +
|
|
||||||
split_char +
|
|
||||||
filetype +
|
|
||||||
split_char +
|
|
||||||
checksum +
|
|
||||||
split_char +
|
|
||||||
data;
|
|
||||||
socketLog.info(data);
|
|
||||||
//socketLog.info(btoa(data))
|
|
||||||
//Btoa / atob will not work with charsets > 8 bits (i.e. the emojis); should probably move away from using it
|
|
||||||
//TODO: Will need to update anyother occurences and throughly test
|
|
||||||
//data = btoa(data)
|
|
||||||
data = FD.btoa_FD(data);
|
|
||||||
|
|
||||||
command =
|
|
||||||
'{"type" : "arq", "command" : "send_raw", "parameter" : [{"dxcallsign" : "' +
|
|
||||||
dxcallsign +
|
|
||||||
'", "mode" : "' +
|
|
||||||
mode +
|
|
||||||
'", "n_frames" : "' +
|
|
||||||
frames +
|
|
||||||
'", "data" : "' +
|
|
||||||
data +
|
|
||||||
'"}]}';
|
|
||||||
writeTncCommand(command);
|
|
||||||
};
|
|
||||||
|
|
||||||
// Send Message
|
|
||||||
exports.sendMessage = function (
|
|
||||||
dxcallsign,
|
|
||||||
mode,
|
|
||||||
frames,
|
|
||||||
data,
|
|
||||||
checksum,
|
|
||||||
uuid,
|
|
||||||
command,
|
|
||||||
) {
|
|
||||||
data = FD.btoa_FD(
|
|
||||||
"m" +
|
|
||||||
split_char +
|
|
||||||
command +
|
|
||||||
split_char +
|
|
||||||
checksum +
|
|
||||||
split_char +
|
|
||||||
uuid +
|
|
||||||
split_char +
|
|
||||||
data,
|
|
||||||
);
|
|
||||||
command =
|
|
||||||
'{"type" : "arq", "command" : "send_raw", "uuid" : "' +
|
|
||||||
uuid +
|
|
||||||
'", "parameter" : [{"dxcallsign" : "' +
|
|
||||||
dxcallsign +
|
|
||||||
'", "mode" : "' +
|
|
||||||
mode +
|
|
||||||
'", "n_frames" : "' +
|
|
||||||
frames +
|
|
||||||
'", "data" : "' +
|
|
||||||
data +
|
|
||||||
'", "attempts": "10"}]}';
|
|
||||||
socketLog.info(command);
|
|
||||||
socketLog.info("-------------------------------------");
|
|
||||||
writeTncCommand(command);
|
|
||||||
};
|
|
||||||
|
|
||||||
// Send Request message
|
|
||||||
//It would be then „m + split + request + split + request-type“
|
|
||||||
function sendRequest(dxcallsign, mode, frames, data, command) {
|
|
||||||
data = FD.btoa_FD("m" + split_char + command + split_char + data);
|
|
||||||
command =
|
|
||||||
'{"type" : "arq", "command" : "send_raw", "parameter" : [{"dxcallsign" : "' +
|
|
||||||
dxcallsign +
|
|
||||||
'", "mode" : "' +
|
|
||||||
mode +
|
|
||||||
'", "n_frames" : "' +
|
|
||||||
frames +
|
|
||||||
'", "data" : "' +
|
|
||||||
data +
|
|
||||||
'", "attempts": "10"}]}';
|
|
||||||
socketLog.info(command);
|
|
||||||
socketLog.info("--------------REQ--------------------");
|
|
||||||
writeTncCommand(command);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Send Response message
|
|
||||||
//It would be then „m + split + request + split + request-type“
|
|
||||||
function sendResponse(dxcallsign, mode, frames, data, command) {
|
|
||||||
data = FD.btoa_FD("m" + split_char + command + split_char + data);
|
|
||||||
command =
|
|
||||||
'{"type" : "arq", "command" : "send_raw", "parameter" : [{"dxcallsign" : "' +
|
|
||||||
dxcallsign +
|
|
||||||
'", "mode" : "' +
|
|
||||||
mode +
|
|
||||||
'", "n_frames" : "' +
|
|
||||||
frames +
|
|
||||||
'", "data" : "' +
|
|
||||||
data +
|
|
||||||
'", "attempts": "10"}]}';
|
|
||||||
socketLog.info(command);
|
|
||||||
socketLog.info("--------------RES--------------------");
|
|
||||||
writeTncCommand(command);
|
|
||||||
}
|
|
||||||
|
|
||||||
//Send station info request
|
|
||||||
exports.sendRequestInfo = function (dxcallsign) {
|
|
||||||
//Command 0 = user/station information
|
|
||||||
//Command 1 = shared folder list
|
|
||||||
//Command 2 = shared file transfer
|
|
||||||
sendRequest(dxcallsign, 255, 1, "0", "req");
|
|
||||||
};
|
|
||||||
|
|
||||||
//Send shared folder file list request
|
|
||||||
exports.sendRequestSharedFolderList = function (dxcallsign) {
|
|
||||||
//Command 0 = user/station information
|
|
||||||
//Command 1 = shared folder list
|
|
||||||
//Command 2 = shared file transfer
|
|
||||||
sendRequest(dxcallsign, 255, 1, "1", "req");
|
|
||||||
};
|
|
||||||
|
|
||||||
//Send shared file request
|
|
||||||
exports.sendRequestSharedFile = function (dxcallsign, file) {
|
|
||||||
//Command 0 = user/station information
|
|
||||||
//Command 1 = shared folder list
|
|
||||||
//Command 2 = shared file transfer
|
|
||||||
sendRequest(dxcallsign, 255, 1, "2" + file, "req");
|
|
||||||
};
|
|
||||||
|
|
||||||
//Send station info response
|
|
||||||
exports.sendResponseInfo = function (dxcallsign, userinfo) {
|
|
||||||
//Command 0 = user/station information
|
|
||||||
//Command 1 = shared folder list
|
|
||||||
//Command 2 = shared file transfer
|
|
||||||
sendResponse(dxcallsign, 255, 1, userinfo, "res-0");
|
|
||||||
};
|
|
||||||
|
|
||||||
//Send shared folder response
|
|
||||||
exports.sendResponseSharedFolderList = function (dxcallsign, sharedFolderList) {
|
|
||||||
//Command 0 = user/station information
|
|
||||||
//Command 1 = shared folder list
|
|
||||||
//Command 2 = shared file transfer
|
|
||||||
sendResponse(dxcallsign, 255, 1, sharedFolderList, "res-1");
|
|
||||||
};
|
|
||||||
|
|
||||||
//Send shared file response
|
|
||||||
exports.sendResponseSharedFile = function (
|
|
||||||
dxcallsign,
|
|
||||||
sharedFile,
|
|
||||||
sharedFileData,
|
|
||||||
) {
|
|
||||||
console.log(
|
|
||||||
"In sendResponseSharedFile",
|
|
||||||
dxcallsign,
|
|
||||||
sharedFile,
|
|
||||||
sharedFileData,
|
|
||||||
);
|
|
||||||
//Command 0 = user/station information
|
|
||||||
//Command 1 = shared folder list
|
|
||||||
//Command 2 = shared file transfer
|
|
||||||
sendResponse(dxcallsign, 255, 1, sharedFile + "/" + sharedFileData, "res-2");
|
|
||||||
};
|
|
||||||
|
|
||||||
//STOP TRANSMISSION
|
|
||||||
exports.stopTransmission = function () {
|
|
||||||
command = '{"type" : "arq", "command": "stop_transmission"}';
|
|
||||||
writeTncCommand(command);
|
|
||||||
};
|
|
||||||
|
|
||||||
// Get RX BUffer
|
|
||||||
exports.getRxBuffer = function () {
|
|
||||||
command = '{"type" : "get", "command" : "rx_buffer"}';
|
|
||||||
|
|
||||||
// call command only if new data arrived
|
|
||||||
if (rxBufferLengthGui != rxBufferLengthTnc) {
|
|
||||||
writeTncCommand(command);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
// START BEACON
|
|
||||||
exports.startBeacon = function (interval) {
|
|
||||||
command =
|
|
||||||
'{"type" : "broadcast", "command" : "start_beacon", "parameter": "' +
|
|
||||||
interval +
|
|
||||||
'"}';
|
|
||||||
writeTncCommand(command);
|
|
||||||
};
|
|
||||||
|
|
||||||
// STOP BEACON
|
|
||||||
exports.stopBeacon = function () {
|
|
||||||
command = '{"type" : "broadcast", "command" : "stop_beacon"}';
|
|
||||||
writeTncCommand(command);
|
|
||||||
};
|
|
||||||
|
|
||||||
// OPEN ARQ SESSION
|
|
||||||
exports.connectARQ = function (dxcallsign) {
|
|
||||||
command =
|
|
||||||
'{"type" : "arq", "command" : "connect", "dxcallsign": "' +
|
|
||||||
dxcallsign +
|
|
||||||
'", "attempts": "10"}';
|
|
||||||
writeTncCommand(command);
|
|
||||||
};
|
|
||||||
|
|
||||||
// CLOSE ARQ SESSION
|
|
||||||
exports.disconnectARQ = function () {
|
|
||||||
command = '{"type" : "arq", "command" : "disconnect"}';
|
|
||||||
writeTncCommand(command);
|
|
||||||
};
|
|
||||||
|
|
||||||
// SEND TEST FRAME
|
|
||||||
exports.sendTestFrame = function () {
|
|
||||||
command = '{"type" : "set", "command" : "send_test_frame"}';
|
|
||||||
writeTncCommand(command);
|
|
||||||
};
|
|
||||||
|
|
||||||
// SEND FEC
|
|
||||||
exports.sendFEC = function (mode, payload) {
|
|
||||||
command =
|
|
||||||
'{"type" : "fec", "command" : "transmit", "mode" : "' +
|
|
||||||
mode +
|
|
||||||
'", "payload" : "' +
|
|
||||||
payload +
|
|
||||||
'"}';
|
|
||||||
writeTncCommand(command);
|
|
||||||
};
|
|
||||||
|
|
||||||
// SEND FEC IS WRITING
|
|
||||||
exports.sendFecIsWriting = function (mycallsign) {
|
|
||||||
command =
|
|
||||||
'{"type" : "fec", "command" : "transmit_is_writing", "mycallsign" : "' +
|
|
||||||
mycallsign +
|
|
||||||
'"}';
|
|
||||||
writeTncCommand(command);
|
|
||||||
};
|
|
||||||
|
|
||||||
// SEND FEC TO BROADCASTCHANNEL
|
|
||||||
exports.sendBroadcastChannel = function (channel, data_out, uuid) {
|
|
||||||
let checksum = "";
|
|
||||||
let command = "";
|
|
||||||
let data = FD.btoa_FD(
|
|
||||||
"m" +
|
|
||||||
split_char +
|
|
||||||
channel +
|
|
||||||
//split_char +
|
|
||||||
//checksum +
|
|
||||||
split_char +
|
|
||||||
uuid +
|
|
||||||
split_char +
|
|
||||||
data_out,
|
|
||||||
);
|
|
||||||
console.log(data.length);
|
|
||||||
let payload = data;
|
|
||||||
command =
|
|
||||||
'{"type" : "fec", "command" : "transmit", "mode": "datac4", "wakeup": "True", "payload" : "' +
|
|
||||||
payload +
|
|
||||||
'"}';
|
|
||||||
writeTncCommand(command);
|
|
||||||
};
|
|
||||||
|
|
||||||
// RECORD AUDIO
|
|
||||||
exports.record_audio = function () {
|
|
||||||
command = '{"type" : "set", "command" : "record_audio"}';
|
|
||||||
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) => {
|
|
||||||
client.destroy();
|
|
||||||
let Data = {
|
|
||||||
busy_state: "-",
|
|
||||||
arq_state: "-",
|
|
||||||
//channel_state: "-",
|
|
||||||
frequency: "-",
|
|
||||||
mode: "-",
|
|
||||||
bandwidth: "-",
|
|
||||||
dbfs_level: 0,
|
|
||||||
};
|
|
||||||
ipcRenderer.send("request-update-tnc-state", Data);
|
|
||||||
tnc_port = arg.port;
|
|
||||||
tnc_host = arg.adress;
|
|
||||||
connectTNC();
|
|
||||||
});
|
|
||||||
|
|
||||||
// https://stackoverflow.com/a/50579690
|
|
||||||
// crc32 calculation
|
|
||||||
//console.log(crc32('abc'));
|
|
||||||
//console.log(crc32('abc').toString(16).toUpperCase()); // hex
|
|
||||||
var crc32 = function (r) {
|
|
||||||
for (var a, o = [], c = 0; c < 256; c++) {
|
|
||||||
a = c;
|
|
||||||
for (var f = 0; f < 8; f++) a = 1 & a ? 3988292384 ^ (a >>> 1) : a >>> 1;
|
|
||||||
o[c] = a;
|
|
||||||
}
|
|
||||||
for (var n = -1, t = 0; t < r.length; t++)
|
|
||||||
n = (n >>> 8) ^ o[255 & (n ^ r.charCodeAt(t))];
|
|
||||||
return (-1 ^ n) >>> 0;
|
|
||||||
};
|
|
|
@ -1,261 +0,0 @@
|
||||||
<option value="1">Hamlib Dummy</option>
|
|
||||||
<option value="2">Hamlib NET rigctl</option>
|
|
||||||
<option value="4">FLRig FLRig</option>
|
|
||||||
<option value="5">TRXManager TRXManager 5.7.630+</option>
|
|
||||||
<option value="6">Hamlib Dummy No VFO</option>
|
|
||||||
<option value="1001">Yaesu FT-847</option>
|
|
||||||
<option value="1003">Yaesu FT-1000D</option>
|
|
||||||
<option value="1004">Yaesu MARK-V FT-1000MP</option>
|
|
||||||
<option value="1005">Yaesu FT-747GX</option>
|
|
||||||
<option value="1006">Yaesu FT-757GX</option>
|
|
||||||
<option value="1007">Yaesu FT-757GXII</option>
|
|
||||||
<option value="1009">Yaesu FT-767GX</option>
|
|
||||||
<option value="1010">Yaesu FT-736R</option>
|
|
||||||
<option value="1011">Yaesu FT-840</option>
|
|
||||||
<option value="1013">Yaesu FT-900</option>
|
|
||||||
<option value="1014">Yaesu FT-920</option>
|
|
||||||
<option value="1015">Yaesu FT-890</option>
|
|
||||||
<option value="1016">Yaesu FT-990</option>
|
|
||||||
<option value="1017">Yaesu FRG-100</option>
|
|
||||||
<option value="1018">Yaesu FRG-9600</option>
|
|
||||||
<option value="1019">Yaesu FRG-8800</option>
|
|
||||||
<option value="1020">Yaesu FT-817</option>
|
|
||||||
<option value="1021">Yaesu FT-100</option>
|
|
||||||
<option value="1022">Yaesu FT-857</option>
|
|
||||||
<option value="1023">Yaesu FT-897</option>
|
|
||||||
<option value="1024">Yaesu FT-1000MP</option>
|
|
||||||
<option value="1025">Yaesu MARK-V Field FT-1000MP</option>
|
|
||||||
<option value="1026">Yaesu VR-5000</option>
|
|
||||||
<option value="1027">Yaesu FT-450</option>
|
|
||||||
<option value="1028">Yaesu FT-950</option>
|
|
||||||
<option value="1029">Yaesu FT-2000</option>
|
|
||||||
<option value="1030">Yaesu FTDX-9000</option>
|
|
||||||
<option value="1031">Yaesu FT-980</option>
|
|
||||||
<option value="1032">Yaesu FTDX-5000</option>
|
|
||||||
<option value="1033">Vertex Standard VX-1700</option>
|
|
||||||
<option value="1034">Yaesu FTDX-1200</option>
|
|
||||||
<option value="1035">Yaesu FT-991</option>
|
|
||||||
<option value="1036">Yaesu FT-891</option>
|
|
||||||
<option value="1037">Yaesu FTDX-3000</option>
|
|
||||||
<option value="1038">Yaesu FT-847UNI</option>
|
|
||||||
<option value="1039">Yaesu FT-600</option>
|
|
||||||
<option value="1040">Yaesu FTDX-101D</option>
|
|
||||||
<option value="1041">Yaesu FT-818</option>
|
|
||||||
<option value="1042">Yaesu FTDX-10</option>
|
|
||||||
<option value="1043">Yaesu FT-897D</option>
|
|
||||||
<option value="1044">Yaesu FTDX-101MP</option>
|
|
||||||
<option value="2001">Kenwood TS-50S</option>
|
|
||||||
<option value="2002">Kenwood TS-440S</option>
|
|
||||||
<option value="2003">Kenwood TS-450S</option>
|
|
||||||
<option value="2004">Kenwood TS-570D</option>
|
|
||||||
<option value="2005">Kenwood TS-690S</option>
|
|
||||||
<option value="2006">Kenwood TS-711</option>
|
|
||||||
<option value="2007">Kenwood TS-790</option>
|
|
||||||
<option value="2008">Kenwood TS-811</option>
|
|
||||||
<option value="2009">Kenwood TS-850</option>
|
|
||||||
<option value="2010">Kenwood TS-870S</option>
|
|
||||||
<option value="2011">Kenwood TS-940S</option>
|
|
||||||
<option value="2012">Kenwood TS-950S</option>
|
|
||||||
<option value="2013">Kenwood TS-950SDX</option>
|
|
||||||
<option value="2014">Kenwood TS-2000</option>
|
|
||||||
<option value="2015">Kenwood R-5000</option>
|
|
||||||
<option value="2016">Kenwood TS-570S</option>
|
|
||||||
<option value="2017">Kenwood TH-D7A</option>
|
|
||||||
<option value="2019">Kenwood TH-F6A</option>
|
|
||||||
<option value="2020">Kenwood TH-F7E</option>
|
|
||||||
<option value="2021">Elecraft K2</option>
|
|
||||||
<option value="2022">Kenwood TS-930</option>
|
|
||||||
<option value="2023">Kenwood TH-G71</option>
|
|
||||||
<option value="2024">Kenwood TS-680S</option>
|
|
||||||
<option value="2025">Kenwood TS-140S</option>
|
|
||||||
<option value="2026">Kenwood TM-D700</option>
|
|
||||||
<option value="2027">Kenwood TM-V7</option>
|
|
||||||
<option value="2028">Kenwood TS-480</option>
|
|
||||||
<option value="2029">Elecraft K3</option>
|
|
||||||
<option value="2030">Kenwood TRC-80</option>
|
|
||||||
<option value="2031">Kenwood TS-590S</option>
|
|
||||||
<option value="2032">SigFox Transfox</option>
|
|
||||||
<option value="2033">Kenwood TH-D72A</option>
|
|
||||||
<option value="2034">Kenwood TM-D710(G)</option>
|
|
||||||
<option value="2036">FlexRadio 6xxx</option>
|
|
||||||
<option value="2037">Kenwood TS-590SG</option>
|
|
||||||
<option value="2038">Elecraft XG3</option>
|
|
||||||
<option value="2039">Kenwood TS-990s</option>
|
|
||||||
<option value="2040">OpenHPSDR PiHPSDR</option>
|
|
||||||
<option value="2041">Kenwood TS-890S</option>
|
|
||||||
<option value="2042">Kenwood TH-D74</option>
|
|
||||||
<option value="2043">Elecraft K3S</option>
|
|
||||||
<option value="2044">Elecraft KX2</option>
|
|
||||||
<option value="2045">Elecraft KX3</option>
|
|
||||||
<option value="2046">Hilberling PT-8000A</option>
|
|
||||||
<option value="2047">Elecraft K4</option>
|
|
||||||
<option value="2048">FlexRadio/ANAN PowerSDR/Thetis</option>
|
|
||||||
<option value="2049">Malachite DSP</option>
|
|
||||||
<option value="3002">Icom IC-1275</option>
|
|
||||||
<option value="3003">Icom IC-271</option>
|
|
||||||
<option value="3004">Icom IC-275</option>
|
|
||||||
<option value="3006">Icom IC-471</option>
|
|
||||||
<option value="3007">Icom IC-475</option>
|
|
||||||
<option value="3009">Icom IC-706</option>
|
|
||||||
<option value="3010">Icom IC-706MkII</option>
|
|
||||||
<option value="3011">Icom IC-706MkIIG</option>
|
|
||||||
<option value="3012">Icom IC-707</option>
|
|
||||||
<option value="3013">Icom IC-718</option>
|
|
||||||
<option value="3014">Icom IC-725</option>
|
|
||||||
<option value="3015">Icom IC-726</option>
|
|
||||||
<option value="3016">Icom IC-728</option>
|
|
||||||
<option value="3017">Icom IC-729</option>
|
|
||||||
<option value="3019">Icom IC-735</option>
|
|
||||||
<option value="3020">Icom IC-736</option>
|
|
||||||
<option value="3021">Icom IC-737</option>
|
|
||||||
<option value="3022">Icom IC-738</option>
|
|
||||||
<option value="3023">Icom IC-746</option>
|
|
||||||
<option value="3024">Icom IC-751</option>
|
|
||||||
<option value="3026">Icom IC-756</option>
|
|
||||||
<option value="3027">Icom IC-756PRO</option>
|
|
||||||
<option value="3028">Icom IC-761</option>
|
|
||||||
<option value="3029">Icom IC-765</option>
|
|
||||||
<option value="3030">Icom IC-775</option>
|
|
||||||
<option value="3031">Icom IC-781</option>
|
|
||||||
<option value="3032">Icom IC-820H</option>
|
|
||||||
<option value="3034">Icom IC-821H</option>
|
|
||||||
<option value="3035">Icom IC-970</option>
|
|
||||||
<option value="3036">Icom IC-R10</option>
|
|
||||||
<option value="3037">Icom IC-R71</option>
|
|
||||||
<option value="3038">Icom IC-R72</option>
|
|
||||||
<option value="3039">Icom IC-R75</option>
|
|
||||||
<option value="3040">Icom IC-R7000</option>
|
|
||||||
<option value="3041">Icom IC-R7100</option>
|
|
||||||
<option value="3042">Icom ICR-8500</option>
|
|
||||||
<option value="3043">Icom IC-R9000</option>
|
|
||||||
<option value="3044">Icom IC-910</option>
|
|
||||||
<option value="3045">Icom IC-78</option>
|
|
||||||
<option value="3046">Icom IC-746PRO</option>
|
|
||||||
<option value="3047">Icom IC-756PROII</option>
|
|
||||||
<option value="3051">Ten-Tec Omni VI Plus</option>
|
|
||||||
<option value="3052">Optoelectronics OptoScan535</option>
|
|
||||||
<option value="3053">Optoelectronics OptoScan456</option>
|
|
||||||
<option value="3054">Icom IC ID-1</option>
|
|
||||||
<option value="3055">Icom IC-703</option>
|
|
||||||
<option value="3056">Icom IC-7800</option>
|
|
||||||
<option value="3057">Icom IC-756PROIII</option>
|
|
||||||
<option value="3058">Icom IC-R20</option>
|
|
||||||
<option value="3060">Icom IC-7000</option>
|
|
||||||
<option value="3061">Icom IC-7200</option>
|
|
||||||
<option value="3062">Icom IC-7700</option>
|
|
||||||
<option value="3063">Icom IC-7600</option>
|
|
||||||
<option value="3064">Ten-Tec Delta II</option>
|
|
||||||
<option value="3065">Icom IC-92D</option>
|
|
||||||
<option value="3066">Icom IC-R9500</option>
|
|
||||||
<option value="3067">Icom IC-7410</option>
|
|
||||||
<option value="3068">Icom IC-9100</option>
|
|
||||||
<option value="3069">Icom IC-RX7</option>
|
|
||||||
<option value="3070">Icom IC-7100</option>
|
|
||||||
<option value="3071">Icom ID-5100</option>
|
|
||||||
<option value="3072">Icom IC-2730</option>
|
|
||||||
<option value="3073">Icom IC-7300</option>
|
|
||||||
<option value="3074">Microtelecom Perseus</option>
|
|
||||||
<option value="3075">Icom IC-785x</option>
|
|
||||||
<option value="3076">Xeigu X108G</option>
|
|
||||||
<option value="3077">Icom IC-R6</option>
|
|
||||||
<option value="3078">Icom IC-7610</option>
|
|
||||||
<option value="3079">Icom IC-R8600</option>
|
|
||||||
<option value="3080">Icom IC-R30</option>
|
|
||||||
<option value="3081">Icom IC-9700</option>
|
|
||||||
<option value="3082">Icom ID-4100</option>
|
|
||||||
<option value="3083">Icom ID-31</option>
|
|
||||||
<option value="3084">Icom ID-51</option>
|
|
||||||
<option value="3085">Icom IC-705</option>
|
|
||||||
<option value="4001">Icom IC-PCR1000</option>
|
|
||||||
<option value="4002">Icom IC-PCR100</option>
|
|
||||||
<option value="4003">Icom IC-PCR1500</option>
|
|
||||||
<option value="4004">Icom IC-PCR2500</option>
|
|
||||||
<option value="5001">AOR AR8200</option>
|
|
||||||
<option value="5002">AOR AR8000</option>
|
|
||||||
<option value="5003">AOR AR7030</option>
|
|
||||||
<option value="5004">AOR AR5000</option>
|
|
||||||
<option value="5005">AOR AR3030</option>
|
|
||||||
<option value="5006">AOR AR3000A</option>
|
|
||||||
<option value="5008">AOR AR2700</option>
|
|
||||||
<option value="5013">AOR AR8600</option>
|
|
||||||
<option value="5014">AOR AR5000A</option>
|
|
||||||
<option value="5015">AOR AR7030 Plus</option>
|
|
||||||
<option value="5016">AOR SR2200</option>
|
|
||||||
<option value="6005">JRC NRD-525</option>
|
|
||||||
<option value="6006">JRC NRD-535D</option>
|
|
||||||
<option value="6007">JRC NRD-545 DSP</option>
|
|
||||||
<option value="8001">Uniden BC780xlt</option>
|
|
||||||
<option value="8002">Uniden BC245xlt</option>
|
|
||||||
<option value="8003">Uniden BC895xlt</option>
|
|
||||||
<option value="8004">Radio Shack PRO-2052</option>
|
|
||||||
<option value="8006">Uniden BC250D</option>
|
|
||||||
<option value="8010">Uniden BCD-396T</option>
|
|
||||||
<option value="8011">Uniden BCD-996T</option>
|
|
||||||
<option value="8012">Uniden BC898T</option>
|
|
||||||
<option value="9002">Drake R-8A</option>
|
|
||||||
<option value="9003">Drake R-8B</option>
|
|
||||||
<option value="10004">Lowe HF-235</option>
|
|
||||||
<option value="11003">Racal RA6790/GM</option>
|
|
||||||
<option value="11005">Racal RA3702</option>
|
|
||||||
<option value="12004">Watkins-Johnson WJ-8888</option>
|
|
||||||
<option value="14002">Skanti TRP8000</option>
|
|
||||||
<option value="14004">Skanti TRP 8255 S R</option>
|
|
||||||
<option value="15001">Winradio WR-1000</option>
|
|
||||||
<option value="15002">Winradio WR-1500</option>
|
|
||||||
<option value="15003">Winradio WR-1550</option>
|
|
||||||
<option value="15004">Winradio WR-3100</option>
|
|
||||||
<option value="15005">Winradio WR-3150</option>
|
|
||||||
<option value="15006">Winradio WR-3500</option>
|
|
||||||
<option value="15007">Winradio WR-3700</option>
|
|
||||||
<option value="15009">Winradio WR-G313</option>
|
|
||||||
<option value="16001">Ten-Tec TT-550</option>
|
|
||||||
<option value="16002">Ten-Tec TT-538 Jupiter</option>
|
|
||||||
<option value="16003">Ten-Tec RX-320</option>
|
|
||||||
<option value="16004">Ten-Tec RX-340</option>
|
|
||||||
<option value="16005">Ten-Tec RX-350</option>
|
|
||||||
<option value="16007">Ten-Tec TT-516 Argonaut V</option>
|
|
||||||
<option value="16008">Ten-Tec TT-565 Orion</option>
|
|
||||||
<option value="16009">Ten-Tec TT-585 Paragon</option>
|
|
||||||
<option value="16011">Ten-Tec TT-588 Omni VII</option>
|
|
||||||
<option value="16012">Ten-Tec RX-331</option>
|
|
||||||
<option value="16013">Ten-Tec TT-599 Eagle</option>
|
|
||||||
<option value="17001">Alinco DX-77</option>
|
|
||||||
<option value="17002">Alinco DX-SR8</option>
|
|
||||||
<option value="18001">Kachina 505DSP</option>
|
|
||||||
<option value="22001">TAPR DSP-10</option>
|
|
||||||
<option value="23001">Flex-radio SDR-1000</option>
|
|
||||||
<option value="23003">DTTS Microwave Society DttSP IPC</option>
|
|
||||||
<option value="23004">DTTS Microwave Society DttSP UDP</option>
|
|
||||||
<option value="24001">RFT EKD-500</option>
|
|
||||||
<option value="25001">Elektor Elektor 3/04</option>
|
|
||||||
<option value="25002">SAT-Schneider DRT1</option>
|
|
||||||
<option value="25003">Coding Technologies Digital World Traveller</option>
|
|
||||||
<option value="25006">AmQRP DDS-60</option>
|
|
||||||
<option value="25007">Elektor Elektor SDR-USB</option>
|
|
||||||
<option value="25008">mRS miniVNA</option>
|
|
||||||
<option value="25009">SoftRock Si570 AVR-USB</option>
|
|
||||||
<option value="25011">KTH-SDR kit Si570 PIC-USB</option>
|
|
||||||
<option value="25012">FiFi FiFi-SDR</option>
|
|
||||||
<option value="25013">AMSAT-UK FUNcube Dongle</option>
|
|
||||||
<option value="25014">N2ADR HiQSDR</option>
|
|
||||||
<option value="25015">Funkamateur FA-SDR</option>
|
|
||||||
<option value="25016">AE9RB Si570 Peaberry V1</option>
|
|
||||||
<option value="25017">AE9RB Si570 Peaberry V2</option>
|
|
||||||
<option value="25018">AMSAT-UK FUNcube Dongle Pro+</option>
|
|
||||||
<option value="25019">HobbyPCB RS-HFIQ</option>
|
|
||||||
<option value="26001">Video4Linux SW/FM radio</option>
|
|
||||||
<option value="26002">Video4Linux2 SW/FM radio</option>
|
|
||||||
<option value="27001">Rohde&Schwarz ESMC</option>
|
|
||||||
<option value="27002">Rohde&Schwarz EB200</option>
|
|
||||||
<option value="27003">Rohde&Schwarz XK2100</option>
|
|
||||||
<option value="28001">Philips/Simoco PRM8060</option>
|
|
||||||
<option value="29001">ADAT www.adat.ch ADT-200A</option>
|
|
||||||
<option value="30001">Icom IC-M700PRO</option>
|
|
||||||
<option value="30002">Icom IC-M802</option>
|
|
||||||
<option value="30003">Icom IC-M710</option>
|
|
||||||
<option value="30004">Icom IC-M803</option>
|
|
||||||
<option value="31001">Dorji DRA818V</option>
|
|
||||||
<option value="31002">Dorji DRA818U</option>
|
|
||||||
<option value="32001">Barrett 2050</option>
|
|
||||||
<option value="32002">Barrett 950</option>
|
|
||||||
<option value="33001">ELAD FDM-DUO</option>
|
|
Binary file not shown.
Before Width: | Height: | Size: 110 KiB |
Binary file not shown.
Before Width: | Height: | Size: 590 KiB |
4946
gui/src/index.html
4946
gui/src/index.html
File diff suppressed because it is too large
Load diff
|
@ -1,11 +0,0 @@
|
||||||
<!doctype html>
|
|
||||||
<html>
|
|
||||||
<head>
|
|
||||||
<meta charset="utf-8" />
|
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
|
||||||
<meta http-equiv="Content-Security-Policy" content="script-src 'self';" />
|
|
||||||
</head>
|
|
||||||
<body style="overflow: hidden">
|
|
||||||
<img src="img/icon_cube_border.png" width="100%" height="100%" />
|
|
||||||
</body>
|
|
||||||
</html>
|
|
|
@ -107,3 +107,11 @@ https://stackoverflow.com/a/9622873
|
||||||
[data-bs-theme="dark"] {
|
[data-bs-theme="dark"] {
|
||||||
/* default dark theme mods */
|
/* default dark theme mods */
|
||||||
}
|
}
|
||||||
|
.modal-backdrop {
|
||||||
|
background-color: transparent;
|
||||||
|
}
|
||||||
|
|
||||||
|
.modal-backdrop.in {
|
||||||
|
filter: alpha(opacity=10);
|
||||||
|
opacity: .1
|
||||||
|
}
|
||||||
|
|
|
@ -1,607 +0,0 @@
|
||||||
/*=============================================================
|
|
||||||
Filename: Spectrogram-1v00.js
|
|
||||||
|
|
||||||
JavaScript graphics functions to draw Spectrograms.
|
|
||||||
|
|
||||||
Date Description By
|
|
||||||
-------|-------------------------------------------------|---
|
|
||||||
12Nov18 First beta ARC
|
|
||||||
17Nov18 Added offset into data buffer ARC
|
|
||||||
08May19 this.imageURL URL added
|
|
||||||
bugfix: fixed isNaN test
|
|
||||||
Changed sgStart, sgStop to start, stop
|
|
||||||
Added options object to constructors ARC
|
|
||||||
10May19 Enabled Left to Right as well as Top to Bottom ARC
|
|
||||||
11May19 Added RasterscanSVG ARC
|
|
||||||
12May19 Added blnkline for horizontal ratser scans ARC
|
|
||||||
13May19 Eliminated unneccessary putImageData ARC
|
|
||||||
14May19 Removed toDataURL, not used drawImage is better
|
|
||||||
bugfix: SVG RHC names swapped ARC
|
|
||||||
02Jun19 bugfix: startOfs not honored in horizontalNewLine ARC
|
|
||||||
03Jun19 Flipped the SVG and RHC names for waterfalls ARC
|
|
||||||
04Jun19 Unflip SVG and RHC for horizontal mode ARC
|
|
||||||
Swap "SVG" & "RHC" strings to match fn names ARC
|
|
||||||
05Jun19 bugfix: WaterfallSVG scrolling wrong way ARC
|
|
||||||
10Jun19 bugfix: support lineRate=0 for static display
|
|
||||||
bugfix: ipBufPtr must be a ptr to a ptr ARC
|
|
||||||
11Jun19 Make ipBuffers an Array of Arrays, if lineRate=0
|
|
||||||
use all buffers else use only ipBuffer[0] ARC
|
|
||||||
13Jun19 Use Waterfall and Rasterscan plus direction
|
|
||||||
Use Boolean rater than string compare ARC
|
|
||||||
16Jun19 Use const and let ARC
|
|
||||||
20Jun19 Change order of parameters ARC
|
|
||||||
21Jun19 Add setLineRate method ARC
|
|
||||||
06Jul19 Released as Rev 1v00 ARC
|
|
||||||
==============================================================*/
|
|
||||||
|
|
||||||
var Waterfall, Rasterscan;
|
|
||||||
|
|
||||||
(function () {
|
|
||||||
Waterfall = function (ipBufAry, w, h, dir, options) {
|
|
||||||
var direction = typeof dir === "string" ? dir.toLowerCase() : "down";
|
|
||||||
|
|
||||||
switch (direction) {
|
|
||||||
case "up":
|
|
||||||
return new Spectrogram(ipBufAry, w, h, "WF", false, true, options);
|
|
||||||
case "down":
|
|
||||||
default:
|
|
||||||
return new Spectrogram(ipBufAry, w, h, "WF", true, true, options);
|
|
||||||
case "left":
|
|
||||||
return new Spectrogram(ipBufAry, w, h, "WF", false, false, options);
|
|
||||||
case "right":
|
|
||||||
return new Spectrogram(ipBufAry, w, h, "WF", true, false, options);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
Rasterscan = function (ipBufAry, w, h, dir, options) {
|
|
||||||
const direction = typeof dir === "string" ? dir.toLowerCase() : "down";
|
|
||||||
|
|
||||||
switch (direction) {
|
|
||||||
case "up":
|
|
||||||
return new Spectrogram(ipBufAry, w, h, "RS", true, true, options);
|
|
||||||
case "down":
|
|
||||||
default:
|
|
||||||
return new Spectrogram(ipBufAry, w, h, "RS", false, true, options);
|
|
||||||
case "left":
|
|
||||||
return new Spectrogram(ipBufAry, w, h, "RS", false, false, options);
|
|
||||||
case "right":
|
|
||||||
return new Spectrogram(ipBufAry, w, h, "RS", true, false, options);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
function Spectrogram(ipBufAry, w, h, sgMode, rhc, vert, options) {
|
|
||||||
const opt = typeof options === "object" ? options : {}; // avoid undeclared object errors
|
|
||||||
let offScreenCtx; // offscreen canvas drawing context
|
|
||||||
const pxPerLine = w || 200;
|
|
||||||
const lines = h || 200;
|
|
||||||
let lineRate = 30; // requested line rate for dynamic waterfalls
|
|
||||||
let interval = 0; // msec
|
|
||||||
let startOfs = 0;
|
|
||||||
const lineBuf = new ArrayBuffer(pxPerLine * 4); // 1 line
|
|
||||||
const lineBuf8 = new Uint8ClampedArray(lineBuf);
|
|
||||||
const lineImgData = new ImageData(lineBuf8, pxPerLine, 1); // 1 line of canvas pixels
|
|
||||||
let pageImgData; // lines * pxPerLine of canvas pixels
|
|
||||||
let ipBuf8; // map input data to 0..255 unsigned bytes
|
|
||||||
const blankBuf = new ArrayBuffer(pxPerLine * 4); // 1 line
|
|
||||||
const blankBuf8 = new Uint8ClampedArray(blankBuf);
|
|
||||||
const blankImgData = new ImageData(blankBuf8, pxPerLine, 1); // 1 line of canvas pixels
|
|
||||||
const clearBuf = new ArrayBuffer(pxPerLine * lines * 4); // fills with 0s ie. rgba 0,0,0,0 = transparent
|
|
||||||
const clearBuf8 = new Uint8ClampedArray(clearBuf);
|
|
||||||
let clearImgData;
|
|
||||||
let nextLine = 0;
|
|
||||||
let timerID = null;
|
|
||||||
let running = false;
|
|
||||||
let sgTime = 0;
|
|
||||||
let sgStartTime = 0;
|
|
||||||
|
|
||||||
// Matlab Jet ref: stackoverflow.com grayscale-to-red-green-blue-matlab-jet-color-scale
|
|
||||||
let colMap = [
|
|
||||||
[0, 0, 128, 255],
|
|
||||||
[0, 0, 131, 255],
|
|
||||||
[0, 0, 135, 255],
|
|
||||||
[0, 0, 139, 255],
|
|
||||||
[0, 0, 143, 255],
|
|
||||||
[0, 0, 147, 255],
|
|
||||||
[0, 0, 151, 255],
|
|
||||||
[0, 0, 155, 255],
|
|
||||||
[0, 0, 159, 255],
|
|
||||||
[0, 0, 163, 255],
|
|
||||||
[0, 0, 167, 255],
|
|
||||||
[0, 0, 171, 255],
|
|
||||||
[0, 0, 175, 255],
|
|
||||||
[0, 0, 179, 255],
|
|
||||||
[0, 0, 183, 255],
|
|
||||||
[0, 0, 187, 255],
|
|
||||||
[0, 0, 191, 255],
|
|
||||||
[0, 0, 195, 255],
|
|
||||||
[0, 0, 199, 255],
|
|
||||||
[0, 0, 203, 255],
|
|
||||||
[0, 0, 207, 255],
|
|
||||||
[0, 0, 211, 255],
|
|
||||||
[0, 0, 215, 255],
|
|
||||||
[0, 0, 219, 255],
|
|
||||||
[0, 0, 223, 255],
|
|
||||||
[0, 0, 227, 255],
|
|
||||||
[0, 0, 231, 255],
|
|
||||||
[0, 0, 235, 255],
|
|
||||||
[0, 0, 239, 255],
|
|
||||||
[0, 0, 243, 255],
|
|
||||||
[0, 0, 247, 255],
|
|
||||||
[0, 0, 251, 255],
|
|
||||||
[0, 0, 255, 255],
|
|
||||||
[0, 4, 255, 255],
|
|
||||||
[0, 8, 255, 255],
|
|
||||||
[0, 12, 255, 255],
|
|
||||||
[0, 16, 255, 255],
|
|
||||||
[0, 20, 255, 255],
|
|
||||||
[0, 24, 255, 255],
|
|
||||||
[0, 28, 255, 255],
|
|
||||||
[0, 32, 255, 255],
|
|
||||||
[0, 36, 255, 255],
|
|
||||||
[0, 40, 255, 255],
|
|
||||||
[0, 44, 255, 255],
|
|
||||||
[0, 48, 255, 255],
|
|
||||||
[0, 52, 255, 255],
|
|
||||||
[0, 56, 255, 255],
|
|
||||||
[0, 60, 255, 255],
|
|
||||||
[0, 64, 255, 255],
|
|
||||||
[0, 68, 255, 255],
|
|
||||||
[0, 72, 255, 255],
|
|
||||||
[0, 76, 255, 255],
|
|
||||||
[0, 80, 255, 255],
|
|
||||||
[0, 84, 255, 255],
|
|
||||||
[0, 88, 255, 255],
|
|
||||||
[0, 92, 255, 255],
|
|
||||||
[0, 96, 255, 255],
|
|
||||||
[0, 100, 255, 255],
|
|
||||||
[0, 104, 255, 255],
|
|
||||||
[0, 108, 255, 255],
|
|
||||||
[0, 112, 255, 255],
|
|
||||||
[0, 116, 255, 255],
|
|
||||||
[0, 120, 255, 255],
|
|
||||||
[0, 124, 255, 255],
|
|
||||||
[0, 128, 255, 255],
|
|
||||||
[0, 131, 255, 255],
|
|
||||||
[0, 135, 255, 255],
|
|
||||||
[0, 139, 255, 255],
|
|
||||||
[0, 143, 255, 255],
|
|
||||||
[0, 147, 255, 255],
|
|
||||||
[0, 151, 255, 255],
|
|
||||||
[0, 155, 255, 255],
|
|
||||||
[0, 159, 255, 255],
|
|
||||||
[0, 163, 255, 255],
|
|
||||||
[0, 167, 255, 255],
|
|
||||||
[0, 171, 255, 255],
|
|
||||||
[0, 175, 255, 255],
|
|
||||||
[0, 179, 255, 255],
|
|
||||||
[0, 183, 255, 255],
|
|
||||||
[0, 187, 255, 255],
|
|
||||||
[0, 191, 255, 255],
|
|
||||||
[0, 195, 255, 255],
|
|
||||||
[0, 199, 255, 255],
|
|
||||||
[0, 203, 255, 255],
|
|
||||||
[0, 207, 255, 255],
|
|
||||||
[0, 211, 255, 255],
|
|
||||||
[0, 215, 255, 255],
|
|
||||||
[0, 219, 255, 255],
|
|
||||||
[0, 223, 255, 255],
|
|
||||||
[0, 227, 255, 255],
|
|
||||||
[0, 231, 255, 255],
|
|
||||||
[0, 235, 255, 255],
|
|
||||||
[0, 239, 255, 255],
|
|
||||||
[0, 243, 255, 255],
|
|
||||||
[0, 247, 255, 255],
|
|
||||||
[0, 251, 255, 255],
|
|
||||||
[0, 255, 255, 255],
|
|
||||||
[4, 255, 251, 255],
|
|
||||||
[8, 255, 247, 255],
|
|
||||||
[12, 255, 243, 255],
|
|
||||||
[16, 255, 239, 255],
|
|
||||||
[20, 255, 235, 255],
|
|
||||||
[24, 255, 231, 255],
|
|
||||||
[28, 255, 227, 255],
|
|
||||||
[32, 255, 223, 255],
|
|
||||||
[36, 255, 219, 255],
|
|
||||||
[40, 255, 215, 255],
|
|
||||||
[44, 255, 211, 255],
|
|
||||||
[48, 255, 207, 255],
|
|
||||||
[52, 255, 203, 255],
|
|
||||||
[56, 255, 199, 255],
|
|
||||||
[60, 255, 195, 255],
|
|
||||||
[64, 255, 191, 255],
|
|
||||||
[68, 255, 187, 255],
|
|
||||||
[72, 255, 183, 255],
|
|
||||||
[76, 255, 179, 255],
|
|
||||||
[80, 255, 175, 255],
|
|
||||||
[84, 255, 171, 255],
|
|
||||||
[88, 255, 167, 255],
|
|
||||||
[92, 255, 163, 255],
|
|
||||||
[96, 255, 159, 255],
|
|
||||||
[100, 255, 155, 255],
|
|
||||||
[104, 255, 151, 255],
|
|
||||||
[108, 255, 147, 255],
|
|
||||||
[112, 255, 143, 255],
|
|
||||||
[116, 255, 139, 255],
|
|
||||||
[120, 255, 135, 255],
|
|
||||||
[124, 255, 131, 255],
|
|
||||||
[128, 255, 128, 255],
|
|
||||||
[131, 255, 124, 255],
|
|
||||||
[135, 255, 120, 255],
|
|
||||||
[139, 255, 116, 255],
|
|
||||||
[143, 255, 112, 255],
|
|
||||||
[147, 255, 108, 255],
|
|
||||||
[151, 255, 104, 255],
|
|
||||||
[155, 255, 100, 255],
|
|
||||||
[159, 255, 96, 255],
|
|
||||||
[163, 255, 92, 255],
|
|
||||||
[167, 255, 88, 255],
|
|
||||||
[171, 255, 84, 255],
|
|
||||||
[175, 255, 80, 255],
|
|
||||||
[179, 255, 76, 255],
|
|
||||||
[183, 255, 72, 255],
|
|
||||||
[187, 255, 68, 255],
|
|
||||||
[191, 255, 64, 255],
|
|
||||||
[195, 255, 60, 255],
|
|
||||||
[199, 255, 56, 255],
|
|
||||||
[203, 255, 52, 255],
|
|
||||||
[207, 255, 48, 255],
|
|
||||||
[211, 255, 44, 255],
|
|
||||||
[215, 255, 40, 255],
|
|
||||||
[219, 255, 36, 255],
|
|
||||||
[223, 255, 32, 255],
|
|
||||||
[227, 255, 28, 255],
|
|
||||||
[231, 255, 24, 255],
|
|
||||||
[235, 255, 20, 255],
|
|
||||||
[239, 255, 16, 255],
|
|
||||||
[243, 255, 12, 255],
|
|
||||||
[247, 255, 8, 255],
|
|
||||||
[251, 255, 4, 255],
|
|
||||||
[255, 255, 0, 255],
|
|
||||||
[255, 251, 0, 255],
|
|
||||||
[255, 247, 0, 255],
|
|
||||||
[255, 243, 0, 255],
|
|
||||||
[255, 239, 0, 255],
|
|
||||||
[255, 235, 0, 255],
|
|
||||||
[255, 231, 0, 255],
|
|
||||||
[255, 227, 0, 255],
|
|
||||||
[255, 223, 0, 255],
|
|
||||||
[255, 219, 0, 255],
|
|
||||||
[255, 215, 0, 255],
|
|
||||||
[255, 211, 0, 255],
|
|
||||||
[255, 207, 0, 255],
|
|
||||||
[255, 203, 0, 255],
|
|
||||||
[255, 199, 0, 255],
|
|
||||||
[255, 195, 0, 255],
|
|
||||||
[255, 191, 0, 255],
|
|
||||||
[255, 187, 0, 255],
|
|
||||||
[255, 183, 0, 255],
|
|
||||||
[255, 179, 0, 255],
|
|
||||||
[255, 175, 0, 255],
|
|
||||||
[255, 171, 0, 255],
|
|
||||||
[255, 167, 0, 255],
|
|
||||||
[255, 163, 0, 255],
|
|
||||||
[255, 159, 0, 255],
|
|
||||||
[255, 155, 0, 255],
|
|
||||||
[255, 151, 0, 255],
|
|
||||||
[255, 147, 0, 255],
|
|
||||||
[255, 143, 0, 255],
|
|
||||||
[255, 139, 0, 255],
|
|
||||||
[255, 135, 0, 255],
|
|
||||||
[255, 131, 0, 255],
|
|
||||||
[255, 128, 0, 255],
|
|
||||||
[255, 124, 0, 255],
|
|
||||||
[255, 120, 0, 255],
|
|
||||||
[255, 116, 0, 255],
|
|
||||||
[255, 112, 0, 255],
|
|
||||||
[255, 108, 0, 255],
|
|
||||||
[255, 104, 0, 255],
|
|
||||||
[255, 100, 0, 255],
|
|
||||||
[255, 96, 0, 255],
|
|
||||||
[255, 92, 0, 255],
|
|
||||||
[255, 88, 0, 255],
|
|
||||||
[255, 84, 0, 255],
|
|
||||||
[255, 80, 0, 255],
|
|
||||||
[255, 76, 0, 255],
|
|
||||||
[255, 72, 0, 255],
|
|
||||||
[255, 68, 0, 255],
|
|
||||||
[255, 64, 0, 255],
|
|
||||||
[255, 60, 0, 255],
|
|
||||||
[255, 56, 0, 255],
|
|
||||||
[255, 52, 0, 255],
|
|
||||||
[255, 48, 0, 255],
|
|
||||||
[255, 44, 0, 255],
|
|
||||||
[255, 40, 0, 255],
|
|
||||||
[255, 36, 0, 255],
|
|
||||||
[255, 32, 0, 255],
|
|
||||||
[255, 28, 0, 255],
|
|
||||||
[255, 24, 0, 255],
|
|
||||||
[255, 20, 0, 255],
|
|
||||||
[255, 16, 0, 255],
|
|
||||||
[255, 12, 0, 255],
|
|
||||||
[255, 8, 0, 255],
|
|
||||||
[255, 4, 0, 255],
|
|
||||||
[255, 0, 0, 255],
|
|
||||||
[251, 0, 0, 255],
|
|
||||||
[247, 0, 0, 255],
|
|
||||||
[243, 0, 0, 255],
|
|
||||||
[239, 0, 0, 255],
|
|
||||||
[235, 0, 0, 255],
|
|
||||||
[231, 0, 0, 255],
|
|
||||||
[227, 0, 0, 255],
|
|
||||||
[223, 0, 0, 255],
|
|
||||||
[219, 0, 0, 255],
|
|
||||||
[215, 0, 0, 255],
|
|
||||||
[211, 0, 0, 255],
|
|
||||||
[207, 0, 0, 255],
|
|
||||||
[203, 0, 0, 255],
|
|
||||||
[199, 0, 0, 255],
|
|
||||||
[195, 0, 0, 255],
|
|
||||||
[191, 0, 0, 255],
|
|
||||||
[187, 0, 0, 255],
|
|
||||||
[183, 0, 0, 255],
|
|
||||||
[179, 0, 0, 255],
|
|
||||||
[175, 0, 0, 255],
|
|
||||||
[171, 0, 0, 255],
|
|
||||||
[167, 0, 0, 255],
|
|
||||||
[163, 0, 0, 255],
|
|
||||||
[159, 0, 0, 255],
|
|
||||||
[155, 0, 0, 255],
|
|
||||||
[151, 0, 0, 255],
|
|
||||||
[147, 0, 0, 255],
|
|
||||||
[143, 0, 0, 255],
|
|
||||||
[139, 0, 0, 255],
|
|
||||||
[135, 0, 0, 255],
|
|
||||||
[131, 0, 0, 255],
|
|
||||||
[0, 0, 0, 0],
|
|
||||||
];
|
|
||||||
|
|
||||||
function incrLine() {
|
|
||||||
if ((vert && !rhc) || (!vert && rhc)) {
|
|
||||||
nextLine++;
|
|
||||||
if (nextLine >= lines) {
|
|
||||||
nextLine = 0;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
nextLine--;
|
|
||||||
if (nextLine < 0) {
|
|
||||||
nextLine = lines - 1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function updateWaterfall() {
|
|
||||||
// update dynamic waterfalls at a fixed rate
|
|
||||||
let sgDiff;
|
|
||||||
|
|
||||||
// grab latest line of data, write it to off screen buffer, inc 'nextLine'
|
|
||||||
sgNewLine();
|
|
||||||
// loop to write data data at the desired rate, data is being updated asynchronously
|
|
||||||
// ref for accurate timeout: http://www.sitepoint.com/creating-accurate-timers-in-javascript
|
|
||||||
sgTime += interval;
|
|
||||||
sgDiff = Date.now() - sgStartTime - sgTime;
|
|
||||||
if (running) {
|
|
||||||
timerID = setTimeout(updateWaterfall, interval - sgDiff);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function sgSetLineRate(newRate) {
|
|
||||||
if (isNaN(newRate) || newRate > 50 || newRate < 0) {
|
|
||||||
console.error("invalid line rate [0 <= lineRate < 50 lines/sec]");
|
|
||||||
// don't change the lineRate;
|
|
||||||
} else if (newRate === 0) {
|
|
||||||
// static (one pass) raster
|
|
||||||
lineRate = 0;
|
|
||||||
} else {
|
|
||||||
lineRate = newRate;
|
|
||||||
interval = 1000 / lineRate; // msec
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
this.setLineRate = sgSetLineRate;
|
|
||||||
|
|
||||||
function setProperty(propertyName, value) {
|
|
||||||
if (typeof propertyName !== "string" || value === undefined) {
|
|
||||||
// null is OK, forces default
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
switch (propertyName.toLowerCase()) {
|
|
||||||
case "linerate":
|
|
||||||
sgSetLineRate(value); // setLine does checks for number etc
|
|
||||||
break;
|
|
||||||
case "startbin":
|
|
||||||
if (!isNaN(value) && value > 0) {
|
|
||||||
startOfs = value;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case "onscreenparentid":
|
|
||||||
if (typeof value === "string" && document.getElementById(value)) {
|
|
||||||
demoCvsId = value;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case "colormap":
|
|
||||||
if (
|
|
||||||
Array.isArray(value) &&
|
|
||||||
Array.isArray(value[0]) &&
|
|
||||||
value[0].length == 4
|
|
||||||
) {
|
|
||||||
colMap = value; // value must be an array of 4 element arrays to get here
|
|
||||||
if (colMap.length < 256) {
|
|
||||||
// fill out the remaining colors with last color
|
|
||||||
for (let i = colMap.length; i < 256; i++) {
|
|
||||||
colMap[i] = colMap[colMap.length - 1];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function verticalNewLine() {
|
|
||||||
let tmpImgData, ipBuf8;
|
|
||||||
|
|
||||||
if (sgMode == "WF") {
|
|
||||||
if (rhc) {
|
|
||||||
// shift the current display down 1 line, oldest line drops off
|
|
||||||
tmpImgData = offScreenCtx.getImageData(0, 0, pxPerLine, lines - 1);
|
|
||||||
offScreenCtx.putImageData(tmpImgData, 0, 1);
|
|
||||||
} else {
|
|
||||||
// shift the current display up 1 line, oldest line drops off
|
|
||||||
tmpImgData = offScreenCtx.getImageData(0, 1, pxPerLine, lines - 1);
|
|
||||||
offScreenCtx.putImageData(tmpImgData, 0, 0);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
ipBuf8 = Uint8ClampedArray.from(ipBufAry[0]);
|
|
||||||
for (
|
|
||||||
let sigVal, rgba, opIdx = 0, ipIdx = startOfs;
|
|
||||||
ipIdx < pxPerLine + startOfs;
|
|
||||||
opIdx += 4, ipIdx++
|
|
||||||
) {
|
|
||||||
sigVal = ipBuf8[ipIdx] || 0; // if input line too short add zeros
|
|
||||||
rgba = colMap[sigVal]; // array of rgba values
|
|
||||||
// byte reverse so number aa bb gg rr
|
|
||||||
lineBuf8[opIdx] = rgba[0]; // red
|
|
||||||
lineBuf8[opIdx + 1] = rgba[1]; // green
|
|
||||||
lineBuf8[opIdx + 2] = rgba[2]; // blue
|
|
||||||
lineBuf8[opIdx + 3] = rgba[3]; // alpha
|
|
||||||
}
|
|
||||||
offScreenCtx.putImageData(lineImgData, 0, nextLine);
|
|
||||||
if (sgMode === "RS") {
|
|
||||||
incrLine();
|
|
||||||
// if not static draw a white line in front of the current line to indicate new data point
|
|
||||||
if (lineRate) {
|
|
||||||
offScreenCtx.putImageData(blankImgData, 0, nextLine);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function horizontalNewLine() {
|
|
||||||
let tmpImgData, ipBuf8;
|
|
||||||
|
|
||||||
if (sgMode == "WF") {
|
|
||||||
if (rhc) {
|
|
||||||
// shift the current display right 1 line, oldest line drops off
|
|
||||||
tmpImgData = offScreenCtx.getImageData(0, 0, lines - 1, pxPerLine);
|
|
||||||
offScreenCtx.putImageData(tmpImgData, 1, 0);
|
|
||||||
} else {
|
|
||||||
// shift the current display left 1 line, oldest line drops off
|
|
||||||
tmpImgData = offScreenCtx.getImageData(1, 0, lines - 1, pxPerLine);
|
|
||||||
offScreenCtx.putImageData(tmpImgData, 0, 0);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// refresh the page image (it was just shifted)
|
|
||||||
pageImgData = offScreenCtx.getImageData(0, 0, lines, pxPerLine);
|
|
||||||
if (ipBufAry[0].constructor !== Uint8Array) {
|
|
||||||
ipBuf8 = Uint8ClampedArray.from(ipBufAry[0]); // clamp input values to 0..255 range
|
|
||||||
} else {
|
|
||||||
ipBuf8 = ipBufAry[0]; // conversion already done
|
|
||||||
}
|
|
||||||
|
|
||||||
for (let sigVal, rgba, opIdx, ipIdx = 0; ipIdx < pxPerLine; ipIdx++) {
|
|
||||||
sigVal = ipBuf8[ipIdx + startOfs] || 0; // if input line too short add zeros
|
|
||||||
rgba = colMap[sigVal]; // array of rgba values
|
|
||||||
opIdx = 4 * ((pxPerLine - ipIdx - 1) * lines + nextLine);
|
|
||||||
// byte reverse so number aa bb gg rr
|
|
||||||
pageImgData.data[opIdx] = rgba[0]; // red
|
|
||||||
pageImgData.data[opIdx + 1] = rgba[1]; // green
|
|
||||||
pageImgData.data[opIdx + 2] = rgba[2]; // blue
|
|
||||||
pageImgData.data[opIdx + 3] = rgba[3]; // alpha
|
|
||||||
}
|
|
||||||
if (sgMode === "RS") {
|
|
||||||
incrLine();
|
|
||||||
// if not draw a white line in front of the current line to indicate new data point
|
|
||||||
if (lineRate) {
|
|
||||||
for (let j = 0; j < pxPerLine; j++) {
|
|
||||||
if (rhc) {
|
|
||||||
opIdx = 4 * (j * lines + nextLine);
|
|
||||||
} else {
|
|
||||||
opIdx = 4 * ((pxPerLine - j - 1) * lines + nextLine);
|
|
||||||
}
|
|
||||||
// byte reverse so number aa bb gg rr
|
|
||||||
pageImgData.data[opIdx] = 255; // red
|
|
||||||
pageImgData.data[opIdx + 1] = 255; // green
|
|
||||||
pageImgData.data[opIdx + 2] = 255; // blue
|
|
||||||
pageImgData.data[opIdx + 3] = 255; // alpha
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
offScreenCtx.putImageData(pageImgData, 0, 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
const sgNewLine = vert ? verticalNewLine : horizontalNewLine; // function pointers
|
|
||||||
|
|
||||||
//===== set all the options ================
|
|
||||||
for (let prop in opt) {
|
|
||||||
// check that this is opt's own property, not inherited from prototype
|
|
||||||
if (opt.hasOwnProperty(prop)) {
|
|
||||||
setProperty(prop, opt[prop]);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// ===== now make the exposed properties and methods ===============
|
|
||||||
this.newLine = sgNewLine;
|
|
||||||
|
|
||||||
this.offScreenCvs = document.createElement("canvas");
|
|
||||||
if (vert) {
|
|
||||||
this.offScreenCvs.setAttribute("width", pxPerLine); // reset canvas pixels width
|
|
||||||
this.offScreenCvs.setAttribute("height", lines); // don't use style for this
|
|
||||||
clearImgData = new ImageData(clearBuf8, pxPerLine, lines);
|
|
||||||
} // data written in columns
|
|
||||||
else {
|
|
||||||
this.offScreenCvs.setAttribute("width", lines); // reset canvas pixels width
|
|
||||||
this.offScreenCvs.setAttribute("height", pxPerLine); // don't use style for this
|
|
||||||
clearImgData = new ImageData(clearBuf8, lines, pxPerLine);
|
|
||||||
}
|
|
||||||
offScreenCtx = this.offScreenCvs.getContext("2d");
|
|
||||||
|
|
||||||
this.clear = function () {
|
|
||||||
offScreenCtx.putImageData(clearImgData, 0, 0);
|
|
||||||
};
|
|
||||||
|
|
||||||
this.start = function () {
|
|
||||||
sgStartTime = Date.now();
|
|
||||||
sgTime = 0;
|
|
||||||
running = true;
|
|
||||||
updateWaterfall(); // start the update loop
|
|
||||||
};
|
|
||||||
|
|
||||||
this.stop = function () {
|
|
||||||
running = false;
|
|
||||||
if (timerID) {
|
|
||||||
clearTimeout(timerID);
|
|
||||||
}
|
|
||||||
// reset where the next line is to be written
|
|
||||||
if (sgMode === "RS") {
|
|
||||||
if (vert) {
|
|
||||||
nextLine = rhc ? lines - 1 : 0;
|
|
||||||
} else {
|
|
||||||
nextLine = rhc ? 0 : lines - 1;
|
|
||||||
}
|
|
||||||
} // WF
|
|
||||||
else {
|
|
||||||
nextLine = rhc ? 0 : lines - 1;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
// make a white line, it will show the input line for RS displays
|
|
||||||
blankBuf8.fill(255);
|
|
||||||
// make a full canvas of the color map 0 values
|
|
||||||
for (let i = 0; i < pxPerLine * lines * 4; i += 4) {
|
|
||||||
// byte reverse so number aa bb gg rr
|
|
||||||
clearBuf8[i] = colMap[0][0]; // red
|
|
||||||
clearBuf8[i + 1] = colMap[0][1]; // green
|
|
||||||
clearBuf8[i + 2] = colMap[0][2]; // blue
|
|
||||||
clearBuf8[i + 3] = colMap[0][3]; // alpha
|
|
||||||
}
|
|
||||||
// for diagnostics only
|
|
||||||
if (typeof demoCvsId == "string") {
|
|
||||||
document.getElementById(demoCvsId).appendChild(this.offScreenCvs);
|
|
||||||
}
|
|
||||||
// initialize the direction and first line position
|
|
||||||
this.stop();
|
|
||||||
|
|
||||||
// everything is set
|
|
||||||
// if dynamic, wait for the start or newLine methods to be called
|
|
||||||
}
|
|
||||||
})();
|
|
|
@ -1,488 +0,0 @@
|
||||||
/*
|
|
||||||
* Copyright (c) 2019 Jeppe Ledet-Pedersen
|
|
||||||
* This software is released under the MIT license.
|
|
||||||
* See the LICENSE file for further details.
|
|
||||||
*/
|
|
||||||
|
|
||||||
"use strict";
|
|
||||||
|
|
||||||
Spectrum.prototype.squeeze = function (value, out_min, out_max) {
|
|
||||||
if (value <= this.min_db) return out_min;
|
|
||||||
else if (value >= this.max_db) return out_max;
|
|
||||||
else
|
|
||||||
return Math.round(
|
|
||||||
((value - this.min_db) / (this.max_db - this.min_db)) * out_max
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
Spectrum.prototype.rowToImageData = function (bins) {
|
|
||||||
for (var i = 0; i < this.imagedata.data.length; i += 4) {
|
|
||||||
var cindex = this.squeeze(bins[i / 4], 0, 255);
|
|
||||||
var color = this.colormap[cindex];
|
|
||||||
this.imagedata.data[i + 0] = color[0];
|
|
||||||
this.imagedata.data[i + 1] = color[1];
|
|
||||||
this.imagedata.data[i + 2] = color[2];
|
|
||||||
this.imagedata.data[i + 3] = 255;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
Spectrum.prototype.addWaterfallRow = function (bins) {
|
|
||||||
// Shift waterfall 1 row down
|
|
||||||
this.ctx_wf.drawImage(
|
|
||||||
this.ctx_wf.canvas,
|
|
||||||
0,
|
|
||||||
0,
|
|
||||||
this.wf_size,
|
|
||||||
this.wf_rows - 1,
|
|
||||||
0,
|
|
||||||
1,
|
|
||||||
this.wf_size,
|
|
||||||
this.wf_rows - 1
|
|
||||||
);
|
|
||||||
|
|
||||||
// Draw new line on waterfall canvas
|
|
||||||
this.rowToImageData(bins);
|
|
||||||
this.ctx_wf.putImageData(this.imagedata, 0, 0);
|
|
||||||
|
|
||||||
var width = this.ctx.canvas.width;
|
|
||||||
var height = this.ctx.canvas.height;
|
|
||||||
|
|
||||||
// Copy scaled FFT canvas to screen. Only copy the number of rows that will
|
|
||||||
// fit in waterfall area to avoid vertical scaling.
|
|
||||||
this.ctx.imageSmoothingEnabled = false;
|
|
||||||
var rows = Math.min(this.wf_rows, height - this.spectrumHeight);
|
|
||||||
this.ctx.drawImage(
|
|
||||||
this.ctx_wf.canvas,
|
|
||||||
0,
|
|
||||||
0,
|
|
||||||
this.wf_size,
|
|
||||||
rows,
|
|
||||||
0,
|
|
||||||
this.spectrumHeight,
|
|
||||||
width,
|
|
||||||
height - this.spectrumHeight
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
Spectrum.prototype.drawFFT = function (bins) {
|
|
||||||
this.ctx.beginPath();
|
|
||||||
this.ctx.moveTo(-1, this.spectrumHeight + 1);
|
|
||||||
for (var i = 0; i < bins.length; i++) {
|
|
||||||
var y = this.spectrumHeight - this.squeeze(bins[i], 0, this.spectrumHeight);
|
|
||||||
if (y > this.spectrumHeight - 1) y = this.spectrumHeight + 1; // Hide underflow
|
|
||||||
if (y < 0) y = 0;
|
|
||||||
if (i == 0) this.ctx.lineTo(-1, y);
|
|
||||||
this.ctx.lineTo(i, y);
|
|
||||||
if (i == bins.length - 1) this.ctx.lineTo(this.wf_size + 1, y);
|
|
||||||
}
|
|
||||||
this.ctx.lineTo(this.wf_size + 1, this.spectrumHeight + 1);
|
|
||||||
this.ctx.strokeStyle = "#fefefe";
|
|
||||||
this.ctx.stroke();
|
|
||||||
};
|
|
||||||
|
|
||||||
//Spectrum.prototype.drawSpectrum = function(bins) {
|
|
||||||
Spectrum.prototype.drawSpectrum = function () {
|
|
||||||
var width = this.ctx.canvas.width;
|
|
||||||
var height = this.ctx.canvas.height;
|
|
||||||
|
|
||||||
// Modification by DJ2LS
|
|
||||||
// Draw bandwidth lines
|
|
||||||
// TODO: Math not correct. But a first attempt
|
|
||||||
// it seems position is more or less equal to frequenzy by factor 10
|
|
||||||
// eg. position 150 == 1500Hz
|
|
||||||
/*
|
|
||||||
// CENTER LINE
|
|
||||||
this.ctx_wf.beginPath();
|
|
||||||
this.ctx_wf.moveTo(150,0);
|
|
||||||
this.ctx_wf.lineTo(150, height);
|
|
||||||
this.ctx_wf.lineWidth = 1;
|
|
||||||
this.ctx_wf.strokeStyle = '#8C8C8C';
|
|
||||||
this.ctx_wf.stroke()
|
|
||||||
*/
|
|
||||||
|
|
||||||
// 586Hz and 1700Hz LINES
|
|
||||||
var linePositionLow = 121.6; //150 - bandwith/20
|
|
||||||
var linePositionHigh = 178.4; //150 + bandwidth/20
|
|
||||||
var linePositionLow2 = 65; //150 - bandwith/20
|
|
||||||
var linePositionHigh2 = 235; //150 + bandwith/20
|
|
||||||
this.ctx_wf.beginPath();
|
|
||||||
this.ctx_wf.moveTo(linePositionLow, 0);
|
|
||||||
this.ctx_wf.lineTo(linePositionLow, height);
|
|
||||||
this.ctx_wf.moveTo(linePositionHigh, 0);
|
|
||||||
this.ctx_wf.lineTo(linePositionHigh, height);
|
|
||||||
this.ctx_wf.moveTo(linePositionLow2, 0);
|
|
||||||
this.ctx_wf.lineTo(linePositionLow2, height);
|
|
||||||
this.ctx_wf.moveTo(linePositionHigh2, 0);
|
|
||||||
this.ctx_wf.lineTo(linePositionHigh2, height);
|
|
||||||
this.ctx_wf.lineWidth = 1;
|
|
||||||
this.ctx_wf.strokeStyle = "#C3C3C3";
|
|
||||||
this.ctx_wf.stroke();
|
|
||||||
|
|
||||||
// ---- END OF MODIFICATION ------
|
|
||||||
|
|
||||||
// Fill with black
|
|
||||||
this.ctx.fillStyle = "white";
|
|
||||||
this.ctx.fillRect(0, 0, width, height);
|
|
||||||
|
|
||||||
//Commenting out the remainder of this code, it's not needed and unused as of 6.9.11 and saves three if statements
|
|
||||||
return;
|
|
||||||
/*
|
|
||||||
// FFT averaging
|
|
||||||
if (this.averaging > 0) {
|
|
||||||
if (!this.binsAverage || this.binsAverage.length != bins.length) {
|
|
||||||
this.binsAverage = Array.from(bins);
|
|
||||||
} else {
|
|
||||||
for (var i = 0; i < bins.length; i++) {
|
|
||||||
this.binsAverage[i] += this.alpha * (bins[i] - this.binsAverage[i]);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
bins = this.binsAverage;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Max hold
|
|
||||||
if (this.maxHold) {
|
|
||||||
if (!this.binsMax || this.binsMax.length != bins.length) {
|
|
||||||
this.binsMax = Array.from(bins);
|
|
||||||
} else {
|
|
||||||
for (var i = 0; i < bins.length; i++) {
|
|
||||||
if (bins[i] > this.binsMax[i]) {
|
|
||||||
this.binsMax[i] = bins[i];
|
|
||||||
} else {
|
|
||||||
// Decay
|
|
||||||
this.binsMax[i] = 1.0025 * this.binsMax[i];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Do not draw anything if spectrum is not visible
|
|
||||||
if (this.ctx_axes.canvas.height < 1)
|
|
||||||
return;
|
|
||||||
|
|
||||||
// Scale for FFT
|
|
||||||
this.ctx.save();
|
|
||||||
this.ctx.scale(width / this.wf_size, 1);
|
|
||||||
|
|
||||||
// Draw maxhold
|
|
||||||
if (this.maxHold)
|
|
||||||
this.drawFFT(this.binsMax);
|
|
||||||
|
|
||||||
// Draw FFT bins
|
|
||||||
this.drawFFT(bins);
|
|
||||||
|
|
||||||
// Restore scale
|
|
||||||
this.ctx.restore();
|
|
||||||
|
|
||||||
// Fill scaled path
|
|
||||||
this.ctx.fillStyle = this.gradient;
|
|
||||||
this.ctx.fill();
|
|
||||||
|
|
||||||
// Copy axes from offscreen canvas
|
|
||||||
this.ctx.drawImage(this.ctx_axes.canvas, 0, 0);
|
|
||||||
*/
|
|
||||||
};
|
|
||||||
|
|
||||||
//Allow setting colormap
|
|
||||||
Spectrum.prototype.setColorMap = function (index) {
|
|
||||||
this.colormap = colormaps[index];
|
|
||||||
};
|
|
||||||
|
|
||||||
Spectrum.prototype.updateAxes = function () {
|
|
||||||
var width = this.ctx_axes.canvas.width;
|
|
||||||
var height = this.ctx_axes.canvas.height;
|
|
||||||
|
|
||||||
// Clear axes canvas
|
|
||||||
this.ctx_axes.clearRect(0, 0, width, height);
|
|
||||||
|
|
||||||
// Draw axes
|
|
||||||
this.ctx_axes.font = "12px sans-serif";
|
|
||||||
this.ctx_axes.fillStyle = "white";
|
|
||||||
this.ctx_axes.textBaseline = "middle";
|
|
||||||
|
|
||||||
this.ctx_axes.textAlign = "left";
|
|
||||||
var step = 10;
|
|
||||||
for (var i = this.min_db + 10; i <= this.max_db - 10; i += step) {
|
|
||||||
var y = height - this.squeeze(i, 0, height);
|
|
||||||
this.ctx_axes.fillText(i, 5, y);
|
|
||||||
|
|
||||||
this.ctx_axes.beginPath();
|
|
||||||
this.ctx_axes.moveTo(20, y);
|
|
||||||
this.ctx_axes.lineTo(width, y);
|
|
||||||
this.ctx_axes.strokeStyle = "rgba(200, 200, 200, 0.10)";
|
|
||||||
this.ctx_axes.stroke();
|
|
||||||
}
|
|
||||||
|
|
||||||
this.ctx_axes.textBaseline = "bottom";
|
|
||||||
for (var i = 0; i < 11; i++) {
|
|
||||||
var x = Math.round(width / 10) * i;
|
|
||||||
|
|
||||||
if (this.spanHz > 0) {
|
|
||||||
var adjust = 0;
|
|
||||||
if (i == 0) {
|
|
||||||
this.ctx_axes.textAlign = "left";
|
|
||||||
adjust = 3;
|
|
||||||
} else if (i == 10) {
|
|
||||||
this.ctx_axes.textAlign = "right";
|
|
||||||
adjust = -3;
|
|
||||||
} else {
|
|
||||||
this.ctx_axes.textAlign = "center";
|
|
||||||
}
|
|
||||||
|
|
||||||
var freq = this.centerHz + (this.spanHz / 10) * (i - 5);
|
|
||||||
if (this.centerHz + this.spanHz > 1e6) freq = freq / 1e6 + "M";
|
|
||||||
else if (this.centerHz + this.spanHz > 1e3) freq = freq / 1e3 + "k";
|
|
||||||
this.ctx_axes.fillText(freq, x + adjust, height - 3);
|
|
||||||
}
|
|
||||||
|
|
||||||
this.ctx_axes.beginPath();
|
|
||||||
this.ctx_axes.moveTo(x, 0);
|
|
||||||
this.ctx_axes.lineTo(x, height);
|
|
||||||
this.ctx_axes.strokeStyle = "rgba(200, 200, 200, 0.10)";
|
|
||||||
this.ctx_axes.stroke();
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
Spectrum.prototype.addData = function (data) {
|
|
||||||
if (!this.paused) {
|
|
||||||
if (data.length != this.wf_size) {
|
|
||||||
this.wf_size = data.length;
|
|
||||||
this.ctx_wf.canvas.width = data.length;
|
|
||||||
this.ctx_wf.fillStyle = "white";
|
|
||||||
this.ctx_wf.fillRect(0, 0, this.wf.width, this.wf.height);
|
|
||||||
this.imagedata = this.ctx_wf.createImageData(data.length, 1);
|
|
||||||
}
|
|
||||||
//this.drawSpectrum(data);
|
|
||||||
this.drawSpectrum();
|
|
||||||
this.addWaterfallRow(data);
|
|
||||||
this.resize();
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
Spectrum.prototype.updateSpectrumRatio = function () {
|
|
||||||
this.spectrumHeight = Math.round(
|
|
||||||
(this.canvas.height * this.spectrumPercent) / 100.0
|
|
||||||
);
|
|
||||||
|
|
||||||
this.gradient = this.ctx.createLinearGradient(0, 0, 0, this.spectrumHeight);
|
|
||||||
for (var i = 0; i < this.colormap.length; i++) {
|
|
||||||
var c = this.colormap[this.colormap.length - 1 - i];
|
|
||||||
this.gradient.addColorStop(
|
|
||||||
i / this.colormap.length,
|
|
||||||
"rgba(" + c[0] + "," + c[1] + "," + c[2] + ", 1.0)"
|
|
||||||
);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
Spectrum.prototype.resize = function () {
|
|
||||||
var width = this.canvas.clientWidth;
|
|
||||||
var height = this.canvas.clientHeight;
|
|
||||||
|
|
||||||
if (this.canvas.width != width || this.canvas.height != height) {
|
|
||||||
this.canvas.width = width;
|
|
||||||
this.canvas.height = height;
|
|
||||||
this.updateSpectrumRatio();
|
|
||||||
}
|
|
||||||
|
|
||||||
if (this.axes.width != width || this.axes.height != this.spectrumHeight) {
|
|
||||||
this.axes.width = width;
|
|
||||||
this.axes.height = this.spectrumHeight;
|
|
||||||
this.updateAxes();
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
Spectrum.prototype.setSpectrumPercent = function (percent) {
|
|
||||||
if (percent >= 0 && percent <= 100) {
|
|
||||||
this.spectrumPercent = percent;
|
|
||||||
this.updateSpectrumRatio();
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
Spectrum.prototype.incrementSpectrumPercent = function () {
|
|
||||||
if (this.spectrumPercent + this.spectrumPercentStep <= 100) {
|
|
||||||
this.setSpectrumPercent(this.spectrumPercent + this.spectrumPercentStep);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
Spectrum.prototype.decrementSpectrumPercent = function () {
|
|
||||||
if (this.spectrumPercent - this.spectrumPercentStep >= 0) {
|
|
||||||
this.setSpectrumPercent(this.spectrumPercent - this.spectrumPercentStep);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
Spectrum.prototype.toggleColor = function () {
|
|
||||||
this.colorindex++;
|
|
||||||
if (this.colorindex >= colormaps.length) this.colorindex = 0;
|
|
||||||
this.colormap = colormaps[this.colorindex];
|
|
||||||
this.updateSpectrumRatio();
|
|
||||||
};
|
|
||||||
|
|
||||||
Spectrum.prototype.setRange = function (min_db, max_db) {
|
|
||||||
this.min_db = min_db;
|
|
||||||
this.max_db = max_db;
|
|
||||||
this.updateAxes();
|
|
||||||
};
|
|
||||||
|
|
||||||
Spectrum.prototype.rangeUp = function () {
|
|
||||||
this.setRange(this.min_db - 5, this.max_db - 5);
|
|
||||||
};
|
|
||||||
|
|
||||||
Spectrum.prototype.rangeDown = function () {
|
|
||||||
this.setRange(this.min_db + 5, this.max_db + 5);
|
|
||||||
};
|
|
||||||
|
|
||||||
Spectrum.prototype.rangeIncrease = function () {
|
|
||||||
this.setRange(this.min_db - 5, this.max_db + 5);
|
|
||||||
};
|
|
||||||
|
|
||||||
Spectrum.prototype.rangeDecrease = function () {
|
|
||||||
if (this.max_db - this.min_db > 10)
|
|
||||||
this.setRange(this.min_db + 5, this.max_db - 5);
|
|
||||||
};
|
|
||||||
|
|
||||||
Spectrum.prototype.setCenterHz = function (hz) {
|
|
||||||
this.centerHz = hz;
|
|
||||||
this.updateAxes();
|
|
||||||
};
|
|
||||||
|
|
||||||
Spectrum.prototype.setSpanHz = function (hz) {
|
|
||||||
this.spanHz = hz;
|
|
||||||
this.updateAxes();
|
|
||||||
};
|
|
||||||
|
|
||||||
Spectrum.prototype.setAveraging = function (num) {
|
|
||||||
if (num >= 0) {
|
|
||||||
this.averaging = num;
|
|
||||||
this.alpha = 2 / (this.averaging + 1);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
Spectrum.prototype.incrementAveraging = function () {
|
|
||||||
this.setAveraging(this.averaging + 1);
|
|
||||||
};
|
|
||||||
|
|
||||||
Spectrum.prototype.decrementAveraging = function () {
|
|
||||||
if (this.averaging > 0) {
|
|
||||||
this.setAveraging(this.averaging - 1);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
Spectrum.prototype.setPaused = function (paused) {
|
|
||||||
this.paused = paused;
|
|
||||||
};
|
|
||||||
|
|
||||||
Spectrum.prototype.togglePaused = function () {
|
|
||||||
this.setPaused(!this.paused);
|
|
||||||
};
|
|
||||||
|
|
||||||
Spectrum.prototype.setMaxHold = function (maxhold) {
|
|
||||||
this.maxHold = maxhold;
|
|
||||||
this.binsMax = undefined;
|
|
||||||
};
|
|
||||||
|
|
||||||
Spectrum.prototype.toggleMaxHold = function () {
|
|
||||||
this.setMaxHold(!this.maxHold);
|
|
||||||
};
|
|
||||||
|
|
||||||
Spectrum.prototype.toggleFullscreen = function () {
|
|
||||||
if (!this.fullscreen) {
|
|
||||||
if (this.canvas.requestFullscreen) {
|
|
||||||
this.canvas.requestFullscreen();
|
|
||||||
} else if (this.canvas.mozRequestFullScreen) {
|
|
||||||
this.canvas.mozRequestFullScreen();
|
|
||||||
} else if (this.canvas.webkitRequestFullscreen) {
|
|
||||||
this.canvas.webkitRequestFullscreen();
|
|
||||||
} else if (this.canvas.msRequestFullscreen) {
|
|
||||||
this.canvas.msRequestFullscreen();
|
|
||||||
}
|
|
||||||
this.fullscreen = true;
|
|
||||||
} else {
|
|
||||||
if (document.exitFullscreen) {
|
|
||||||
document.exitFullscreen();
|
|
||||||
} else if (document.mozCancelFullScreen) {
|
|
||||||
document.mozCancelFullScreen();
|
|
||||||
} else if (document.webkitExitFullscreen) {
|
|
||||||
document.webkitExitFullscreen();
|
|
||||||
} else if (document.msExitFullscreen) {
|
|
||||||
document.msExitFullscreen();
|
|
||||||
}
|
|
||||||
this.fullscreen = false;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
Spectrum.prototype.onKeypress = function (e) {
|
|
||||||
if (e.key == " ") {
|
|
||||||
this.togglePaused();
|
|
||||||
} else if (e.key == "f") {
|
|
||||||
this.toggleFullscreen();
|
|
||||||
} else if (e.key == "c") {
|
|
||||||
this.toggleColor();
|
|
||||||
} else if (e.key == "ArrowUp") {
|
|
||||||
this.rangeUp();
|
|
||||||
} else if (e.key == "ArrowDown") {
|
|
||||||
this.rangeDown();
|
|
||||||
} else if (e.key == "ArrowLeft") {
|
|
||||||
this.rangeDecrease();
|
|
||||||
} else if (e.key == "ArrowRight") {
|
|
||||||
this.rangeIncrease();
|
|
||||||
} else if (e.key == "s") {
|
|
||||||
this.incrementSpectrumPercent();
|
|
||||||
} else if (e.key == "w") {
|
|
||||||
this.decrementSpectrumPercent();
|
|
||||||
} else if (e.key == "+") {
|
|
||||||
this.incrementAveraging();
|
|
||||||
} else if (e.key == "-") {
|
|
||||||
this.decrementAveraging();
|
|
||||||
} else if (e.key == "m") {
|
|
||||||
this.toggleMaxHold();
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
function Spectrum(id, options) {
|
|
||||||
// Handle options
|
|
||||||
this.centerHz = options && options.centerHz ? options.centerHz : 1500;
|
|
||||||
this.spanHz = options && options.spanHz ? options.spanHz : 0;
|
|
||||||
this.wf_size = options && options.wf_size ? options.wf_size : 0;
|
|
||||||
this.wf_rows = options && options.wf_rows ? options.wf_rows : 1024;
|
|
||||||
this.spectrumPercent =
|
|
||||||
options && options.spectrumPercent ? options.spectrumPercent : 0;
|
|
||||||
this.spectrumPercentStep =
|
|
||||||
options && options.spectrumPercentStep ? options.spectrumPercentStep : 0;
|
|
||||||
this.averaging = options && options.averaging ? options.averaging : 0;
|
|
||||||
this.maxHold = options && options.maxHold ? options.maxHold : false;
|
|
||||||
|
|
||||||
// Setup state
|
|
||||||
this.paused = false;
|
|
||||||
this.fullscreen = false;
|
|
||||||
this.min_db = 0;
|
|
||||||
this.max_db = 70;
|
|
||||||
this.spectrumHeight = 0;
|
|
||||||
|
|
||||||
// Colors
|
|
||||||
this.colorindex = 0;
|
|
||||||
this.colormap = colormaps[2];
|
|
||||||
|
|
||||||
// Create main canvas and adjust dimensions to match actual
|
|
||||||
this.canvas = document.getElementById(id);
|
|
||||||
this.canvas.height = this.canvas.clientHeight;
|
|
||||||
this.canvas.width = this.canvas.clientWidth;
|
|
||||||
this.ctx = this.canvas.getContext("2d");
|
|
||||||
this.ctx.fillStyle = "white";
|
|
||||||
this.ctx.fillRect(0, 0, this.canvas.width, this.canvas.height);
|
|
||||||
|
|
||||||
// Create offscreen canvas for axes
|
|
||||||
this.axes = document.createElement("canvas");
|
|
||||||
this.axes.height = 1; // Updated later
|
|
||||||
this.axes.width = this.canvas.width;
|
|
||||||
this.ctx_axes = this.axes.getContext("2d");
|
|
||||||
|
|
||||||
// Create offscreen canvas for waterfall
|
|
||||||
this.wf = document.createElement("canvas");
|
|
||||||
this.wf.height = this.wf_rows;
|
|
||||||
this.wf.width = this.wf_size;
|
|
||||||
this.ctx_wf = this.wf.getContext("2d");
|
|
||||||
|
|
||||||
// Trigger first render
|
|
||||||
this.setAveraging(this.averaging);
|
|
||||||
this.updateSpectrumRatio();
|
|
||||||
this.resize();
|
|
||||||
}
|
|
|
@ -1,16 +0,0 @@
|
||||||
<?xml version="1.0" encoding="UTF-8"?>
|
|
||||||
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
|
||||||
<plist version="1.0">
|
|
||||||
<dict>
|
|
||||||
<key>com.apple.security.cs.allow-jit</key>
|
|
||||||
<true/>
|
|
||||||
<key>com.apple.security.cs.allow-unsigned-executable-memory</key>
|
|
||||||
<true/>
|
|
||||||
<key>com.apple.security.cs.disable-library-validation</key>
|
|
||||||
<true/>
|
|
||||||
<key>com.apple.security.cs.disable-executable-page-protection</key>
|
|
||||||
<true/>
|
|
||||||
<key>com.apple.security.automation.apple-events</key>
|
|
||||||
<true/>
|
|
||||||
</dict>
|
|
||||||
</plist>
|
|
Binary file not shown.
Before Width: | Height: | Size: 590 KiB |
|
@ -1,82 +0,0 @@
|
||||||
{
|
|
||||||
"name": "FreeDATA",
|
|
||||||
"description": "FreeDATA ",
|
|
||||||
"private": true,
|
|
||||||
"version": "0.11.0-alpha",
|
|
||||||
"main": "dist-electron/main/index.js",
|
|
||||||
"scripts": {
|
|
||||||
"start": "git pull && npm i && vite",
|
|
||||||
"dev": "vite",
|
|
||||||
"check" : "vue-tsc --noEmit",
|
|
||||||
"build": "vue-tsc --noEmit && vite build && electron-builder",
|
|
||||||
"preview": "vite preview",
|
|
||||||
"lint": "eslint --ext .js,.vue src",
|
|
||||||
"lint-fix": "eslint --ext .js,.vue --fix src"
|
|
||||||
},
|
|
||||||
"repository": {
|
|
||||||
"type": "git",
|
|
||||||
"url": "https://github.com/DJ2LS/FreeDATA.git"
|
|
||||||
},
|
|
||||||
"keywords": [
|
|
||||||
"TNC",
|
|
||||||
"GUI",
|
|
||||||
"FreeDATA",
|
|
||||||
"codec2"
|
|
||||||
],
|
|
||||||
"author": "DJ2LS",
|
|
||||||
"license": "GPL-3.0",
|
|
||||||
"bugs": {
|
|
||||||
"url": "https://github.com/DJ2LS/FreeDATA/issues"
|
|
||||||
},
|
|
||||||
"homepage": "https://freedata.app",
|
|
||||||
"dependencies": {
|
|
||||||
"@electron/notarize": "^2.1.0",
|
|
||||||
"@vueuse/electron": "^10.4.1",
|
|
||||||
"blob-util": "^2.0.2",
|
|
||||||
"bootstrap": "^5.3.1",
|
|
||||||
"bootstrap-icons": "^1.10.5",
|
|
||||||
"bootswatch": "^5.3.1",
|
|
||||||
"browser-image-compression": "^2.0.2",
|
|
||||||
"chart.js": "^4.3.3",
|
|
||||||
"chartjs-plugin-annotation": "^3.0.1",
|
|
||||||
"electron-builder-notarize": "^1.5.1",
|
|
||||||
"electron-log": "^4.4.8",
|
|
||||||
"electron-updater": "^6.1.4",
|
|
||||||
"emoji-picker-element": "^1.18.3",
|
|
||||||
"emoji-picker-element-data": "^1.4.0",
|
|
||||||
"file-saver": "^2.0.5",
|
|
||||||
"mime": "^3.0.0",
|
|
||||||
"pinia": "^2.1.6",
|
|
||||||
"pouchdb": "^8.0.1",
|
|
||||||
"pouchdb-browser": "^8.0.1",
|
|
||||||
"pouchdb-find": "^8.0.1",
|
|
||||||
"pouchdb-upsert": "^2.2.0",
|
|
||||||
"qth-locator": "^2.1.0",
|
|
||||||
"sass": "^1.66.1",
|
|
||||||
"socket.io": "^4.7.2",
|
|
||||||
"uuid": "^9.0.0",
|
|
||||||
"vue": "^3.3.4",
|
|
||||||
"vue-chartjs": "^5.2.0",
|
|
||||||
"vuemoji-picker": "^0.2.0"
|
|
||||||
},
|
|
||||||
"devDependencies": {
|
|
||||||
"@typescript-eslint/eslint-plugin": "^6.7.4",
|
|
||||||
"@vitejs/plugin-vue": "^4.4.0",
|
|
||||||
"electron": "^27.0.0",
|
|
||||||
"electron-builder": "^24.6.3",
|
|
||||||
"eslint": "^8.50.0",
|
|
||||||
"eslint-config-prettier": "^9.0.0",
|
|
||||||
"eslint-config-standard-with-typescript": "^39.1.0",
|
|
||||||
"eslint-plugin-import": "^2.28.1",
|
|
||||||
"eslint-plugin-n": "^16.1.0",
|
|
||||||
"eslint-plugin-prettier": "^5.0.0",
|
|
||||||
"eslint-plugin-promise": "^6.1.1",
|
|
||||||
"eslint-plugin-vue": "^9.17.0",
|
|
||||||
"typescript": "^5.2.2",
|
|
||||||
"vite": "^4.3.2",
|
|
||||||
"vite-plugin-electron": "^0.14.0",
|
|
||||||
"vite-plugin-electron-renderer": "^0.14.5",
|
|
||||||
"vue": "^3.3.4",
|
|
||||||
"vue-tsc": "^1.4.2"
|
|
||||||
}
|
|
||||||
}
|
|
Binary file not shown.
Before Width: | Height: | Size: 590 KiB |
|
@ -1,21 +0,0 @@
|
||||||
MIT License
|
|
||||||
|
|
||||||
Copyright (c) 2019 Jeppe Ledet-Pedersen
|
|
||||||
|
|
||||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
||||||
of this software and associated documentation files (the "Software"), to deal
|
|
||||||
in the Software without restriction, including without limitation the rights
|
|
||||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
||||||
copies of the Software, and to permit persons to whom the Software is
|
|
||||||
furnished to do so, subject to the following conditions:
|
|
||||||
|
|
||||||
The above copyright notice and this permission notice shall be included in all
|
|
||||||
copies or substantial portions of the Software.
|
|
||||||
|
|
||||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
||||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
||||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
||||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
||||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
||||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
||||||
SOFTWARE.
|
|
|
@ -1,13 +0,0 @@
|
||||||
********************************
|
|
||||||
HTML Canvas/WebSockets Waterfall
|
|
||||||
********************************
|
|
||||||
|
|
||||||
This is a small experiment to create a waterfall plot with HTML Canvas and WebSockets to stream live FFT data from an SDR:
|
|
||||||
|
|
||||||
.. image:: img/waterfall.png
|
|
||||||
|
|
||||||
``spectrum.js`` contains the main JavaScript source code for the plot, while ``colormap.js`` contains colormaps generated using ``make_colormap.py``.
|
|
||||||
|
|
||||||
``index.html``, ``style.css``, ``script.js`` contain an example page that receives FFT data on a WebSocket and plots it on the waterfall plot.
|
|
||||||
|
|
||||||
``server.py`` contains a example `Bottle <https://bottlepy.org/docs/dev/>`_ and `gevent-websocket <https://pypi.org/project/gevent-websocket/>`_ server that broadcasts FFT data to connected clients. The FFT data is generated using `GNU radio <https://www.gnuradio.org/>`_ using a USRP but it should be fairly easy to change it to a different SDR.
|
|
File diff suppressed because it is too large
Load diff
|
@ -1,16 +0,0 @@
|
||||||
<!DOCTYPE html>
|
|
||||||
<html lang="en">
|
|
||||||
<head>
|
|
||||||
<meta charset="utf-8" />
|
|
||||||
<meta name="author" content="Jeppe Ledet-Pedersen" />
|
|
||||||
<title>Spectrum Plot</title>
|
|
||||||
<link rel="stylesheet" type="text/css" href="style.css" />
|
|
||||||
</head>
|
|
||||||
|
|
||||||
<body>
|
|
||||||
<canvas id="waterfall"></canvas>
|
|
||||||
<script src="colormap.js"></script>
|
|
||||||
<script src="spectrum.js"></script>
|
|
||||||
<script src="script.js"></script>
|
|
||||||
</body>
|
|
||||||
</html>
|
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Reference in a new issue