GNSS Firehose update

Some updates on the GNSS Firehose system as it approaches general usability:

  • now supports three 50 MHz RF channels (nominally covering L1/L2/E5), 840 Mbit/s total payload
  • supports command and status over Ethernet (in addition to UART)
  • firmware now in C, running on a RISC-V soft CPU (picorv32 from Clifford Wolf)
  • change over to KiCad for schematic and PCB layout

The enclosure is the same as before, a Hammond 1455L1201, with fancier artwork this time:




A run of 20 boards was assembled, with a very pleasant yield of 100% (thanks, Sparqtron!). Here’s a shot of some of them. They have yet to receive the small 3D-printed plastic cap for the TCXO. That turns out to be quite important to reduce air currents near the device—it dramatically improves the performance of the local clock, so that, for a reasonably strong signal, there’s only the occasional excursion of 1 Hz or so over timescales of a few seconds. (I should make an Allan-variance plot from one of the PLL tracks.)


GNSS Firehose status, example L1/L2 sky recording

Just a quick update on the GNSS Firehose digitizer project. I’ve decided to get a few systems professionally assembled; they will be similar to this prototype unit:


Here is some sample data, a sky recording taken on May 6 at around 13:38 UTC:

gnss-3.dat (743 MByte)

This represents 10.2 seconds of data simultaneously sampled on bands centered at ~1584.8 MHz and ~1227.7 MHz; each channel has a useful bandwidth of about 50 MHz. The format is raw Ethernet packets as written by tcpdump. The packet2wav utility unpacks the various sample streams from these packets and checks timestamps.

To use this file with my software receiver, here’s a command to acquire all the GPS L1 C/A signals in view:

$ <gnss-3.dat packet2wav | ./ /dev/stdin 69984000 -9334875
prn   1 doppler  3200.0 metric  3.45 code_offset  863.2
prn   2 doppler  1800.0 metric  1.45 code_offset  603.4
prn   3 doppler  4200.0 metric  2.33 code_offset  456.6
prn   4 doppler  1000.0 metric 10.23 code_offset  134.4
prn   5 doppler -3600.0 metric  1.54 code_offset   57.2
prn   6 doppler -4800.0 metric  1.46 code_offset  433.3
prn   7 doppler  -800.0 metric  1.45 code_offset  222.5
prn   8 doppler  3000.0 metric  1.46 code_offset  488.3
prn   9 doppler   400.0 metric  1.57 code_offset  362.4
prn  10 doppler  3400.0 metric  1.54 code_offset  506.3
prn  11 doppler  1000.0 metric  7.62 code_offset  728.8
prn  12 doppler  -400.0 metric  1.46 code_offset   93.4
prn  13 doppler -2600.0 metric  1.44 code_offset  595.4
prn  14 doppler  -800.0 metric  7.23 code_offset  558.5
prn  15 doppler  -600.0 metric  1.51 code_offset   90.7
prn  16 doppler     0.0 metric  1.43 code_offset  772.2
prn  17 doppler  3200.0 metric  1.44 code_offset  301.7
prn  18 doppler -1200.0 metric  2.20 code_offset  760.5
prn  19 doppler -1600.0 metric  1.59 code_offset  657.4
prn  20 doppler -4400.0 metric  1.50 code_offset  910.4
prn  21 doppler  2200.0 metric  1.42 code_offset  674.6
prn  22 doppler  -800.0 metric  6.54 code_offset  923.6
prn  23 doppler  4200.0 metric  2.08 code_offset   94.4
prn  24 doppler  2200.0 metric  1.55 code_offset  406.4
prn  25 doppler  3000.0 metric  4.29 code_offset  513.2
prn  26 doppler  1600.0 metric  1.47 code_offset  628.4
prn  27 doppler   600.0 metric  1.51 code_offset  135.4
prn  28 doppler   200.0 metric  1.45 code_offset  631.4
prn  29 doppler  1600.0 metric  1.47 code_offset  378.4
prn  30 doppler -2800.0 metric  1.52 code_offset  737.5
prn  31 doppler  3000.0 metric  6.61 code_offset  367.1
prn  32 doppler  2600.0 metric  5.98 code_offset  176.8
prn 133 doppler  1600.0 metric  3.20 code_offset  481.3
prn 135 doppler  1400.0 metric  3.82 code_offset   86.7
prn 138 doppler  1400.0 metric  2.67 code_offset  824.9

A plot of these acquisition metrics across the various PRNs:


Here’s a summary of the visible signals on L1 after acquisition is done across all the GNSS services:

GPS L1 C/A:      1 3 4 11 14 18 22 23 25 31 32 133 135 138
GLONASS L1 C/A:  (none found)
Galileo E1b:     14
Galileo E1c:     14
BeiDou B1I:      11 14

and on L2:

GPS L2CM:        1 3 25 31
GLONASS L2 C/A:  -2 -1 3 5 6
GLONASS L3I:     (none found)
GLONASS L3Q:     (none found)
Galileo E5bI:    14
Galileo E5bQ:    14
BeiDou B2I:      11 14

My L1/L2 antenna, an AeroAntenna AT2775-42, doesn’t quite cover GLONASS on L1. Occasionally a signal on channels -7 or -6 is strong enough to show up, but no luck for this capture. GLONASS L2 is fine though, as are the newer GLONASS L3 CDMA signals (not present in this capture unfortunately). Of course other signals such as L2CL and P will be acquirable and trackable as well, but they are more difficult to acquire blindly.

Just for fun, here are the raw samples from the L1 channel. Clearly not much can be seen from this, but it can be useful as a quick check. Samples are 2 bits (represented on the output stream as two’s-complement 8-bit for processing convenience), alternating I and Q, at 69.984 Msa/s.

$ <gnss-3.dat packet2wav | od -Ad -tx1 | head
0000000 ff 03 03 03 01 ff fd 01 ff 01 03 01 ff 01 03 fd
0000016 01 fd ff fd ff fd ff ff ff fd ff ff ff 03 ff 01
0000032 01 03 01 03 ff 03 03 03 01 ff ff ff ff 03 01 01
0000048 03 01 01 fd fd 01 01 03 01 01 01 01 03 01 01 03
0000064 fd fd fd ff fd 03 fd ff ff 03 01 ff ff fd fd 01
0000080 ff 01 03 ff ff ff 03 03 03 01 ff ff ff ff 01 ff
0000096 fd 01 ff ff 01 01 ff ff ff fd 03 fd ff fd ff fd
0000112 01 01 fd 01 ff fd ff 01 fd ff ff ff 01 01 ff ff
0000128 01 01 01 01 ff 01 03 01 03 ff 01 ff 01 01 01 01
0000144 01 fd fd 01 ff 01 01 ff 01 03 ff 01 01 03 01 01

And the raw samples from channel 2, the downconverter channel looking at L2:

$ <gnss-3.dat packet2wav 2 | od -Ad -tx1 | head
0000000 ff 01 fd 01 01 01 01 ff ff ff fd 01 ff 03 01 03
0000016 03 01 fd 01 fd ff ff 01 ff 03 fd 03 fd ff ff 01
0000032 03 ff 01 fd ff fd 01 01 ff 01 fd 01 01 fd ff ff
0000048 ff 01 03 ff 01 fd 01 ff 01 ff 03 ff ff ff ff ff
0000064 ff ff 01 fd 03 01 03 ff 01 fd ff 01 ff ff fd 01
0000080 ff 01 01 ff 03 01 01 03 03 01 01 ff ff ff 03 ff
0000096 ff ff 01 ff 01 ff 01 01 03 01 01 fd ff fd ff ff
0000112 fd 03 ff 01 ff ff 03 ff 01 ff fd 01 01 fd 01 fd
0000128 01 fd ff ff ff fd fd fd 01 01 01 03 ff 03 01 03
0000144 ff 01 ff 01 01 01 01 ff 03 01 01 ff ff ff ff fd

I plan to add the FPGA and software support for the third RF channel (set to cover L5 by default) over the next few weeks while the boards are being manufactured and assembled. I now have a simple helical antenna suitable for L5, so I should be able to do tri-band experiments.

Pointers to the GitHub repositories containing hardware design and software receiver: (contains packet2wav.c) (contains etc.)

New “GNSS Firehose” board

I’ve finally gotten around to updating the GNSS front-end digitizer. Along with a new Ethernet PHY chip (the old one from Vitesse seems to be no longer available), there is an external clock option, an expanded auxiliary header, and a number of small improvements in signal integrity. The external-clock header can accept an external OCXO or rubidium signal, for example; and multiple boards can be driven with a common clock.


Here’s a spectrum at L1. Despite the poor antenna placement (almost surrounded by tall trees), the GPS C/A signal shows up quite well as a broad peak of 2 MHz bandwidth. There is substantial ripple in the antenna’s ~35 MHz passband, and unfortunately the antenna filtering cuts off around 1595 MHz, so GLONASS signals are suppressed. The signals near 1557 MHz are probably satellite downlinks, and the peak near 1584 MHz is the receiver’s DC spur.


The usable alias-free bandwidth of the system is about 50 MHz per channel. At L1 this is enough to cover all the services, from BeiDou B1 starting at 1559 MHz to GLONASS extending to 1610 MHz.

Here’s a C/A correlation peak from this recording (PRN 13). The nice sharp corners are a result of using all of the C/A bandwidth:


Next steps are to clean up the software and HDL and to test the other two channels. See previous blog posts for a pointer to the GitHub repository containing the newly-updated design files.

SMT stencil cutting

I’ve been making some SMT stencils using a Silhouette Cameo craft cutter (vinyl cutter). It’s great for fast turnaround time and low materials cost, though the quality is not as high as a laser-cut stainless-steel stencil. Still, they’re useful down to 0.5 mm pitch and 0201, and possibly a little better, and that’s good enough for many applications.

Here’s a stencil cut by the Cameo. The partial QFP footprint is 0.5 mm pitch and the smallest discretes are 0402.

gerber2graphtec examples/test_0.5mm_0402.gbr >/dev/usb/lp0

Stencil 1

And a test coupon with QFP pitch from 0.65 mm to 0.3 mm, discretes from 0603 to 01005, and BGA pitch from 1.0 mm to 0.5 mm:


The web page that got me looking at craft cutters was this one:

These results are very nice, but on the software side I wanted something that fits into a normal PCB workflow with no hassle, by working directly from the solderpaste Gerber file as exported by a PCB CAM tool.

In addition, I wanted the best quality possible. Using the cutter in its default mode rounds off corners considerably due to the drag-knife mechanics, so instead I dice all features into individual line segments and draw them separately in multiple passes. Also, machine backlash is an issue, so the software works around that, at the expense of speed.

Fortunately, the low-level protocol for these machines has been documented, and the rest is mere geometry conversion that’s considerably helped by existing tools like gerbv and pstoedit. The software can be found here:

Also included are some example Gerber files. A test coupon with QFP/QFN and BGA pitches from 0.65 mm down to 0.3 mm and two-pad footprints from 0603 to 01005 is included, as well as a few larger examples.

The generated files run well on my Silhouette Cameo and probably on other similar Graphtec cutters as well.


Polyester film is a natural choice. It’s inexpensive, dimensionally stable, and very available in the form of laser-printer or copier transparency sheets. Thickness of these sheets is usually around 4 mils, close to the IPC-recommended values for fine-pitch work. Other thicknesses can be obtained easily enough from sources like McMaster-Carr.

I’m using Highland 901 sheets (a 3M brand apparently) together with full-sheet Avery labels, number 5353, as an adhesive backing sheet. The adhesive is a little too aggressive and can be difficult to remove cleanly once the stencil is finished. One can use Goo-Gone or similar citrus-oil cleaner to remove all the adhesive, and this results in a squeaky-clean stencil, but it takes a few minutes of extra time. Perhaps it would be better to use the cutter’s cutting mat, though cleaning off the small plastic chads is a bother too. Another option might be to use a separate full-sheet double-sided low-tack adhesive to laminate a plastic sheet to a plain paper backing.


Two aspects of the machine should be calibrated for best performance: cutting force and the spatial coordinate system.

For force, the software includes an example script that produces 30 small squares, each cut with a different force. Just have a look at the result to see which force settings result in good performance with your material stackup (mylar plus adhesive backing): first, a reasonable initial cut, to score the material, and second, a final pass that aims to cleanly separate the unwanted material from the stencil background.

For axis calibration, a script is provided to cut a calibration artifact. Measure the distance between marks along each direction (x, y, 45 degrees, and -45 degrees), then calculate a matrix to take out the distortion. (Rub in a bit of felt-tip-pen ink to make the marks more visible when comparing against a good ruler. The provided script produces a 17-step vernier against a 1/16-inch ruler; modify this for 11 steps against a 1 mm ruler if you’re using a metric ruler.) My machine is pretty reasonable in x, has a rather large 0.6% error in y, and has a skew of about 1 milliradian. After compensation I think the error is down to less than 0.1%. Even this is uncomfortably high: it is still a 50-micron positioning error across half the dimension of a 100 mm board.

Platform notes

So far I’ve run this only under Linux (Fedora), which provides a device node at /dev/usb/lp0 when the device is plugged in. Other platforms may need different device-driver arrangments. One can always send the output of gerber2graphtec to a file and deal with getting it to the cutter separately. Fortunately no feedback from the cutter seems to be necessary.

Application notes

Perhaps these stencils are best suited to prototyping that needs very fast turnaround. For example, it’s sometimes convenient to populate and test only parts of a board, and for this separate stencils can be cut for each region.

I plan to evaluate at some point this source of laser-cut Kapton stencils:

as well as the various lower-end laser-cut stainless vendors.

GNSS Firehose

Wideband front end for GPS, Glonass, Galileo, Compass

I’ve long wanted a fully flexible GPS receiver. Starting from the raw RF samples gives complete visibility into the signal processing and estimation algorithms for the observables. Unfortunately, existing commercial products, either in the test-equipment class (e.g. vector signal analyzer, USRP), the L1-only USB dongle class, or the “front-end box driving expensive closed-source GNSS software receiver” class, are either narrowband, expensive, bulky, power hungry, or perhaps all of these attributes together.

Especially after reading this paper I wanted a small, cheap front end that gives access to everything a software receiver could want. From there it’s a small matter of programming to derive useful measurements from the sample stream.

Pictured above is a prototype board with two of the three RF channels populated. (Once I have an antenna that reaches down to L5, perhaps a homemade helibowl, I’ll solder down the third channel.)

Goals for the project:

  • high-quality signals from all current and near-future GNSS systems (GPS, Glonass, Galileo, Compass)
  • wide bandwidth—provides three 50 MHz channels, nominally at L1, L2, and L5
  • low cost—currently about $170 parts cost in single quantity, ~$110 in qty 100
  • simplicity of use—emits streams of 2-bit samples to gigabit Ethernet, feeding a downstream software-receiver farm
  • two baseband clock inputs for use by timing receivers—any combination of 10 MHz, 100 MHz, 1 PPS
  • tunability typically from 0.7 to 2.2 GHz on each channel independently, for non-GPS applications such as radio astronomy
  • easy to fabricate and procure parts—4-layer PCB, everything available from friendly distributors such as Digikey and Mouser
  • free and open-source licensing: TAPR Open Hardware License version 1.0 for hardware, GPLv2 for HDL, firmware, and software

Design files, including schematic, PCB artwork, HDL, and software, are available at my github repository:

Here is a sky recording of L1 and L2 with 2-bit samples at 64 MHz in libpcap/tcpdump format. The github software has a tool to extract samples, but briefly, this file has 20000 packets, each with 1024 byte payload; each byte is {I_L1, Q_L1, I_L2, Q_L2} where each field is 2 bits; samples are encoded as 00, 01, 10, 11 from most negative to most positive; and the center frequencies are 65*fref for L1 and 51*fref for L2 where fref is 24.380952 MHz. (In any given byte the L1 and L2 samples are simultaneous, modulo any small yet-to-be-characterized interchannel biases (of order ~100 ps perhaps).) Thus GPS L1 is offset by -9.341880 MHz plus any particular satellite’s doppler, and L2 is offset by -15.828552 MHz plus doppler. Length of capture is 0.32 seconds.


There are some strong L1 and L2C signals in this recording, though my antenna location could be better (trees). Use your favorite software receiver (e.g. fastgps) to acquire and track. For the next spin I may change the TCXO from 40 MHz to 38.88 MHz, since that frequency seems to be more available from the distributors.

I’m still in the process of characterizing the prototype; also some HDL for ancillary functions like AGC needs to be written—this and a few other configuration tasks are currently driven from an external PC, so the board is not quite autonomous yet.

Design considerations

While I considered direct sampling, the overall design ended up as a classical direct-conversion quadrature receiver much like the USRP’s DBSRX2 board: LNAs, followed by MAX2112 downconverters with pretty reasonable integrated synthesizers (running in integer-N mode for repeatable interchannel phase), followed by 8-bit ADCs. For clocks I went with a TCXO (or, optionally in a future revision, the external 10 MHz or 100 MHz reference) driving National’s LMK03806 low-jitter clock synthesizer.

For output format, Ethernet is attractive. USB might be a little cheaper and a little more convenient for small deployments, but the low data rate of USB 2.0 is a showstopper, ubiquitous and easily-embeddable USB 3.0 is not quite here yet, and the clear trend in radio astronomy at least is flexible commodity networking feeding general-purpose receiver farms. I wanted something that could fit into that mold. An emerging radio-astronomy packet format, VDIF, might be a good way to go; are there GNU Radio sources and sinks for VDIF yet? Currently I’m just using raw broadcast Ethernet packets and tcpdump (though perhaps gulp would be better) for capturing the ~800 Mbit/s stream on a Linux box.


It would be nice to have a software-receiver chain that gives very high quality GNSS code and phase observables for every open or semi-open signal available. These could be dumped to a RINEX file for postprocessing or used in real time for navigation or timing. Timing, in particular, could benefit from dual- or triple-frequency observables, multi-GNSS processing (especially with the Galileo clocks as they are launched), and the availability of real-time clock information from IGS in the NTRIP format. The usual single-frequency autonomous GPSDO seems a bit limited. I’d like a multi-frequency, multi-system GNSSDO that is getting up-to-the-second clock and orbit data from the net. While I have no direct experience with systems of this type yet, from what I can tell, reliable real-time timing at the few-ns level might be possible (relative to some notional UTC(GPS+IGS/NTRIP+other_metadata) timescale), along with frequency comparisons at the ~5e-15/day level with suitable postprocessing.

Increasingly, open-source software is filling in these areas. Interesting projects include RTKLIB, GPSTk, and GNSS-SDR:

The only thing missing seems to be inexpensive wideband front-end hardware, including, by the way, inexpensive antennas with full frequency coverage and stable phase center—still thinking about that. Certainly for L5/E5, wide bandwidth is required, and for L1 and L2 as well when going the semicodeless route with the P(Y) signals.

I’ll put descriptions of any project updates in future posts.