Added some features.
This commit is contained in:
parent
c9bf26f1ac
commit
4b3cc10924
20
README.md
20
README.md
@ -14,7 +14,7 @@ It has the following features:
|
|||||||
- it works in Google Chrome, Chromium (above version 37) and Mozilla Firefox (above version 28),
|
- 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.
|
- currently only supports RTL-SDR, but other SDR hardware may be easily added.
|
||||||
|
|
||||||
**News:**
|
**News (2015-08-18)**
|
||||||
- My BSc. thesis written on OpenWebRX is <a href="http://openwebrx.org/bsc-thesis.pdf">available here.</a>
|
- 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.
|
- 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 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`.)
|
||||||
@ -22,6 +22,13 @@ It has the following features:
|
|||||||
- Receivers can now be listed on <a href="http://sdr.hu/">sdr.hu</a>.
|
- Receivers can now be listed on <a href="http://sdr.hu/">sdr.hu</a>.
|
||||||
- License for OpenWebRX is now Affero GPL v3.
|
- License for OpenWebRX is now Affero GPL v3.
|
||||||
|
|
||||||
|
**News (2015-09-01)**
|
||||||
|
- The DDC in *csdr* has been hand-optimized for ARM NEON, so it runs 3× faster on the Raspberry Pi than before.
|
||||||
|
- Also we use *ncat* instead of *rtl_mus*, and it is also 3× faster.
|
||||||
|
- OpenWebRX now supports URLs like: http://localhost:8073/#freq=145555000,mod=usb
|
||||||
|
|
||||||
|
- When upgrading OpenWebRX, please make sure that you upgrade *csdr*, and install the new (optional) dependency *ncat*!
|
||||||
|
|
||||||
## Setup
|
## Setup
|
||||||
|
|
||||||
OpenWebRX currently requires Linux and python 2.7 to run.
|
OpenWebRX currently requires Linux and python 2.7 to run.
|
||||||
@ -30,6 +37,7 @@ First you will need to install the dependencies:
|
|||||||
|
|
||||||
- <a href="https://github.com/simonyiszk/csdr">libcsdr</a>
|
- <a href="https://github.com/simonyiszk/csdr">libcsdr</a>
|
||||||
- <a href="http://sdr.osmocom.org/trac/wiki/rtl-sdr">rtl-sdr</a>
|
- <a href="http://sdr.osmocom.org/trac/wiki/rtl-sdr">rtl-sdr</a>
|
||||||
|
- ncat (on Debian/Ubuntu, it is in the *nmap* package). *(It is optional, but highly advised.)*
|
||||||
|
|
||||||
After cloning this repository and connecting an RTL-SDR dongle to your computer, you can run the server:
|
After cloning this repository and connecting an RTL-SDR dongle to your computer, you can run the server:
|
||||||
|
|
||||||
@ -57,14 +65,10 @@ However, if you hold down the shift key, you can drag the center line (BFO) or t
|
|||||||
|
|
||||||
## Configuration tips
|
## Configuration tips
|
||||||
|
|
||||||
If you want to run OpenWebRX on a remote server instead of localhost, do not forget to set *server_hostname* in `config_webrx.py`, or you may get a WebSocket error.
|
Now we have a %[Wiki](https://github.com/simonyiszk/openwebrx/wiki) with some how-tos. However, some quick tips:
|
||||||
|
|
||||||
|
If you want to run OpenWebRX on a remote server instead of localhost, do not forget to set *server_hostname* in `config_webrx.py`.
|
||||||
|
|
||||||
DSP CPU usage can be fine-tuned in `plugins/dsp/csdr/plugin.py`: you can set transition bandwidths higher (thus degrade filter performance by decreasing the length of the kernel, but also decrease CPU usage), and also set `fft_size` lower.
|
DSP CPU usage can be fine-tuned in `plugins/dsp/csdr/plugin.py`: you can set transition bandwidths higher (thus degrade filter performance by decreasing the length of the kernel, but also decrease CPU usage), and also set `fft_size` lower.
|
||||||
|
|
||||||
If you constantly get *audio overrun* errors, you may change `audio_buffer_maximal_length_sec` in `openwebrx.js` from the default 1.7 to 3.
|
|
||||||
|
|
||||||
If you want a chat-box to the top of the page, <a href="https://gist.github.com/ha7ilm/15c4c5e4c80cef9b3144">here is a snippet</a> for you to include in `config_webrx.py`.
|
If you want a chat-box to the top of the page, <a href="https://gist.github.com/ha7ilm/15c4c5e4c80cef9b3144">here is a snippet</a> for you to include in `config_webrx.py`.
|
||||||
|
|
||||||
## Todo
|
|
||||||
|
|
||||||
Currently, clients use up a lot of bandwidth. This will be improved later.
|
|
||||||
|
@ -66,6 +66,7 @@ sdrhu_public_listing = False
|
|||||||
dsp_plugin="csdr"
|
dsp_plugin="csdr"
|
||||||
fft_fps=9
|
fft_fps=9
|
||||||
fft_size=4096
|
fft_size=4096
|
||||||
|
#samp_rate = 2048000
|
||||||
samp_rate = 250000
|
samp_rate = 250000
|
||||||
|
|
||||||
center_freq = 145525000
|
center_freq = 145525000
|
||||||
@ -103,7 +104,13 @@ format_conversion="csdr convert_u8_f"
|
|||||||
|
|
||||||
shown_center_freq = center_freq #you can change this if you use an upconverter
|
shown_center_freq = center_freq #you can change this if you use an upconverter
|
||||||
|
|
||||||
client_audio_buffer_size = 4
|
client_audio_buffer_size = 5
|
||||||
#increasing client_audio_buffer_size will:
|
#increasing client_audio_buffer_size will:
|
||||||
# - also increase the latency
|
# - also increase the latency
|
||||||
# - decrease the chance of audio underruns
|
# - decrease the chance of audio underruns
|
||||||
|
|
||||||
|
start_freq = center_freq
|
||||||
|
start_mod = "nfm" #nfm, am, lsb, usb, cw
|
||||||
|
|
||||||
|
iq_server_port = 4951
|
||||||
|
# (if ncat is not available on your system, rtl_mus will be used, thus you will have to set the same port as "my_listening_port" in config_rtl.py as well)
|
||||||
|
@ -23,11 +23,13 @@
|
|||||||
<head>
|
<head>
|
||||||
<title>OpenWebRX | Open Source SDR Web App for Everyone!</title>
|
<title>OpenWebRX | Open Source SDR Web App for Everyone!</title>
|
||||||
<script type="text/javascript">
|
<script type="text/javascript">
|
||||||
//Local variables
|
//Global variables
|
||||||
client_id="%[CLIENT_ID]";
|
var client_id="%[CLIENT_ID]";
|
||||||
ws_url="%[WS_URL]";
|
var ws_url="%[WS_URL]";
|
||||||
rx_photo_height=%[RX_PHOTO_HEIGHT];
|
var rx_photo_height=%[RX_PHOTO_HEIGHT];
|
||||||
var audio_buffering_fill_to=%[AUDIO_BUFSIZE];
|
var audio_buffering_fill_to=%[AUDIO_BUFSIZE];
|
||||||
|
var starting_mod = "%[START_MOD]";
|
||||||
|
var starting_offset_frequency = %[START_OFFSET_FREQ];
|
||||||
</script>
|
</script>
|
||||||
<script src="sdr.js"></script>
|
<script src="sdr.js"></script>
|
||||||
<script src="openwebrx.js"></script>
|
<script src="openwebrx.js"></script>
|
||||||
|
@ -959,6 +959,32 @@ function resize_waterfall_container(check_init)
|
|||||||
canvas_container.style.height=(window.innerHeight-e("webrx-top-container").clientHeight-e("openwebrx-scale-container").clientHeight).toString()+"px";
|
canvas_container.style.height=(window.innerHeight-e("webrx-top-container").clientHeight-e("openwebrx-scale-container").clientHeight).toString()+"px";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
audio_server_output_rate=11025;
|
||||||
|
audio_client_resampling_factor=4;
|
||||||
|
|
||||||
|
|
||||||
|
function audio_calculate_resampling(targetRate)
|
||||||
|
{ //both at the server and the client
|
||||||
|
output_range_max = 12000;
|
||||||
|
output_range_min = 8000;
|
||||||
|
i = 1;
|
||||||
|
while(true)
|
||||||
|
{
|
||||||
|
audio_server_output_rate = Math.floor(targetRate / i);
|
||||||
|
if(audio_server_output_rate < output_range_min)
|
||||||
|
{
|
||||||
|
audio_client_resampling_factor = audio_server_output_rate = 0;
|
||||||
|
divlog("Your audio card sampling rate ("+targetRate.toString()+") is not supported.<br />Please change your operating system default settings in order to fix this.",1);
|
||||||
|
}
|
||||||
|
if(audio_server_output_rate >= output_range_min && audio_server_output_rate <= output_range_max) break; //okay, we're done
|
||||||
|
i++;
|
||||||
|
}
|
||||||
|
audio_client_resampling_factor=i;
|
||||||
|
console.log("audio_calculate_resampling() :: "+audio_client_resampling_factor.toString()+", "+audio_server_output_rate.toString());
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
debug_ws_data_received=0;
|
debug_ws_data_received=0;
|
||||||
max_clients_num=0;
|
max_clients_num=0;
|
||||||
|
|
||||||
@ -974,6 +1000,7 @@ function on_ws_recv(evt)
|
|||||||
{
|
{
|
||||||
var stringData=arrayBufferToString(evt.data);
|
var stringData=arrayBufferToString(evt.data);
|
||||||
if(stringData.substring(0,16)=="CLIENT DE SERVER") divlog("Acknowledged WebSocket connection: "+stringData);
|
if(stringData.substring(0,16)=="CLIENT DE SERVER") divlog("Acknowledged WebSocket connection: "+stringData);
|
||||||
|
|
||||||
}
|
}
|
||||||
if(firstChars=="AUD")
|
if(firstChars=="AUD")
|
||||||
{
|
{
|
||||||
@ -1010,6 +1037,7 @@ function on_ws_recv(evt)
|
|||||||
{
|
{
|
||||||
case "setup":
|
case "setup":
|
||||||
waterfall_init();
|
waterfall_init();
|
||||||
|
audio_preinit();
|
||||||
break;
|
break;
|
||||||
case "bandwidth":
|
case "bandwidth":
|
||||||
bandwidth=parseInt(param[1]);
|
bandwidth=parseInt(param[1]);
|
||||||
@ -1122,7 +1150,7 @@ var audio_initialized=0;
|
|||||||
|
|
||||||
var audio_received = Array();
|
var audio_received = Array();
|
||||||
var audio_buffer_index = 0;
|
var audio_buffer_index = 0;
|
||||||
var audio_resampler=new sdrjs.RationalResamplerFF(4,1);
|
var audio_resampler;
|
||||||
var audio_codec=new sdrjs.ImaAdpcm();
|
var audio_codec=new sdrjs.ImaAdpcm();
|
||||||
var audio_compression="unknown";
|
var audio_compression="unknown";
|
||||||
var audio_node;
|
var audio_node;
|
||||||
@ -1167,7 +1195,7 @@ function audio_prepare(data)
|
|||||||
audio_prepared_buffers.push(audio_rebuffer.take());
|
audio_prepared_buffers.push(audio_rebuffer.take());
|
||||||
audio_buffer_current_count_debug++;
|
audio_buffer_current_count_debug++;
|
||||||
}
|
}
|
||||||
if(audio_buffering && audio_prepared_buffers.length>audio_buffering_fill_to) audio_buffering=false;
|
if(audio_buffering && audio_prepared_buffers.length>audio_buffering_fill_to) { console.log("buffers now: "+audio_prepared_buffers.length.toString()); audio_buffering=false; }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -1246,6 +1274,8 @@ var audio_buffer_progressbar_update_disabled=false;
|
|||||||
|
|
||||||
var audio_buffer_total_average_level=0;
|
var audio_buffer_total_average_level=0;
|
||||||
var audio_buffer_total_average_level_length=0;
|
var audio_buffer_total_average_level_length=0;
|
||||||
|
var audio_overrun_cnt = 0;
|
||||||
|
var audio_underrun_cnt = 0;
|
||||||
|
|
||||||
function audio_buffer_progressbar_update()
|
function audio_buffer_progressbar_update()
|
||||||
{
|
{
|
||||||
@ -1255,8 +1285,8 @@ function audio_buffer_progressbar_update()
|
|||||||
var overrun=audio_buffer_value>audio_buffer_maximal_length_sec;
|
var overrun=audio_buffer_value>audio_buffer_maximal_length_sec;
|
||||||
var underrun=audio_prepared_buffers.length==0;
|
var underrun=audio_prepared_buffers.length==0;
|
||||||
var text="buffer";
|
var text="buffer";
|
||||||
if(overrun) text="overrun";
|
if(overrun) { text="overrun"; console.log("audio overrun, "+(++audio_overrun_cnt).toString()); }
|
||||||
if(underrun) text="underrun";
|
if(underrun) { text="underrun"; console.log("audio underrun, "+(++audio_underrun_cnt).toString()); }
|
||||||
if(overrun||underrun)
|
if(overrun||underrun)
|
||||||
{
|
{
|
||||||
audio_buffer_progressbar_update_disabled=true;
|
audio_buffer_progressbar_update_disabled=true;
|
||||||
@ -1345,13 +1375,26 @@ function webrx_set_param(what, value)
|
|||||||
ws.send("SET "+what+"="+value.toString());
|
ws.send("SET "+what+"="+value.toString());
|
||||||
}
|
}
|
||||||
|
|
||||||
function audio_init()
|
function parsehash()
|
||||||
{
|
{
|
||||||
audio_debug_time_start=(new Date()).getTime();
|
if(h=window.location.hash)
|
||||||
audio_debug_time_last_start=audio_debug_time_start;
|
{
|
||||||
|
h.substring(1).split(",").forEach(function(x){
|
||||||
|
harr=x.split("=");
|
||||||
|
console.log(harr);
|
||||||
|
if(harr[0]=="mod") starting_mod = harr[1];
|
||||||
|
if(harr[0]=="freq") {
|
||||||
|
console.log(parseInt(harr[1]));
|
||||||
|
console.log(center_freq);
|
||||||
|
starting_offset_frequency = parseInt(harr[1])-center_freq;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
//https://github.com/0xfe/experiments/blob/master/www/tone/js/sinewave.js
|
function audio_preinit()
|
||||||
audio_initialized=1; // only tell on_ws_recv() not to call it again
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
window.AudioContext = window.AudioContext||window.webkitAudioContext;
|
window.AudioContext = window.AudioContext||window.webkitAudioContext;
|
||||||
@ -1362,6 +1405,28 @@ function audio_init()
|
|||||||
divlog('Your browser does not support Web Audio API, which is required for WebRX to run. Please upgrade to a HTML5 compatible browser.', 1);
|
divlog('Your browser does not support Web Audio API, which is required for WebRX to run. Please upgrade to a HTML5 compatible browser.', 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//we send our setup packet
|
||||||
|
|
||||||
|
parsehash();
|
||||||
|
//needs audio_context.sampleRate to exist
|
||||||
|
|
||||||
|
audio_calculate_resampling(audio_context.sampleRate);
|
||||||
|
audio_resampler = new sdrjs.RationalResamplerFF(audio_client_resampling_factor,1);
|
||||||
|
ws.send("SET output_rate="+audio_server_output_rate.toString()+" action=start"); //now we'll get AUD packets as well
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
function audio_init()
|
||||||
|
{
|
||||||
|
if(audio_client_resampling_factor==0) return; //if failed to find a valid resampling factor...
|
||||||
|
|
||||||
|
audio_debug_time_start=(new Date()).getTime();
|
||||||
|
audio_debug_time_last_start=audio_debug_time_start;
|
||||||
|
|
||||||
|
//https://github.com/0xfe/experiments/blob/master/www/tone/js/sinewave.js
|
||||||
|
audio_initialized=1; // only tell on_ws_recv() not to call it again
|
||||||
|
|
||||||
|
|
||||||
//on Chrome v36, createJavaScriptNode has been replaced by createScriptProcessor
|
//on Chrome v36, createJavaScriptNode has been replaced by createScriptProcessor
|
||||||
createjsnode_function = (audio_context.createJavaScriptNode == undefined)?audio_context.createScriptProcessor.bind(audio_context):audio_context.createJavaScriptNode.bind(audio_context);
|
createjsnode_function = (audio_context.createJavaScriptNode == undefined)?audio_context.createScriptProcessor.bind(audio_context):audio_context.createJavaScriptNode.bind(audio_context);
|
||||||
audio_node = createjsnode_function(audio_buffer_size, 0, 1);
|
audio_node = createjsnode_function(audio_buffer_size, 0, 1);
|
||||||
@ -1379,7 +1444,14 @@ function audio_init()
|
|||||||
audio_buffer = audio_context.createBuffer(xhr.response, false);
|
audio_buffer = audio_context.createBuffer(xhr.response, false);
|
||||||
audio_source.buffer = buffer;
|
audio_source.buffer = buffer;
|
||||||
audio_source.noteOn(0);*/
|
audio_source.noteOn(0);*/
|
||||||
demodulator_analog_replace('nfm'); //needs audio_context.sampleRate to exist
|
demodulator_analog_replace(starting_mod);
|
||||||
|
if(starting_offset_frequency)
|
||||||
|
{
|
||||||
|
demodulators[0].offset_frequency = starting_offset_frequency;
|
||||||
|
demodulators[0].set();
|
||||||
|
mkscale();
|
||||||
|
}
|
||||||
|
|
||||||
//hide log panel in a second (if user has not hidden it yet)
|
//hide log panel in a second (if user has not hidden it yet)
|
||||||
window.setTimeout(function(){
|
window.setTimeout(function(){
|
||||||
if(typeof e("openwebrx-panel-log").openwebrxHidden == "undefined" && !was_error)
|
if(typeof e("openwebrx-panel-log").openwebrxHidden == "undefined" && !was_error)
|
||||||
|
85
openwebrx.py
85
openwebrx.py
@ -47,7 +47,6 @@ import ctypes
|
|||||||
#import rtl_mus
|
#import rtl_mus
|
||||||
import rxws
|
import rxws
|
||||||
import uuid
|
import uuid
|
||||||
import config_webrx as cfg
|
|
||||||
import signal
|
import signal
|
||||||
import socket
|
import socket
|
||||||
|
|
||||||
@ -75,11 +74,17 @@ class MultiThreadHTTPServer(ThreadingMixIn, HTTPServer):
|
|||||||
pass
|
pass
|
||||||
|
|
||||||
def handle_signal(signal, frame):
|
def handle_signal(signal, frame):
|
||||||
|
global spectrum_dsp
|
||||||
print "[openwebrx] Ctrl+C: aborting."
|
print "[openwebrx] Ctrl+C: aborting."
|
||||||
|
cleanup_clients(True)
|
||||||
|
spectrum_dsp.stop()
|
||||||
os._exit(1) #not too graceful exit
|
os._exit(1) #not too graceful exit
|
||||||
|
|
||||||
|
rtl_thread=spectrum_dsp=server_fail=None
|
||||||
|
|
||||||
def main():
|
def main():
|
||||||
global clients, clients_mutex, pypy, lock_try_time, avatar_ctime
|
global clients, clients_mutex, pypy, lock_try_time, avatar_ctime, cfg
|
||||||
|
global serverfail, rtl_thread
|
||||||
print
|
print
|
||||||
print "OpenWebRX - Open Source SDR Web App 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 "_________________________________________________________________________________________________"
|
||||||
@ -87,6 +92,10 @@ def main():
|
|||||||
print "Author contact info: Andras Retzler, HA7ILM <randras@sdr.hu>"
|
print "Author contact info: Andras Retzler, HA7ILM <randras@sdr.hu>"
|
||||||
print
|
print
|
||||||
|
|
||||||
|
no_arguments=len(sys.argv)==1
|
||||||
|
if no_arguments: print "[openwebrx] Configuration script not specified. I will use: \"config_webrx.py\""
|
||||||
|
cfg=__import__("config_webrx" if no_arguments else sys.argv[1])
|
||||||
|
|
||||||
#Set signal handler
|
#Set signal handler
|
||||||
signal.signal(signal.SIGINT, handle_signal) #http://stackoverflow.com/questions/1112343/how-do-i-capture-sigint-in-python
|
signal.signal(signal.SIGINT, handle_signal) #http://stackoverflow.com/questions/1112343/how-do-i-capture-sigint-in-python
|
||||||
|
|
||||||
@ -108,13 +117,18 @@ def main():
|
|||||||
|
|
||||||
#Start rtl thread
|
#Start rtl thread
|
||||||
if cfg.start_rtl_thread:
|
if cfg.start_rtl_thread:
|
||||||
rtl_thread=threading.Thread(target = lambda:subprocess.Popen(cfg.start_rtl_command, shell=True), args=())
|
rtl_thread=threading.Thread(target = lambda:subprocess.Popen(cfg.start_rtl_command, shell=True), args=())
|
||||||
rtl_thread.start()
|
rtl_thread.start()
|
||||||
print "[openwebrx-main] Started rtl thread: "+cfg.start_rtl_command
|
print "[openwebrx-main] Started rtl_thread: "+cfg.start_rtl_command
|
||||||
|
|
||||||
#Run rtl_mus.py in a different OS thread
|
#Run rtl_mus.py in a different OS thread
|
||||||
python_command="pypy" if pypy else "python2"
|
python_command="pypy" if pypy else "python2"
|
||||||
rtl_mus_thread=threading.Thread(target = lambda:subprocess.Popen(python_command+" rtl_mus.py config_rtl", shell=True), args=())
|
rtl_mus_cmd = python_command+" rtl_mus.py config_rtl"
|
||||||
|
if os.system("ncat --version > /dev/null") != 32512:
|
||||||
|
print "[openwebrx-main] ncat detected, using it instead of rtl_mus:"
|
||||||
|
rtl_mus_cmd = "ncat localhost 8888 | ncat -4l %d -k --send-only --allow 127.0.0.1 " % cfg.iq_server_port
|
||||||
|
print rtl_mus_cmd
|
||||||
|
rtl_mus_thread=threading.Thread(target = lambda:subprocess.Popen(rtl_mus_cmd, shell=True), args=())
|
||||||
rtl_mus_thread.start() # The new feature in GNU Radio 3.7: top_block() locks up ALL python threads until it gets the TCP connection.
|
rtl_mus_thread.start() # The new feature in GNU Radio 3.7: top_block() locks up ALL python threads until it gets the TCP connection.
|
||||||
print "[openwebrx-main] Started rtl_mus."
|
print "[openwebrx-main] Started rtl_mus."
|
||||||
time.sleep(1) #wait until it really starts
|
time.sleep(1) #wait until it really starts
|
||||||
@ -207,10 +221,20 @@ def mutex_watchdog_thread_function():
|
|||||||
print "[openwebrx-watchdog] Mutex unlock timeout. Locker: \""+str(clients_mutex_locker)+"\" Now unlocking..."
|
print "[openwebrx-watchdog] Mutex unlock timeout. Locker: \""+str(clients_mutex_locker)+"\" Now unlocking..."
|
||||||
clients_mutex.release()
|
clients_mutex.release()
|
||||||
time.sleep(0.5)
|
time.sleep(0.5)
|
||||||
|
|
||||||
|
def check_server():
|
||||||
|
global spectrum_dsp, server_fail, rtl_thread
|
||||||
|
if server_fail: return server_fail
|
||||||
|
#print spectrum_dsp.process.poll()
|
||||||
|
if spectrum_dsp and spectrum_dsp.process.poll()!=None: server_fail = "spectrum_thread dsp subprocess failed"
|
||||||
|
#if rtl_thread and not rtl_thread.is_alive(): server_fail = "rtl_thread failed"
|
||||||
|
if server_fail: print "[openwebrx-check_server] >>>>>>> ERROR:", server_fail
|
||||||
|
return server_fail
|
||||||
|
|
||||||
def spectrum_thread_function():
|
def spectrum_thread_function():
|
||||||
global clients
|
global clients, spectrum_dsp
|
||||||
dsp=getattr(plugins.dsp,cfg.dsp_plugin).plugin.dsp_plugin()
|
spectrum_dsp=dsp=getattr(plugins.dsp,cfg.dsp_plugin).plugin.dsp_plugin()
|
||||||
|
dsp.nc_port=cfg.iq_server_port
|
||||||
dsp.set_demodulator("fft")
|
dsp.set_demodulator("fft")
|
||||||
dsp.set_samp_rate(cfg.samp_rate)
|
dsp.set_samp_rate(cfg.samp_rate)
|
||||||
dsp.set_fft_size(cfg.fft_size)
|
dsp.set_fft_size(cfg.fft_size)
|
||||||
@ -220,6 +244,7 @@ def spectrum_thread_function():
|
|||||||
sleep_sec=0.87/cfg.fft_fps
|
sleep_sec=0.87/cfg.fft_fps
|
||||||
print "[openwebrx-spectrum] Spectrum thread initialized successfully."
|
print "[openwebrx-spectrum] Spectrum thread initialized successfully."
|
||||||
dsp.start()
|
dsp.start()
|
||||||
|
dsp.read(8) #dummy read to skip bufsize & preamble
|
||||||
print "[openwebrx-spectrum] Spectrum thread started."
|
print "[openwebrx-spectrum] Spectrum thread started."
|
||||||
bytes_to_read=int(dsp.get_fft_bytes_to_read())
|
bytes_to_read=int(dsp.get_fft_bytes_to_read())
|
||||||
while True:
|
while True:
|
||||||
@ -255,16 +280,17 @@ def get_client_by_id(client_id, use_mutex=True):
|
|||||||
def log_client(client, what):
|
def log_client(client, what):
|
||||||
print "[openwebrx-httpd] client {0}#{1} :: {2}".format(client.ip,client.id,what)
|
print "[openwebrx-httpd] client {0}#{1} :: {2}".format(client.ip,client.id,what)
|
||||||
|
|
||||||
def cleanup_clients():
|
def cleanup_clients(end_all=False):
|
||||||
# if client doesn't open websocket for too long time, we drop it
|
# - if a client doesn't open websocket for too long time, we drop it
|
||||||
|
# - or if end_all is true, we drop all clients
|
||||||
global clients
|
global clients
|
||||||
cma("cleanup_clients")
|
cma("cleanup_clients")
|
||||||
correction=0
|
correction=0
|
||||||
for i in range(0,len(clients)):
|
for i in range(0,len(clients)):
|
||||||
i-=correction
|
i-=correction
|
||||||
#print "cleanup_clients:: len(clients)=", len(clients), "i=", i
|
#print "cleanup_clients:: len(clients)=", len(clients), "i=", i
|
||||||
if (not clients[i].ws_started) and (time.time()-clients[i].gen_time)>45:
|
if end_all or ((not clients[i].ws_started) and (time.time()-clients[i].gen_time)>45):
|
||||||
print "[openwebrx] cleanup_clients :: client timeout to open WebSocket"
|
if not end_all: print "[openwebrx] cleanup_clients :: client timeout to open WebSocket"
|
||||||
close_client(i, False)
|
close_client(i, False)
|
||||||
correction+=1
|
correction+=1
|
||||||
cmr()
|
cmr()
|
||||||
@ -363,13 +389,12 @@ class WebRXHandler(BaseHTTPRequestHandler):
|
|||||||
|
|
||||||
# ========= Initialize DSP =========
|
# ========= Initialize DSP =========
|
||||||
dsp=getattr(plugins.dsp,cfg.dsp_plugin).plugin.dsp_plugin()
|
dsp=getattr(plugins.dsp,cfg.dsp_plugin).plugin.dsp_plugin()
|
||||||
dsp.set_samp_rate(cfg.samp_rate)
|
dsp_initialized=False
|
||||||
dsp.set_demodulator("nfm")
|
|
||||||
dsp.set_offset_freq(0)
|
|
||||||
dsp.set_bpf(-4000,4000)
|
|
||||||
dsp.set_audio_compression(cfg.audio_compression)
|
dsp.set_audio_compression(cfg.audio_compression)
|
||||||
dsp.set_format_conversion(cfg.format_conversion)
|
dsp.set_format_conversion(cfg.format_conversion)
|
||||||
dsp.start()
|
dsp.set_offset_freq(0)
|
||||||
|
dsp.set_bpf(-4000,4000)
|
||||||
|
dsp.nc_port=cfg.iq_server_port
|
||||||
myclient.dsp=dsp
|
myclient.dsp=dsp
|
||||||
|
|
||||||
while True:
|
while True:
|
||||||
@ -378,8 +403,9 @@ class WebRXHandler(BaseHTTPRequestHandler):
|
|||||||
break
|
break
|
||||||
|
|
||||||
# ========= send audio =========
|
# ========= send audio =========
|
||||||
temp_audio_data=dsp.read(256)
|
if dsp_initialized:
|
||||||
rxws.send(self, temp_audio_data, "AUD ")
|
temp_audio_data=dsp.read(256)
|
||||||
|
rxws.send(self, temp_audio_data, "AUD ")
|
||||||
|
|
||||||
# ========= send spectrum =========
|
# ========= send spectrum =========
|
||||||
while not myclient.spectrum_queue.empty():
|
while not myclient.spectrum_queue.empty():
|
||||||
@ -417,9 +443,17 @@ class WebRXHandler(BaseHTTPRequestHandler):
|
|||||||
dsp.set_offset_freq(int(param_value))
|
dsp.set_offset_freq(int(param_value))
|
||||||
elif param_name=="mod":
|
elif param_name=="mod":
|
||||||
if (dsp.get_demodulator()!=param_value):
|
if (dsp.get_demodulator()!=param_value):
|
||||||
dsp.stop()
|
if dsp_initialized: dsp.stop()
|
||||||
dsp.set_demodulator(param_value)
|
dsp.set_demodulator(param_value)
|
||||||
|
if dsp_initialized: dsp.start()
|
||||||
|
elif param_name == "output_rate":
|
||||||
|
if not dsp_initialized:
|
||||||
|
dsp.set_output_rate(int(param_value))
|
||||||
|
dsp.set_samp_rate(cfg.samp_rate)
|
||||||
|
elif param_name=="action" and param_value=="start":
|
||||||
|
if not dsp_initialized:
|
||||||
dsp.start()
|
dsp.start()
|
||||||
|
dsp_initialized=True
|
||||||
else:
|
else:
|
||||||
print "[openwebrx-httpd:ws] invalid parameter"
|
print "[openwebrx-httpd:ws] invalid parameter"
|
||||||
if bpf_set:
|
if bpf_set:
|
||||||
@ -464,6 +498,13 @@ class WebRXHandler(BaseHTTPRequestHandler):
|
|||||||
data=f.read()
|
data=f.read()
|
||||||
extension=self.path[(len(self.path)-4):len(self.path)]
|
extension=self.path[(len(self.path)-4):len(self.path)]
|
||||||
extension=extension[2:] if extension[1]=='.' else extension[1:]
|
extension=extension[2:] if extension[1]=='.' else extension[1:]
|
||||||
|
checkresult=check_server()
|
||||||
|
if extension == "wrx" and checkresult:
|
||||||
|
self.send_response(500)
|
||||||
|
self.send_header('Content-type','text/html')
|
||||||
|
self.end_headers()
|
||||||
|
self.wfile.write("<html><body><h1>OpenWebRX Internal Server Error</h1>Please check the server log for details.</body></html>")
|
||||||
|
return
|
||||||
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")):
|
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")
|
self.send_302("upgrade.html")
|
||||||
return
|
return
|
||||||
@ -492,7 +533,9 @@ class WebRXHandler(BaseHTTPRequestHandler):
|
|||||||
("%[RX_ADMIN]",cfg.receiver_admin),
|
("%[RX_ADMIN]",cfg.receiver_admin),
|
||||||
("%[RX_ANT]",cfg.receiver_ant),
|
("%[RX_ANT]",cfg.receiver_ant),
|
||||||
("%[RX_DEVICE]",cfg.receiver_device),
|
("%[RX_DEVICE]",cfg.receiver_device),
|
||||||
("%[AUDIO_BUFSIZE]",str(cfg.client_audio_buffer_size))
|
("%[AUDIO_BUFSIZE]",str(cfg.client_audio_buffer_size)),
|
||||||
|
("%[START_OFFSET_FREQ]",str(cfg.start_freq-cfg.center_freq)),
|
||||||
|
("%[START_MOD]",cfg.start_mod)
|
||||||
)
|
)
|
||||||
for rule in replace_dictionary:
|
for rule in replace_dictionary:
|
||||||
while data.find(rule[0])!=-1:
|
while data.find(rule[0])!=-1:
|
||||||
|
@ -44,13 +44,15 @@ class dsp_plugin:
|
|||||||
self.demodulator = "nfm"
|
self.demodulator = "nfm"
|
||||||
self.name = "csdr"
|
self.name = "csdr"
|
||||||
self.format_conversion = "csdr convert_u8_f"
|
self.format_conversion = "csdr convert_u8_f"
|
||||||
|
self.base_bufsize = 512
|
||||||
|
self.nc_port = 4951
|
||||||
try:
|
try:
|
||||||
subprocess.Popen("nc",stdout=subprocess.PIPE,stderr=subprocess.PIPE).kill()
|
subprocess.Popen("nc",stdout=subprocess.PIPE,stderr=subprocess.PIPE).kill()
|
||||||
except:
|
except:
|
||||||
print "[openwebrx-plugin:csdr] error: netcat not found, please install netcat!"
|
print "[openwebrx-plugin:csdr] error: netcat not found, please install netcat!"
|
||||||
|
|
||||||
def chain(self,which):
|
def chain(self,which):
|
||||||
any_chain_base="nc -v localhost 4951 | "+self.format_conversion+(" | " if self.format_conversion!="" else "")+"csdr flowcontrol {flowcontrol} 10 | "
|
any_chain_base="nc -v localhost {nc_port} | csdr setbuf {start_bufsize} | csdr through | "+self.format_conversion+(" | " if self.format_conversion!="" else "") ##"csdr flowcontrol {flowcontrol} auto 1.5 10 | "
|
||||||
if which == "fft":
|
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}"
|
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":
|
if self.fft_compression=="adpcm":
|
||||||
@ -61,9 +63,9 @@ class dsp_plugin:
|
|||||||
chain_end = ""
|
chain_end = ""
|
||||||
if self.audio_compression=="adpcm":
|
if self.audio_compression=="adpcm":
|
||||||
chain_end = " | csdr encode_ima_adpcm_i16_u8"
|
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
|
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 1024 | 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 == "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
|
elif which == "ssb": return chain_begin + "csdr realpart_cf | csdr fractional_decimator_ff {last_decimation} | csdr agc_ff | csdr clipdetect_ff | csdr limit_ff | csdr convert_f_i16"+chain_end
|
||||||
|
|
||||||
def set_audio_compression(self,what):
|
def set_audio_compression(self,what):
|
||||||
self.audio_compression = what
|
self.audio_compression = what
|
||||||
@ -92,6 +94,10 @@ class dsp_plugin:
|
|||||||
def get_output_rate(self):
|
def get_output_rate(self):
|
||||||
return self.output_rate
|
return self.output_rate
|
||||||
|
|
||||||
|
def set_output_rate(self,output_rate):
|
||||||
|
self.output_rate=output_rate
|
||||||
|
self.set_samp_rate(self.samp_rate) #as it depends on output_rate
|
||||||
|
|
||||||
def set_demodulator(self,demodulator):
|
def set_demodulator(self,demodulator):
|
||||||
#to change this, restart is required
|
#to change this, restart is required
|
||||||
self.demodulator=demodulator
|
self.demodulator=demodulator
|
||||||
@ -153,10 +159,16 @@ class dsp_plugin:
|
|||||||
self.mkfifo(self.shift_pipe)
|
self.mkfifo(self.shift_pipe)
|
||||||
|
|
||||||
#run the command
|
#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(),flowcontrol=int(self.samp_rate*4*2*1.5))
|
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*2), start_bufsize=self.base_bufsize*self.decimation, nc_port=self.nc_port)
|
||||||
|
|
||||||
print "[openwebrx-dsp-plugin:csdr] Command =",command
|
print "[openwebrx-dsp-plugin:csdr] Command =",command
|
||||||
#code.interact(local=locals())
|
#code.interact(local=locals())
|
||||||
self.process = subprocess.Popen(command, stdout=subprocess.PIPE, shell=True, preexec_fn=os.setpgrp)
|
my_env=os.environ.copy()
|
||||||
|
my_env["CSDR_DYNAMIC_BUFSIZE_ON"]="1";
|
||||||
|
self.process = subprocess.Popen(command, stdout=subprocess.PIPE, shell=True, preexec_fn=os.setpgrp, env=my_env)
|
||||||
self.running = True
|
self.running = True
|
||||||
|
|
||||||
#open control pipes for csdr and send initialization data
|
#open control pipes for csdr and send initialization data
|
||||||
@ -179,14 +191,16 @@ class dsp_plugin:
|
|||||||
# os.killpg(self.process.pid, signal.SIGTERM)
|
# os.killpg(self.process.pid, signal.SIGTERM)
|
||||||
#
|
#
|
||||||
# time.sleep(0.1)
|
# time.sleep(0.1)
|
||||||
try:
|
if self.bpf_pipe:
|
||||||
os.unlink(self.bpf_pipe)
|
try:
|
||||||
except:
|
os.unlink(self.bpf_pipe)
|
||||||
print "[openwebrx-dsp-plugin:csdr] stop() :: unlink failed: " + self.bpf_pipe
|
except:
|
||||||
try:
|
print "[openwebrx-dsp-plugin:csdr] stop() :: unlink failed: " + self.bpf_pipe
|
||||||
os.unlink(self.shift_pipe)
|
if self.shift_pipe:
|
||||||
except:
|
try:
|
||||||
print "[openwebrx-dsp-plugin:csdr] stop() :: unlink failed: " + self.bpf_pipe
|
os.unlink(self.shift_pipe)
|
||||||
|
except:
|
||||||
|
print "[openwebrx-dsp-plugin:csdr] stop() :: unlink failed: " + self.bpf_pipe
|
||||||
self.running = False
|
self.running = False
|
||||||
|
|
||||||
def restart(self):
|
def restart(self):
|
||||||
|
Loading…
Reference in New Issue
Block a user