mirror of https://github.com/DJ2LS/FreeDATA
renamed gui_vue to gui
This commit is contained in:
parent
2684d3be39
commit
ff82b0e0f4
|
@ -16,13 +16,11 @@ updates:
|
|||
|
||||
# Maintain dependencies for npm
|
||||
- package-ecosystem: "npm"
|
||||
directory: "/gui_vue"
|
||||
directory: "/gui"
|
||||
schedule:
|
||||
interval: "daily"
|
||||
target-branch: "ls-gui-single-pager"
|
||||
|
||||
|
||||
|
||||
# Maintain dependencies for pip
|
||||
- package-ecosystem: "pip"
|
||||
directory: "/"
|
||||
|
|
|
@ -24,9 +24,9 @@ coverage.sh
|
|||
coverage.xml
|
||||
|
||||
#ignore node_modules
|
||||
/gui_vue/node_modules/
|
||||
/gui/node_modules/
|
||||
|
||||
#Ignore gui_vue build items
|
||||
/gui_vue/dist
|
||||
/gui_vue/release
|
||||
/gui_vue/dist-electron
|
||||
#Ignore gui build items
|
||||
/gui/dist
|
||||
/gui/release
|
||||
/gui/dist-electron
|
|
@ -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",
|
||||
"version": "0.10.2-alpha.1",
|
||||
"description": "FreeDATA ",
|
||||
"main": "main.js",
|
||||
"private": true,
|
||||
"version": "0.11.0-alpha",
|
||||
"main": "dist-electron/main/index.js",
|
||||
"scripts": {
|
||||
"start": "electron .",
|
||||
"test": "echo \"Error: no test specified\" && exit 1"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=18.17.0",
|
||||
"npm": ">=9.0.0"
|
||||
"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",
|
||||
|
@ -28,9 +30,8 @@
|
|||
},
|
||||
"homepage": "https://freedata.app",
|
||||
"dependencies": {
|
||||
"@electron/asar": "^3.2.4",
|
||||
"@electron/osx-sign": "^1.0.4",
|
||||
"@popperjs/core": "^2.11.8",
|
||||
"@electron/notarize": "^2.1.0",
|
||||
"@vueuse/electron": "^10.4.1",
|
||||
"blob-util": "^2.0.2",
|
||||
"bootstrap": "^5.3.1",
|
||||
"bootstrap-icons": "^1.10.5",
|
||||
|
@ -38,73 +39,44 @@
|
|||
"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.1",
|
||||
"electron-updater": "^6.1.4",
|
||||
"emoji-picker-element": "^1.18.3",
|
||||
"emoji-picker-element-data": "^1.4.0",
|
||||
"express-pouchdb": "^4.2.0",
|
||||
"file-saver": "^2.0.5",
|
||||
"mime": "^3.0.0",
|
||||
"pinia": "^2.1.6",
|
||||
"pouchdb": "^8.0.1",
|
||||
"pouchdb-browser": "^8.0.1",
|
||||
"pouchdb-express-router": "^0.0.11",
|
||||
"pouchdb-find": "^8.0.1",
|
||||
"pouchdb-replication": "^8.0.1",
|
||||
"pouchdb-upsert": "^2.2.0",
|
||||
"qth-locator": "^2.1.0",
|
||||
"utf8": "^3.0.0",
|
||||
"uuid": "^9.0.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": {
|
||||
"@electron/notarize": "^2.1.0",
|
||||
"@typescript-eslint/eslint-plugin": "^6.7.4",
|
||||
"@vitejs/plugin-vue": "^4.4.0",
|
||||
"electron": "^27.0.0",
|
||||
"electron-builder": "^24.6.3",
|
||||
"electron-builder-notarize": "^1.5.1"
|
||||
},
|
||||
"build": {
|
||||
"productName": "FreeDATA",
|
||||
"appId": "app.freedata",
|
||||
"afterSign": "electron-builder-notarize",
|
||||
"npmRebuild": "false",
|
||||
"directories": {
|
||||
"buildResources": "build",
|
||||
"output": "dist"
|
||||
},
|
||||
"mac": {
|
||||
"target": [
|
||||
"default"
|
||||
],
|
||||
"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"
|
||||
]
|
||||
}
|
||||
]
|
||||
"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"
|
||||
}
|
||||
}
|
||||
|
|
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"] {
|
||||
/* 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 New Issue