new chat release

This commit is contained in:
dj2ls 2022-03-29 22:24:35 +02:00
parent 5439120f1d
commit 16f23d2f1d
7 changed files with 222 additions and 137 deletions

View file

@ -181,10 +181,10 @@ function createWindow() {
win.loadFile('src/index.html')
chat = new BrowserWindow({
height: 900,
width: 600,
height: 600,
width: 1000,
show: false,
parent: win,
//parent: win,
webPreferences: {
preload: require.resolve('./preload-chat.js'),
nodeIntegration: true,
@ -200,7 +200,7 @@ function createWindow() {
height: 900,
width: 600,
show: false,
parent: win,
//parent: win,
webPreferences: {
preload: require.resolve('./preload-log.js'),
nodeIntegration: true,

View file

@ -1,6 +1,6 @@
{
"name": "FreeDATA",
"version": "0.2.0-alpha.1",
"version": "0.3.0-alpha.1",
"description": "FreeDATA ",
"main": "main.js",
"scripts": {
@ -28,14 +28,14 @@
},
"homepage": "https://freedata.app",
"dependencies": {
"bootstrap": "^5.1.0",
"bootstrap": "^5.1.3",
"bootstrap-icons": "^1.8.1",
"bootswatch": "^5.1.3",
"chart.js": "^3.5.1",
"chartjs-plugin-annotation": "^1.0.2",
"chart.js": "^3.7.1",
"chartjs-plugin-annotation": "^1.4.0",
"electron-log": "^4.4.6",
"electron-updater": "^5.0.0",
"emoji-picker-element": "^1.11.0",
"emoji-picker-element": "^1.11.1",
"emoji-picker-element-data": "^1.3.0",
"mime": "^3.0.0",
"pouchdb": "^7.2.2",
@ -45,7 +45,7 @@
"uuid": "^8.3.2"
},
"devDependencies": {
"electron": "^17.0.0",
"electron": "^18.0.0",
"electron-builder": "^22.14.13"
},
"build": {

View file

@ -29,10 +29,13 @@ const dateFormatShort = new Intl.DateTimeFormat('en-GB', {
// split character
const split_char = '\0;'
// global for our selected file we want to transmit
// ----------------- some chat globals
var filetype = '';
var file = '';
var filename = '';
var callsign_counter = 0
var callsign_counter = 0;
var selected_callsign = '';
// -----------------------------------
var chatDB = path.join(configFolder, 'chatDB')
// ---- MessageDB
var PouchDB = require('pouchdb');
@ -84,15 +87,50 @@ window.addEventListener('DOMContentLoaded', () => {
element.style.display = "none";
}
})
document.getElementById("delete_selected_chat").addEventListener("click", () => {
db.find({
selector: {
dxcallsign: selected_callsign
}
}).then(function(result) {
// handle result
if (typeof(result) !== 'undefined') {
result.docs.forEach(function(item) {
console.log(item)
db.get(item._id).then(function(doc) {
return db.remove(doc);
});
location.reload();
});
}
}).catch(function(err) {
console.log(err);
});
})
document.getElementById("selectFilesButton").addEventListener("click", () => {
//document.getElementById('selectFiles').click();
ipcRenderer.send('select-file', {
title: 'Title',
});
});
})
document.getElementById("ping").addEventListener("click", () => {
ipcRenderer.send('run-tnc-command', {
command: 'ping', dxcallsign: selected_callsign
});
})
document.addEventListener("keyup", function(event) {
// Number 13 == Enter
if (event.keyCode === 13) {
// Cancel the default action, if needed
event.preventDefault();
// Trigger the button element with a click
document.getElementById("sendMessage").click();
}
});
// SEND MSG
@ -101,19 +139,14 @@ window.addEventListener('DOMContentLoaded', () => {
var dxcallsign = document.getElementById('chatModuleDxCall').value;
dxcallsign = dxcallsign.toUpperCase();
var chatmessage = document.getElementById('chatModuleMessage').value;
console.log(file);
console.log(filename);
console.log(filetype);
console.log(filetype);
var data_with_attachment = chatmessage + split_char + filename + split_char + filetype + split_char + file;
document.getElementById('selectFilesButton').innerHTML = `
<i class="bi bi-paperclip" style="font-size: 1.2rem; color: white;"></i>
`;
var uuid = uuidv4();
console.log(data_with_attachment)
let Data = {
command: "send_message",
@ -125,7 +158,6 @@ window.addEventListener('DOMContentLoaded', () => {
uuid: uuid
};
ipcRenderer.send('run-tnc-command', Data);
db.post({
_id: uuid,
timestamp: Math.floor(Date.now() / 1000),
@ -168,10 +200,6 @@ window.addEventListener('DOMContentLoaded', () => {
file = '';
filename = '';
});
ipcRenderer.on('return-selected-files', (event, arg) => {
filetype = arg.mime;
file = arg.data;
@ -233,7 +261,6 @@ ipcRenderer.on('action-new-msg-received', (event, arg) => {
obj.status = item.status;
obj.snr = item.snr;
obj.type = item.type;
db.put({
_id: obj.uuid,
timestamp: obj.timestamp,
@ -257,12 +284,11 @@ ipcRenderer.on('action-new-msg-received', (event, arg) => {
}).then(function(doc) {
console.log(doc)
update_chat(doc);
}).catch(function(err) {
console.log(err);
});
} else if (item.type == 'beacon') {
obj.timestamp = item.timestamp;
obj.dxcallsign = item.dxcallsign;
obj.dxgrid = item.dxgrid;
@ -273,7 +299,6 @@ ipcRenderer.on('action-new-msg-received', (event, arg) => {
obj.status = item.status;
obj.snr = item.snr;
obj.type = item.type;
db.put({
_id: obj.uuid,
timestamp: obj.timestamp,
@ -295,12 +320,11 @@ ipcRenderer.on('action-new-msg-received', (event, arg) => {
db.get(item.uuid, {
attachments: true
}).then(function(doc) {
console.log(doc)
console.log(doc);
update_chat(doc);
}).catch(function(err) {
console.log(err);
});
} else if (item.arq == 'received') {
var encoded_data = atob(item.data);
var splitted_data = encoded_data.split(split_char);
@ -318,8 +342,6 @@ ipcRenderer.on('action-new-msg-received', (event, arg) => {
obj.filename = utf8.decode(splitted_data[5]);
obj.filetype = utf8.decode(splitted_data[6]);
obj.file = btoa(utf8.decode(splitted_data[7]));
db.put({
_id: obj.uuid,
timestamp: obj.timestamp,
@ -344,16 +366,17 @@ ipcRenderer.on('action-new-msg-received', (event, arg) => {
}).catch(function(err) {
console.log(err);
});
db.get(item.uuid, {
db.get(obj.uuid, {
attachments: true
}).then(function(doc) {
console.log(doc)
console.log(doc);
update_chat(doc);
}).catch(function(err) {
console.log(err);
});
}
});
//window.location = window.location;
});
// Update chat list
update_chat = function(obj) {
@ -362,14 +385,14 @@ update_chat = function(obj) {
var timestamp = dateFormat.format(obj.timestamp * 1000);
var timestampShort = dateFormatShort.format(obj.timestamp * 1000);
var dxgrid = obj.dxgrid;
try{
try {
console.log(Object.keys(obj._attachments)[0].length)
if (typeof(obj._attachments) !== 'undefined' && Object.keys(obj._attachments)[0].length > 0) {
//var filename = obj._attachments;
var filename = Object.keys(obj._attachments)[0]
var filetype = filename.split('.')[1]
var filesize = obj._attachments[filename]["length"] + " Bytes";
var fileheader = `
if (typeof(obj._attachments) !== 'undefined' && Object.keys(obj._attachments)[0].length > 0) {
//var filename = obj._attachments;
var filename = Object.keys(obj._attachments)[0]
var filetype = filename.split('.')[1]
var filesize = obj._attachments[filename]["length"] + " Bytes";
var fileheader = `
<div class="card-header text-end p-0 mb-0 hover-overlay" id="save-file-msg-${obj._id}">
<p class="text-right mb-0 p-1 text-black" style="text-align: right; font-size : 1rem">
@ -379,23 +402,21 @@ update_chat = function(obj) {
</p>
</div>
`;
} else {
var filename = ''
var fileheader = ''
}
} catch {
} else {
var filename = ''
var fileheader = ''
}
} catch {
console.log("error with database parsing...")
}
// CALLSIGN LIST
if (!(document.getElementById('chat-' + dxcallsign + '-list'))) {
// increment callsign counter
callsign_counter++;
if (callsign_counter == 1){
if (callsign_counter == 1) {
var callsign_selected = 'active show'
document.getElementById('chatModuleDxCall').value = dxcallsign;
selected_callsign = dxcallsign;
}
var new_callsign = `
<a class="list-group-item list-group-item-action rounded-4 rounded-top rounded-bottom border-1 mb-2 ${callsign_selected}" id="chat-${dxcallsign}-list" data-bs-toggle="list" href="#chat-${dxcallsign}" role="tab" aria-controls="chat-${dxcallsign}">
@ -415,14 +436,13 @@ update_chat = function(obj) {
// create eventlistener for listening on clicking on a callsign
document.getElementById('chat-' + dxcallsign + '-list').addEventListener('click', function() {
document.getElementById('chatModuleDxCall').value = dxcallsign;
selected_callsign = dxcallsign;
// scroll to bottom
var element = document.getElementById("message-container");
element.scrollTo(0, element.scrollHeight);
});
}
// APPEND MESSAGES TO CALLSIGN
if (obj.status == 'transmit') {
var status = '<i class="bi bi-check" style="font-size:1rem;"></i>';
} else if (obj.status == 'transmitting') {
@ -434,8 +454,6 @@ update_chat = function(obj) {
} else {
var status = '<i class="bi bi-question" style="font-size:1rem;"></i>';
}
if (!(document.getElementById('msg-' + obj._id))) {
if (obj.type == 'ping') {
var new_message = `
@ -445,7 +463,6 @@ update_chat = function(obj) {
`;
}
if (obj.type == 'beacon') {
var new_message = `
<div class="m-1 p-0 rounded-pill w-100 bg-info" id="msg-${obj._id}">
<p class="font-monospace text-small text-white text-break"><i class="m-3 bi bi-broadcast"></i>snr: ${obj.snr} - ${timestampShort} </p>
@ -470,7 +487,6 @@ update_chat = function(obj) {
`;
}
if (obj.type == 'transmit') {
var new_message = `
<div class="ml-auto rounded-3 mt-3 mb-0 w-75" style="margin-left: auto;">
<!--<p class="font-monospace text-right mb-0 text-muted" style="text-align: right;">${timestamp}</p>-->
@ -511,47 +527,47 @@ update_chat = function(obj) {
document.getElementById('save-file-msg-' + obj._id).addEventListener("mouseleave", () => {
document.getElementById('save-file-msg-' + obj._id).style.backgroundColor = "rgba(0,0,0,.03)";
});
}
// CREATE RESEND MSG EVENT LISTENER
if ((document.getElementById('retransmit-msg-' + obj._id))) {
// check if element exists and if we already created NOT created an event listener
if (document.getElementById('retransmit-msg-' + obj._id) && !document.getElementById('retransmit-msg-' + obj._id).hasAttribute('listenerOnClick')) {
document.getElementById('retransmit-msg-' + obj._id).addEventListener("click", () => {
// set Attribute to determine if we already created an EventListener for this element
document.getElementById('retransmit-msg-' + obj._id).setAttribute('listenerOnClick', 'true');
db.get(obj._id, {
attachments: true
}).then(function(doc) {
// handle doc
console.log(doc)
var filename = Object.keys(obj._attachments)[0]
var filetype = obj._attachments[filename]["content_type"]
//var file = atob(obj._attachments[filename]["data"])
db.getAttachment(obj._id, filename).then(function (data) {
console.log(data)
var file = atob(data)
var data_with_attachment = doc.msg + split_char + filename + split_char + filetype + split_char + file;
let Data = {
command: "send_message",
dxcallsign: doc.dxcallsign,
mode: 255,
frames: 1,
data: data_with_attachment,
checksum: doc.checksum,
uuid: doc.uuid
};
console.log(Data)
ipcRenderer.send('run-tnc-command', Data);
db.getAttachment(obj._id, filename).then(function(data) {
console.log(data)
var file = atob(data)
var data_with_attachment = doc.msg + split_char + filename + split_char + filetype + split_char + file;
let Data = {
command: "send_message",
dxcallsign: doc.dxcallsign,
mode: 255,
frames: 1,
data: data_with_attachment,
checksum: doc.checksum,
uuid: doc.uuid
};
console.log(Data)
ipcRenderer.send('run-tnc-command', Data);
});
}).catch(function(err) {
console.log(err);
});
});
};
//window.location = window.location
}
function getObjByID(id) {
@ -579,17 +595,19 @@ function getObjByID(id) {
}
**/
db.get(id, {
attachments: true
}).then(function(doc) {
return obj
}).catch(function(err) {
console.log(err);
return false
});
}
attachments: true
}).then(function(doc) {
return obj
}).catch(function(err) {
console.log(err);
return false
});
}
function saveFileToFolder(id) {
db.get(id,{attachments: true}).then(function(obj) {
db.get(id, {
attachments: true
}).then(function(obj) {
console.log(obj)
console.log(Object.keys(obj._attachments)[0].content_type)
var filename = Object.keys(obj._attachments)[0]
@ -597,35 +615,23 @@ function saveFileToFolder(id) {
var file = filename.data
console.log(file)
console.log(filename.data)
db.getAttachment(id, filename).then(function (data) {
// handle result
console.log(data.length)
//data = new Blob([data.buffer], { type: 'image/png' } /* (1) */)
console.log(data)
let Data = {
file: data,
filename: filename,
filetype: filetype,
}
console.log(Data)
ipcRenderer.send('save-file-to-folder', Data);
db.getAttachment(id, filename).then(function(data) {
// handle result
console.log(data.length)
//data = new Blob([data.buffer], { type: 'image/png' } /* (1) */)
console.log(data)
let Data = {
file: data,
filename: filename,
filetype: filetype,
}
console.log(Data)
ipcRenderer.send('save-file-to-folder', Data);
}).catch(function(err) {
console.log(err);
return false
});
}).catch(function(err) {
console.log(err);
return false
});
}).catch(function (err) {
console.log(err);
});
}

View file

@ -40,8 +40,41 @@
</div>
<div class="col-8 border vh-100 ">
<! ------ chat navbar ---------------------------------------------------------------------->
<div class="container-fluid m-2 p-0">
<div class="input-group bottom-0">
<button class="btn btn-sm btn-secondary me-2" id="ping" type="button">Ping</button>
<button id="chatSettingsDropDown" type="button" class="btn btn-outline-secondary dropdown-toggle" data-bs-toggle="dropdown" aria-expanded="false">
<i class="bi bi-three-dots-vertical"></i>
</button>
<ul class="dropdown-menu" aria-labelledby="chatSettingsDropDown">
<li><a class="dropdown-item bg-danger text-white" id="delete_selected_chat" href="#">Delete chat</a></li>
</ul>
</div>
</div>
<! ------messages area ---------------------------------------------------------------------->
<div class="container overflow-auto" id="message-container" style="height: 90%">
<div class="container overflow-auto" id="message-container" style="height: calc(100% - 105px);">
<div class="tab-content" id="nav-tabContent">
</div>
@ -51,11 +84,10 @@
</div>
<!-- </div>-->
<div class="container-fluid mt-4 p-0">
<div class="container-fluid mt-2 p-0">
<div class="input-group bottom-0 w-100">
<input class="form-control" maxlength="8" style="max-width: 6rem; text-transform:uppercase" id="chatModuleDxCall" placeholder="DX CALL"></input>
<input class="form-control" maxlength="8" style="max-width: 6rem; text-transform:uppercase; display:none" id="chatModuleDxCall" placeholder="DX CALL"></input>
<input class="form-control" id="chatModuleMessage" placeholder="Message"></input>
<button class="btn btn-sm btn-primary me-2" id="emojipickerbutton" type="button"><i class="bi bi-emoji-smile"></i></button>

View file

@ -19,7 +19,7 @@ parser.add_argument('--bursts', dest="N_BURSTS", default=1, type=int)
parser.add_argument('--framesperburst', dest="N_FRAMES_PER_BURST", default=1, type=int)
parser.add_argument('--delay', dest="DELAY_BETWEEN_BURSTS", default=500, type=int,
help="delay between bursts in ms")
parser.add_argument('--mode', dest="FREEDV_MODE", type=str, choices=['datac0', 'datac1', 'datac3'])
parser.add_argument('--mode', dest="FREEDV_MODE", type=str, choices=['datac0', 'datac1', 'datac3', 'fsk_ldpc'])
parser.add_argument('--audiodev', dest="AUDIO_OUTPUT_DEVICE", default=-1, type=int,
help="audio output device number to use, use -2 to automatically select a loopback device")
parser.add_argument('--list', dest="LIST", action="store_true", help="list audio devices by number and exit")
@ -86,18 +86,63 @@ if args.TESTFRAMES:
else:
data_out = b'HELLO WORLD!'
# ----------------------------------------------------------------
class ADVANCED(ctypes.Structure):
""" """
_fields_ = [
("interleave_frames", ctypes.c_int),
("M", ctypes.c_int),
("Rs", ctypes.c_int),
("Fs", ctypes.c_int),
("first_tone", ctypes.c_int),
("tone_spacing", ctypes.c_int),
("codename", ctypes.c_char_p),
]
adv = ADVANCED()
adv.interleave_frames = 0 # max amplitude
adv.M = 2 # number of fsk tones 2/4
adv.Rs = 100 # symbol rate
adv.Fs = 8000 # sample rate
adv.first_tone = 1500 # first tone freq
adv.tone_spacing = 200 # shift between tones
'''
HRA_112_112 rate 0.50 (224,112) # BPF: 14
HRA_56_56 rate 0.50 (112,56) # BPF: 7, geht nicht
H_2064_516_sparse rate 0.80 (2580,2064) #BPF: 258,
HRAb_396_504 rate 0.79 (504,396) # BPF: 49, geht nicht payload !== %8
H_256_768_22 rate 0.33 (768,256) # BPF: 32, geht --> geht gut
H_256_512_4 rate 0.50 (512,256) # BPF: 32, geht
HRAa_1536_512 rate 0.75 (2048,1536) #BPF: 192, geht nicht
H_128_256_5 rate 0.50 (256,128) # BPF: 16, geht nicht
H_4096_8192_3d rate 0.50 (8192,4096) # BPF: 512, geht nicht
H_16200_9720 rate 0.60 (16200,9720) # BPF: 1215
H_1024_2048_4f rate 0.50 (2048,1024) # BPF: 128, geht nicht
'''
# open codec2 instance
freedv = cast(codec2.api.freedv_open(MODE), c_void_p)
adv.codename = 'H_256_512_4'.encode('utf-8') # code word
if MODE == 9:
# open codec2 advanced instance for ldpc codes
print(f"adv.interleave_frames {adv.interleave_frames}")
print(f"adv.M {adv.M}")
print(f"adv.Rs {adv.Rs}")
print(f"adv.Fs {adv.Fs}")
print(f"adv.first_tone {adv.first_tone}")
print(f"adv.tone_spacing {adv.tone_spacing}")
print(f"adv.codename {adv.codename}")
#freedv = codec2.api.freedv_open_advanced(MODE, pointer(adv))
freedv = cast(codec2.api.freedv_open_advanced(MODE, ctypes.byref(adv)), c_void_p)
else:
# open codec2 instance
freedv = cast(codec2.api.freedv_open(MODE), c_void_p)
# get number of bytes per frame for mode
bytes_per_frame = int(codec2.api.freedv_get_bits_per_modem_frame(freedv)/8)
payload_bytes_per_frame = bytes_per_frame -2
print(bytes_per_frame)
# init buffer for data
n_tx_modem_samples = codec2.api.freedv_get_n_tx_modem_samples(freedv)
mod_out = create_string_buffer(n_tx_modem_samples * 2)
@ -110,7 +155,6 @@ mod_out_preamble = create_string_buffer(n_tx_preamble_modem_samples * 2)
n_tx_postamble_modem_samples = codec2.api.freedv_get_n_tx_postamble_modem_samples(freedv)
mod_out_postamble = create_string_buffer(n_tx_postamble_modem_samples * 2)
# create buffer for data
buffer = bytearray(payload_bytes_per_frame) # use this if CRC16 checksum is required ( DATA1-3)
buffer[:len(data_out)] = data_out # set buffersize to length of data which will be send
@ -127,8 +171,8 @@ for i in range(1,N_BURSTS+1):
# write preamble to txbuffer
codec2.api.freedv_rawdatapreambletx(freedv, mod_out_preamble)
txbuffer = bytes(mod_out_preamble)
#txbuffer = bytes(mod_out_preamble)
txbuffer = bytes()
# create modulaton for N = FRAMESPERBURST and append it to txbuffer
for n in range(1,N_FRAMES_PER_BURST+1):
@ -136,12 +180,11 @@ for i in range(1,N_BURSTS+1):
codec2.api.freedv_rawdatatx(freedv,mod_out,data) # modulate DATA and save it into mod_out pointer
txbuffer += bytes(mod_out)
print(f"TX BURST: {i}/{N_BURSTS} FRAME: {n}/{N_FRAMES_PER_BURST}", file=sys.stderr)
# append postamble to txbuffer
codec2.api.freedv_rawdatapostambletx(freedv, mod_out_postamble)
txbuffer += bytes(mod_out_postamble)
#txbuffer += bytes(mod_out_postamble)
# append a delay between bursts as audio silence
samples_delay = int(MODEM_SAMPLE_RATE*DELAY_BETWEEN_BURSTS)

View file

@ -93,6 +93,10 @@ def fetch_audio_devices(input_devices, output_devices):
#input_devices = []
#output_devices = []
'''
sd._terminate()
sd._initialize()
devices = sd.query_devices(device=None, kind=None)
index = 0
for device in devices:
@ -117,4 +121,4 @@ def fetch_audio_devices(input_devices, output_devices):
if maxOutputChannels > 0:
output_devices.append({"id": index, "name": str(name)})
index += 1
#p.terminate()

View file

@ -8,7 +8,7 @@ Here we are saving application wide variables and stats, which have to be access
Not nice, suggestions are appreciated :-)
"""
VERSION = '0.2.1-alpha'
VERSION = '0.3.0-alpha'
# DAEMON
DAEMONPORT = 3001
@ -73,7 +73,7 @@ AUDIO_INPUT_DEVICES = []
AUDIO_OUTPUT_DEVICES = []
AUDIO_INPUT_DEVICE = -2
AUDIO_OUTPUT_DEVICE = -2
BUFFER_OVERFLOW_COUNTER = [0,0,0]
BUFFER_OVERFLOW_COUNTER = [0,0,0,0,0]
AUDIO_RMS = 0
FFT = [0]