diff --git a/gui/main.js b/gui/main.js index 37272b39..74fd81ef 100644 --- a/gui/main.js +++ b/gui/main.js @@ -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, diff --git a/gui/package.json b/gui/package.json index 15de829c..f63e02f9 100644 --- a/gui/package.json +++ b/gui/package.json @@ -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": { diff --git a/gui/preload-chat.js b/gui/preload-chat.js index eddfebd2..a76d0848 100644 --- a/gui/preload-chat.js +++ b/gui/preload-chat.js @@ -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 = ` `; 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 = `

@@ -379,23 +402,21 @@ update_chat = function(obj) {

`; - } 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 = ` @@ -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 = ''; } else if (obj.status == 'transmitting') { @@ -434,8 +454,6 @@ update_chat = function(obj) { } else { var status = ''; } - - 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 = `

snr: ${obj.snr} - ${timestampShort}

@@ -470,7 +487,6 @@ update_chat = function(obj) { `; } if (obj.type == 'transmit') { - var new_message = `
@@ -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); -}); - - - - } diff --git a/gui/src/chat-module.html b/gui/src/chat-module.html index 54052c82..d1b0f13c 100644 --- a/gui/src/chat-module.html +++ b/gui/src/chat-module.html @@ -40,8 +40,41 @@
+ + + + -
+
+ + + + + + + + + @@ -51,11 +84,10 @@
- -
+
- + diff --git a/test/test_tx.py b/test/test_tx.py index bffe1ae6..0aa65aef 100644 --- a/test/test_tx.py +++ b/test/test_tx.py @@ -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) diff --git a/tnc/audio.py b/tnc/audio.py index 4f685302..baa2be0a 100644 --- a/tnc/audio.py +++ b/tnc/audio.py @@ -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() + diff --git a/tnc/static.py b/tnc/static.py index bd1c8b83..4f7c2815 100644 --- a/tnc/static.py +++ b/tnc/static.py @@ -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]