many fixes and new features like IMA ADPCM compression
@@ -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 <a href="http://openwebrx.org/bsc-thesis.pdf">available here.</a>
 | 
			
		||||
- 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 <a href="https://github.com/simonyiszk/csdr#sdr.js">sdr.js</a> (*libcsdr* compiled to JavaScript) for some client-side DSP tasks. 
 | 
			
		||||
- Auto-update capability for <a href="http://sdr.hu/">sdr.hu</a> added (currently only <a href="http://sdr.hu/">sdr.hu</a> beta testers can use it).
 | 
			
		||||
- License for OpenWebRX is now Affero GPL v3. 
 | 
			
		||||
 | 
			
		||||
## Setup
 | 
			
		||||
 | 
			
		||||
OpenWebRX currently requires Linux and python 2.7 to run. 
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										44
									
								
								config_rtl.py
									
									
									
									
									
										
										
										Executable file → Normal file
									
								
							
							
						
						@@ -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 <randras@sdr.hu>
 | 
			
		||||
	Copyright (c) 2013-2015 by Andras Retzler <randras@sdr.hu>
 | 
			
		||||
 | 
			
		||||
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 <http://www.gnu.org/licenses/>.
 | 
			
		||||
    You should have received a copy of the GNU Affero General Public License
 | 
			
		||||
    along with this program.  If not, see <http://www.gnu.org/licenses/>.
 | 
			
		||||
 | 
			
		||||
	++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
 | 
			
		||||
 | 
			
		||||
	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.
 | 
			
		||||
'''
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										91
									
								
								config_webrx.py
									
									
									
									
									
										
										
										Executable file → Normal file
									
								
							
							
						
						@@ -3,36 +3,46 @@
 | 
			
		||||
"""
 | 
			
		||||
config_webrx: configuration options for OpenWebRX
 | 
			
		||||
 | 
			
		||||
OpenWebRX (c) Copyright 2013-2014 Andras Retzler <randras@sdr.hu>
 | 
			
		||||
	This file is part of OpenWebRX, 
 | 
			
		||||
	an open-source SDR receiver software with a web UI.
 | 
			
		||||
	Copyright (c) 2013-2015 by Andras Retzler <randras@sdr.hu>
 | 
			
		||||
 | 
			
		||||
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 <http://www.gnu.org/licenses/>.
 | 
			
		||||
    You should have received a copy of the GNU Affero General Public License
 | 
			
		||||
    along with this program.  If not, see <http://www.gnu.org/licenses/>.
 | 
			
		||||
 | 
			
		||||
	++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
 | 
			
		||||
 | 
			
		||||
	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]<br />
 | 
			
		||||
Website: <a href="http://localhost" target="_blank">http://localhost</a>
 | 
			
		||||
"""
 | 
			
		||||
 | 
			
		||||
#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
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										0
									
								
								htdocs/gfx/openwebrx-avatar-background.png
									
									
									
									
									
										
										
										Executable file → Normal file
									
								
							
							
						
						| 
		 Before Width: | Height: | Size: 459 B After Width: | Height: | Size: 459 B  | 
							
								
								
									
										0
									
								
								htdocs/gfx/openwebrx-avatar.png
									
									
									
									
									
										
										
										Executable file → Normal file
									
								
							
							
						
						| 
		 Before Width: | Height: | Size: 742 B After Width: | Height: | Size: 742 B  | 
							
								
								
									
										0
									
								
								htdocs/gfx/openwebrx-background-cool-blue.png
									
									
									
									
									
										
										
										Executable file → Normal file
									
								
							
							
						
						| 
		 Before Width: | Height: | Size: 90 KiB After Width: | Height: | Size: 90 KiB  | 
							
								
								
									
										0
									
								
								htdocs/gfx/openwebrx-background-lingrad.png
									
									
									
									
									
										
										
										Executable file → Normal file
									
								
							
							
						
						| 
		 Before Width: | Height: | Size: 679 B After Width: | Height: | Size: 679 B  | 
							
								
								
									
										0
									
								
								htdocs/gfx/openwebrx-ha5kfu-top-logo.png
									
									
									
									
									
										
										
										Executable file → Normal file
									
								
							
							
						
						| 
		 Before Width: | Height: | Size: 2.0 KiB After Width: | Height: | Size: 2.0 KiB  | 
							
								
								
									
										0
									
								
								htdocs/gfx/openwebrx-logo-big.png
									
									
									
									
									
										
										
										Executable file → Normal file
									
								
							
							
						
						| 
		 Before Width: | Height: | Size: 44 KiB After Width: | Height: | Size: 44 KiB  | 
							
								
								
									
										
											BIN
										
									
								
								htdocs/gfx/openwebrx-panel-log.png
									
									
									
									
									
										Normal file
									
								
							
							
						
						| 
		 After Width: | Height: | Size: 1.2 KiB  | 
							
								
								
									
										
											BIN
										
									
								
								htdocs/gfx/openwebrx-panel-receiver.png
									
									
									
									
									
										Normal file
									
								
							
							
						
						| 
		 After Width: | Height: | Size: 1.8 KiB  | 
							
								
								
									
										
											BIN
										
									
								
								htdocs/gfx/openwebrx-panel-status.png
									
									
									
									
									
										Normal file
									
								
							
							
						
						| 
		 After Width: | Height: | Size: 1.9 KiB  | 
							
								
								
									
										0
									
								
								htdocs/gfx/openwebrx-rx-details-arrow-up.png
									
									
									
									
									
										
										
										Executable file → Normal file
									
								
							
							
						
						| 
		 Before Width: | Height: | Size: 518 B After Width: | Height: | Size: 518 B  | 
							
								
								
									
										0
									
								
								htdocs/gfx/openwebrx-rx-details-arrow.png
									
									
									
									
									
										
										
										Executable file → Normal file
									
								
							
							
						
						| 
		 Before Width: | Height: | Size: 505 B After Width: | Height: | Size: 505 B  | 
							
								
								
									
										0
									
								
								htdocs/gfx/openwebrx-scale-background.png
									
									
									
									
									
										
										
										Executable file → Normal file
									
								
							
							
						
						| 
		 Before Width: | Height: | Size: 64 KiB After Width: | Height: | Size: 64 KiB  | 
							
								
								
									
										0
									
								
								htdocs/gfx/openwebrx-top-logo.png
									
									
									
									
									
										
										
										Executable file → Normal file
									
								
							
							
						
						| 
		 Before Width: | Height: | Size: 16 KiB After Width: | Height: | Size: 16 KiB  | 
							
								
								
									
										0
									
								
								htdocs/gfx/openwebrx-top-photo.jpg
									
									
									
									
									
										
										
										Executable file → Normal file
									
								
							
							
						
						| 
		 Before Width: | Height: | Size: 125 KiB After Width: | Height: | Size: 125 KiB  | 
							
								
								
									
										51
									
								
								htdocs/index.wrx
									
									
									
									
									
										
										
										Executable file → Normal file
									
								
							
							
						
						@@ -1,31 +1,35 @@
 | 
			
		||||
<!DOCTYPE HTML>
 | 
			
		||||
<!--
 | 
			
		||||
OpenWebRX (c) Copyright 2013-2014 Andras Retzler <randras@sdr.hu>
 | 
			
		||||
 | 
			
		||||
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 <randras@sdr.hu>
 | 
			
		||||
 | 
			
		||||
    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 <http://www.gnu.org/licenses/>.
 | 
			
		||||
 | 
			
		||||
    You should have received a copy of the GNU General Public License
 | 
			
		||||
    along with OpenWebRX. If not, see <http://www.gnu.org/licenses/>.
 | 
			
		||||
-->
 | 
			
		||||
<html>
 | 
			
		||||
	<head>
 | 
			
		||||
		<title>OpenWebRX | Open Source Web-based SDR for everyone!</title>
 | 
			
		||||
		<title>OpenWebRX | Open Source SDR Web App for Everyone!</title>
 | 
			
		||||
		<script type="text/javascript">
 | 
			
		||||
			//Local variables
 | 
			
		||||
			client_id="%[CLIENT_ID]";
 | 
			
		||||
			ws_url="%[WS_URL]";
 | 
			
		||||
			rx_photo_height=%[RX_PHOTO_HEIGHT];
 | 
			
		||||
			var audio_buffering_fill_to=%[AUDIO_BUFSIZE];
 | 
			
		||||
		</script>
 | 
			
		||||
		<script src="sdr.js"></script>
 | 
			
		||||
		<script src="openwebrx.js"></script>
 | 
			
		||||
		<link rel="stylesheet" type="text/css" href="openwebrx.css" />
 | 
			
		||||
		<meta charset="utf-8">
 | 
			
		||||
@@ -50,6 +54,13 @@ This file is part of OpenWebRX.
 | 
			
		||||
				<a id="openwebrx-rx-details-arrow-up" onclick="toggle_rx_photo();"><img src="gfx/openwebrx-rx-details-arrow-up.png" /></a>
 | 
			
		||||
				<a id="openwebrx-rx-details-arrow-down" onclick="toggle_rx_photo();"><img src="gfx/openwebrx-rx-details-arrow.png" /></a>
 | 
			
		||||
			</div>
 | 
			
		||||
			<section id="openwebrx-main-buttons">
 | 
			
		||||
				<ul>
 | 
			
		||||
					<li onmouseup="toggle_panel('openwebrx-panel-status');"><img src="gfx/openwebrx-panel-status.png" /><br/>Status</li>
 | 
			
		||||
					<li onmouseup="toggle_panel('openwebrx-panel-log');"><img  src="gfx/openwebrx-panel-log.png" /><br/>Log</li>
 | 
			
		||||
					<li onmouseup="toggle_panel('openwebrx-panel-receiver');"><img src="gfx/openwebrx-panel-receiver.png" /><br/>Receiver</li>
 | 
			
		||||
				</ul>
 | 
			
		||||
			</section>
 | 
			
		||||
		</div>
 | 
			
		||||
	</div>
 | 
			
		||||
	<div id="webrx-main-container">
 | 
			
		||||
@@ -61,7 +72,7 @@ This file is part of OpenWebRX.
 | 
			
		||||
				<!-- add canvas here by javascript -->
 | 
			
		||||
			</div>
 | 
			
		||||
			<div id="openwebrx-panels-container">
 | 
			
		||||
				<div class="openwebrx-panel" data-panel-name="client-params" data-panel-pos="right" data-panel-order="0" data-panel-size="215,70">
 | 
			
		||||
				<div class="openwebrx-panel" id="openwebrx-panel-receiver" data-panel-name="client-params" data-panel-pos="right" data-panel-order="0" data-panel-size="215,70">
 | 
			
		||||
					<div id="webrx-actual-freq">---.--- MHz</div>
 | 
			
		||||
					<div id="webrx-mouse-freq">---.--- MHz</div>
 | 
			
		||||
					<!--<div class="openwebrx-button" onclick="ws.send('SET mod=wfm');" >WFM</div>-->
 | 
			
		||||
@@ -71,17 +82,23 @@ This file is part of OpenWebRX.
 | 
			
		||||
					<div class="openwebrx-button" onclick="demodulator_analog_replace('usb');">USB</div>
 | 
			
		||||
					<div class="openwebrx-button" onclick="demodulator_analog_replace('cw');">CW</div>
 | 
			
		||||
				</div>
 | 
			
		||||
				<div class="openwebrx-panel" id="webrx-config" data-panel-name="debug" data-panel-pos="left" data-panel-order="0" data-panel-size="585,130">
 | 
			
		||||
					<div class="openwebrx-panel-inner">
 | 
			
		||||
						<div id="openwebrx-client-log-title">openwebrx.js (beta) client log </strong><span id="openwebrx-problems"></span></div>
 | 
			
		||||
				<div class="openwebrx-panel" id="openwebrx-panel-log" data-panel-name="debug" data-panel-pos="left" data-panel-order="1" data-panel-size="619,142">
 | 
			
		||||
					<div class="openwebrx-panel-inner" id="openwebrx-log-scroll">
 | 
			
		||||
						<div id="openwebrx-client-log-title">OpenWebRX (beta) client log<span style="color: #ff5900;"></span> </strong><span id="openwebrx-problems"></span></div>
 | 
			
		||||
						Author: <a href="javascript:sendmail2('pi7qtu=alz$pc');">HA7ILM</a>. Please send me bug reports and suggestions.<br/>
 | 
			
		||||
						Client status: <span id="openwebrx-client-status">
 | 
			
		||||
							<span id="openwebrx-audio-sps"></span><br/>
 | 
			
		||||
						<!--Server status: <span id="openwebrx-server-status">no information</span><br/>-->
 | 
			
		||||
						Your client ID is: <em>%[CLIENT_ID]</em><br />
 | 
			
		||||
						<div id="openwebrx-debugdiv"></div>
 | 
			
		||||
					</div>
 | 
			
		||||
				</div>
 | 
			
		||||
				<div class="openwebrx-panel" id="openwebrx-panel-status" data-panel-name="status" data-panel-pos="left" data-panel-order="0" data-panel-size="615,50" data-panel-transparent="true">
 | 
			
		||||
					<div class="openwebrx-progressbar" id="openwebrx-bar-audio-buffer"> <span class="openwebrx-progressbar-text">Audio buffer [0 ms]</span><div class="openwebrx-progressbar-bar"></div></div>
 | 
			
		||||
					<div class="openwebrx-progressbar" id="openwebrx-bar-audio-output"> <span class="openwebrx-progressbar-text">Audio output [0 sps]</span><div class="openwebrx-progressbar-bar"></div></div>
 | 
			
		||||
					<div class="openwebrx-progressbar" id="openwebrx-bar-audio-speed"> <span class="openwebrx-progressbar-text">Audio stream [0 kbps]</span><div class="openwebrx-progressbar-bar"></div></div>
 | 
			
		||||
					<div class="openwebrx-progressbar" id="openwebrx-bar-network-speed"> <span class="openwebrx-progressbar-text">Network speed [0 kbps]</span><div class="openwebrx-progressbar-bar"></div></div>
 | 
			
		||||
					<div class="openwebrx-progressbar" id="openwebrx-bar-server-cpu"> <span class="openwebrx-progressbar-text">Server CPU [0%]</span><div class="openwebrx-progressbar-bar"></div></div>
 | 
			
		||||
					<div class="openwebrx-progressbar" id="openwebrx-bar-clients"> <span class="openwebrx-progressbar-text">Clients [1]</span><div class="openwebrx-progressbar-bar"></div></div>
 | 
			
		||||
				</div>
 | 
			
		||||
				<div class="openwebrx-panel" data-panel-name="client-under-devel" data-panel-pos="none" data-panel-order="0" data-panel-size="245,55" style="background-color: Red;">
 | 
			
		||||
					<span style="font-size: 15pt; font-weight: bold;">Under construction</span>
 | 
			
		||||
					<br />We're working on the code right now, so the application might fail.
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										132
									
								
								htdocs/openwebrx.css
									
									
									
									
									
										
										
										Executable file → Normal file
									
								
							
							
						
						@@ -1,20 +1,22 @@
 | 
			
		||||
/*
 | 
			
		||||
OpenWebRX (c) Copyright 2013-2014 Andras Retzler <randras@sdr.hu>
 | 
			
		||||
 | 
			
		||||
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 <randras@sdr.hu>
 | 
			
		||||
 | 
			
		||||
    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 <http://www.gnu.org/licenses/>.
 | 
			
		||||
 | 
			
		||||
    You should have received a copy of the GNU General Public License
 | 
			
		||||
    along with OpenWebRX. If not, see <http://www.gnu.org/licenses/>.
 | 
			
		||||
*/
 | 
			
		||||
 | 
			
		||||
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;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										379
									
								
								htdocs/openwebrx.js
									
									
									
									
									
										
										
										Executable file → Normal file
									
								
							
							
						
						@@ -1,21 +1,23 @@
 | 
			
		||||
/*
 | 
			
		||||
 | 
			
		||||
OpenWebRX (c) Copyright 2013-2014 Andras Retzler <randras@sdr.hu>
 | 
			
		||||
	This file is part of OpenWebRX, 
 | 
			
		||||
	an open-source SDR receiver software with a web UI.
 | 
			
		||||
	Copyright (c) 2013-2015 by Andras Retzler <randras@sdr.hu>
 | 
			
		||||
 | 
			
		||||
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 <http://www.gnu.org/licenses/>.
 | 
			
		||||
    You should have received a copy of the GNU Affero General Public License
 | 
			
		||||
    along with this program.  If not, see <http://www.gnu.org/licenses/>.
 | 
			
		||||
 | 
			
		||||
"""
 | 
			
		||||
 | 
			
		||||
*/
 | 
			
		||||
 | 
			
		||||
@@ -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<text_measured.width/2)
 | 
			
		||||
				{ //and if it would be clipped off the screen
 | 
			
		||||
					if(scale_px_from_freq(marker_hz+spacing.smallbw*spacing.ratio,range)-text_measured.width>=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<text_measured.width/2) )
 | 
			
		||||
			{ //if this is the first overall marker when zoomed out...                  and if it would be clipped off the screen...
 | 
			
		||||
				if(scale_px_from_freq(marker_hz+spacing.smallbw*spacing.ratio,range)-text_measured.width>=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.ratio<marker_hz)  
 | 
			
		||||
			{ //if this is the last overall marker when zoomed out
 | 
			
		||||
				if(x>window.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.ratio<marker_hz) && (x>window.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;i<waterfall_i16.length;i++) waterfall_f32[i]=waterfall_i16[i+COMPRESS_FFT_PAD_N]/100;
 | 
			
		||||
			waterfall_add_queue(waterfall_f32);
 | 
			
		||||
		}
 | 
			
		||||
	} else if(firstChars=="MSG")
 | 
			
		||||
	{
 | 
			
		||||
		/*try
 | 
			
		||||
@@ -999,18 +1012,36 @@ function on_ws_recv(evt)
 | 
			
		||||
						waterfall_init();
 | 
			
		||||
						break;					
 | 
			
		||||
					case "bandwidth":
 | 
			
		||||
						bandwidth=parseInt(param[1])
 | 
			
		||||
						bandwidth=parseInt(param[1]);
 | 
			
		||||
						break;		
 | 
			
		||||
					case "center_freq":
 | 
			
		||||
						center_freq=parseInt(param[1])
 | 
			
		||||
						center_freq=parseInt(param[1]); //there was no ; and it was no problem... why?
 | 
			
		||||
						break;
 | 
			
		||||
					case "fft_size":
 | 
			
		||||
						fft_size=parseInt(param[1])
 | 
			
		||||
						fft_size=parseInt(param[1]);
 | 
			
		||||
						break;
 | 
			
		||||
					case "fft_fps":
 | 
			
		||||
						fft_fps=parseInt(param[1])
 | 
			
		||||
						fft_fps=parseInt(param[1]);
 | 
			
		||||
						break;
 | 
			
		||||
					case "audio_compression":
 | 
			
		||||
						audio_compression=param[1];
 | 
			
		||||
						divlog( "Audio stream is "+ ((audio_compression=="adpcm")?"compressed":"uncompressed")+"." )
 | 
			
		||||
						break;
 | 
			
		||||
					case "fft_compression":
 | 
			
		||||
						fft_compression=param[1];
 | 
			
		||||
						divlog( "FFT stream is "+ ((fft_compression=="adpcm")?"compressed":"uncompressed")+"." )
 | 
			
		||||
						break;
 | 
			
		||||
					case "cpu_usage":
 | 
			
		||||
						var server_cpu_usage=parseInt(param[1]);
 | 
			
		||||
						progressbar_set(e("openwebrx-bar-server-cpu"),server_cpu_usage/100,"Server CPU ["+param[1]+"%]",server_cpu_usage>85);
 | 
			
		||||
						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="<span class=\"webrx-error\">"+what+"</span>";
 | 
			
		||||
	is_error=!!is_error;
 | 
			
		||||
	was_error |= is_error;
 | 
			
		||||
	if(is_error) 
 | 
			
		||||
	{
 | 
			
		||||
		what="<span class=\"webrx-error\">"+what+"</span>";
 | 
			
		||||
		if(e("openwebrx-panel-log").openwebrxHidden) toggle_panel("openwebrx-panel-log"); //show panel if any error is present
 | 
			
		||||
	}
 | 
			
		||||
	e("openwebrx-debugdiv").innerHTML+=what+"<br />";
 | 
			
		||||
	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;i<data.length;i++)
 | 
			
		||||
	data[i]*=gain_value;
 | 
			
		||||
	return data;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
function audio_prepare(data)
 | 
			
		||||
{
 | 
			
		||||
	
 | 
			
		||||
	//audio_rebuffer.push(sdrjs.ConvertI16_F(data));//no resampling
 | 
			
		||||
	//audio_rebuffer.push(audio_resampler.process(sdrjs.ConvertI16_F(data)));//resampling without ADPCM
 | 
			
		||||
	if(audio_compression=="none")
 | 
			
		||||
		audio_rebuffer.push(audio_resampler.process(gain_ff(0.9,sdrjs.ConvertI16_F(data))));//resampling without ADPCM
 | 
			
		||||
	else if(audio_compression=="adpcm")
 | 
			
		||||
		audio_rebuffer.push(audio_resampler.process(gain_ff(0.9,sdrjs.ConvertI16_F(audio_codec.decode(data))))); //resampling & ADPCM
 | 
			
		||||
	else return;
 | 
			
		||||
 | 
			
		||||
	//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_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;i<data.length;i++) f32data[i]=data[i]/32768; //convert_i16_f
 | 
			
		||||
	data=audio_resampler.process(f32data);
 | 
			
		||||
	console.log(data,data.length,original_data_length);
 | 
			
		||||
	if(data.length==0) return;
 | 
			
		||||
	if(audio_last_output_offset+data.length<=audio_buffer_size)
 | 
			
		||||
	{	//array fits into output buffer
 | 
			
		||||
		for(var i=0;i<data.length;i++) audio_last_output_buffer[i+audio_last_output_offset]=data[i]/32768;
 | 
			
		||||
		for(var i=0;i<data.length;i++) audio_last_output_buffer[i+audio_last_output_offset]=data[i];
 | 
			
		||||
		audio_last_output_offset+=data.length;
 | 
			
		||||
		//console.log("fits into; offset="+audio_last_output_offset.toString());
 | 
			
		||||
		console.log("fits into; offset="+audio_last_output_offset.toString());
 | 
			
		||||
		if(audio_last_output_offset==audio_buffer_size) dopush();
 | 
			
		||||
	}
 | 
			
		||||
	else
 | 
			
		||||
	{	//array is larger than the remaining space in the output buffer
 | 
			
		||||
	{	//array is larger than the remaining space in the output buffer		
 | 
			
		||||
		var copied=audio_buffer_size-audio_last_output_offset;
 | 
			
		||||
		var remain=data.length-copied;
 | 
			
		||||
		for(var i=0;i<audio_buffer_size-audio_last_output_offset;i++) //fill the remaining space in the output buffer
 | 
			
		||||
			audio_last_output_buffer[i+audio_last_output_offset]=data[i]/32768;
 | 
			
		||||
			audio_last_output_buffer[i+audio_last_output_offset]=data[i];///32768;
 | 
			
		||||
		dopush();//push the output buffer and create a new one
 | 
			
		||||
		//console.log("larger than; copied half: "+copied.toString()+", now at: "+audio_last_output_offset.toString());
 | 
			
		||||
		console.log("larger than; copied half: "+copied.toString()+", now at: "+audio_last_output_offset.toString());
 | 
			
		||||
		for(var i=0;i<remain;i++) //copy the remaining input samples to the new output buffer
 | 
			
		||||
			audio_last_output_buffer[i]=data[i+copied]/32768;
 | 
			
		||||
			audio_last_output_buffer[i]=data[i+copied];///32768;
 | 
			
		||||
		audio_last_output_offset+=remain;
 | 
			
		||||
		//console.log("larger than; remained: "+remain.toString()+", now at: "+audio_last_output_offset.toString());
 | 
			
		||||
		console.log("larger than; remained: "+remain.toString()+", now at: "+audio_last_output_offset.toString());
 | 
			
		||||
	}
 | 
			
		||||
	if(audio_buffering && audio_prepared_buffers.length>audio_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<audio_prepared_buffers.length*audio_buffer_size)
 | 
			
		||||
	we_have_more_than=function(sec){ return sec*audio_context.sampleRate<audio_prepared_buffers.length*audio_buffer_size; }
 | 
			
		||||
	if(we_have_more_than(audio_buffer_maximal_length_sec)) while(we_have_more_than(audio_buffer_decrease_to_on_overrun_sec))
 | 
			
		||||
	{
 | 
			
		||||
		if(!flushed) audio_buffer_progressbar_update();
 | 
			
		||||
		flushed=true;
 | 
			
		||||
		audio_prepared_buffers.shift();
 | 
			
		||||
	}
 | 
			
		||||
	if(flushed) add_problem("audio overrun");
 | 
			
		||||
	//if(flushed) add_problem("audio overrun");
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@@ -1238,7 +1371,8 @@ function audio_init()
 | 
			
		||||
	//https://github.com/grantgalitz/XAudioJS/blob/master/XAudioServer.js
 | 
			
		||||
	//audio_resampler = new Resampler(audio_received_sample_rate, audio_context.sampleRate, 1, audio_buffer_size, true);
 | 
			
		||||
	//audio_input_buffer_size = audio_buffer_size*(audio_received_sample_rate/audio_context.sampleRate);
 | 
			
		||||
	webrx_set_param("audio_rate",audio_context.sampleRate); //Don't try to resample
 | 
			
		||||
	webrx_set_param("audio_rate",audio_context.sampleRate); //Don't try to resample //TODO remove this
 | 
			
		||||
 | 
			
		||||
	window.setInterval(audio_flush,audio_flush_interval_ms);
 | 
			
		||||
	divlog('Web Audio API succesfully initialized, sample rate: '+audio_context.sampleRate.toString()+ " sps");
 | 
			
		||||
	/*audio_source=audio_context.createBufferSource();
 | 
			
		||||
@@ -1246,6 +1380,14 @@ function audio_init()
 | 
			
		||||
	audio_source.buffer = buffer;
 | 
			
		||||
	audio_source.noteOn(0);*/
 | 
			
		||||
	demodulator_analog_replace('nfm'); //needs audio_context.sampleRate to exist
 | 
			
		||||
	//hide log panel in a second (if user has not hidden it yet)
 | 
			
		||||
	window.setTimeout(function(){
 | 
			
		||||
		if(typeof e("openwebrx-panel-log").openwebrxHidden == "undefined" && !was_error)
 | 
			
		||||
		{
 | 
			
		||||
			animate(e("openwebrx-panel-log"),"opacity","",1,0,0.9,1000,60);
 | 
			
		||||
			window.setTimeout(function(){toggle_panel("openwebrx-panel-log");e("openwebrx-panel-log").style.opacity="1";},1200)
 | 
			
		||||
		}
 | 
			
		||||
	},1000);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
function on_ws_closed()
 | 
			
		||||
@@ -1267,11 +1409,11 @@ String.prototype.startswith=function(str){ return this.indexOf(str) == 0; }; //h
 | 
			
		||||
 | 
			
		||||
function open_websocket()
 | 
			
		||||
{
 | 
			
		||||
	if(ws_url.startswith("ws://localhost:")&&window.location.hostname!="127.0.0.1"&&window.location.hostname!="localhost")
 | 
			
		||||
	{ 
 | 
			
		||||
		divlog("Server administrator should set <em>server_hostname</em> correctly, because it is left as <em>\"localhost\"</em>. Now guessing hostname from page URL.",1); 
 | 
			
		||||
		ws_url="ws://"+(window.location.origin.split("://")[1])+"/ws/"; //guess automatically
 | 
			
		||||
	}
 | 
			
		||||
	//if(ws_url.startswith("ws://localhost:")&&window.location.hostname!="127.0.0.1"&&window.location.hostname!="localhost")
 | 
			
		||||
	//{ 
 | 
			
		||||
		//divlog("Server administrator should set <em>server_hostname</em> correctly, because it is left as <em>\"localhost\"</em>. Now guessing hostname from page URL.",1); 
 | 
			
		||||
		ws_url="ws://"+(window.location.origin.split("://")[1])+"/ws/"; //guess automatically -> now default behaviour
 | 
			
		||||
	//}
 | 
			
		||||
	if (!("WebSocket" in window)) 
 | 
			
		||||
		divlog("Your browser does not support WebSocket, which is required for WebRX to run. Please upgrade to a HTML5 compatible browser.");
 | 
			
		||||
	ws = new WebSocket(ws_url+client_id);
 | 
			
		||||
@@ -1293,14 +1435,16 @@ function open_websocket()
 | 
			
		||||
//var color_scale=[ 0x000000FF, 0xff5656ff, 0xffffffff];
 | 
			
		||||
 | 
			
		||||
//2014-04-22
 | 
			
		||||
var color_scale=[0x2e6893ff, 0x69a5d0ff, 0x214b69ff, 0x9dc4e0ff,  0xfff775ff, 0xff8a8aff, 0xb20000ff];
 | 
			
		||||
var color_scale=[0x000000ff,0x2e6893ff, 0x69a5d0ff, 0x214b69ff, 0x9dc4e0ff,  0xfff775ff, 0xff8a8aff, 0xb20000ff]
 | 
			
		||||
//2015-04-10
 | 
			
		||||
//var color_scale=[0x112634ff,0x4991c6ff,0x18364cff,0x9dc4e0ff,0xfff775ff,0xff9f60,0xff4d4dff,0x8d0000ff];
 | 
			
		||||
 | 
			
		||||
function waterfall_mkcolor(db_value)
 | 
			
		||||
{
 | 
			
		||||
	min_value=-100; //in dB
 | 
			
		||||
	max_value=10
 | 
			
		||||
	if(db_value<min_value) db_value=min_value
 | 
			
		||||
	if(db_value>max_value) db_value=max_value
 | 
			
		||||
	min_value=-115; //in dB
 | 
			
		||||
	max_value=0;
 | 
			
		||||
	if(db_value<min_value) db_value=min_value;
 | 
			
		||||
	if(db_value>max_value) db_value=max_value;
 | 
			
		||||
	full_scale=max_value-min_value;
 | 
			
		||||
	relative_value=db_value-min_value;
 | 
			
		||||
	value_percent=relative_value/full_scale;
 | 
			
		||||
@@ -1503,11 +1647,12 @@ function waterfall_add(data)
 | 
			
		||||
			oneline_image.data[x*4+i] = ((color>>>0)>>((3-i)*8))&0xff;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
	
 | 
			
		||||
	//Draw image
 | 
			
		||||
	canvas_context.putImageData(oneline_image, 0, canvas_actual_line--);
 | 
			
		||||
	shift_canvases();
 | 
			
		||||
	if(canvas_actual_line<0) add_canvas();
 | 
			
		||||
	
 | 
			
		||||
	//divlog("Drawn FFT");
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@@ -1525,10 +1670,17 @@ function waterfall_shift()
 | 
			
		||||
 | 
			
		||||
function check_top_bar_congestion()
 | 
			
		||||
{
 | 
			
		||||
	var wt=e("webrx-rx-title");
 | 
			
		||||
	var tl=e("webrx-ha5kfu-top-logo");
 | 
			
		||||
	if(wt.offsetLeft+wt.offsetWidth>tl.offsetLeft-20) tl.style.display="none";
 | 
			
		||||
	else tl.style.display="block";
 | 
			
		||||
	var rmf=function(x){ return x.offsetLeft+x.offsetWidth; };
 | 
			
		||||
	var wet=e("webrx-rx-title");
 | 
			
		||||
	var wed=e("webrx-rx-desc");
 | 
			
		||||
	var rightmost=Math.max(rmf(wet),rmf(wed));
 | 
			
		||||
	var tl=e("openwebrx-main-buttons");
 | 
			
		||||
 | 
			
		||||
	[wet, wed].map(function(what) { 
 | 
			
		||||
		if(rmf(what)>tl.offsetLeft-20) what.style.opacity=what.style.opacity="0"; 
 | 
			
		||||
		else wet.style.opacity=wed.style.opacity="1";
 | 
			
		||||
	});
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
function openwebrx_resize() 
 | 
			
		||||
@@ -1546,6 +1698,7 @@ function openwebrx_init()
 | 
			
		||||
	place_panels();
 | 
			
		||||
	window.setTimeout(function(){window.setInterval(debug_audio,1000);},1000);
 | 
			
		||||
	window.addEventListener("resize",openwebrx_resize);
 | 
			
		||||
	check_top_bar_congestion();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/*
 | 
			
		||||
@@ -1577,18 +1730,33 @@ function debug_audio()
 | 
			
		||||
	audio_debug_time_since_last_call=(time_now-audio_debug_time_last_start)/1000;
 | 
			
		||||
	audio_debug_time_last_start=time_now; //now
 | 
			
		||||
	audio_debug_time_taken=(time_now-audio_debug_time_start)/1000;
 | 
			
		||||
	e("openwebrx-audio-sps").innerHTML=
 | 
			
		||||
		"audio recv. at "+(audio_buffer_current_size_debug/audio_debug_time_since_last_call).toFixed(0)+" sps ("+
 | 
			
		||||
		(audio_buffer_all_size_debug/audio_debug_time_taken).toFixed(1)+" sps avg.), feed at "+
 | 
			
		||||
		((audio_buffer_current_count_debug*audio_buffer_size)/audio_debug_time_taken).toFixed(1)+" sps output";
 | 
			
		||||
	kbps_mult=(audio_compression=="adpcm")?8:16;
 | 
			
		||||
	//e("openwebrx-audio-sps").innerHTML=
 | 
			
		||||
	//	((audio_compression=="adpcm")?"ADPCM compressed":"uncompressed")+" audio downlink:<br/> "+(audio_buffer_current_size_debug*kbps_mult/audio_debug_time_since_last_call).toFixed(0)+" kbps ("+
 | 
			
		||||
	//	(audio_buffer_all_size_debug*kbps_mult/audio_debug_time_taken).toFixed(1)+" kbps avg.), feed at "+
 | 
			
		||||
	//	((audio_buffer_current_count_debug*audio_buffer_size)/audio_debug_time_taken).toFixed(1)+" sps output";
 | 
			
		||||
 | 
			
		||||
	var audio_speed_value=audio_buffer_current_size_debug*kbps_mult/audio_debug_time_since_last_call;
 | 
			
		||||
	progressbar_set(e("openwebrx-bar-audio-speed"),audio_speed_value/500000,"Audio stream ["+(audio_speed_value/1000).toFixed(0)+" kbps]",false);	
 | 
			
		||||
 | 
			
		||||
	var audio_output_value=(audio_buffer_current_count_debug*audio_buffer_size)/audio_debug_time_taken;
 | 
			
		||||
	progressbar_set(e("openwebrx-bar-audio-output"),audio_output_value/55000,"Audio output ["+(audio_output_value/1000).toFixed(1)+" ksps]",audio_output_value>55000||audio_output_value<10000);	
 | 
			
		||||
 | 
			
		||||
	audio_buffer_progressbar_update();
 | 
			
		||||
 | 
			
		||||
	var network_speed_value=debug_ws_data_received/audio_debug_time_taken;
 | 
			
		||||
	progressbar_set(e("openwebrx-bar-network-speed"),network_speed_value*8/2000,"Network usage ["+(network_speed_value*8).toFixed(1)+" kbps]",false);	
 | 
			
		||||
 | 
			
		||||
	audio_buffer_current_size_debug=0;
 | 
			
		||||
 | 
			
		||||
	if(waterfall_measure_minmax) waterfall_measure_minmax_print();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// ========================================================
 | 
			
		||||
// =======================  PANELS  =======================
 | 
			
		||||
// ========================================================
 | 
			
		||||
 | 
			
		||||
panel_margin=10;
 | 
			
		||||
panel_margin=5.9;
 | 
			
		||||
 | 
			
		||||
function pop_bottommost_panel(from)
 | 
			
		||||
{
 | 
			
		||||
@@ -1608,8 +1776,19 @@ function pop_bottommost_panel(from)
 | 
			
		||||
	return to_return;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
function toggle_panel(what)
 | 
			
		||||
{
 | 
			
		||||
	var item=e(what);
 | 
			
		||||
	if(item.openwebrxDisableClick) return;
 | 
			
		||||
	item.openwebrxHidden=!item.openwebrxHidden;
 | 
			
		||||
	place_panels();
 | 
			
		||||
	item.openwebrxDisableClick=true;
 | 
			
		||||
	window.setTimeout(function(){item.openwebrxDisableClick=false;},100);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
function place_panels()
 | 
			
		||||
{
 | 
			
		||||
	var hoffset=0; //added this because the first panel should not have such great gap below
 | 
			
		||||
	var left_col=[];
 | 
			
		||||
	var right_col=[];
 | 
			
		||||
	var plist=e("openwebrx-panels-container").children;
 | 
			
		||||
@@ -1618,33 +1797,61 @@ function place_panels()
 | 
			
		||||
		c=plist[i];
 | 
			
		||||
		if(c.className=="openwebrx-panel")
 | 
			
		||||
		{
 | 
			
		||||
			if(c.openwebrxHidden) 
 | 
			
		||||
			{
 | 
			
		||||
				c.style.display="none";
 | 
			
		||||
				continue;
 | 
			
		||||
			}
 | 
			
		||||
			c.style.display="block";
 | 
			
		||||
			c.openwebrxPanelTransparent=(!!c.dataset.panelTransparent);
 | 
			
		||||
			newSize=c.dataset.panelSize.split(",");
 | 
			
		||||
			if (c.dataset.panelPos=="left") { left_col.push(c); }
 | 
			
		||||
			else if(c.dataset.panelPos=="right") { right_col.push(c); }
 | 
			
		||||
			c.style.width=newSize[0]+"px";
 | 
			
		||||
			c.style.height=newSize[1]+"px";
 | 
			
		||||
			c.style.margin=panel_margin.toString()+"px";
 | 
			
		||||
			if(!c.openwebrxPanelTransparent) c.style.margin=panel_margin.toString()+"px";
 | 
			
		||||
			else c.style.marginLeft=panel_margin.toString()+"px";
 | 
			
		||||
			c.openwebrxPanelWidth=parseInt(newSize[0]);			
 | 
			
		||||
			c.openwebrxPanelHeight=parseInt(newSize[1]);
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	y=0;
 | 
			
		||||
	y=hoffset; //was y=0 before hoffset
 | 
			
		||||
	while(left_col.length>0)
 | 
			
		||||
	{
 | 
			
		||||
		p=pop_bottommost_panel(left_col);
 | 
			
		||||
		p.style.left="0px";
 | 
			
		||||
		p.style.bottom=y.toString()+"px";
 | 
			
		||||
		p.style.visibility="visible";
 | 
			
		||||
		y+=p.openwebrxPanelHeight+3*panel_margin;
 | 
			
		||||
		y+=p.openwebrxPanelHeight+((p.openwebrxPanelTransparent)?0:3)*panel_margin;
 | 
			
		||||
	}
 | 
			
		||||
	y=0;
 | 
			
		||||
	y=hoffset;
 | 
			
		||||
	while(right_col.length>0)
 | 
			
		||||
	{
 | 
			
		||||
		p=pop_bottommost_panel(right_col);
 | 
			
		||||
		p.style.right="10px";
 | 
			
		||||
		p.style.right=(e("webrx-canvas-container").offsetWidth-e("webrx-canvas-container").clientWidth).toString()+"px"; //get scrollbar width
 | 
			
		||||
		p.style.bottom=y.toString()+"px";
 | 
			
		||||
		p.style.visibility="visible";
 | 
			
		||||
		y+=p.openwebrxPanelHeight+3*panel_margin;
 | 
			
		||||
		y+=p.openwebrxPanelHeight+((p.openwebrxPanelTransparent)?0:3)*panel_margin;
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
function progressbar_set(obj,val,text,over)
 | 
			
		||||
{
 | 
			
		||||
	if (val<0.05) val=0;
 | 
			
		||||
	if (val>1) val=1;
 | 
			
		||||
	var innerBar=null;
 | 
			
		||||
	var innerText=null;
 | 
			
		||||
	for(var i=0;i<obj.children.length;i++)	
 | 
			
		||||
	{
 | 
			
		||||
		if(obj.children[i].className=="openwebrx-progressbar-text") innerText=obj.children[i];
 | 
			
		||||
		else if(obj.children[i].className=="openwebrx-progressbar-bar") innerBar=obj.children[i];
 | 
			
		||||
	}
 | 
			
		||||
	if(innerBar==null) return;
 | 
			
		||||
	//.h: function animate(object,style_name,unit,from,to,accel,time_ms,fps,to_exec)
 | 
			
		||||
	animate(innerBar,"width","px",innerBar.clientWidth,val*obj.clientWidth,0.7,700,60);
 | 
			
		||||
	//innerBar.style.width=(val*100).toFixed(0)+"%";
 | 
			
		||||
	innerBar.style.backgroundColor=(over)?"#ff6262":"#00aba6";
 | 
			
		||||
	if(innerText==null) return;
 | 
			
		||||
	innerText.innerHTML=text;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										94
									
								
								htdocs/retry.html
									
									
									
									
									
										Normal file
									
								
							
							
						
						@@ -0,0 +1,94 @@
 | 
			
		||||
<html>
 | 
			
		||||
<!--
 | 
			
		||||
 | 
			
		||||
	This file is part of OpenWebRX, 
 | 
			
		||||
	an open-source SDR receiver software with a web UI.
 | 
			
		||||
	Copyright (c) 2013-2015 by Andras Retzler <randras@sdr.hu>
 | 
			
		||||
 | 
			
		||||
    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.
 | 
			
		||||
 | 
			
		||||
    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 Affero General Public License
 | 
			
		||||
    along with this program.  If not, see <http://www.gnu.org/licenses/>.
 | 
			
		||||
 | 
			
		||||
-->
 | 
			
		||||
<head><title>OpenWebRX</title><meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
 | 
			
		||||
<style>
 | 
			
		||||
html, body
 | 
			
		||||
{
 | 
			
		||||
	font-family: "DejaVu Sans", Verdana, Geneva, sans-serif;
 | 
			
		||||
	width: 100%;
 | 
			
		||||
	text-align: center;
 | 
			
		||||
	margin: 0;
 | 
			
		||||
	padding: 0;
 | 
			
		||||
}
 | 
			
		||||
img.logo
 | 
			
		||||
{ 
 | 
			
		||||
	margin-top: 120px;
 | 
			
		||||
}
 | 
			
		||||
div.frame
 | 
			
		||||
{
 | 
			
		||||
	text-align: left;
 | 
			
		||||
	margin:0px auto;
 | 
			
		||||
	width: 800px;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
div.panel
 | 
			
		||||
{
 | 
			
		||||
	text-align: center;
 | 
			
		||||
	background-color:#777777; 
 | 
			
		||||
	border-radius: 15px;
 | 
			
		||||
	padding: 12px;
 | 
			
		||||
	font-weight: bold;
 | 
			
		||||
	color: White;
 | 
			
		||||
	font-size: 13pt;
 | 
			
		||||
	/*text-shadow: 1px 1px 4px #444;*/
 | 
			
		||||
	font-family: sans;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
div.alt
 | 
			
		||||
{
 | 
			
		||||
	font-size: 10pt;
 | 
			
		||||
	padding-top: 10px;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
body div a
 | 
			
		||||
{
 | 
			
		||||
	color: #5ca8ff;
 | 
			
		||||
	text-shadow: none;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
span.browser
 | 
			
		||||
{
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
</style>
 | 
			
		||||
<script>
 | 
			
		||||
var irt = function (s,n) {return s.replace(/[a-zA-Z]/g,function(c){return String.fromCharCode((c>="a"?97:65)<=(c=c.charCodeAt(0)-n)?c:c+26);});}
 | 
			
		||||
var sendmail2 = function (s) { window.location.href="mailto:"+irt(s.replace("=",String.fromCharCode(0100)).replace("$","."),8); }
 | 
			
		||||
window.addEventListener("load",function(){rs=document.getElementById("reconnect-secs"); rt=document.getElementById("reconnect-text"); cnt=29;window.setInterval(function(){if(cnt<=-1) window.location.href=window.location.href.split("retry.")[0]; else if(cnt==0) {rt.innerHTML="Reconnecting..."; cnt--;} else rs.innerHTML=(cnt--).toString();},1000);},false);
 | 
			
		||||
</script>
 | 
			
		||||
 | 
			
		||||
</head>
 | 
			
		||||
<body>
 | 
			
		||||
 | 
			
		||||
<div class="frame">
 | 
			
		||||
	<img class="logo" src="gfx/openwebrx-logo-big.png" style="height: 60px;"/>
 | 
			
		||||
	<div class="panel">
 | 
			
		||||
		There are no client slots left on this server.
 | 
			
		||||
		<div class="alt">
 | 
			
		||||
			Please wait until a client disconnects.<br /><span id="reconnect-text">We will try to reconnect in <span id="reconnect-secs">30</span> seconds...</span> 
 | 
			
		||||
		</div>
 | 
			
		||||
	</div>
 | 
			
		||||
</div>
 | 
			
		||||
</body>
 | 
			
		||||
</html>
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										11683
									
								
								htdocs/sdr.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						@@ -1,21 +1,23 @@
 | 
			
		||||
<html>
 | 
			
		||||
<!--
 | 
			
		||||
OpenWebRX (c) Copyright 2013 Andras Retzler <ha7ilm@sdr.hu>
 | 
			
		||||
 | 
			
		||||
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 <randras@sdr.hu>
 | 
			
		||||
 | 
			
		||||
    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 <http://www.gnu.org/licenses/>.
 | 
			
		||||
 | 
			
		||||
    You should have received a copy of the GNU General Public License
 | 
			
		||||
    along with OpenWebRX. If not, see <http://www.gnu.org/licenses/>.
 | 
			
		||||
-->
 | 
			
		||||
<head><title>OpenWebRX</title><meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
 | 
			
		||||
<style>
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										237
									
								
								openwebrx.py
									
									
									
									
									
								
							
							
						
						@@ -1,31 +1,26 @@
 | 
			
		||||
#!/usr/bin/python2
 | 
			
		||||
print "" # python2.7 is required to run OpenWebRX instead of python3. Please run me by: python2 openwebrx.py
 | 
			
		||||
"""
 | 
			
		||||
OpenWebRX: open-source web based SDR for everyone!
 | 
			
		||||
 | 
			
		||||
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 <randras@sdr.hu>
 | 
			
		||||
 | 
			
		||||
    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 General Public License
 | 
			
		||||
    along with OpenWebRX.  If not, see <http://www.gnu.org/licenses/>.
 | 
			
		||||
 | 
			
		||||
Authors:
 | 
			
		||||
    Andras Retzler, HA7ILM <randras@sdr.hu>
 | 
			
		||||
    You should have received a copy of the GNU Affero General Public License
 | 
			
		||||
    along with this program.  If not, see <http://www.gnu.org/licenses/>.
 | 
			
		||||
 | 
			
		||||
"""
 | 
			
		||||
 | 
			
		||||
# http://www.codeproject.com/Articles/462525/Simple-HTTP-Server-and-Client-in-Python
 | 
			
		||||
# some ideas are used from the artice above
 | 
			
		||||
 | 
			
		||||
sw_version="v0.12+"
 | 
			
		||||
 | 
			
		||||
import os
 | 
			
		||||
import code
 | 
			
		||||
@@ -54,6 +49,11 @@ import rxws
 | 
			
		||||
import uuid
 | 
			
		||||
import config_webrx as cfg
 | 
			
		||||
import signal
 | 
			
		||||
import socket
 | 
			
		||||
 | 
			
		||||
try: import sdrhu
 | 
			
		||||
except: sdrhu=False
 | 
			
		||||
avatar_ctime=""
 | 
			
		||||
 | 
			
		||||
#pypy compatibility
 | 
			
		||||
try: import dl
 | 
			
		||||
@@ -62,7 +62,6 @@ try: import __pypy__
 | 
			
		||||
except: pass
 | 
			
		||||
pypy="__pypy__" in globals()
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def import_all_plugins(directory):
 | 
			
		||||
	for subdir in os.listdir(directory):
 | 
			
		||||
		if os.path.isdir(directory+subdir) and not subdir[0]=="_":
 | 
			
		||||
@@ -80,9 +79,9 @@ def handle_signal(signal, frame):
 | 
			
		||||
	os._exit(1) #not too graceful exit
 | 
			
		||||
 | 
			
		||||
def main():
 | 
			
		||||
	global clients, clients_mutex, pypy
 | 
			
		||||
	global clients, clients_mutex, pypy, lock_try_time, avatar_ctime
 | 
			
		||||
	print
 | 
			
		||||
	print "OpenWebRX - Open Source Web Based SDR for Everyone  | for license see LICENSE file in the package"
 | 
			
		||||
	print "OpenWebRX - Open Source SDR Web App for Everyone!  | for license see LICENSE file in the package"
 | 
			
		||||
	print "_________________________________________________________________________________________________"
 | 
			
		||||
	print 
 | 
			
		||||
	print "Author contact info:    Andras Retzler, HA7ILM <randras@sdr.hu>"
 | 
			
		||||
@@ -123,13 +122,33 @@ def main():
 | 
			
		||||
	#Initialize clients
 | 
			
		||||
	clients=[]
 | 
			
		||||
	clients_mutex=threading.Lock()
 | 
			
		||||
	lock_try_time=0
 | 
			
		||||
 | 
			
		||||
	#Start watchdog thread
 | 
			
		||||
	print "[openwebrx-main] Starting watchdog threads."
 | 
			
		||||
	mutex_test_thread=threading.Thread(target = mutex_test_thread_function, args = ())
 | 
			
		||||
	mutex_test_thread.start()
 | 
			
		||||
	mutex_watchdog_thread=threading.Thread(target = mutex_watchdog_thread_function, args = ())
 | 
			
		||||
	mutex_watchdog_thread.start()
 | 
			
		||||
	
 | 
			
		||||
 | 
			
		||||
	#Start spectrum thread
 | 
			
		||||
	print "[openwebrx-main] Starting spectrum thread."
 | 
			
		||||
	spectrum_thread=threading.Thread(target = spectrum_thread_function, args = ())
 | 
			
		||||
	spectrum_thread.start()
 | 
			
		||||
 | 
			
		||||
	get_cpu_usage()
 | 
			
		||||
	bcastmsg_thread=threading.Thread(target = bcastmsg_thread_function, args = ())
 | 
			
		||||
	bcastmsg_thread.start()
 | 
			
		||||
	
 | 
			
		||||
	#threading.Thread(target = measure_thread_function, args = ()).start()
 | 
			
		||||
 | 
			
		||||
	#Start sdr.hu update thread
 | 
			
		||||
	if sdrhu and cfg.sdrhu_key and cfg.sdrhu_public_listing: 
 | 
			
		||||
		print "[openwebrx-main] Starting sdr.hu update thread..."
 | 
			
		||||
		sdrhu_thread=threading.Thread(target = sdrhu.run, args = ())
 | 
			
		||||
		sdrhu_thread.start()
 | 
			
		||||
		avatar_ctime=str(os.path.getctime("htdocs/gfx/openwebrx-avatar.png"))
 | 
			
		||||
	
 | 
			
		||||
	#Start HTTP thread
 | 
			
		||||
	httpd = MultiThreadHTTPServer(('', cfg.web_port), WebRXHandler)
 | 
			
		||||
@@ -146,23 +165,67 @@ def measure_thread_function():
 | 
			
		||||
		measure_value=0
 | 
			
		||||
		time.sleep(1)
 | 
			
		||||
 | 
			
		||||
def bcastmsg_thread_function():
 | 
			
		||||
	global clients
 | 
			
		||||
	while True:
 | 
			
		||||
		time.sleep(3)
 | 
			
		||||
		try: cpu_usage=get_cpu_usage()
 | 
			
		||||
		except: cpu_usage=0
 | 
			
		||||
		cma("bcastmsg_thread")
 | 
			
		||||
		for i in range(0,len(clients)):
 | 
			
		||||
			clients[i].bcastmsg="MSG cpu_usage={0} clients={1}".format(int(cpu_usage*100),len(clients))
 | 
			
		||||
		cmr()
 | 
			
		||||
 | 
			
		||||
def spectrum_thread_function():
 | 
			
		||||
def mutex_test_thread_function():
 | 
			
		||||
	global clients_mutex, lock_try_time
 | 
			
		||||
	while True:
 | 
			
		||||
		time.sleep(0.5)
 | 
			
		||||
		lock_try_time=time.time()
 | 
			
		||||
		clients_mutex.acquire()
 | 
			
		||||
		clients_mutex.release()
 | 
			
		||||
		lock_try_time=0
 | 
			
		||||
 | 
			
		||||
def cma(what): #clients_mutex acquire
 | 
			
		||||
	global clients_mutex
 | 
			
		||||
	global clients_mutex_locker
 | 
			
		||||
	if not clients_mutex.locked(): clients_mutex_locker = what
 | 
			
		||||
	clients_mutex.acquire()
 | 
			
		||||
 | 
			
		||||
def cmr():
 | 
			
		||||
	global clients_mutex
 | 
			
		||||
	global clients_mutex_locker
 | 
			
		||||
	clients_mutex_locker = None
 | 
			
		||||
	clients_mutex.release()
 | 
			
		||||
 | 
			
		||||
def mutex_watchdog_thread_function():
 | 
			
		||||
	global lock_try_time
 | 
			
		||||
	global clients_mutex_locker
 | 
			
		||||
	global clients_mutex
 | 
			
		||||
	while True:
 | 
			
		||||
		if lock_try_time != 0 and time.time()-lock_try_time > 3.0: 
 | 
			
		||||
			#if 3 seconds pass without unlock
 | 
			
		||||
			print "[openwebrx-watchdog] Mutex unlock timeout. Locker: \""+str(clients_mutex_locker)+"\" Now unlocking..." 
 | 
			
		||||
			clients_mutex.release()
 | 
			
		||||
		time.sleep(0.5)	
 | 
			
		||||
		
 | 
			
		||||
def spectrum_thread_function():
 | 
			
		||||
	global clients
 | 
			
		||||
	dsp=getattr(plugins.dsp,cfg.dsp_plugin).plugin.dsp_plugin()
 | 
			
		||||
	dsp.set_demodulator("fft")
 | 
			
		||||
	dsp.set_samp_rate(cfg.samp_rate)
 | 
			
		||||
	dsp.set_fft_size(cfg.fft_size)
 | 
			
		||||
	dsp.set_fft_fps(cfg.fft_fps)
 | 
			
		||||
	dsp.set_fft_compression(cfg.fft_compression)
 | 
			
		||||
	dsp.set_format_conversion(cfg.format_conversion)
 | 
			
		||||
	sleep_sec=0.87/cfg.fft_fps
 | 
			
		||||
	print "[openwebrx-spectrum] Spectrum thread initialized successfully."
 | 
			
		||||
	dsp.start()
 | 
			
		||||
	print "[openwebrx-spectrum] Spectrum thread started." 
 | 
			
		||||
	bytes_to_read=int(dsp.get_fft_bytes_to_read())
 | 
			
		||||
	while True:
 | 
			
		||||
		data=dsp.read(cfg.fft_size*4)
 | 
			
		||||
		data=dsp.read(bytes_to_read)
 | 
			
		||||
		#print "gotcha",len(data),"bytes of spectrum data via spectrum_thread_function()"
 | 
			
		||||
		clients_mutex.acquire()
 | 
			
		||||
		cma("spectrum_thread")
 | 
			
		||||
		correction=0
 | 
			
		||||
		for i in range(0,len(clients)):
 | 
			
		||||
			i-=correction
 | 
			
		||||
@@ -173,18 +236,17 @@ def spectrum_thread_function():
 | 
			
		||||
					correction+=1
 | 
			
		||||
				else:
 | 
			
		||||
					clients[i].spectrum_queue.put([data]) # add new string by "reference" to all clients
 | 
			
		||||
		clients_mutex.release()
 | 
			
		||||
		cmr()
 | 
			
		||||
	
 | 
			
		||||
def get_client_by_id(client_id, use_mutex=True):
 | 
			
		||||
	global clients_mutex
 | 
			
		||||
	global clients
 | 
			
		||||
	output=-1
 | 
			
		||||
	if use_mutex: clients_mutex.acquire()
 | 
			
		||||
	if use_mutex: cma("get_client_by_id")
 | 
			
		||||
	for i in range(0,len(clients)):
 | 
			
		||||
		if(clients[i].id==client_id):
 | 
			
		||||
			output=i
 | 
			
		||||
			break
 | 
			
		||||
	if use_mutex: clients_mutex.release()
 | 
			
		||||
	if use_mutex: cmr()
 | 
			
		||||
	if output==-1:
 | 
			
		||||
		raise ClientNotFoundException
 | 
			
		||||
	else:
 | 
			
		||||
@@ -195,53 +257,69 @@ def log_client(client, what):
 | 
			
		||||
 | 
			
		||||
def cleanup_clients():
 | 
			
		||||
	# if client doesn't open websocket for too long time, we drop it
 | 
			
		||||
	global clients_mutex
 | 
			
		||||
	global clients
 | 
			
		||||
	clients_mutex.acquire()
 | 
			
		||||
	cma("cleanup_clients")
 | 
			
		||||
	correction=0
 | 
			
		||||
	for i in range(0,len(clients)):
 | 
			
		||||
		i-=correction
 | 
			
		||||
		#print "cleanup_clients:: len(clients)=", len(clients), "i=", i
 | 
			
		||||
		if (not clients[i].ws_started) and (time.time()-clients[i].gen_time)>180:
 | 
			
		||||
		if (not clients[i].ws_started) and (time.time()-clients[i].gen_time)>45:
 | 
			
		||||
			print "[openwebrx] cleanup_clients :: client timeout to open WebSocket"
 | 
			
		||||
			close_client(i, False)
 | 
			
		||||
			correction+=1
 | 
			
		||||
	clients_mutex.release()
 | 
			
		||||
	cmr()
 | 
			
		||||
 | 
			
		||||
def generate_client_id(ip):
 | 
			
		||||
	#add a client
 | 
			
		||||
	global clients
 | 
			
		||||
	global clients_mutex
 | 
			
		||||
	new_client=namedtuple("ClientStruct", "id gen_time ws_started sprectum_queue ip closed")	
 | 
			
		||||
	new_client=namedtuple("ClientStruct", "id gen_time ws_started sprectum_queue ip closed bcastmsg dsp")	
 | 
			
		||||
	new_client.id=md5.md5(str(random.random())).hexdigest()
 | 
			
		||||
	new_client.gen_time=time.time()
 | 
			
		||||
	new_client.ws_started=False # to check whether client has ever tried to open the websocket
 | 
			
		||||
	new_client.spectrum_queue=Queue.Queue(1000)
 | 
			
		||||
	new_client.ip=ip
 | 
			
		||||
	new_client.bcastmsg=""
 | 
			
		||||
	new_client.closed=[False] #byref, not exactly sure if required
 | 
			
		||||
	clients_mutex.acquire()
 | 
			
		||||
	new_client.dsp=None
 | 
			
		||||
	cma("generate_client_id")
 | 
			
		||||
	clients.append(new_client)
 | 
			
		||||
	log_client(new_client,"client added. Clients now: {0}".format(len(clients)))
 | 
			
		||||
	clients_mutex.release()
 | 
			
		||||
	cmr()
 | 
			
		||||
	cleanup_clients()
 | 
			
		||||
	return new_client.id
 | 
			
		||||
 | 
			
		||||
def close_client(i, use_mutex=True):
 | 
			
		||||
	global clients_mutex
 | 
			
		||||
	global clients
 | 
			
		||||
	log_client(clients[i],"client being closed.")
 | 
			
		||||
	if use_mutex: clients_mutex.acquire()
 | 
			
		||||
	if use_mutex: cma("close_client")
 | 
			
		||||
	try:
 | 
			
		||||
		clients[i].dsp.stop()
 | 
			
		||||
	except:
 | 
			
		||||
		exc_type, exc_value, exc_traceback = sys.exc_info()
 | 
			
		||||
		print "[openwebrx] close_client dsp.stop() :: error -",exc_type,exc_value
 | 
			
		||||
		traceback.print_tb(exc_traceback)
 | 
			
		||||
	clients[i].closed[0]=True
 | 
			
		||||
	del clients[i]
 | 
			
		||||
	if use_mutex: clients_mutex.release()
 | 
			
		||||
	
 | 
			
		||||
	if use_mutex: cmr()
 | 
			
		||||
 | 
			
		||||
# http://www.codeproject.com/Articles/462525/Simple-HTTP-Server-and-Client-in-Python
 | 
			
		||||
# some ideas are used from the artice above
 | 
			
		||||
 | 
			
		||||
class WebRXHandler(BaseHTTPRequestHandler):    
 | 
			
		||||
	def proc_read_thread():
 | 
			
		||||
		pass
 | 
			
		||||
 | 
			
		||||
	def send_302(self,what):
 | 
			
		||||
		self.send_response(302)
 | 
			
		||||
		self.send_header('Content-type','text/html')
 | 
			
		||||
		self.send_header("Location", "http://{0}:{1}/{2}".format(cfg.server_hostname,cfg.web_port,what))
 | 
			
		||||
		self.end_headers()
 | 
			
		||||
		self.wfile.write("<html><body><h1>Object moved</h1>Please <a href=\"/{0}\">click here</a> to continue.</body></html>".format(what))
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
	def do_GET(self):
 | 
			
		||||
		global dsp_plugin
 | 
			
		||||
		global clients_mutex
 | 
			
		||||
		self.connection.setsockopt(socket.IPPROTO_TCP, socket.TCP_NODELAY, 1)
 | 
			
		||||
		global dsp_plugin, clients_mutex, clients, avatar_ctime, sw_version
 | 
			
		||||
		rootdir = 'htdocs' 
 | 
			
		||||
		self.path=self.path.replace("..","")
 | 
			
		||||
		path_temp_parts=self.path.split("?")
 | 
			
		||||
@@ -251,19 +329,20 @@ class WebRXHandler(BaseHTTPRequestHandler):
 | 
			
		||||
			if self.path=="/":
 | 
			
		||||
				self.path="/index.wrx"
 | 
			
		||||
			# there's even another cool tip at http://stackoverflow.com/questions/4419650/how-to-implement-timeout-in-basehttpserver-basehttprequesthandler-python
 | 
			
		||||
			#if self.path[:5]=="/lock": cma("do_GET /lock/") # to test mutex_watchdog_thread. Do not uncomment in production environment!
 | 
			
		||||
			if self.path[:4]=="/ws/":
 | 
			
		||||
				try:
 | 
			
		||||
					# ========= WebSocket handshake  =========
 | 
			
		||||
					ws_success=True
 | 
			
		||||
					try:				
 | 
			
		||||
						rxws.handshake(self)
 | 
			
		||||
						clients_mutex.acquire()				
 | 
			
		||||
						cma("do_GET /ws/")				
 | 
			
		||||
						client_i=get_client_by_id(self.path[4:], False)
 | 
			
		||||
						myclient=clients[client_i]
 | 
			
		||||
					except rxws.WebSocketException: ws_success=False
 | 
			
		||||
					except ClientNotFoundException: ws_success=False
 | 
			
		||||
					finally:
 | 
			
		||||
						if clients_mutex.locked(): clients_mutex.release()
 | 
			
		||||
						if clients_mutex.locked(): cmr()
 | 
			
		||||
					if not ws_success:
 | 
			
		||||
						self.send_error(400, 'Bad request.')
 | 
			
		||||
						return
 | 
			
		||||
@@ -280,7 +359,7 @@ class WebRXHandler(BaseHTTPRequestHandler):
 | 
			
		||||
						return
 | 
			
		||||
					myclient.ws_started=True
 | 
			
		||||
					#send default parameters
 | 
			
		||||
					rxws.send(self, "MSG center_freq={0} bandwidth={1} fft_size={2} fft_fps={3} setup".format(str(cfg.center_freq),str(cfg.samp_rate),cfg.fft_size,cfg.fft_fps))
 | 
			
		||||
					rxws.send(self, "MSG center_freq={0} bandwidth={1} fft_size={2} fft_fps={3} audio_compression={4} fft_compression={5} max_clients={6} setup".format(str(cfg.shown_center_freq),str(cfg.samp_rate),cfg.fft_size,cfg.fft_fps,cfg.audio_compression,cfg.fft_compression,cfg.max_clients))
 | 
			
		||||
 | 
			
		||||
					# ========= Initialize DSP =========
 | 
			
		||||
					dsp=getattr(plugins.dsp,cfg.dsp_plugin).plugin.dsp_plugin()
 | 
			
		||||
@@ -288,7 +367,10 @@ class WebRXHandler(BaseHTTPRequestHandler):
 | 
			
		||||
					dsp.set_demodulator("nfm")
 | 
			
		||||
					dsp.set_offset_freq(0)
 | 
			
		||||
					dsp.set_bpf(-4000,4000)
 | 
			
		||||
					dsp.set_audio_compression(cfg.audio_compression)
 | 
			
		||||
					dsp.set_format_conversion(cfg.format_conversion)
 | 
			
		||||
					dsp.start()
 | 
			
		||||
					myclient.dsp=dsp
 | 
			
		||||
					
 | 
			
		||||
					while True:
 | 
			
		||||
						if myclient.closed[0]: 
 | 
			
		||||
@@ -296,15 +378,21 @@ class WebRXHandler(BaseHTTPRequestHandler):
 | 
			
		||||
							break
 | 
			
		||||
 | 
			
		||||
						# ========= send audio =========
 | 
			
		||||
						temp_audio_data=dsp.read(1024*8)
 | 
			
		||||
						temp_audio_data=dsp.read(256)
 | 
			
		||||
						rxws.send(self, temp_audio_data, "AUD ")
 | 
			
		||||
 | 
			
		||||
						# ========= send spectrum =========
 | 
			
		||||
						while not myclient.spectrum_queue.empty():
 | 
			
		||||
							spectrum_data=myclient.spectrum_queue.get()
 | 
			
		||||
							spectrum_data_mid=len(spectrum_data[0])/2
 | 
			
		||||
							rxws.send(self, spectrum_data[0][spectrum_data_mid:]+spectrum_data[0][:spectrum_data_mid], "FFT ") 
 | 
			
		||||
							#spectrum_data_mid=len(spectrum_data[0])/2
 | 
			
		||||
							#rxws.send(self, spectrum_data[0][spectrum_data_mid:]+spectrum_data[0][:spectrum_data_mid], "FFT ") 
 | 
			
		||||
							# (it seems GNU Radio exchanges the first and second part of the FFT output, we correct it)
 | 
			
		||||
							rxws.send(self, spectrum_data[0],"FFT ")
 | 
			
		||||
 | 
			
		||||
						# ========= send bcastmsg =========
 | 
			
		||||
						if myclient.bcastmsg!="":
 | 
			
		||||
							rxws.send(self,myclient.bcastmsg)
 | 
			
		||||
							myclient.bcastmsg=""
 | 
			
		||||
 | 
			
		||||
						# ========= process commands =========
 | 
			
		||||
						while True:
 | 
			
		||||
@@ -328,9 +416,10 @@ class WebRXHandler(BaseHTTPRequestHandler):
 | 
			
		||||
									elif param_name == "offset_freq" and -cfg.samp_rate/2 <= float(param_value) <= cfg.samp_rate/2:
 | 
			
		||||
										dsp.set_offset_freq(int(param_value))
 | 
			
		||||
									elif param_name=="mod":
 | 
			
		||||
										dsp.stop()
 | 
			
		||||
										dsp.set_demodulator(param_value)
 | 
			
		||||
										dsp.start()
 | 
			
		||||
										if (dsp.get_demodulator()!=param_value):
 | 
			
		||||
											dsp.stop()
 | 
			
		||||
											dsp.set_demodulator(param_value)
 | 
			
		||||
											dsp.start()
 | 
			
		||||
									else:
 | 
			
		||||
										print "[openwebrx-httpd:ws] invalid parameter"
 | 
			
		||||
								if bpf_set:
 | 
			
		||||
@@ -355,7 +444,7 @@ class WebRXHandler(BaseHTTPRequestHandler):
 | 
			
		||||
 | 
			
		||||
				#delete disconnected client
 | 
			
		||||
				try:
 | 
			
		||||
					clients_mutex.acquire()
 | 
			
		||||
					cma("do_GET /ws/ delete disconnected")
 | 
			
		||||
					id_to_close=get_client_by_id(myclient.id,False)
 | 
			
		||||
					close_client(id_to_close,False)
 | 
			
		||||
				except:
 | 
			
		||||
@@ -363,19 +452,23 @@ class WebRXHandler(BaseHTTPRequestHandler):
 | 
			
		||||
					print "[openwebrx-httpd] client cannot be closed: ",exc_type,exc_value
 | 
			
		||||
					traceback.print_tb(exc_traceback)
 | 
			
		||||
				finally:
 | 
			
		||||
					clients_mutex.release()
 | 
			
		||||
					cmr()
 | 
			
		||||
				return
 | 
			
		||||
			elif self.path in ("/status", "/status/"):
 | 
			
		||||
				#self.send_header('Content-type','text/plain')
 | 
			
		||||
				getbands=lambda: str(int(cfg.shown_center_freq-cfg.samp_rate/2))+"-"+str(int(cfg.shown_center_freq+cfg.samp_rate/2))
 | 
			
		||||
				self.wfile.write("status=active\nname="+cfg.receiver_name+"\nsdr_hw="+cfg.receiver_device+"\nop_email="+cfg.receiver_admin+"\nbands="+getbands()+"\nusers="+str(len(clients))+"\navatar_ctime="+avatar_ctime+"\ngps="+str(cfg.receiver_gps)+"\nasl="+str(cfg.receiver_asl)+"\nloc="+cfg.receiver_location+"\nsw_version="+sw_version+"\nantenna="+cfg.receiver_ant+"\n")
 | 
			
		||||
				print "[openwebrx-httpd] GET /status/ from",self.client_address[0]			
 | 
			
		||||
			else:
 | 
			
		||||
				f=open(rootdir+self.path)
 | 
			
		||||
				data=f.read()
 | 
			
		||||
				extension=self.path[(len(self.path)-4):len(self.path)]
 | 
			
		||||
				extension=extension[2:] if extension[1]=='.' else extension[1:]
 | 
			
		||||
				if extension == "wrx" and ((self.headers['user-agent'].count("Chrome")==0 and self.headers['user-agent'].count("Firefox")==0) if 'user-agent' in self.headers.keys() else True) and (not request_param.count("unsupported")):
 | 
			
		||||
					self.send_response(302)
 | 
			
		||||
					self.send_header('Content-type','text/html')
 | 
			
		||||
					self.send_header("Location", "http://{0}:{1}/upgrade.html".format(cfg.server_hostname,cfg.web_port))
 | 
			
		||||
					self.end_headers()
 | 
			
		||||
					self.wfile.write("<html><body><h1>Object moved</h1>Please <a href=\"/upgrade.html\">click here</a> to continue.</body></html>")
 | 
			
		||||
				if extension == "wrx" and ((self.headers['user-agent'].count("Chrome")==0 and self.headers['user-agent'].count("Firefox")==0 and (not "Googlebot" in self.headers['user-agent'])) if 'user-agent' in self.headers.keys() else True) and (not request_param.count("unsupported")):
 | 
			
		||||
					self.send_302("upgrade.html")
 | 
			
		||||
					return
 | 
			
		||||
				if extension == "wrx" and cfg.max_clients<=len(clients):
 | 
			
		||||
					self.send_302("retry.html")
 | 
			
		||||
					return
 | 
			
		||||
				self.send_response(200)
 | 
			
		||||
				if(("wrx","html","htm").count(extension)):
 | 
			
		||||
@@ -384,11 +477,11 @@ class WebRXHandler(BaseHTTPRequestHandler):
 | 
			
		||||
					self.send_header('Content-type','text/javascript')
 | 
			
		||||
				elif(extension=="css"):
 | 
			
		||||
					self.send_header('Content-type','text/css')
 | 
			
		||||
				self.end_headers()
 | 
			
		||||
				self.end_headers()		
 | 
			
		||||
				if extension == "wrx":
 | 
			
		||||
					replace_dictionary=(
 | 
			
		||||
						("%[RX_PHOTO_DESC]",cfg.photo_desc),
 | 
			
		||||
						("%[CLIENT_ID]",generate_client_id(self.client_address[0])),
 | 
			
		||||
						("%[CLIENT_ID]", generate_client_id(self.client_address[0])) if "%[CLIENT_ID]" in data else "",
 | 
			
		||||
						("%[WS_URL]","ws://"+cfg.server_hostname+":"+str(cfg.web_port)+"/ws/"),
 | 
			
		||||
						("%[RX_TITLE]",cfg.receiver_name),
 | 
			
		||||
						("%[RX_LOC]",cfg.receiver_location),
 | 
			
		||||
@@ -398,7 +491,8 @@ class WebRXHandler(BaseHTTPRequestHandler):
 | 
			
		||||
						("%[RX_PHOTO_HEIGHT]",str(cfg.photo_height)),("%[RX_PHOTO_TITLE]",cfg.photo_title),
 | 
			
		||||
						("%[RX_ADMIN]",cfg.receiver_admin),
 | 
			
		||||
						("%[RX_ANT]",cfg.receiver_ant),
 | 
			
		||||
						("%[RX_DEVICE]",cfg.receiver_device)
 | 
			
		||||
						("%[RX_DEVICE]",cfg.receiver_device),
 | 
			
		||||
						("%[AUDIO_BUFSIZE]",str(cfg.client_audio_buffer_size))
 | 
			
		||||
					)
 | 
			
		||||
					for rule in replace_dictionary:
 | 
			
		||||
						while data.find(rule[0])!=-1:
 | 
			
		||||
@@ -417,6 +511,27 @@ class WebRXHandler(BaseHTTPRequestHandler):
 | 
			
		||||
class ClientNotFoundException(Exception):
 | 
			
		||||
	pass
 | 
			
		||||
 | 
			
		||||
last_worktime=0
 | 
			
		||||
last_idletime=0
 | 
			
		||||
 | 
			
		||||
def get_cpu_usage():
 | 
			
		||||
	global last_worktime, last_idletime
 | 
			
		||||
	f=open("/proc/stat","r")
 | 
			
		||||
	line=""
 | 
			
		||||
	while not "cpu " in line: line=f.readline()
 | 
			
		||||
	f.close()
 | 
			
		||||
	spl=line.split(" ")
 | 
			
		||||
	worktime=int(spl[2])+int(spl[3])+int(spl[4])
 | 
			
		||||
	idletime=int(spl[5])
 | 
			
		||||
	dworktime=(worktime-last_worktime)
 | 
			
		||||
	didletime=(idletime-last_idletime)
 | 
			
		||||
	rate=float(dworktime)/(didletime+dworktime)
 | 
			
		||||
	last_worktime=worktime
 | 
			
		||||
	last_idletime=idletime
 | 
			
		||||
	if(last_worktime==0): return 0
 | 
			
		||||
	return rate
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
if __name__=="__main__":
 | 
			
		||||
	main()
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										0
									
								
								plugins/__init__.py
									
									
									
									
									
										
										
										Executable file → Normal file
									
								
							
							
						
						
							
								
								
									
										0
									
								
								plugins/dsp/__init__.py
									
									
									
									
									
										
										
										Executable file → Normal file
									
								
							
							
						
						
							
								
								
									
										0
									
								
								plugins/dsp/csdr/__init__.py
									
									
									
									
									
										
										
										Executable file → Normal file
									
								
							
							
						
						@@ -1,13 +1,36 @@
 | 
			
		||||
"""
 | 
			
		||||
OpenWebRX csdr plugin: do the signal processing with csdr
 | 
			
		||||
 | 
			
		||||
	This file is part of OpenWebRX, 
 | 
			
		||||
	an open-source SDR receiver software with a web UI.
 | 
			
		||||
	Copyright (c) 2013-2015 by Andras Retzler <randras@sdr.hu>
 | 
			
		||||
 | 
			
		||||
    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.
 | 
			
		||||
 | 
			
		||||
    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 Affero General Public License
 | 
			
		||||
    along with this program.  If not, see <http://www.gnu.org/licenses/>.
 | 
			
		||||
 | 
			
		||||
"""
 | 
			
		||||
 | 
			
		||||
import subprocess
 | 
			
		||||
import time
 | 
			
		||||
import os
 | 
			
		||||
import code
 | 
			
		||||
import signal
 | 
			
		||||
 | 
			
		||||
class dsp_plugin:
 | 
			
		||||
 | 
			
		||||
	def __init__(self):
 | 
			
		||||
		self.samp_rate = 250000
 | 
			
		||||
		self.output_rate = 44100 #this is default, and cannot be set at the moment
 | 
			
		||||
		self.output_rate = 11025 #this is default, and cannot be set at the moment
 | 
			
		||||
		self.fft_size = 1024
 | 
			
		||||
		self.fft_fps = 5
 | 
			
		||||
		self.offset_freq = 0
 | 
			
		||||
@@ -16,20 +39,42 @@ class dsp_plugin:
 | 
			
		||||
		self.bpf_transition_bw = 320 #Hz, and this is a constant
 | 
			
		||||
		self.ddc_transition_bw_rate = 0.15 # of the IF sample rate
 | 
			
		||||
		self.running = False
 | 
			
		||||
		chain_begin="nc localhost 4951 | csdr convert_u8_f | csdr shift_addition_cc --fifo {shift_pipe} | csdr fir_decimate_cc {decimation} {ddc_transition_bw} HAMMING | csdr bandpass_fir_fft_cc --fifo {bpf_pipe} {bpf_transition_bw} HAMMING | "
 | 
			
		||||
		self.chains = {
 | 
			
		||||
			"nfm" :  chain_begin + "csdr fmdemod_quadri_cf | csdr limit_ff | csdr fractional_decimator_ff {last_decimation} | csdr deemphasis_nfm_ff 44100 | csdr fastagc_ff | csdr convert_f_i16",
 | 
			
		||||
			"am" :  chain_begin + "csdr amdemod_cf | csdr fastdcblock_ff | csdr fractional_decimator_ff {last_decimation} | csdr agc_ff | csdr limit_ff | csdr convert_f_i16",
 | 
			
		||||
			"ssb" :  chain_begin + "csdr realpart_cf | csdr fractional_decimator_ff {last_decimation} | csdr agc_ff | csdr limit_ff | csdr convert_f_i16",
 | 
			
		||||
			"fft" : "nc -vv localhost 4951 | csdr convert_u8_f | csdr fft_cc {fft_size} {fft_block_size} | csdr logpower_cf -70"
 | 
			
		||||
			}
 | 
			
		||||
		self.audio_compression = "none"
 | 
			
		||||
		self.fft_compression = "none"
 | 
			
		||||
		self.demodulator = "nfm"
 | 
			
		||||
		self.name = "csdr"
 | 
			
		||||
		self.format_conversion = "csdr convert_u8_f"
 | 
			
		||||
		try:	
 | 
			
		||||
			subprocess.Popen("nc",stdout=subprocess.PIPE,stderr=subprocess.PIPE)
 | 
			
		||||
			subprocess.Popen("nc",stdout=subprocess.PIPE,stderr=subprocess.PIPE).kill()
 | 
			
		||||
		except:
 | 
			
		||||
			print "[openwebrx-plugin:csdr] error: netcat not found, please install netcat!"
 | 
			
		||||
 | 
			
		||||
	def chain(self,which):
 | 
			
		||||
		any_chain_base="nc -v localhost 4951 | "+self.format_conversion+(" | " if  self.format_conversion!="" else "")+"csdr flowcontrol {flowcontrol} 10 | "
 | 
			
		||||
		if which == "fft": 
 | 
			
		||||
			fft_chain_base = "sleep 1; "+any_chain_base+"csdr fft_cc {fft_size} {fft_block_size} | csdr logpower_cf -70 | csdr fft_exchange_sides_ff {fft_size}"
 | 
			
		||||
			if self.fft_compression=="adpcm":
 | 
			
		||||
				return fft_chain_base+" | csdr compress_fft_adpcm_f_u8 {fft_size}"
 | 
			
		||||
			else:
 | 
			
		||||
				return fft_chain_base
 | 
			
		||||
		chain_begin=any_chain_base+"csdr shift_addition_cc --fifo {shift_pipe} | csdr fir_decimate_cc {decimation} {ddc_transition_bw} HAMMING | csdr bandpass_fir_fft_cc --fifo {bpf_pipe} {bpf_transition_bw} HAMMING | "
 | 
			
		||||
		chain_end = "" 
 | 
			
		||||
		if self.audio_compression=="adpcm":
 | 
			
		||||
			chain_end = " | csdr encode_ima_adpcm_i16_u8"
 | 
			
		||||
		if which == "nfm": return chain_begin + "csdr fmdemod_quadri_cf | csdr limit_ff | csdr fractional_decimator_ff {last_decimation} | csdr deemphasis_nfm_ff 11025 | csdr fastagc_ff | csdr convert_f_i16"+chain_end
 | 
			
		||||
		elif which == "am":	return chain_begin + "csdr amdemod_cf | csdr fastdcblock_ff | csdr fractional_decimator_ff {last_decimation} | csdr agc_ff | csdr limit_ff | csdr convert_f_i16"+chain_end
 | 
			
		||||
		elif which == "ssb": return chain_begin + "csdr realpart_cf | csdr fractional_decimator_ff {last_decimation} | csdr agc_ff | csdr limit_ff | csdr convert_f_i16"+chain_end
 | 
			
		||||
 | 
			
		||||
	def set_audio_compression(self,what):
 | 
			
		||||
		self.audio_compression = what
 | 
			
		||||
 | 
			
		||||
	def set_fft_compression(self,what):
 | 
			
		||||
		self.fft_compression = what
 | 
			
		||||
 | 
			
		||||
	def get_fft_bytes_to_read(self):
 | 
			
		||||
		if self.fft_compression=="none": return self.fft_size*4
 | 
			
		||||
		if self.fft_compression=="adpcm": return (self.fft_size/2)+(10/2)
 | 
			
		||||
 | 
			
		||||
	def set_samp_rate(self,samp_rate):
 | 
			
		||||
		#to change this, restart is required
 | 
			
		||||
		self.samp_rate=samp_rate
 | 
			
		||||
@@ -47,11 +92,13 @@ class dsp_plugin:
 | 
			
		||||
	def get_output_rate(self):
 | 
			
		||||
		return self.output_rate
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
	def set_demodulator(self,demodulator):
 | 
			
		||||
		#to change this, restart is required
 | 
			
		||||
		self.demodulator=demodulator
 | 
			
		||||
 | 
			
		||||
	def get_demodulator(self):
 | 
			
		||||
		return self.demodulator
 | 
			
		||||
 | 
			
		||||
	def set_fft_size(self,fft_size):
 | 
			
		||||
		#to change this, restart is required
 | 
			
		||||
		self.fft_size=fft_size
 | 
			
		||||
@@ -63,6 +110,9 @@ class dsp_plugin:
 | 
			
		||||
	def fft_block_size(self):
 | 
			
		||||
		return self.samp_rate/self.fft_fps
 | 
			
		||||
 | 
			
		||||
	def set_format_conversion(self,format_conversion):
 | 
			
		||||
		self.format_conversion=format_conversion
 | 
			
		||||
 | 
			
		||||
	def set_offset_freq(self,offset_freq):
 | 
			
		||||
		self.offset_freq=offset_freq
 | 
			
		||||
		if self.running: 
 | 
			
		||||
@@ -90,7 +140,7 @@ class dsp_plugin:
 | 
			
		||||
		return self.ddc_transition_bw_rate*(self.if_samp_rate()/float(self.samp_rate))
 | 
			
		||||
 | 
			
		||||
	def start(self):
 | 
			
		||||
		command_base=self.chains[self.demodulator]
 | 
			
		||||
		command_base=self.chain(self.demodulator)
 | 
			
		||||
		
 | 
			
		||||
		#create control pipes for csdr
 | 
			
		||||
		pipe_base_path="/tmp/openwebrx_pipe_{myid}_".format(myid=id(self))
 | 
			
		||||
@@ -103,10 +153,10 @@ class dsp_plugin:
 | 
			
		||||
			self.mkfifo(self.shift_pipe)
 | 
			
		||||
 | 
			
		||||
		#run the command
 | 
			
		||||
		command=command_base.format(bpf_pipe=self.bpf_pipe,shift_pipe=self.shift_pipe,decimation=self.decimation,last_decimation=self.last_decimation,fft_size=self.fft_size,fft_block_size=self.fft_block_size(),bpf_transition_bw=float(self.bpf_transition_bw)/self.if_samp_rate(),ddc_transition_bw=self.ddc_transition_bw())
 | 
			
		||||
		command=command_base.format(bpf_pipe=self.bpf_pipe,shift_pipe=self.shift_pipe,decimation=self.decimation,last_decimation=self.last_decimation,fft_size=self.fft_size,fft_block_size=self.fft_block_size(),bpf_transition_bw=float(self.bpf_transition_bw)/self.if_samp_rate(),ddc_transition_bw=self.ddc_transition_bw(),flowcontrol=int(self.samp_rate*4*2*1.5))
 | 
			
		||||
		print "[openwebrx-dsp-plugin:csdr] Command =",command
 | 
			
		||||
		#code.interact(local=locals())
 | 
			
		||||
		self.process = subprocess.Popen(command, stdout=subprocess.PIPE, shell=True)
 | 
			
		||||
		self.process = subprocess.Popen(command, stdout=subprocess.PIPE, shell=True, preexec_fn=os.setpgrp)
 | 
			
		||||
		self.running = True
 | 
			
		||||
 | 
			
		||||
		#open control pipes for csdr and send initialization data
 | 
			
		||||
@@ -121,12 +171,22 @@ class dsp_plugin:
 | 
			
		||||
		return self.process.stdout.read(size)
 | 
			
		||||
		
 | 
			
		||||
	def stop(self):
 | 
			
		||||
		if(self.process!=None):return # returns None while subprocess is running
 | 
			
		||||
		while(self.process.poll()==None):
 | 
			
		||||
			self.process.kill()
 | 
			
		||||
			time.sleep(0.1)
 | 
			
		||||
		os.unlink(self.bpf_pipe)
 | 
			
		||||
		os.unlink(self.shift_pipe)
 | 
			
		||||
		os.killpg(os.getpgid(self.process.pid), signal.SIGTERM) 
 | 
			
		||||
		#if(self.process.poll()!=None):return # returns None while subprocess is running
 | 
			
		||||
		#while(self.process.poll()==None):
 | 
			
		||||
		#	#self.process.kill()
 | 
			
		||||
		#	print "killproc",os.getpgid(self.process.pid),self.process.pid
 | 
			
		||||
		#	os.killpg(self.process.pid, signal.SIGTERM) 
 | 
			
		||||
		#	
 | 
			
		||||
		#	time.sleep(0.1)
 | 
			
		||||
		try:
 | 
			
		||||
			os.unlink(self.bpf_pipe)
 | 
			
		||||
		except:
 | 
			
		||||
			print "[openwebrx-dsp-plugin:csdr] stop() :: unlink failed: " + self.bpf_pipe
 | 
			
		||||
		try:
 | 
			
		||||
			os.unlink(self.shift_pipe)
 | 
			
		||||
		except:
 | 
			
		||||
			print "[openwebrx-dsp-plugin:csdr] stop() :: unlink failed: " + self.bpf_pipe
 | 
			
		||||
		self.running = False
 | 
			
		||||
 | 
			
		||||
	def restart(self):
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										54
									
								
								rtl_mus.py
									
									
									
									
									
								
							
							
						
						@@ -1,20 +1,20 @@
 | 
			
		||||
'''
 | 
			
		||||
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, HA7ILM <randras@sdr.hu>
 | 
			
		||||
	Copyright (c) 2013-2015 by Andras Retzler <randras@sdr.hu>
 | 
			
		||||
 | 
			
		||||
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 <http://www.gnu.org/licenses/>.
 | 
			
		||||
    You should have received a copy of the GNU Affero General Public License
 | 
			
		||||
    along with this program.  If not, see <http://www.gnu.org/licenses/>.
 | 
			
		||||
 | 
			
		||||
-----
 | 
			
		||||
 | 
			
		||||
@@ -87,7 +87,7 @@ def add_data_to_clients(new_data):
 | 
			
		||||
			elif cfg.cache_full_behaviour == 1:
 | 
			
		||||
				#rather closing client:
 | 
			
		||||
				log.error("client cache full, dropping client: "+str(client[0].ident)+"@"+client[0].socket[1][0])
 | 
			
		||||
				client[0].close()
 | 
			
		||||
				client[0].close(False)
 | 
			
		||||
			elif cfg.cache_full_behaviour == 2:
 | 
			
		||||
				pass #client cache full, just not taking care
 | 
			
		||||
			else: log.error("invalid value for cfg.cache_full_behaviour")
 | 
			
		||||
@@ -131,6 +131,7 @@ class client_handler(asyncore.dispatcher):
 | 
			
		||||
		self.sent_dongle_id=False
 | 
			
		||||
		self.last_waiting_buffer=""
 | 
			
		||||
		asyncore.dispatcher.__init__(self, self.client[0].socket[0])
 | 
			
		||||
		self.socket.setsockopt(socket.IPPROTO_TCP, socket.TCP_NODELAY, 1)
 | 
			
		||||
 | 
			
		||||
	def handle_read(self):
 | 
			
		||||
		global commands
 | 
			
		||||
@@ -174,6 +175,7 @@ class server_asyncore(asyncore.dispatcher):
 | 
			
		||||
		self.set_reuse_addr()
 | 
			
		||||
		self.bind((cfg.my_ip, cfg.my_listening_port))
 | 
			
		||||
		self.listen(5)
 | 
			
		||||
		self.socket.setsockopt(socket.IPPROTO_TCP, socket.TCP_NODELAY, 1)
 | 
			
		||||
		log.info("Server listening on port: "+str(cfg.my_listening_port))
 | 
			
		||||
 | 
			
		||||
	def handle_accept(self):
 | 
			
		||||
@@ -188,7 +190,7 @@ class server_asyncore(asyncore.dispatcher):
 | 
			
		||||
			my_client[0].ident=max_client_id
 | 
			
		||||
			max_client_id+=1
 | 
			
		||||
			my_client[0].start_time=time.time()
 | 
			
		||||
			my_client[0].waiting_data=multiprocessing.Queue(250)
 | 
			
		||||
			my_client[0].waiting_data=multiprocessing.Queue(500)
 | 
			
		||||
			clients_mutex.acquire()
 | 
			
		||||
			clients.append(my_client)
 | 
			
		||||
			clients_mutex.release()
 | 
			
		||||
@@ -223,6 +225,7 @@ class rtl_tcp_asyncore(asyncore.dispatcher):
 | 
			
		||||
	def __init__(self):
 | 
			
		||||
		global server_missing_logged
 | 
			
		||||
		asyncore.dispatcher.__init__(self)
 | 
			
		||||
		self.password_sent = False
 | 
			
		||||
		self.ok=True
 | 
			
		||||
		self.create_socket(socket.AF_INET, socket.SOCK_STREAM)
 | 
			
		||||
		try:		
 | 
			
		||||
@@ -255,6 +258,7 @@ class rtl_tcp_asyncore(asyncore.dispatcher):
 | 
			
		||||
		global server_missing_logged
 | 
			
		||||
		global rtl_tcp_connected
 | 
			
		||||
		self.socket.settimeout(0.1)
 | 
			
		||||
		self.password_sent = False
 | 
			
		||||
		rtl_tcp_connected=True
 | 
			
		||||
		if self.ok:
 | 
			
		||||
			log.info("rtl_tcp host connection estabilished")
 | 
			
		||||
@@ -278,9 +282,9 @@ class rtl_tcp_asyncore(asyncore.dispatcher):
 | 
			
		||||
		if(len(rtl_dongle_identifier)==0):
 | 
			
		||||
			rtl_dongle_identifier=self.recv(12)
 | 
			
		||||
			return
 | 
			
		||||
		new_data_buffer=self.recv(16348)
 | 
			
		||||
		new_data_buffer=self.recv(1024*16)
 | 
			
		||||
		if cfg.watchdog_interval:
 | 
			
		||||
			watchdog_data_count+=16348
 | 
			
		||||
			watchdog_data_count+=1024*16
 | 
			
		||||
		if cfg.use_dsp_command:
 | 
			
		||||
			dsp_input_queue.put(new_data_buffer)
 | 
			
		||||
			#print "did put anyway"
 | 
			
		||||
@@ -288,11 +292,16 @@ class rtl_tcp_asyncore(asyncore.dispatcher):
 | 
			
		||||
			add_data_to_clients(new_data_buffer)
 | 
			
		||||
 | 
			
		||||
	def writable(self):
 | 
			
		||||
		
 | 
			
		||||
		#check if any new commands to write
 | 
			
		||||
		global commands
 | 
			
		||||
		return not commands.empty()
 | 
			
		||||
		return (not self.password_sent and cfg.rtl_tcp_password != None) or not commands.empty()
 | 
			
		||||
 | 
			
		||||
	def handle_write(self):
 | 
			
		||||
		if(not self.password_sent and cfg.rtl_tcp_password != None):
 | 
			
		||||
			log.info("Sending rtl_tcp_password...")
 | 
			
		||||
			self.send(cfg.rtl_tcp_password)
 | 
			
		||||
			self.password_sent = True
 | 
			
		||||
		global commands
 | 
			
		||||
		while not commands.empty():
 | 
			
		||||
			mcmd=commands.get()
 | 
			
		||||
@@ -418,11 +427,13 @@ class client:
 | 
			
		||||
	socket=None
 | 
			
		||||
	asyncore=None
 | 
			
		||||
 | 
			
		||||
	def close(self):
 | 
			
		||||
	def close(self, use_mutex=True):
 | 
			
		||||
		global clients_mutex
 | 
			
		||||
		global clients
 | 
			
		||||
		clients_mutex.acquire()
 | 
			
		||||
		if use_mutex: clients_mutex.acquire()
 | 
			
		||||
		correction=0
 | 
			
		||||
		for i in range(0,len(clients)):
 | 
			
		||||
			i-=correction
 | 
			
		||||
			if clients[i][0].ident==self.ident:
 | 
			
		||||
				try:
 | 
			
		||||
					self.socket.close()
 | 
			
		||||
@@ -433,8 +444,9 @@ class client:
 | 
			
		||||
					del self.asyncore
 | 
			
		||||
				except:
 | 
			
		||||
					pass
 | 
			
		||||
				break
 | 
			
		||||
		clients_mutex.release()
 | 
			
		||||
				del clients[i]
 | 
			
		||||
				correction+=1
 | 
			
		||||
		if use_mutex: clients_mutex.release()
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def main():
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										23
									
								
								rxws.py
									
									
									
									
									
								
							
							
						
						@@ -1,23 +1,22 @@
 | 
			
		||||
"""
 | 
			
		||||
rxws: WebSocket methods implemented for OpenWebRX
 | 
			
		||||
 | 
			
		||||
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 <randras@sdr.hu>
 | 
			
		||||
 | 
			
		||||
    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 General Public License
 | 
			
		||||
    along with OpenWebRX.  If not, see <http://www.gnu.org/licenses/>.
 | 
			
		||||
 | 
			
		||||
Authors:
 | 
			
		||||
    Andras Retzler, HA7ILM <retzlerandras@gmail.com>
 | 
			
		||||
    You should have received a copy of the GNU Affero General Public License
 | 
			
		||||
    along with this program.  If not, see <http://www.gnu.org/licenses/>.
 | 
			
		||||
 | 
			
		||||
"""
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										
											BIN
										
									
								
								screenshot.png
									
									
									
									
									
								
							
							
						
						| 
		 Before Width: | Height: | Size: 1.2 MiB After Width: | Height: | Size: 1.1 MiB  | 
							
								
								
									
										51
									
								
								sdrhu.py
									
									
									
									
									
										Executable file
									
								
							
							
						
						@@ -0,0 +1,51 @@
 | 
			
		||||
#!/usr/bin/python2
 | 
			
		||||
"""
 | 
			
		||||
 | 
			
		||||
	This file is part of OpenWebRX, 
 | 
			
		||||
	an open-source SDR receiver software with a web UI.
 | 
			
		||||
	Copyright (c) 2013-2015 by Andras Retzler <randras@sdr.hu>
 | 
			
		||||
 | 
			
		||||
    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.
 | 
			
		||||
 | 
			
		||||
    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 Affero General Public License
 | 
			
		||||
    along with this program.  If not, see <http://www.gnu.org/licenses/>.
 | 
			
		||||
 | 
			
		||||
"""
 | 
			
		||||
 | 
			
		||||
import config_webrx as cfg, time, subprocess
 | 
			
		||||
 | 
			
		||||
def run(continuously=True):
 | 
			
		||||
	if not cfg.sdrhu_key: return 
 | 
			
		||||
	firsttime="(Your receiver is soon getting listed on sdr.hu!)"
 | 
			
		||||
	while True:
 | 
			
		||||
		cmd = "wget --timeout=15 -qO- http://sdr.hu/update --post-data \"url=http://"+cfg.server_hostname+":"+str(cfg.web_port)+"&apikey="+cfg.sdrhu_key+"\" 2>&1"
 | 
			
		||||
		#print "[openwebrx-sdrhu]", cmd
 | 
			
		||||
		returned=subprocess.Popen(cmd, shell=True, stdout=subprocess.PIPE).communicate()
 | 
			
		||||
		returned=returned[0]
 | 
			
		||||
		#print returned
 | 
			
		||||
		if "UPDATE:" in returned:
 | 
			
		||||
			retrytime_mins = 20
 | 
			
		||||
			value=returned.split("UPDATE:")[1].split("\n",1)[0]
 | 
			
		||||
			if value.startswith("SUCCESS"):
 | 
			
		||||
				print "[openwebrx-sdrhu] Update succeeded! "+firsttime
 | 
			
		||||
				firsttime=""
 | 
			
		||||
			else:
 | 
			
		||||
				print "[openwebrx-sdrhu] Update failed, your receiver cannot be listed on sdr.hu! Reason:", value
 | 
			
		||||
		else:
 | 
			
		||||
			retrytime_mins = 2
 | 
			
		||||
			print "[openwebrx-sdrhu] wget failed while updating, your receiver cannot be listed on sdr.hu!"
 | 
			
		||||
		if not continuously: break
 | 
			
		||||
		time.sleep(60*retrytime_mins)
 | 
			
		||||
 | 
			
		||||
if __name__=="__main__":
 | 
			
		||||
	run(False)
 | 
			
		||||
 | 
			
		||||
	
 | 
			
		||||