diff --git a/README.md b/README.md index e08d2d5..3a54613 100644 --- a/README.md +++ b/README.md @@ -14,6 +14,14 @@ It has the following features: - it works in Google Chrome, Chromium (above version 37) and Mozilla Firefox (above version 28), - currently only supports RTL-SDR, but other SDR hardware may be easily added. +**News:** +- My BSc. thesis written on OpenWebRX is available here. +- Several bugs were fixed to improve reliability and stability. +- OpenWebRX now supports compression of audio and waterfall stream, so the required network uplink bandwidth has been decreased from 2 Mbit/s to about 200 kbit/s per client! (Measured with the default settings. It is also dependent on `fft_size`.) +- OpenWebRX now uses sdr.js (*libcsdr* compiled to JavaScript) for some client-side DSP tasks. +- Auto-update capability for sdr.hu added (currently only sdr.hu beta testers can use it). +- License for OpenWebRX is now Affero GPL v3. + ## Setup OpenWebRX currently requires Linux and python 2.7 to run. diff --git a/config_rtl.py b/config_rtl.py old mode 100755 new mode 100644 index c759978..d31d951 --- a/config_rtl.py +++ b/config_rtl.py @@ -1,20 +1,30 @@ ''' -This file is part of RTL Multi-User Server, + This file is part of RTL Multi-User Server, that makes multi-user access to your DVB-T dongle used as an SDR. -Copyright (c) 2013-2014 by Andras Retzler + Copyright (c) 2013-2015 by Andras Retzler -RTL Multi-User Server is free software: you can redistribute it and/or modify -it under the terms of the GNU General Public License as published by -the Free Software Foundation, either version 3 of the License, or -(at your option) any later version. + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU Affero General Public License as + published by the Free Software Foundation, either version 3 of the + License, or (at your option) any later version. -RTL Multi-User Server is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Affero General Public License for more details. -You should have received a copy of the GNU General Public License -along with RTL Multi-User Server. If not, see . + You should have received a copy of the GNU Affero General Public License + along with this program. If not, see . + + ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ + + In addition, as a special exception, the copyright holders + state that config_rtl.py and config_webrx.py are not part of the + Corresponding Source defined in GNU AGPL version 3 section 1. + + (It means that you do not have to redistribute config_rtl.py and + config_webrx.py if you make any changes to these two configuration files, + and use them for running your own web service with OpenWebRX.) ''' my_ip='127.0.0.1' # leave blank for listening on all interfaces @@ -70,11 +80,11 @@ Example DSP commands: * Decompress FLAC-coded I/Q data: flac --force-raw-format --decode --endian=little --sign=unsigned - - ''' -watchdog_interval=1.5 +watchdog_interval=0 reconnect_interval=10 ''' If there's no input I/Q data after N seconds, input will be filled with zero samples, -so that GNU Radio won't fail in openwebrx. It may reconnect rtl_tcp_tread. +so that GNU Radio won't fail in OpenWebRX. It may reconnect rtl_tcp_thread. If watchdog_interval is 0, then watchdog thread is not started. ''' @@ -85,3 +95,9 @@ cache_full_behaviour=2 2 = openwebrx: don't care about that client until it wants samples again (gr-osmosdr bug workaround) ''' +rtl_tcp_password=None +''' +This one applies to a special version of rtl_tcp that has authentication. +# You can find more info here: https://github.com/ha7ilm/rtl-sdr +# If it is set to a string (e.g. rtl_tcp_password="changeme"), rtl_mus will try to authenticate against the rtl_tcp server. +''' diff --git a/config_webrx.py b/config_webrx.py old mode 100755 new mode 100644 index 230e349..55f1528 --- a/config_webrx.py +++ b/config_webrx.py @@ -3,36 +3,46 @@ """ config_webrx: configuration options for OpenWebRX -OpenWebRX (c) Copyright 2013-2014 Andras Retzler + This file is part of OpenWebRX, + an open-source SDR receiver software with a web UI. + Copyright (c) 2013-2015 by Andras Retzler -This file is part of OpenWebRX. + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU Affero General Public License as + published by the Free Software Foundation, either version 3 of the + License, or (at your option) any later version. - OpenWebRX is free software: you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation, either version 3 of the License, or - (at your option) any later version. - - OpenWebRX is distributed in the hope that it will be useful, + This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. + GNU Affero General Public License for more details. - You should have received a copy of the GNU General Public License - along with OpenWebRX. If not, see . + You should have received a copy of the GNU Affero General Public License + along with this program. If not, see . + ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ + + In addition, as a special exception, the copyright holders + state that config_rtl.py and config_webrx.py are not part of the + Corresponding Source defined in GNU AGPL version 3 section 1. + + (It means that you do not have to redistribute config_rtl.py and + config_webrx.py if you make any changes to these two configuration files, + and use them for running your web service with OpenWebRX.) """ -#Server settings +# ==== Server settings ==== web_port=8073 server_hostname="localhost" # If this contains an incorrect value, the web UI may freeze on load (it can't open websocket) +max_clients=20 -#Web GUI configuration +# ==== Web GUI configuration ==== receiver_name="[Callsign]" receiver_location="Budapest, Hungary" receiver_qra="JN97ML" -receiver_asl=182 +receiver_asl=200 receiver_ant="Longwire" receiver_device="RTL-SDR" -receiver_admin="localhost@localhost" +receiver_admin="example@example.com" receiver_gps=(47.000000,19.000000) photo_height=350 photo_title="Panorama of Budapest from Schönherz Zoltán Dormitory" @@ -44,16 +54,57 @@ Antenna: %[RX_ANT]
Website: http://localhost """ -#DSP/RX settings +# ==== sdr.hu listing ==== +# (This feature is only available to sdr.hu beta testers by now.) +# If you want your ham receiver to be listed publicly on sdr.hu, then take the following steps: +# 1. Register at: http://sdr.hu/register +# 2. You will get an unique key by email. Copy it and paste here: +sdrhu_key = "" +# 3. Set this setting to True to enable listing: +sdrhu_public_listing = False + +# ==== DSP/RX settings ==== dsp_plugin="csdr" fft_fps=9 fft_size=4096 samp_rate = 250000 + center_freq = 145525000 rf_gain = 5 +ppm = 0 -start_rtl_thread=True #rtl_sdr is more stable than rtl_tcp... -start_rtl_command="rtl_sdr -s {samp_rate} -f {center_freq} - | nc -vvl 127.0.0.1 -p 8888".format(rf_gain=rf_gain, center_freq=center_freq, samp_rate=samp_rate) -#start_rtl_tcp_command="rtl_tcp -s 250000 -f 145525000 -g 0 -p 8888" -#You can use other SDR hardware as well, but if the command above outputs samples in a format other than [unsigned char], then the dsp plugin has to be slightly modified (at the csdr convert_u8_f part). +audio_compression="adpcm" #valid values: "adpcm", "none" +fft_compression="adpcm" #valid values: "adpcm", "none" +start_rtl_thread=True + +# ==== I/Q sources (uncomment the appropriate) ==== + +# >> RTL-SDR via rtl_sdr + +start_rtl_command="rtl_sdr -s {samp_rate} -f {center_freq} -p {ppm} - | nc -vvl 127.0.0.1 8888".format(rf_gain=rf_gain, center_freq=center_freq, samp_rate=samp_rate, ppm=ppm) +format_conversion="csdr convert_u8_f" + +# >> Sound card SDR (needs ALSA) +#I did not have the chance to properly test it. +#samp_rate = 96000 +#start_rtl_command="arecord -f S16_LE -r {samp_rate} -c2 - | nc -vvl 127.0.0.1 8888".format(samp_rate=samp_rate) +#format_conversion="csdr convert_i16_f | csdr gain_ff 30" + +# >> RTL_SDR via rtl_tcp +#start_rtl_command="rtl_tcp -s {samp_rate} -f {center_freq} -g {rf_gain} -P {ppm} -p 8888".format(rf_gain=rf_gain, center_freq=center_freq, samp_rate=samp_rate, ppm=ppm) +#format_conversion="csdr convert_u8_f" + +# >> /dev/urandom test signal source +#samp_rate = 2400000 +#start_rtl_command="cat /dev/urandom | (pv -qL `python -c 'print int({samp_rate} * 2.2)'` 2>&1) | nc -vvl 127.0.0.1 8888".format(rf_gain=rf_gain, center_freq=center_freq, samp_rate=samp_rate) +#format_conversion="csdr convert_u8_f" + +#You can use other SDR hardware as well, by giving your own command that outputs the I/Q samples... + +shown_center_freq = center_freq #you can change this if you use an upconverter + +client_audio_buffer_size = 4 +#increasing client_audio_buffer_size will: +# - also increase the latency +# - decrease the chance of audio underruns diff --git a/htdocs/gfx/openwebrx-avatar-background.png b/htdocs/gfx/openwebrx-avatar-background.png old mode 100755 new mode 100644 diff --git a/htdocs/gfx/openwebrx-avatar.png b/htdocs/gfx/openwebrx-avatar.png old mode 100755 new mode 100644 diff --git a/htdocs/gfx/openwebrx-background-cool-blue.png b/htdocs/gfx/openwebrx-background-cool-blue.png old mode 100755 new mode 100644 diff --git a/htdocs/gfx/openwebrx-background-lingrad.png b/htdocs/gfx/openwebrx-background-lingrad.png old mode 100755 new mode 100644 diff --git a/htdocs/gfx/openwebrx-ha5kfu-top-logo.png b/htdocs/gfx/openwebrx-ha5kfu-top-logo.png old mode 100755 new mode 100644 diff --git a/htdocs/gfx/openwebrx-logo-big.png b/htdocs/gfx/openwebrx-logo-big.png old mode 100755 new mode 100644 diff --git a/htdocs/gfx/openwebrx-panel-log.png b/htdocs/gfx/openwebrx-panel-log.png new file mode 100644 index 0000000..58e6fd5 Binary files /dev/null and b/htdocs/gfx/openwebrx-panel-log.png differ diff --git a/htdocs/gfx/openwebrx-panel-receiver.png b/htdocs/gfx/openwebrx-panel-receiver.png new file mode 100644 index 0000000..5c80c3b Binary files /dev/null and b/htdocs/gfx/openwebrx-panel-receiver.png differ diff --git a/htdocs/gfx/openwebrx-panel-status.png b/htdocs/gfx/openwebrx-panel-status.png new file mode 100644 index 0000000..064b54f Binary files /dev/null and b/htdocs/gfx/openwebrx-panel-status.png differ diff --git a/htdocs/gfx/openwebrx-rx-details-arrow-up.png b/htdocs/gfx/openwebrx-rx-details-arrow-up.png old mode 100755 new mode 100644 diff --git a/htdocs/gfx/openwebrx-rx-details-arrow.png b/htdocs/gfx/openwebrx-rx-details-arrow.png old mode 100755 new mode 100644 diff --git a/htdocs/gfx/openwebrx-scale-background.png b/htdocs/gfx/openwebrx-scale-background.png old mode 100755 new mode 100644 diff --git a/htdocs/gfx/openwebrx-top-logo.png b/htdocs/gfx/openwebrx-top-logo.png old mode 100755 new mode 100644 diff --git a/htdocs/gfx/openwebrx-top-photo.jpg b/htdocs/gfx/openwebrx-top-photo.jpg old mode 100755 new mode 100644 diff --git a/htdocs/index.wrx b/htdocs/index.wrx old mode 100755 new mode 100644 index 7cf73ba..f9b85db --- a/htdocs/index.wrx +++ b/htdocs/index.wrx @@ -1,31 +1,35 @@ - OpenWebRX | Open Source Web-based SDR for everyone! + OpenWebRX | Open Source SDR Web App for Everyone! + @@ -50,6 +54,13 @@ This file is part of OpenWebRX. +
+
    +

  • Status
  • +

  • Log
  • +

  • Receiver
  • +
+
@@ -61,7 +72,7 @@ This file is part of OpenWebRX.
-
+
---.--- MHz
---.--- MHz
@@ -71,17 +82,23 @@ This file is part of OpenWebRX.
USB
CW
-
-
-
openwebrx.js (beta) client log
+
+
+
OpenWebRX (beta) client log
Author: HA7ILM. Please send me bug reports and suggestions.
- Client status: -
Your client ID is: %[CLIENT_ID]
+
+
Audio buffer [0 ms]
+
Audio output [0 sps]
+
Audio stream [0 kbps]
+
Network speed [0 kbps]
+
Server CPU [0%]
+
Clients [1]
+
Under construction
We're working on the code right now, so the application might fail. diff --git a/htdocs/openwebrx.css b/htdocs/openwebrx.css old mode 100755 new mode 100644 index 15993fc..b3dc9b4 --- a/htdocs/openwebrx.css +++ b/htdocs/openwebrx.css @@ -1,20 +1,22 @@ /* -OpenWebRX (c) Copyright 2013-2014 Andras Retzler -This file is part of OpenWebRX. + This file is part of OpenWebRX, + an open-source SDR receiver software with a web UI. + Copyright (c) 2013-2015 by Andras Retzler - OpenWebRX is free software: you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation, either version 3 of the License, or - (at your option) any later version. + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU Affero General Public License as + published by the Free Software Foundation, either version 3 of the + License, or (at your option) any later version. - OpenWebRX is distributed in the hope that it will be useful, + This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. + GNU Affero General Public License for more details. + + You should have received a copy of the GNU Affero General Public License + along with this program. If not, see . - You should have received a copy of the GNU General Public License - along with OpenWebRX. If not, see . */ html, body @@ -52,9 +54,14 @@ html, body { margin:0; padding:0; + user-select: none; + -webkit-touch-callout: none; + -webkit-user-select: none; + -khtml-user-select: none; + -moz-user-select: none; + -ms-user-select: none; } - #webrx-top-logo { position: absolute; @@ -65,7 +72,7 @@ html, body #webrx-ha5kfu-top-logo { position: absolute; - top: 19px; + top: 15px; right: 15px; } @@ -177,8 +184,10 @@ html, body #webrx-rx-photo-desc a { + /*color: #007df1;*/ color: #5ca8ff; text-shadow: none; + /*text-shadow: 0px 0px 7px #fff;*/ } #webrx-rx-title @@ -269,6 +278,8 @@ html, body { position: absolute; border-style: none; + image-rendering: crisp-edges; + image-rendering: -webkit-optimize-contrast; } #openwebrx-phantom-canvas @@ -410,6 +421,12 @@ html, body cursor: pointer; background:-webkit-gradient( linear, left top, left bottom, color-stop(0.0 , #373737), color-stop(1, #4F4F4F) ); background:-moz-linear-gradient( center top, #373737 0%, #4F4F4F 100% ); + user-select: none; + -webkit-touch-callout: none; + -webkit-user-select: none; + -khtml-user-select: none; + -moz-user-select: none; + -ms-user-select: none; } .openwebrx-button:hover @@ -431,3 +448,94 @@ html, body margin-bottom: 5px; font-weight: bold; } + +.openwebrx-progressbar +{ + position: relative; + border-radius: 5px; + background-color: #003850; /*#006235;*/ + display: inline-block; + text-align: center; + font-size: 8pt; + font-weight: bold; + text-shadow: 0px 0px 4px #000000; + cursor: default; + user-select: none; + -webkit-touch-callout: none; + -webkit-user-select: none; + -khtml-user-select: none; + -moz-user-select: none; + -ms-user-select: none; +} + +.openwebrx-progressbar-bar +{ + border-radius: 5px; + height: 100%; + width: 100%; +} + +.openwebrx-progressbar-text +{ + position: absolute; + left:0px; + top:4px; + width: inherit; +} + +#openwebrx-panel-status +{ + margin: 0px; + padding: 0px; + background-color:rgba(0, 0, 0, 0); +} + +#openwebrx-panel-status div.openwebrx-progressbar +{ + width: 200px; + height: 20px; +} + +#openwebrx-main-buttons img +{ +} + +#openwebrx-main-buttons ul +{ + display: table; + margin:0; +} + + +#openwebrx-main-buttons ul li +{ + display: table-cell; + padding-left: 5px; + padding-right: 5px; + cursor:pointer; +} + +#openwebrx-main-buttons li:hover +{ + background-color: rgba(255, 255, 255, 0.3); +} + +#openwebrx-main-buttons li:active +{ + background-color: rgba(255, 255, 255, 0.55); +} + + +#openwebrx-main-buttons +{ + position: absolute; + right: 133px; + top: 3px; + margin:0; + color: white; + text-shadow: 0px 0px 4px #000000; + text-align: center; + font-size: 9pt; + font-weight: bold; +} + diff --git a/htdocs/openwebrx.js b/htdocs/openwebrx.js old mode 100755 new mode 100644 index 3947f5d..1deeaf7 --- a/htdocs/openwebrx.js +++ b/htdocs/openwebrx.js @@ -1,21 +1,23 @@ /* -OpenWebRX (c) Copyright 2013-2014 Andras Retzler + This file is part of OpenWebRX, + an open-source SDR receiver software with a web UI. + Copyright (c) 2013-2015 by Andras Retzler -This file is part of OpenWebRX. + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU Affero General Public License as + published by the Free Software Foundation, either version 3 of the + License, or (at your option) any later version. - OpenWebRX is free software: you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation, either version 3 of the License, or - (at your option) any later version. - - OpenWebRX is distributed in the hope that it will be useful, + This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. + GNU Affero General Public License for more details. - You should have received a copy of the GNU General Public License - along with OpenWebRX. If not, see . + You should have received a copy of the GNU Affero General Public License + along with this program. If not, see . + +""" */ @@ -43,6 +45,9 @@ var audio_buffer_current_count_debug=0; var audio_buffer_current_size=0; var fft_size; var fft_fps; +var fft_compression="none"; +var fft_codec=new sdrjs.ImaAdpcm(); +var audio_compression="none"; var waterfall_setup_done=0; var waterfall_queue = []; var waterfall_timer; @@ -270,7 +275,7 @@ demodulator.draggable_ranges={none: 0, beginning:1 /*from*/, ending: 2 /*to*/, a // This can be used as a base for basic audio demodulators. // It already supports most basic modulations used for ham radio and commercial services: AM/FM/LSB/USB -demodulator_response_time=100; +demodulator_response_time=50; //in ms; if we don't limit the number of SETs sent to the server, audio will underrun (possibly output buffer is cleared on SETs in GNU Radio function demodulator_default_analog(offset_frequency,subtype) @@ -703,27 +708,21 @@ function mkscale() var text_measured=scale_ctx.measureText(text_to_draw); scale_ctx.textAlign = "center"; //advanced text drawing begins - if(zoom_level==0&&range.start+spacing.smallbw*spacing.ratio>marker_hz) - { //if this is the first overall marker when zoomed out - if(x=scale_min_space_bw_texts) - { //and if we have enough space to draw it correctly without clipping - scale_ctx.textAlign = "left"; - scale_ctx.fillText(text_to_draw, 0, text_h_pos); - } + if( zoom_level==0 && (range.start+spacing.smallbw*spacing.ratio>marker_hz) && (x=scale_min_space_bw_texts) + { //and if we have enough space to draw it correctly without clipping + scale_ctx.textAlign = "left"; + scale_ctx.fillText(text_to_draw, 0, text_h_pos); } } - else if(zoom_level==0&&range.end-spacing.smallbw*spacing.ratiowindow.innerWidth-text_measured.width/2) - { //and if it would be clipped off the screen - if(window.innerWidth-text_measured.width-scale_px_from_freq(marker_hz-spacing.smallbw*spacing.ratio,range)>=scale_min_space_bw_texts) - { //and if we have enough space to draw it correctly without clipping - scale_ctx.textAlign = "right"; - scale_ctx.fillText(text_to_draw, window.innerWidth, text_h_pos); - } - } + else if( zoom_level==0 && (range.end-spacing.smallbw*spacing.ratiowindow.innerWidth-text_measured.width/2) ) + { // if this is the last overall marker when zoomed out... and if it would be clipped off the screen... + if(window.innerWidth-text_measured.width-scale_px_from_freq(marker_hz-spacing.smallbw*spacing.ratio,range)>=scale_min_space_bw_texts) + { //and if we have enough space to draw it correctly without clipping + scale_ctx.textAlign = "right"; + scale_ctx.fillText(text_to_draw, window.innerWidth, text_h_pos); + } } else scale_ctx.fillText(text_to_draw, x, text_h_pos); //draw text normally } @@ -960,11 +959,16 @@ function resize_waterfall_container(check_init) canvas_container.style.height=(window.innerHeight-e("webrx-top-container").clientHeight-e("openwebrx-scale-container").clientHeight).toString()+"px"; } +debug_ws_data_received=0; +max_clients_num=0; + +var COMPRESS_FFT_PAD_N=10; //should be the same as in csdr.c function on_ws_recv(evt) { if(!(evt.data instanceof ArrayBuffer)) { divlog("on_ws_recv(): Not ArrayBuffer received...",1); return; } // + debug_ws_data_received+=evt.data.byteLength/1000; firstChars=getFirstChars(evt.data,3); if(firstChars=="CLI") { @@ -973,7 +977,9 @@ function on_ws_recv(evt) } if(firstChars=="AUD") { - var audio_data=new Int16Array(evt.data,4); + var audio_data; + if(audio_compression=="adpcm") audio_data=new Uint8Array(evt.data,4) + else audio_data=new Int16Array(evt.data,4); audio_prepare(audio_data); audio_buffer_current_size_debug+=audio_data.length; audio_buffer_all_size_debug+=audio_data.length; @@ -982,8 +988,15 @@ function on_ws_recv(evt) else if(firstChars=="FFT") { //alert("Yupee! Doing FFT"); - var floatArray = new Float32Array(evt.data,4); - waterfall_add_queue(floatArray); + if(fft_compression=="none") waterfall_add_queue(new Float32Array(evt.data,4)); + else if(fft_compression="adpcm") + { + fft_codec.reset(); + var waterfall_i16=fft_codec.decode(new Uint8Array(evt.data,4)); + var waterfall_f32=new Float32Array(waterfall_i16.length-COMPRESS_FFT_PAD_N); + for(var i=0;i85); + break; + case "clients": + var clients_num=parseInt(param[1]); + progressbar_set(e("openwebrx-bar-clients"),clients_num/max_clients_num,"Clients ["+param[1]+"]",clients_num>max_clients_num*0.85); + break; + case "max_clients": + max_clients_num=parseInt(param[1]); break; - } } /*} @@ -1032,17 +1063,33 @@ function add_problem(what) window.setTimeout(function(ps,ns) { ps.removeChild(ns); }, 1000,problems_span,new_span); } +waterfall_measure_minmax=false; +waterfall_measure_minmax_min=1e100; +waterfall_measure_minmax_max=-1e100; + +function waterfall_measure_minmax_do(what) +{ + waterfall_measure_minmax_min=Math.min(waterfall_measure_minmax_min,Math.min.apply(Math,what)); + waterfall_measure_minmax_max=Math.max(waterfall_measure_minmax_max,Math.max.apply(Math,what)); +} + +function waterfall_measure_minmax_print() +{ + console.log("Waterfall | min = "+waterfall_measure_minmax_min.toString()+" dB | max = "+waterfall_measure_minmax_max.toString()+" dB"); +} + function waterfall_add_queue(what) { + if(waterfall_measure_minmax) waterfall_measure_minmax_do(what); waterfall_queue.push(what); } function waterfall_dequeue() { if(waterfall_queue.length) waterfall_add(waterfall_queue.shift()); - if(waterfall_queue.length>Math.max(fft_fps/2,8)) //in case of emergency + if(waterfall_queue.length>Math.max(fft_fps/2,20)) //in case of emergency { - console.log(waterfall_queue.length); + console.log("waterfall queue length:", waterfall_queue.length); add_problem("fft overflow"); while(waterfall_queue.length) waterfall_add(waterfall_queue.shift()); } @@ -1054,10 +1101,20 @@ function on_ws_opened() divlog("WebSocket opened to "+ws_url); } +var was_error=0; + function divlog(what, is_error) { - if(typeof is_error !== undefined && is_error == 1) what=""+what+""; + is_error=!!is_error; + was_error |= is_error; + if(is_error) + { + what=""+what+""; + if(e("openwebrx-panel-log").openwebrxHidden) toggle_panel("openwebrx-panel-log"); //show panel if any error is present + } e("openwebrx-debugdiv").innerHTML+=what+"
"; + var wls=e("openwebrx-log-scroll"); + wls.scrollTop=wls.scrollHeight; //scroll to bottom } var audio_context; @@ -1065,54 +1122,105 @@ var audio_initialized=0; var audio_received = Array(); var audio_buffer_index = 0; -var audio_resampler; +var audio_resampler=new sdrjs.RationalResamplerFF(4,1); +var audio_codec=new sdrjs.ImaAdpcm(); +var audio_compression="unknown"; var audio_node; //var audio_received_sample_rate = 48000; var audio_input_buffer_size; // Optimalise these if audio lags or is choppy: -var audio_buffer_size = 8192;//2048 was choppy -var audio_buffer_maximal_length_sec=1.7; //actual number of samples are calculated from sample rate -var audio_flush_interval_ms=250; //the interval in which audio_flush() is called +var audio_buffer_size = 4096;//2048 was choppy +var audio_buffer_maximal_length_sec=3; //actual number of samples are calculated from sample rate +var audio_buffer_decrease_to_on_overrun_sec=2.2; +var audio_flush_interval_ms=500; //the interval in which audio_flush() is called var audio_prepared_buffers = Array(); +var audio_rebuffer = new sdrjs.Rebuffer(audio_buffer_size,sdrjs.REBUFFER_FIXED); var audio_last_output_buffer = new Float32Array(audio_buffer_size); var audio_last_output_offset = 0; var audio_buffering = false; -var audio_buffering_fill_to=10; //on audio underrun we wait until this n*audio_buffer_size samples are present +//var audio_buffering_fill_to=4; //on audio underrun we wait until this n*audio_buffer_size samples are present + //tnx to the hint from HA3FLT, now we have about half the response time! (original value: 10) + +function gain_ff(gain_value,data) //great! solved clicking! will have to move to sdr.js +{ + for(var i=0;iaudio_buffering_fill_to) audio_buffering=false; +} + + +function audio_prepare_without_resampler(data) +{ + audio_rebuffer.push(sdrjs.ConvertI16_F(data)); + console.log("prepare",data.length,audio_rebuffer.remaining()); + while(audio_rebuffer.remaining()) + { + audio_prepared_buffers.push(audio_rebuffer.take()); + audio_buffer_current_count_debug++; + } + if(audio_buffering && audio_prepared_buffers.length>audio_buffering_fill_to) audio_buffering=false; +} + +function audio_prepare_old(data) { //console.log("audio_prepare :: "+data.length.toString()); //console.log("data.len = "+data.length.toString()); var dopush=function() { + console.log(audio_last_output_buffer); audio_prepared_buffers.push(audio_last_output_buffer); audio_last_output_offset=0; audio_last_output_buffer=new Float32Array(audio_buffer_size); audio_buffer_current_count_debug++; }; + var original_data_length=data.length; + var f32data=new Float32Array(data.length); + for(var i=0;iaudio_buffering_fill_to) audio_buffering=false; } @@ -1127,24 +1235,49 @@ if (!AudioBuffer.prototype.copyToChannel) } function audio_onprocess(e) -{ +{ + //console.log("audio onprocess"); if(audio_buffering) return; - if(audio_prepared_buffers.length==0) { add_problem("audio underrun"); audio_buffering=true; } - else e.outputBuffer.copyToChannel(audio_prepared_buffers.shift(),0); + if(audio_prepared_buffers.length==0) { audio_buffer_progressbar_update(); /*add_problem("audio underrun");*/ audio_buffering=true; } + else { e.outputBuffer.copyToChannel(audio_prepared_buffers.shift(),0); } } +var audio_buffer_progressbar_update_disabled=false; + +var audio_buffer_total_average_level=0; +var audio_buffer_total_average_level_length=0; + +function audio_buffer_progressbar_update() +{ + if(audio_buffer_progressbar_update_disabled) return; + var audio_buffer_value=(audio_prepared_buffers.length*audio_buffer_size)/44100; + audio_buffer_total_average_level_length++; audio_buffer_total_average_level=(audio_buffer_total_average_level*((audio_buffer_total_average_level_length-1)/audio_buffer_total_average_level_length))+(audio_buffer_value/audio_buffer_total_average_level_length); + var overrun=audio_buffer_value>audio_buffer_maximal_length_sec; + var underrun=audio_prepared_buffers.length==0; + var text="buffer"; + if(overrun) text="overrun"; + if(underrun) text="underrun"; + if(overrun||underrun) + { + audio_buffer_progressbar_update_disabled=true; + window.setTimeout(function(){audio_buffer_progressbar_update_disabled=false; audio_buffer_progressbar_update();},1000); + } + progressbar_set(e("openwebrx-bar-audio-buffer"),(underrun)?1:audio_buffer_value/1.5,"Audio "+text+" ["+(audio_buffer_value).toFixed(1)+" s]",overrun||underrun||audio_buffer_value<0.25); +} function audio_flush() { flushed=false; - while(audio_buffer_maximal_length_sec*audio_context.sampleRate