Using Python to Proxy RTL SDR Data From rtl_tcp.exe

Posted:10/21/2015 4:00PM

Using Python to Proxy RTL SDR Data From rtl_tcp.exe

Mike Mclain discusses how to use Python to Proxy RTL SDR Data From rtl_tcp.exe to permit multiple client connections simultaneously.

Preface:

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).

The Problem:

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).

The Solution:

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.

Conclusion:

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)

Enjoy!

Comments:

comments powered by Disqus