3D Printing a USRP B200 Mini Case


[edit] Download or Order this model here

USRPs are incredibly handy devices, they let us play all over the spectrum with the signal processing algorithms and software of the day.  The USRP B210 was an awesome step in practicality requiring only USB3 for I/O and power, minimizing the number of cables required to haul around.   However, it’s size and chunky case options have been a source of frustration.   It’s a nice thing to always keep with you, but when packing bags and conserving space, it just can’t always make the cut.

The Ettus Research USRP B200 Mini recently changed all that by releasing a very compact version of the B200 which takes up virtually no space, but frustratingly doesn’t ship with a case to protect it from abuse!   Carrying around padded electrostatic wrap bags isn’t particularly appealing or protective, so I set about to put together a functional case for the device that would at least protect it from physical abuse.

The top and bottom case renderings of the resulting design are shown below, about the size of a stack of business cards. As long as a GPS-DO isn’t needed, this is now pretty much the perfect compact carrying companion for GNU Radio.

case_bottom case_top

The first print, on a pretty low end 3D printer is shown below.   After a few tweaks, fitment around the SMA plugs is very tight, the board fitment into the case is snug otherwise, a bit of space was added around the USB port to allow various sized plugs to clear it.

2016-02-09 (1)

For scale, we show it here next to a full size B210 + case.   While much of the design of the underlying board is the same here, the size reduction, and more tightly fitted case, and resulting hauling size of this device step is pretty amazing!

2016-02-10 (1)

The fit isn’t completely perfect, it could use a little bit more clearance in a few spots, but it shouldn’t be putting too much tension on any overly fragile areas, and seems like it could take quite a bit of beating.   We’ll see how long this one survives!

For anyone interested in having one of these, the STL Case Models have been made available for purchase, or download on shapeways at https://www.shapeways.com/shops/osh

A bit more eye candy below …

the micro-shibu




Note: I would suggest using something like a #4-40 thread and 3/8″length screw size for securing this, see links below.

Black #4-40, 3/8″ Machine Screws   Black #4-40 Hex Nut


Visualizing Radio Propagation in GNU Radio with gr-uhdgps, gr-eventstream and Google Earth

Radio propagation is a complex process in the real world, elaborate models are often used to predict expected propagation effects over known terrain, but in many cases there is no substitute for measuring ground truth.  This can now be accomplished quite easily, quickly, and cheaply using GNU Radio for a wide range of protocols.   Using gr-uhdgps (https://github.com/osh/gr-uhdgps), gr-eventstream (https://github.com/osh/gr-eventstream) and Google Earth, we demonstrate a tool to measure broadcast VHF FM Radio received signal strength over a metropolitan area. In this case we use WAMU 88.5 MHz, the local NPR radio station in Washington D.C., to demonstrate the tool and plot the results in Google Earth.

Building the GNU Radio Flowgraph

Looking at the WAMU FM Broadcast channel in a QtGui frequency and time sink plot is a good first sanity check.  Since FM broadcast is typically allocated a 200khz channel, we set up a USRP B210 to tune to 88.5 MHz with a complex sample rate of 200 ksamp/s shown below, the expected time and frequency spectrum behavior of the FM audio signal can be seen below.

      FM_WAMU_Plot   uhdgps_rssi_log.grc

In this flow graph we extract and event of 512 samples exactly every 1 second’s worth of samples from the incoming complex sample stream off the UHD source block.  Since we are throwing away the rest of the samples not extracted, this should be a low duty cycle, low CPU load process to run for the most part.   The trigger sample timer event allows us to trigger events at this sample-periodicity of a fixed length and pass them downstream to the “Handler PDU” block, this block simply converts the eventstream events into normal complex float PDUs which can be handled by any message passing block.

At this point we introduce a simple python message block called “CPDU Average Power” which computes the average sample power within the event’s samples, tags a “power” key and value to the PDU’s metadata structue and passes it downstream.    The functionality of this python block is contained within this simple python/numpy handler function which computes the power, adds the tag, and sends the output message — for now we are throwing away the PDU data field and sending PMT_NIL in its place since we don’t do anything with it downstream.

def handler(self, pdu):
    data = pmt.to_python(pmt.cdr(pdu))
    meta = pmt.car(pdu)
    data = data - numpy.mean(data) # remove DC
    mag_sq = numpy.mean(numpy.real(data*numpy.conj(data))) #compute average Mag Sq
    p = self.k + 10*numpy.log10(mag_sq)
    meta = pmt.dict_add(meta, pmt.intern("power"), pmt.from_float( p ) )
    self.message_port_pub( pmt.intern("cpdus"), pmt.cons( meta, pmt.PMT_NIL ) );

At this point we remove the “event_buffer” item from each PDU’s metadata structure using the “PDU Remove” block since it is also large and we don’t really want to keep it around downstream for any reason — trying to serialize this into our JSON storage would waste a lot of space and makes the message debug print outs massive.

Since the B210 is set up with an internal GPSDO board, we have both a GPS disciplined clock and oscillator as well as GPS NMEA fix data available from the UHD host driver.  By using the uhdgps gps probe block, we can poll the USRP source block for the NMEA fix data every time we need to store an updated signal strength record as shown in the flow graph above.  Passing our CPDUS through this GPS Probe block appends the NMEA data and a bunch of current USRP state information from the device which we can look at later.

Finally, we store a sequence of the JSON’ized PDUs to two files, one which has been filtered for records where “gps_locked” is “true” and one which simply stores all records regardless of GPS lock state.

Record Format

Each JSON’ized record corresponding to a single PDU from the flowgraph and a single eventstream extracted sample event, now looks like this in our output files.

We see that this structure now contains gps state information pulled from the GPS probe block, the stream tags which were added to the event/PDU by eventstream, and our power value computed by the python CPDU power block.

 "es::event_length": 512,
 "es::event_time": 20201024,
 "es::event_type": "sample_timer_event",
 "gain": 30.0,
 "gps_gpgga": "$GPGGA,003340.00,3852.6453,N,07707.3960,W,0,10,1.2,85.4,M,-34.7,M,,*6E",
 "gps_gprmc": "$GPRMC,003340.00,V,3852.6453,N,07707.3960,W,0.0,0.0,290315,,*3D",
 "gps_locked": "true",
 "gps_present": true,
 "gps_servo": "15-03-29 307 47943 -469.10 -3.21E-09 15 13 2 0x20",
 "gps_time": "1427589220",
 "power": -119.32362365722656,
 "ref_locked": "true",
 "rx_freq": [
 "rx_rate": [
 "rx_time": [

Drive Testing

To test this graph I securely fasten a macbook into the passenger seat, wedge a B210 between the center console and passenger seat of the Subaru test vehicle, and plunk a GPS antenna and a poorly band matched magnetic duck antenna onto the trunk of the car and go for a drive.   After launching the application, it takes a while for the GPSDO to obtain GPS lock since it is not network assisted like many devices these days.  After a few minutes of waiting and impatiently moving the GPS antenna to the roof of the car, I obtained lock and pretty much kept it for the entirety of my drive.

seat-scaled  antenna-scaled

Inspecting Results

After driving a thorough route around town and past the broadcast tower, I dump JSON file through uhggps’s json_to_kml.py tool to convert the rss measurements into a Google Earth friendly KML file with vertical lines of height proportional to the RSS at each event location. This provides a nice intuitive visual way to look at and understand received signal strength effects over various terrain changes, obstacles, urban canyons and other such phenomena.   An overview of the route can be seen here.


A few notable spots around the route are shown below.

Driving through Rosslyn, VA we can see massive variation in received signal strength at each sample point likely due to massive amounts of urban multi-path fading resulting in large amounts of constructive and destructive interference.


Driving from the George Washington Parkway North onto the US 1 memorial bridge on the far side of the river, we can see that going from a lower elevation shadowed by a small hill and a handful of trees up onto the elevated and unobstructed bridge resulted in a significant sustained climb in signal strength level.


Driving north on Connecticut Avenue, taking the tunnel underneath Dupont Circle, we briefly lose GPS lock, but upon reacquiring show a very low received signal strength down in the lowered, partially covered, and heavily shadowed roadway coming out of the tunnel.   To deal with this kind of GPS loss of lock we may need to implement some kind of host based interpolation of fixes occurring on either side of the positioning outage, but for now the samples are discarded.


There are numerous other effects that can be observed in the KML plot from the drive which is linked below.

KML Issues

One issue that I’m still considering is how exactly to map RSSI to amplitude, there are two modes which KML supports and each has their own issue.   These plots are using “aboveGroundLevel” which maps “0” elevation to the ground elevation at a specific LAT/LON, this is good because we can just use 0 for our base height and not worry about putting a line underground somewhere, however its annoying in that the line elevation tracks with the ground contours distorting your perception of RSSI a bit.   Absolute doesnt have this problem, since 0 is (I think) locked at sea level, however in places farther from the coast this is annoying because low elevations wind up underground.  Ultimately it might be neccisary to do some kind of aboveAverageGroundLevel for a local area, but this is as far as I know not supported in Google Earth / KML.


The resulting KML file can be opened in Google Earth from https://github.com/osh/kml/blob/master/WAMU_RSS.locked_1427589112.17.json.kml.   Countless hours of fun can be had zooming around inspecting fades that occur passing various obstacles along the way and signal strength variation going up and down hills in various areas.

This flow graph is available at https://github.com/osh/gr-uhdgps/blob/master/examples/uhdgps_rssi_log.grc   To use it make sure you have gr-eventstream, gr-uhdgps, and relevent python modules installed.