BorIP is a system that allows you to control a Software Defined Radio over a network, and stream the received baseband samples from your remote hardware back to a client application (e.g. HDSDR or GNU Radio). The following multi-platform BorIP servers therefore allow you to make your SDR hardware available on your LAN for remote control.
To learn about the protocol (control channel and baseband format), please see the BorIP page.
The Windows server (available on the USRP Interfaces page) is a self-contained server with support for the same devices at the ExtIO plugin:
- USRP accessible through the Legacy and UHD hosts
- Realtek RTL2832U-based DVB-T dongles
- the FUNcube Dongle
If you feel brave, you can compile GNU Radio for Windows using MinGW or Cygwin, and then run the Linux/Mac version (below) on Windows too!
Linux & Mac
This is available in the apps directory of gr-baz as a Python 2.x program:
Currently, there is support for:
HDSDR under Wine
HDSDR works well under Wine on both Linux and Mac OS X.
Install it the same way (using Wine), along with the ExtIO plugin, by using the bundled installer from the USRP Interfaces page.
Then connect to a Remote device using the Device Control window, and set the address to localhost to connect to the BorIP server running on your own computer. It will then send baseband samples via the loopback interface!
For a demonstration on both platforms, please watch the videos referenced in the Introduction.
If you encounter any issues, please consult this thread on the ultra-cheap-sdr forum.
- If the installer fails to install the VC2008 runtime, you can do this manually instead.
- The first time you run HDSDR with the ExtIO plugin installed, the Device Control window may be created behind the main HDSDR window (you may think instead the ExtIO hasn't loaded) - make sure you look behind HDSDR.
- The program may appear to freeze after clicking 'Create' in the Device Control window, or 'Start' in the HDSDR window. Simply move the mouse over either window and it will start working again.
Setting up OS X
Alain De Carolis says in this thread (edited after I have successfully replicated the following steps):
- Install Xcode with command line utilities
- Install MacPorts
- Install GNU Radio by issuing (will take a long time):
sudo port install gnuradio
- Get gr-baz from SVN or git
- Change the 'bootstrap' script on line 28 from:
'libtoolize --automake -c -f'
'glibtoolize --automake -c -f'
- Compile and install gr-baz
- Copy the files inside gr-baz/grc/ to ~/.grc_gnuradio/
- Run the following command:
Remember the last step for all future Terminal sessions!
Usage: borip_server.py: [options] Options: -h, --help show this help message and exit -l LISTEN, --listen=LISTEN server listen port -p PORT, --port=PORT default data port -v, --verbose verbose output -g, --debug debug output -c, --command command output -r, --realtime realtime scheduling -R, --no-reload disable dynamic reload -d DEVICE, --device=DEVICE immediately create device -L, --lock lock device -D DEFAULT, --default=DEFAULT device to create when default hint supplied
Notes on Included Device Flowgraphs
The following titles are the names of the GRC flowgraphs (not the generated Python file).
- This uses my patched version of the Legacy USRP Source which outputs status messages (i.e. uO overruns, etc).
- If you wish to use this with the original (un-patched) version, load the GRC file, and turn the Accept status property Off in the UDP Sink. Otherwise you will get a complaint about setting up a message queue at device creation.
- Make sure that the 'unit' Parameter is first in the __init__ constructor if you re-generate the file!
- This will generate a self-contained top block.
- Only three supported arguments currently: tuner, readlen & buf.
- This will generate a Hierarchical block.
- Only three supported arguments currently: tuner, readlen & buf.
- Remember: the generated file will be placed in ~/.grc_gnuradio/.
Please note that the architecture of this version means the above main Python server program does not contain any native hardware interfaces (like the Windows version). It dynamically loads separate Python modules at runtime (e.g. GNU Radio flowgraphs) that provide access to hardware receivers, based on the Device Hint supplied at the client.
If you wish to add your own interfaces to access other hardware, you may:
- Write your own, and implement the Device and NetworkTransport classes (see class interfaces in apps/borip_server.py
- Create a GNU Radio flowgraph (e.g. in GR Companion), generate the Python code, and the server will automatically load it using a wrapper to make it look like the above classes. The name in the GRC Options block should be the same as the Device Hint you intend to use.
Note: the second method requires you have GNU Radio installed!
Place Python files in locations where they will be picked up by Python (e.g. the apps directory). You can also add paths to the PYTHONPATH environement variable.
The server will load files in the following manner:
- Call the 'device hint mapper' function with the Device Hint from the connected client, and see if it returns a name
- If no name is returned, then use the first (non-whitespace) part of the Device Hint as the name
The server will then attempt to import the following Python module (file):
Certain Device Hints (e.g. Legacy USRP device index, UHD Device Hint) are not suitable for direct use in a file name, which is where the 'device hint mapper' is used.
The default function is:
def _default_device_hint_mapper(hint): if hint is None or len(hint) == 0 or hint == "-": return "usrp_uhd" parts = hint.split(" ") try: idx = int(parts) return "usrp_legacy" except: pass try: kv = parts.index('=') # key=value return "usrp_uhd" except: pass return parts.upper() # FCD, RTL
If you wish to create your own to support other devices, you can:
- edit this code directly, or (preferably):
- create another Python file called borip_server_devmap which will be imported on startup.
borip_server_devmap should contain a function called device_hint_mapper that should conform to the same interface as _default_device_hint_mapper above (take a string, and return one).
IMPORTANT: If you are creating Hierarchical Blocks, GRC will place the generated Python code in this directory:
For the server to pick up files in this path, you could do something like:
REMEMBER: You must set the name of the generated file in the Options block in GRC! Otherwise it will just end up 'top_block' and will not be picked-up by the server!
GNU Radio Flowgraph Wrapper
The wrapper will attempt to find 'helpers' in the flowgraph to fill in the information it needs. Helpers are Python attributes that are either normal (value) variables, or functions. You can create a 'function' variable easily in GRC by using the Variable block and entering a lambda function as the value.
The search order for helpers is:
- the local flowgraph object
- the local source (the GRC block, and therefore variable, should be called source)
- the local flowgraph's parent (when a Hierarchical Block is loaded by the ServerWrapper class)
Please see the GnuRadioDevice implementation of the Device class to see which helpers are sought after by which functions, and in what order.
The list so far is:
source_name, name (for 'name', try local source first) serial, serial_number gain_range master_clock samples_per_packet, recv_samples_per_packet antennas set_gain gain set_samp_rate, set_sample_rate samp_rate, sample_rate set_freq, set_frequency freq, frequency was_tune_successful, was_tuning_successful tune_tolerance, tuning_tolerance last_tune_result set_antenna antenna
GRC Parameter blocks introduce variables to the __init__ constructor of the flowgraph. These will be filled in by additional arguments on the Device Hint. However only those Parameters that exist will be accepted during instantiation of the generated Python class!
If you are creating a Hierarchical Block, and wish to output status messages from the source block, you can use the Message Callback in dummy mode (and the Variable value set to '0'). The Message Callback won't actually call a function - it is just there so the BorIP server can get a handle on the message queue that GRC generates for it.
Self-contained Server in BorIP Sink
This will start the server internally, but external devices will not be created. Any Device Hint from a client will be ignored.
To prevent the sample rate being changed (i.e. stop the client from changing the samp_rate variable), create two Variable blocks:
lambda f: self.samp_rate
To set the number of samples transmitted in each packet, create a samples_per_packet Variable and set its value (this will set the payload size of the internal BorIP-enhanced UDP Sink block).
IMPORTANT: Remember to set the value of the BorIP Sink's Contant Multiplier property to ensure a normalised floating-point sample stream ([-1, +1]) is expanded to the range of a 16-bit signed integer ([-32768, +32767]), otherwise you won't get anything useful at the client!