diff --git a/README.md b/README.md
index f80c2f2..d0ea8d2 100644
--- a/README.md
+++ b/README.md
@@ -24,10 +24,13 @@ It has the following features:
**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.
+- Also we use *ncat* instead of *rtl_mus*, and it is 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*!
+**News (2016-01-23)**
+- *ncat* is now a requirement for OpenWebRX.
+
+When upgrading OpenWebRX, please make sure that you upgrade *csdr*, and install the new (optional) dependency *ncat*!
## Setup
@@ -37,7 +40,11 @@ First you will need to install the dependencies:
- libcsdr
- rtl-sdr
-- ncat (on Debian/Ubuntu, it is in the *nmap* package). *(It is optional, but highly advised.)*
+- ncat (On Debian/Ubuntu, it is in the *nmap* package).
+
+> By the way, *nmap* is tool commonly used for auditing network security, and it is not used by OpenWebRX in any way. We need it because the *ncat* command is packaged with it.
+>
+> *ncat* is a better *netcat* alternative, which is used by OpenWebRX for internally distributing the I/Q data stream. It also solves the problem of having different versions of *netcat* on different Linux distributions, which are not compatible by their command-line arguments.
After cloning this repository and connecting an RTL-SDR dongle to your computer, you can run the server:
diff --git a/config_rtl.py b/config_rtl.py
deleted file mode 100644
index d31d951..0000000
--- a/config_rtl.py
+++ /dev/null
@@ -1,103 +0,0 @@
-'''
- 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-2015 by Andras Retzler
-
- 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 .
-
- ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
-
- 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
-my_listening_port = 4951
-
-rtl_tcp_host,rtl_tcp_port='localhost',8888
-
-send_first=""
-#send_first=chr(9)+chr(0)+chr(0)+chr(0)+chr(1) # set direct sampling
-
-setuid_on_start = 0 # we normally start with root privileges and setuid() to another user
-uid = 999 # determine by issuing: $ id -u username
-ignore_clients_without_commands = 1 # we won't serve data to telnet sessions and things like that
- # we'll start to serve data after getting the first valid command
-
-freq_allowed_ranges = [[0,2200000000]]
-
-client_cant_set_until=0
-first_client_can_set=True # openwebrx - spectrum thread will set things on start # no good, clients set parameters and things
-buffer_size=25000000 # per client
-log_file_path = "/dev/null" # Might be set to /dev/null to turn off logging
-
-'''
-Allow any host to connect:
- use_ip_access_control=0
-
-Allow from specific ranges:
- use_ip_access_control=1
- order_allow_deny=0 # deny and then allow
- denied_ip_ranges=() # deny from all
- allowed_ip_ranges=('192.168.','44.','127.0.0.1') # allow only from ...
-
-Deny from specific ranges:
- use_ip_access_control=1
- order_allow_deny=0 # allow and then deny
- allowed_ip_ranges=() # allow from all
- denied_ip_ranges=('192.168.') # deny any hosts from ...
-'''
-use_ip_access_control=1 #You may want to open up the I/Q server to the public, then set this to zero.
-order_allow_deny=0
-denied_ip_ranges=() # deny from all
-allowed_ip_ranges=('127.0.0.1') # allow only local connections (from openwebrx).
-allow_gain_set=1
-
-use_dsp_command=False # you can process raw I/Q data with a custom command that starts a process that we can pipe the data into, and also pipe out of.
-debug_dsp_command=False # show sample rate before and after the dsp command
-dsp_command=""
-
-'''
-Example DSP commands:
- * Compress I/Q data with FLAC:
- flac --force-raw-format --channels 2 --sample-rate=250000 --sign=unsigned --bps=8 --endian=little -o - -
- * Decompress FLAC-coded I/Q data:
- flac --force-raw-format --decode --endian=little --sign=unsigned - -
-'''
-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_thread.
-If watchdog_interval is 0, then watchdog thread is not started.
-
-'''
-cache_full_behaviour=2
-'''
- 0 = drop samples
- 1 = close client
- 2 = openwebrx: don't care about that client until it wants samples again (gr-osmosdr bug workaround)
-'''
-
-rtl_tcp_password=None
-'''
-This one applies to a special version of rtl_tcp that has authentication.
-# You can find more info here: https://github.com/ha7ilm/rtl-sdr
-# If it is set to a string (e.g. rtl_tcp_password="changeme"), rtl_mus will try to authenticate against the rtl_tcp server.
-'''
diff --git a/config_webrx.py b/config_webrx.py
index a4a6fdf..b79dbdc 100644
--- a/config_webrx.py
+++ b/config_webrx.py
@@ -66,8 +66,8 @@ sdrhu_public_listing = False
dsp_plugin="csdr"
fft_fps=9
fft_size=4096
-#samp_rate = 2048000
-samp_rate = 250000
+samp_rate = 2048000
+#samp_rate = 250000
center_freq = 145525000
rf_gain = 5
@@ -82,22 +82,18 @@ start_rtl_thread=True
# >> RTL-SDR via rtl_sdr
-start_rtl_command="rtl_sdr -s {samp_rate} -f {center_freq} -p {ppm} -g {rf_gain} - | nc -vvl 127.0.0.1 8888".format(rf_gain=rf_gain, center_freq=center_freq, samp_rate=samp_rate, ppm=ppm)
+start_rtl_command="rtl_sdr -s {samp_rate} -f {center_freq} -p {ppm} -g {rf_gain} -".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)
+#start_rtl_command="arecord -f S16_LE -r {samp_rate} -c2 -".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)
+#start_rtl_command="cat /dev/urandom | (pv -qL `python -c 'print int({samp_rate} * 2.2)'` 2>&1)".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...
@@ -112,5 +108,4 @@ client_audio_buffer_size = 5
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)
+iq_server_port = 4951 #TCP port for ncat to listen on. It will send I/Q data over its connections, for internal use in OpenWebRX. It is only accessible from the localhost by default.
diff --git a/openwebrx.py b/openwebrx.py
index 38b53b6..117f327 100755
--- a/openwebrx.py
+++ b/openwebrx.py
@@ -116,21 +116,14 @@ def main():
pass
#Start rtl thread
+ if os.system("ncat --version > /dev/null") == 32512: #check for ncat
+ print "[openwebrx-main] Error: ncat not detected, please install it! The ncat tool is a netcat alternative, used for distributing the I/Q data stream. It is usually available in the nmap package (sudo apt-get install nmap). For more explanation, look into the README.md"
+ return
if cfg.start_rtl_thread:
+ cfg.start_rtl_command += "| ncat -4l %d -k --send-only --allow 127.0.0.1" % cfg.iq_server_port
rtl_thread=threading.Thread(target = lambda:subprocess.Popen(cfg.start_rtl_command, shell=True), args=())
rtl_thread.start()
print "[openwebrx-main] Started rtl_thread: "+cfg.start_rtl_command
-
- #Run rtl_mus.py in a different OS thread
- python_command="pypy" if pypy else "python2"
- 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.
- print "[openwebrx-main] Started rtl_mus."
time.sleep(1) #wait until it really starts
#Initialize clients
diff --git a/plugins/dsp/csdr/plugin.py b/plugins/dsp/csdr/plugin.py
index 091b218..ecbfffc 100644
--- a/plugins/dsp/csdr/plugin.py
+++ b/plugins/dsp/csdr/plugin.py
@@ -46,13 +46,9 @@ class dsp_plugin:
self.format_conversion = "csdr convert_u8_f"
self.base_bufsize = 512
self.nc_port = 4951
- try:
- 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 127.0.0.1 {nc_port} | csdr setbuf {start_bufsize} | csdr through | "+self.format_conversion+(" | " if self.format_conversion!="" else "") ##"csdr flowcontrol {flowcontrol} auto 1.5 10 | "
+ any_chain_base="ncat -v 127.0.0.1 {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":
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":
diff --git a/rtl_mus.py b/rtl_mus.py
deleted file mode 100644
index 9f5e230..0000000
--- a/rtl_mus.py
+++ /dev/null
@@ -1,539 +0,0 @@
-'''
- 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-2015 by Andras Retzler
-
- 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 .
-
------
-
-2013-11? Asyncore version
-2014-03 Fill with null on no data
-
-'''
-
-import socket
-import sys
-import array
-import time
-import logging
-import os
-import time
-import subprocess
-import fcntl
-import thread
-import pdb
-import asyncore
-import multiprocessing
-import signal
-#pypy compatiblity
-try: import dl
-except: pass
-
-import code
-import traceback
-
-def handle_signal(signal, frame):
- log.info("Ctrl+C: aborting.")
- os._exit(1) #not too graceful exit
-
-def ip_match(this,ip_ranges,for_allow):
- if not len(ip_ranges):
- return 1 #empty list matches all ip addresses
- for ip_range in ip_ranges:
- #print this[0:len(ip_range)], ip_range
- if this[0:len(ip_range)]==ip_range:
- return 1
- return 0
-
-def ip_access_control(ip):
- if(not cfg.use_ip_access_control): return 1
- allowed=0
- if(cfg.order_allow_deny):
- if ip_match(ip,cfg.allowed_ip_ranges,1): allowed=1
- if ip_match(ip,cfg.denied_ip_ranges,0): allowed=0
- else:
- if ip_match(ip,cfg.denied_ip_ranges,0):
- allowed=0
- if ip_match(ip,cfg.allowed_ip_ranges,1):
- allowed=1
- return allowed
-
-def add_data_to_clients(new_data):
- # might be called from:
- # -> dsp_read
- # -> rtl_tcp_asyncore.handle_read
- global clients
- global clients_mutex
- clients_mutex.acquire()
- for client in clients:
- #print "client %d size: %d"%(client[0].ident,client[0].waiting_data.qsize())
- if(client[0].waiting_data.full()):
- if cfg.cache_full_behaviour == 0:
- log.error("client cache full, dropping samples: "+str(client[0].ident)+"@"+client[0].socket[1][0])
- while not client[0].waiting_data.empty(): # clear queue
- client[0].waiting_data.get(False, None)
- 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(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")
- else:
- client[0].waiting_data.put(new_data)
- clients_mutex.release()
-
-def dsp_read_thread():
- global proc
- global dsp_data_count
- while True:
- try:
- my_buffer=proc.stdout.read(1024)
- except IOError:
- log.error("DSP subprocess is not ready for reading.")
- time.sleep(1)
- continue
- add_data_to_clients(my_buffer)
- if cfg.debug_dsp_command:
- dsp_data_count+=len(my_buffer)
-
-def dsp_write_thread():
- global proc
- global dsp_input_queue
- global original_data_count
- while True:
- try:
- my_buffer=dsp_input_queue.get(timeout=0.3)
- except:
- continue
- proc.stdin.write(my_buffer)
- proc.stdin.flush()
- if cfg.debug_dsp_command:
- original_data_count+=len(my_buffer)
-
-class client_handler(asyncore.dispatcher):
-
- def __init__(self,client_param):
- self.client=client_param
- self.client[0].asyncore=self
- 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
- new_command = self.recv(5)
- if len(new_command)>=5:
- if handle_command(new_command, self.client):
- commands.put(new_command)
-
- def handle_error(self):
- exc_type, exc_value, exc_traceback = sys.exc_info()
- log.info("client error: "+str(self.client[0].ident)+"@"+self.client[0].socket[1][0])
- traceback.print_tb(exc_traceback)
- self.close()
-
- def handle_close(self):
- self.client[0].close()
- log.info("client disconnected: "+str(self.client[0].ident)+"@"+self.client[0].socket[1][0])
-
- def writable(self):
- #print "queryWritable",not self.client[0].waiting_data.empty()
- return not self.client[0].waiting_data.empty()
-
- def handle_write(self):
- global last_waiting
- global rtl_dongle_identifier
- global sample_rate
- if not self.sent_dongle_id:
- self.send(rtl_dongle_identifier)
- self.sent_dongle_id=True
- return
- #print "write2client",self.client[0].waiting_data.qsize()
- next=self.last_waiting_buffer+self.client[0].waiting_data.get()
- sent=asyncore.dispatcher.send(self, next)
- self.last_waiting_buffer=next[sent:]
-
-class server_asyncore(asyncore.dispatcher):
-
- def __init__(self):
- asyncore.dispatcher.__init__(self)
- self.create_socket(socket.AF_INET, socket.SOCK_STREAM)
- 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):
- global max_client_id
- global clients_mutex
- global clients
- my_client=[client()]
- my_client[0].socket=self.accept()
- if (my_client[0].socket is None): # not sure if required
- return
- if (ip_access_control(my_client[0].socket[1][0])):
- 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(500)
- clients_mutex.acquire()
- clients.append(my_client)
- clients_mutex.release()
- handler = client_handler(my_client)
- log.info("client accepted: "+str(len(clients)-1)+"@"+my_client[0].socket[1][0]+":"+str(my_client[0].socket[1][1])+" users now: "+str(len(clients)))
- else:
- log.info("client denied: "+str(len(clients)-1)+"@"+my_client[0].socket[1][0]+":"+str(my_client[0].socket[1][1])+" blocked by ip")
- my_client.socket.close()
-
-rtl_tcp_resetting=False #put me away
-
-def rtl_tcp_asyncore_reset(timeout):
- global rtl_tcp_core
- global rtl_tcp_resetting
- if rtl_tcp_resetting: return
- #print "rtl_tcp_asyncore_reset"
- rtl_tcp_resetting=True
- time.sleep(timeout)
- try:
- rtl_tcp_core.close()
- except:
- pass
- try:
- del rtl_tcp_core
- except:
- pass
- rtl_tcp_core=rtl_tcp_asyncore()
- #print asyncore.socket_map
- rtl_tcp_resetting=False
-
-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:
- self.connect((cfg.rtl_tcp_host, cfg.rtl_tcp_port))
- self.socket.settimeout(0.1)
- except:
- log.error("rtl_tcp connection refused. Retrying.")
- thread.start_new_thread(rtl_tcp_asyncore_reset, (1,))
- self.close()
- return
-
- def handle_error(self):
- global server_missing_logged
- global rtl_tcp_connected
- rtl_tcp_connected=False
- exc_type, exc_value, exc_traceback = sys.exc_info()
- self.ok=False
- server_is_missing=hasattr(exc_value,"errno") and exc_value.errno==111
- if (not server_is_missing) or (not server_missing_logged):
- log.error("with rtl_tcp host connection: "+str(exc_value))
- #traceback.print_tb(exc_traceback)
- server_missing_logged|=server_is_missing
- try:
- self.close()
- except:
- pass
- thread.start_new_thread(rtl_tcp_asyncore_reset, (2,))
-
- def handle_connect(self):
- 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")
- server_missing_logged=False
-
- def handle_close(self):
- global rtl_tcp_connected
- global rtl_tcp_core
- rtl_tcp_connected=False
- log.error("rtl_tcp host connection has closed, now trying to reopen")
- try:
- self.close()
- except:
- pass
- thread.start_new_thread(rtl_tcp_asyncore_reset, (2,))
-
- def handle_read(self):
- global rtl_dongle_identifier
- global dsp_input_queue
- global watchdog_data_count
- if(len(rtl_dongle_identifier)==0):
- rtl_dongle_identifier=self.recv(12)
- return
- new_data_buffer=self.recv(1024*16)
- if cfg.watchdog_interval:
- watchdog_data_count+=1024*16
- if cfg.use_dsp_command:
- dsp_input_queue.put(new_data_buffer)
- #print "did put anyway"
- else:
- add_data_to_clients(new_data_buffer)
-
- def writable(self):
-
- #check if any new commands to write
- global commands
- 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()
- self.send(mcmd)
-
-def xxd(data):
- #diagnostic purposes only
- output=""
- for d in data:
- output+=hex(ord(d))[2:].zfill(2)+" "
- return output
-
-def handle_command(command, client_param):
- global sample_rate
- client=client_param[0]
- param=array.array("I", command[1:5])[0]
- param=socket.ntohl(param)
- command_id=ord(command[0])
- client_info=str(client.ident)+"@"+client.socket[1][0]+":"+str(client.socket[1][1])
- if(time.time()-client.start_time client can't set anything until "+str(cfg.client_cant_set_until)+" seconds")
- return 0
- if command_id == 1:
- if max(map((lambda r: param>=r[0] and param<=r[1]),cfg.freq_allowed_ranges)):
- log.debug("allow: "+client_info+" -> set freq "+str(param))
- return 1
- else:
- log.debug("deny: "+client_info+" -> set freq - out of range: "+str(param))
- elif command_id == 2:
- log.debug("deny: "+client_info+" -> set sample rate: "+str(param))
- sample_rate=param
- return 0 # ordinary clients are not allowed to do this
- elif command_id == 3:
- log.debug("deny/allow: "+client_info+" -> set gain mode: "+str(param))
- return cfg.allow_gain_set
- elif command_id == 4:
- log.debug("deny/allow: "+client_info+" -> set gain: "+str(param))
- return cfg.allow_gain_set
- elif command_id == 5:
- log.debug("deny: "+client_info+" -> set freq correction: "+str(param))
- return 0
- elif command_id == 6:
- log.debug("deny/allow: set if stage gain")
- return cfg.allow_gain_set
- elif command_id == 7:
- log.debug("deny: set test mode")
- return 0
- elif command_id == 8:
- log.debug("deny/allow: set agc mode")
- return cfg.allow_gain_set
- elif command_id == 9:
- log.debug("deny: set direct sampling")
- return 0
- elif command_id == 10:
- log.debug("deny: set offset tuning")
- return 0
- elif command_id == 11:
- log.debug("deny: set rtl xtal")
- return 0
- elif command_id == 12:
- log.debug("deny: set tuner xtal")
- return 0
- elif command_id == 13:
- log.debug("deny/allow: set tuner gain by index")
- return cfg.allow_gain_set
- else:
- log.debug("deny: "+client_info+" sent an ivalid command: "+str(param))
- return 0
-
-def watchdog_thread():
- global rtl_tcp_connected
- global rtl_tcp_core
- global watchdog_data_count
- global sample_rate
- zero_buffer_size=16348
- second_frac=10
- zero_buffer='\x7f'*zero_buffer_size
- watchdog_data_count=0
- rtl_tcp_connected=False
- null_fill=False
- time.sleep(4) # wait before activating this thread
- log.info("watchdog started")
- first_start=True
- n=0
- while True:
- wait_altogether=cfg.watchdog_interval if rtl_tcp_connected or first_start else cfg.reconnect_interval
- first_start=False
- if null_fill:
- log.error("watchdog: filling buffer with zeros.")
- while wait_altogether>0:
- wait_altogether-=1.0/second_frac
- for i in range(0,((2*sample_rate)/second_frac)/zero_buffer_size):
- add_data_to_clients(zero_buffer)
- n+=len(zero_buffer)
- time.sleep(0) #yield
- if watchdog_data_count: break
- if watchdog_data_count: break
- time.sleep(1.0/second_frac)
- #print "sent altogether",n
- else:
- time.sleep(wait_altogether)
- null_fill=not watchdog_data_count
- if not watchdog_data_count:
- log.error("watchdog: restarting rtl_tcp_asyncore() now.")
- rtl_tcp_asyncore_reset(0)
- watchdog_data_count=0
-
-
-
-def dsp_debug_thread():
- global dsp_data_count
- global original_data_count
- while 1:
- time.sleep(1)
- print "[rtl-mus] DSP | Original data: "+str(int(original_data_count/1000))+"kB/sec | Processed data: "+str(int(dsp_data_count/1000))+"kB/sec"
- dsp_data_count = original_data_count=0
-
-class client:
- ident=None #id
- to_close=False
- waiting_data=None
- start_time=None
- socket=None
- asyncore=None
-
- def close(self, use_mutex=True):
- global clients_mutex
- global clients
- 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()
- except:
- pass
- try:
- self.asyncore.close()
- del self.asyncore
- except:
- pass
- del clients[i]
- correction+=1
- if use_mutex: clients_mutex.release()
-
-
-def main():
- global server_missing_logged
- global rtl_dongle_identifier
- global log
- global clients
- global clients_mutex
- global original_data_count
- global dsp_input_queue
- global dsp_data_count
- global proc
- global commands
- global max_client_id
- global rtl_tcp_core
- global sample_rate
-
- #Set signal handler
- signal.signal(signal.SIGINT, handle_signal) #http://stackoverflow.com/questions/1112343/how-do-i-capture-sigint-in-python
-
- # set up logging
- log = logging.getLogger("rtl_mus")
- log.setLevel(logging.DEBUG)
- formatter = logging.Formatter('%(asctime)s %(levelname)s %(message)s')
- stream_handler = logging.StreamHandler()
- stream_handler.setLevel(logging.DEBUG)
- stream_handler.setFormatter(formatter)
- log.addHandler(stream_handler)
- file_handler = logging.FileHandler(cfg.log_file_path)
- file_handler.setLevel(logging.INFO)
- file_handler.setFormatter(formatter)
- log.addHandler(file_handler)
- log.info("Server is UP")
-
- server_missing_logged=0 # Not to flood the screen with messages related to rtl_tcp disconnect
- rtl_dongle_identifier='' # rtl_tcp sends some identifier on dongle type and gain values in the first few bytes right after connection
- clients=[]
- dsp_data_count=original_data_count=0
- commands=multiprocessing.Queue()
- dsp_input_queue=multiprocessing.Queue()
- clients_mutex=multiprocessing.Lock()
- max_client_id=0
- sample_rate=250000 # so far only watchdog thread uses it to fill buffer up with zeros on missing input
-
- # start dsp threads
- if cfg.use_dsp_command:
- print "[rtl_mus] Opening DSP process..."
- proc = subprocess.Popen (cfg.dsp_command.split(" "), stdin = subprocess.PIPE, stdout = subprocess.PIPE) #!! should fix the split :-S
- dsp_read_thread_v=thread.start_new_thread(dsp_read_thread, ())
- dsp_write_thread_v=thread.start_new_thread(dsp_write_thread, ())
- if cfg.debug_dsp_command:
- dsp_debug_thread_v=thread.start_new_thread(dsp_debug_thread,())
-
- # start watchdog thread
- if cfg.watchdog_interval != 0:
- watchdog_thread_v=thread.start_new_thread(watchdog_thread,())
-
- # start asyncores
- rtl_tcp_core = rtl_tcp_asyncore()
- server_core = server_asyncore()
-
- asyncore.loop(0.1)
-
-
-if __name__=="__main__":
- print
- print "rtl_mus: Multi-User I/Q Data Server for RTL-SDR v0.22, made at HA5KFU Amateur Radio Club (http://ha5kfu.hu)"
- print " code by Andras Retzler, HA7ILM "
- print " distributed under GNU GPL v3"
- print
-
- try:
- for libcpath in ["/lib/i386-linux-gnu/libc.so.6","/lib/libc.so.6"]:
- if os.path.exists(libcpath):
- libc = dl.open(libcpath)
- libc.call("prctl", 15, "rtl_mus", 0, 0, 0)
- break
- except:
- pass
-
- # === Load configuration script ===
- if len(sys.argv)==1:
- print "[rtl_mus] Warning! Configuration script not specified. I will use: \"config_rtl.py\""
- config_script="config_rtl"
- else:
- config_script=sys.argv[1]
- cfg=__import__(config_script)
- if cfg.setuid_on_start:
- os.setuid(cfg.uid)
- main()