Mike Mclain discusses how to use Python to Proxy RTL SDR Data From rtl_tcp.exe to permit multiple client connections simultaneously.
Over the last couple of years, I have been very interested in software defined radio (SDR) technology, and have routinely followed developments within the hobbyist (Realtek) RTL SDR (RTL2832U) community. Likewise, during this period of time, I have dabbled with a number of SDR projects (like GNU Radio, SDR Sharp, and SDR Console) along with other, more specific, RTL software tools (like rtl_tcp, and rtl_433).
Now, while such encounters are all --- well and good --- especially since most of the tools available are extremely flexible. Nevertheless, I have had a number of software snags over the years when it came to particular areas of interest (like decoding a whole Motorola type II trunking network simultaneously, or integrating a cheap Acurite thermometer into the Weather Underground accurately) because of innate limitations within the --- aforementioned --- software (with the connotation that specific problems typically merit specific solutions).
Conversely, while I have been working to develop my own software to implement my desired functionality (and learning DSP has been a fun adventure), I have also dabbled with cobbling together existing software solutions (like SDR Console and rtl_tcp) into something quickly functional (in the case of the Motorola type II trunking project) and have developed a Python script:
# A Simplistic Proxy Server to allow multiple clients to connect to the same rtl_tcp.exe instance import sys # Threaded Server Socket (TSsock) and Threaded Client Socket (TCsock) # are wrapper classes I wrote to add additional features to the standard python socket class. from TSsock import tssock from TCsock import tcsock # becuase we are streaming data we should also have thread suport # this function also requires python threads from threading import Thread # all servers simply inherent the base threaded server socket class class proxyserver(tssock,Thread): # our class initialization def __init__(self): # setup the tssocket class tssock.__init__(self) # setup our inherented thread Thread.__init__(self) # Define the address and port of the rtl_tcp.exe instance self.RTL_TCP_Host="127.0.0.1" self.RTL_TCP_Port=1234 # Create a client connection to the rtl_tcp.exe instance print "Setup RTLSDR" self.device_info = None self.rtlsdr = tcsock() # The read buffer size is a rather tricky attribute given the data # rate of the rtl_tcp.exe instance, this value works but your mileage may vary. self.rtlsdr.set_read_buffer_size(1024*10) # Startup connections to rtl_tcp.exe instance results additional device info # being sent that must be processed differently than normal IQ traffic so we # use a startup function here and swap over after this data is obtained. self.rtlsdr.data_received_event=self.event_rtl_sdr_data_arrive_startup self.rtlsdr.connect(self.RTL_TCP_Host,self.RTL_TCP_Port) # rtl_tcp.exe will send 12 bytes of data with device info that we must buffer # so do so def event_rtl_sdr_data_arrive_startup(self,client,data): if len(data)<12: # we dont have all 12 bytes, so tell tcsock to keep the data we have so far return 0 self.device_info = data[0:12] print "Device Info Set To ",self.device_info # once we have that info swap to the normal IQ processor self.rtlsdr.data_received_event=self.event_rtl_sdr_data_arrive # tell our tcsock to eat 12 bytes of data return 12 # handle iq data from rtl_tcp.exe def event_rtl_sdr_data_arrive(self,client,data): # send this data to all connected clients for index,aclient in enumerate(self.clients): #print "write",index,len(data) aclient.write(data) # tell tcsock to eat the data len return len(data) # this event occurs when a client received data def event_data_received(self,client,data): self.rtlsdr.write(data) #return len(data) #return super(proxyserver, self).event_data_received(client,data) # this event occurs when a client is connected to the server def event_client_connected(self,client): print "Client Connected" if not self.device_info == None: client.write(self.device_info) # the default action is to ignore the event pass # this event occurs if the client connection is closed def event_client_close(self,client,code): print "Client Closed" return super(proxyserver, self).event_client_close(client,code) if __name__ == "__main__": #setup SDR Proxy Server # Connect via 127.0.0.1 on port 7000 rather than 127.0.0.1 on port 1234 Proxy_Server = proxyserver() Proxy_Server.listen(7000) while 1: line = raw_input ("type q to quit> ") if line == "q": break print "Stop RTL SDR Link" Proxy_Server.rtlsdr.shutdown() print "Stop Proxy Server" Proxy_Server.shutdown()
to allow multiple SDR Console instances to obtain data from a single rtl_tcp instance.
While such notions might seem a little bit extreme (especially since SDR Console has 8 VFOs) and a RTL SDR dongle is bandwidth limited to around 1.5MHz; however, the ability to mirror information from the rtl_tcp application can be highly beneficial (especially during the development of other SDR applications) and I figured I would share my tools with the community at large (although I believe other projects similar to mine also exist)
By Mike Mclain