GNSS sky recordings

I’d like to make available to a wider audience a stream of GNSS sky recordings.

The recordings are made with the GNSS Firehose digitizers and Tallysman TW3972 antennas. Each recording is 200 ms long and contains three GNSS bands, each sampled at 70 MHz with a useful bandwidth of about 50 MHz. The schedule repeats every 5 minutes:

GPS time modulo 300 seconds Recorded bands
0 L1, L2, L5
150 L1, E6, L5

Thus, the cadence for L1 and L5 is every 150 seconds and for L2 and E6 every 300 seconds.

Here are the nominal center frequencies and spans for the various bands:

Band Center frequency, MHz Approximate useful span, MHz
L1 1584.754875 1558-1610
E6 1273.654125 1248-1300
L2 1227.727125 1201-1253
L5 1191.641625 1165-1217

I think this covers every GNSS signal except the S-band signal from IRNSS. (Incidentally, my antennas are not characterized for reception at E6, but the signal is good enough to be useful.)

Now, these are pretty short recordings, but they should still be long enough to obtain good-quality observables for both pseudorange and carrier phase (or at least the fractional parts thereof). The timing is chosen to coincide with observables from other GNSS receivers, which conventionally output estimates every 30 seconds (or faster), aligned with GPS time. The maximum difference in arrival times between observers on Earth is about 20 ms, so a recording duration of 200 ms guarantees at least 180 ms of overlap.

Currently I have two locations continuously uploading these recordings, one in Indiana, USA and one in California, USA. Indiana has the better sky coverage—about half the sky is available, from azimuth 85 to 275, down to elevation 10 degrees or so. I’d like to encourage others to upload waveforms of this type, so that worldwide signal monitoring becomes possible. Of course, IGS and IGS/MGEX have been covering this ground for many years, but at the observable level, not the waveform level.

The carrier-phase observables from successive waveform snippets hundreds of seconds apart will not be linked by integer cycles, unlike those from a continuously-tracking receiver (unless the receiver clock happens to be very stable, with TDEV(300s) << 1 cycle). But this is not an insurmountable drawback. The integers can be put back in by differencing against a nearby reference receiver that does continously track; alternatively, analysis methods can be used that don't depend on integer cycles, for example the ambiguity function.

Storage details

The files are being stored on Amazon’s S3 cloud-storage platform. For the first month, standard S3 is used, so the data is available immediately upon request (with request latency of less than one second). After one month, the files are migrated to S3 Glacier Deep Archive, which has much lower storage cost but retrieval latency of up to 48 hours. All the files are still accessible, but you’ll have to wait up to 48 hours to get a copy, so it’s best to do any processing (such as observable estimation, cross-correlation among stations, signal-health metrics, etc.) during the first month.

The S3 bucket is “s3://gnss-recordings-pmonta”, and its region is us-east-1 (Northern Virginia). If you want to do computation with these recordings, it would be cheapest to use EC2 compute resources in that region, since data transfer between S3 and EC2 is then free. If you transfer the files to other AWS regions, or download them over the Internet to your own storage and computation, then transfer costs are imposed.

If you upload GNSS waveforms to AWS, please also use us-east-1 if possible.

Speaking of costs, the S3 bucket is marked “public” and “requester pays”. This is so I don’t have to foot the bill for possibly voluminous worldwide downloads of these files (I do have to pay for the storage costs though). So downloading these files requires that you have an AWS account. I’d prefer to not have this speed bump, but I don’t see any way around it. Sorry. Maybe at some point AWS can make it part of their “free public data set” offering, in which case anonymous access would be free I think. Note that there is a free tier for AWS, which, after signup, allows a limited amount of storage, computation, and data transfer per month.

A shallow hierarchy is used inside the s3://gnss-recordings-pmonta bucket, of the form <year>/<doy>/filename, where <doy> is the ordinal day within the year ranging from 1 to 366. All dates and times are in GPS time, that is, TAI minus 19 seconds.

2020/
  029/
    ...
    PAOC00USA_2020029103500.json
    PAOC00USA_2020029103500_L1.iq.bz2
    ...
    SBTH00USA_2020029103500.json
    SBTH00USA_2020029103500_L1.iq.bz2
    ...

To reduce the friction of getting started with these files, I have a small subset of them available here on a public basis with no need for AWS credentials:

http://gnss-recordings-pmonta-sample.s3-website-us-east-1.amazonaws.com/index.html

This document is an index to the available files: two sets of recordings, seven hours apart, from the two sites. The sample files can be downloaded with a web browser using the provided links, or alternatively with wget as follows:

wget http://gnss-recordings-pmonta-sample.s3-website-us-east-1.amazonaws.com/PAOC00USA_2020029103500.json
wget http://gnss-recordings-pmonta-sample.s3-website-us-east-1.amazonaws.com/PAOC00USA_2020029103500_L1.iq.bz2
...

File format and naming

Each recording consists of a small amount of metadata and sample streams for the various RF bands. There are many choices for packaging them: HDF5, or the recent ION standard for GNSS waveform files [1], or the various RF/IF formats from radio astronomy. After reading this interesting essay about HDF5, archivalness, and software transparency [2], I opted for flat binary files compressed with a well-known compressor (bzip2), together with metadata in JSON. I don’t claim it’s the best choice, but at least it is easy to understand and can be easily translated into any of these other formats.

Prior to compression, the sample streams consist of 8-bit signed integers, alternating between in-phase and quadrature part (or real and imaginary part): i,q,i,q, etc. This is a common format for SDR sample streams. 8 bits is ordinarily enough precision for GNSS signals; in fact my recordings are 2-bit (4-level), with signal values -3,-1,1,3. (A signal value of 0 is used for missing data resulting from a dropped packet during recording, which is rare.) Higher-precision data, such as 3-bit or 4-bit, would fit seamlessly into this scheme. The compression reduces the file size to approximately the entropy of the underlying source.

File names roughly follow the RINEX3 convention:

  • a 9-character site string containing a 4-character site ID, 1-digit marker number, 1-digit receiver number, and 3-character country code
  • Date and time (GPS time)
  • Suffix, either .json for JSON metadata or .iq.bz2 for IQ sample data

A full set of four files for a given site and time looks like this:

PAOC00USA_2020029103500.json
PAOC00USA_2020029103500_L1.iq.bz2
PAOC00USA_2020029103500_L2.iq.bz2
PAOC00USA_2020029103500_L5.iq.bz2

This scheme results in many small files (about 1000 per site per day), but has the advantage that the user can request just the data desired.

Metadata

I chose metadata fields mostly inspired by RINEX3. In fact one goal is to be able to construct a single-epoch RINEX3 file from each file-set, and for that one needs observables from all the systems of interest and enough RINEX3 metadata to fill in the blanks.

Here is an example JSON file:

{
    "antenna": {
        "serial_number": "xxxx", 
        "type": "Tallysman TW3972"
    }, 
    "approx_position": [
        -2701201.6, 
        -4291624.4, 
        3855647.9
    ], 
    "bands": [
        {
            "center_freq": [
                5797, 
                256
            ], 
            "filename": "PAOC00USA_2020029103500_L1.iq.bz2", 
            "name": "L1", 
            "receiver_channel": 1
        }, 
        {
            "center_freq": [
                4491, 
                256
            ], 
            "filename": "PAOC00USA_2020029103500_L2.iq.bz2", 
            "name": "L2", 
            "receiver_channel": 2
        }, 
        {
            "center_freq": [
                4359, 
                256
            ], 
            "filename": "PAOC00USA_2020029103500_L5.iq.bz2", 
            "name": "L5", 
            "receiver_channel": 3
        }
    ], 
    "marker_to_ARP": [
        0, 
        0, 
        0
    ], 
    "observer": {
        "name": "Peter Monta"
    }, 
    "receiver": {
        "serial_number": "70:B3:D5:F7:90:09", 
        "software_version": "2", 
        "type": "GNSS Firehose"
    }, 
    "sample_rate": 69984000, 
    "site": {
        "country": "USA", 
        "marker_number": 0, 
        "name": "PAOC", 
        "receiver_number": 0
    }, 
    "time_duration": "0.2", 
    "time_start": "1580294099.9"
}

The fields are mostly self-explanatory (when read together with the RINEX3 spec) except for the “bands” list. For each band, a center frequency is given as a ratio of integers P/Q, which is to be multiplied by the sample rate. This represents the coherence between the sample rate and each channel’s downconverter local oscillator. If, for example, the user wanted to construct a single wideband signal from multiple overlapping subbands, then the exact ratios would allow the frequency difference to be set in stone, i.e., not estimated. Only the relative phases would need to be estimated. Those phases are fixed because the various PLLs in the receiver frequency chain never go out of lock. (What, never? Well, hardly ever.)

Licensing

I’m not an expert on copyright, but my intent is to provide these files to be used freely by anyone, roughly according to the Creative Commons license CC BY-SA 4.0. If the attribution or derivative portions of this license turn out to be unwieldy, especially if others contribute similar data, then it might be revised. I don’t know if entities like IGS or IERS have a formal license for their data, but if so, that might be a good model.

Applications

Possible applications, exclusive of the usual ones involving observables, positioning, etc.:

  • satellite health monitoring
  • receiver development and testing
  • observables obtainable only from waveforms, e.g. cross-correlation of GPS M code, Galileo PRS, etc.
  • cooperative estimation of things like W bits or M bits (requires many stations)
  • archival “history” of GNSS

Using the current upload schedule, the average data rate per station is about 1 Mbit/s, with a duty cycle of either 0.07% or 0.13% depending on the band. It is not currently feasible to provide megabit-class uplinks from GNSS stations in the middle of nowhere, although the large LEO constellations under construction might change that in the near future. Continuous waveforms would be even better [3], but currently require deep pockets for the storage and bandwidth. A reasonable path might be a gradual increase in duty cycle.

Disclaimer

I can’t make any guarantees about data uploads or data availability. “Best effort.”

References

[1] GNSS Software Defined Receiver Metadata Standard, http://sdr.ion.org.s3-website-us-east-1.amazonaws.com/
[2] Moving away from HDF5, https://cyrille.rossant.net/moving-away-hdf5/
[3] Considerations for Future IGS Receivers, http://www.ngs.noaa.gov/IGSWorkshop2008/docs/recDev-positionpaper.pdf

The L1C signal on GPS III

The recently-launched GPS III satellite, SVN 74, is transmitting on PRN 4, though not yet set healthy. But it’s a good opportunity to have a look at the L1C signal.

The pilot is TMBOC; here is its correlation peak:

peak_l1cp

It looks proper to me, though I haven’t compared it against the ideal waveform. The BOC(6,1) wiggles are clearly present, narrowing the central peak. Note the small amount of signal on the quadrature phase. This could have a number of sources: the passband of my receiver is not ideally flat in amplitude and group delay (I may add some equalization at some point), and the GPS L1 carrier is significantly offset from the center of the passband (by about 9 MHz) so as to accommodate GLONASS. The satellite signal itself could also be contributing. The recording was taken with a gain antenna (a 15-turn helical, about 11 dBi), and the integration time for the correlation was two seconds.

The data signal is pure BOC(1,1) with no BOC(6,1) content:

peak_l1cd

I’m not sure what tracking is most appropriate for GPS III. Of course the individual observables for C/A and L1C{p,d} are available, and, in the case of L1C, a combined mode with PLL tracking of the pilot and Costas-loop tracking of the data channel (or PLL tracking after data wipeoff), suitably weighted. But should a joint weighting of both C/A and L1C be considered, which would seem to include even more signal energy, yielding a higher-quality observable? There’s no RINEX name for this mode.

BeiDou B2b codes

It turns out to be an interesting exercise to extract the BeiDou B2b codes from GNSS recordings. If there were a pilot on B2b, there would have been plenty of SNR with even a short recording to see the pilot using a technique similar to that shown by Yudanov [1]. But, when assuming a pilot with period 100 ms, and then trying a great many other possible periods, there appeared to be nothing there.

As it turns out, there is no pilot. There are data signals on both B2bi and B2bq. One of them has what looks like a 2-second frame structure, but the data in the 2-second window changes periodically, so it is not a fixed pattern as the secondary code of a pilot would be.

So rather than coherently combining many samples of the pilot, we instead need to estimate the data symbols individually by differentially-coherently correlating a designated 1 ms interval with successive intervals. This entails a squaring loss. Even with signals of 45 to 47 dBHz during favorable passes, the QPSK data symbols are quite weak, and the chip decisions after noncoherently integrating for a few seconds are not error-free. But there is enough there to hard-limit the chip estimates, track the signal conventionally using this code-with-some-errors, and then use the track to refine the chip estimates to be truly error-free. (Perhaps there is some relationship to iterative decoding and belief propagation, and maybe a more sophisticated algorithm could deal optimally with even weaker signals.)

As an example, here is a plot of the QPSK B2b chips of BeiDou PRN 34, integrated for 4 seconds after wiping off the data modulation (using the unreliable estimates). The B2a signal is tracked (with its known code) to serve as a guide for code timing and to remove fluctuations of the local clock (a TCXO in my case). The carrier for B2b is then blindly applied as B2a + 30.69 MHz + a differential-doppler correction, which must be good to 0.1 Hz or so for the coherence to be maintained.

chips

The real part of these chips (after rotating a bit), hard limited, is taken as the B2bi code, and the imaginary part the B2bq code. Re-tracking with those codes yields a very much improved estimate of the code chips. One of the two quadratures, call it B2bi_improved, is shown below; the other is very similar when tracked using the other code. (I’m a little puzzled by the discrete nature of the imaginary part of these chips; perhaps it represents part of the ACEBOC multiplex. But the code is just taken as the real part.)

chips-tracked-b2bi-prn34

Tracking with this refined code yields the 1 ms data symbols shown below. Shown first are the complex values of the prompt correlator output (which are purely BPSK, showing that tracking is good), and second the real part over time, showing some of the structure of the data. (These are now the data symbols for B2bi over time, not the code chips.)

track-b2bi-prn34

track-b2bi-prn34-i

Codes for the current set of 18 MEO BeiDou-3 satellites are available here:

https://github.com/pmonta/GNSS-DSP-tools

Note that there is a 4-way ambiguity in each of these codes because of the (apparently equal-power) QPSK. But with the data format as yet unknown, this doesn’t matter. Or, possibly, looking at B2a (for example by implementing B2ab tracking) could resolve the ambiguity.

It might be possible to reverse-engineer the B2b codes to some underlying Gold-code parameters, if they are in fact of similar provenance as B2a, but there seems to be no tangible benefit to doing that. I think I’ll just leave them expressed as memory codes and wait for the ICD.

[1] https://www.gpsworld.com/signal-decoding-with-conventional-receiver-and-antenna-a-case-history-using-the-new-galileo-e6-bc-signal/

GNSS tri-band, quad-constellation sky recording

I’d like to share my first tri-band GNSS sky recording along with scripts for acquisition and tracking.

The recording is 50 seconds long with three channels of simultaneous 2-bit complex samples at 70 Msa/s (approx. 50 MHz usable bandwidth per channel) centered on L1, L2, and E5 (~1191 MHz). It’s therefore guaranteed to contain a complete data frame for each satellite—the length of an ephemeris frame is 30 seconds for all the GNSS systems, at least for the legacy data formats (though Galileo cuts this in half when using dual frequency via interleaving). An autonomous solution can be obtained for GPS, GLONASS, and Galileo (all of which have at least 4 satellites present), and nearly for BeiDou (only 3 satellites present).

Here is the recording in tcpdump / libpcap format. The utility packet2wav_3ch extracts one of the three RF channels from this packet stream and converts to a raw stream of signed 8-bit values for a downstream software receiver or other tool.

gnss-20170427-L1L2L5.pcap (5.4 GB)

By my count there are 34 satellites in this recording that are emitting 64 distinct GNSS signals (more if you include P/Y and M and CS/PRS codes and count I and Q separately). The satellites with active signals are as follows:

GNSS system PRNs or FDMA channels
GPS 2 4 5 12 13 15 18 20 21 25 26 29
GLONASS -7 -5 -3 -2 1 2 3 5 6
Galileo 7 12 14 19 20 24 26
BeiDou 7 14 34
SBAS 133 135 138

and the signals themselves:

GNSS system Band PRNs or FDMA channels
GPS L1 2 4 5 12 13 15 18 20 21 25 26 29
GPS L2C 4 5 12 25 26 29
GPS L5 25
GLONASS L1 -7 -5 -3 -2 2 5 6
GLONASS L2 -7 -5 -3 -2 1 2 3 5 6
GLONASS L3OC 9 26
Galileo E1 7 12 14 19 20 24 26
Galileo E5b 7 12 14 19 24 26
Galileo E5a 7 12 14 19 24 26
BeiDou B1I 7 14 34
BeiDou B2I 14
SBAS L1 135
SBAS L5 133 135 138

Below are plots of some of the acquisition metrics (but I’ve not yet automated any sort of detection threshold). Click for larger image:

acquisition-composite

The L5/E5 channel should support tracking of the full Galileo E5 signal (E5ab). I haven’t implemented this tracking mode yet, but it involves using a combination of a pure PLL for the pilot signals and a Costas loop for the data signals, optimally weighted and combined. (These would be the “C8X” and “L8X” observables in the taxonomy of RINEX 3.0.)

A makefile is provided in the GNSS-DSP-tools repository that will download the recording, run all the acquisition routines, and track the signals. Acquisition of GPS L1 is quick, but some other signal types are slower because they search all PRNs exhaustively and with a fairly high sensitivity.

Some miscellaneous notes on the signals:

  • I’m not sure why SBAS 133 and 138 are not showing up on L1. They have strong signals on L5, and other recordings in the past have shown them on both L1 and L5 with no trouble. Perhaps there’s some flaw in the acquisition related to the higher data rate on SBAS (although I do only 1 ms integrations, so it should not matter).
  • Same for GLONASS channels 1 and 3 on L1 vs. L2.
  • BeiDou PRNs 7 and 34 are apparently not transmitting on B2I. (PRN 34 is probably transmitting the BeiDou phase 3 L1 MBOC signal, incidentally.)
  • The GLONASS L3OC PRNs nominally run from 1 to 24, corresponding to the slot number, at least according to this GPS World article. Not sure why there’s a PRN 26 being transmitted by one of the GLONASS-K satellites. As I recall, there were other PRNs outside of the range 1–24 also used by GLONASS-K; perhaps the code assignments have changed over time.

The antenna is a homemade 3-turn helical with highpass filter and LNA, both from Mini-Circuits. It’s pretty much the simplest thing that could possibly work while still having acceptable bandwidth (1150–1610 MHz) and acceptable spatial pattern and circular polarization. Some slight interference from LTE is visible in the waterfall plots (using baudline), but it’s weak enough that the GNSS signals are not affected. Probably better preselection filtering is needed, or a stronger LNA, or both.

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:

f-perspective

f-front

f-back

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

f-board-6up

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:

gnss-firehose-proto-2

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 | ./acquire-gps-l1.py /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:

acquisition

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:

https://github.com/pmonta/GNSS_Firehose (contains packet2wav.c)
https://github.com/pmonta/GNSS-DSP-tools (contains acquire-gps-l1.py etc.)

Squaring receiver for Galileo blind search

I’ve been following the drama of the two recently-launched Galileo FOC satellites. To my knowledge, nothing is yet known about the status of the transmitters, nor have the PRNs been revealed, nor does an acquisition search over all 50 codes in the Galileo ICD yield any signals when the satellites are in view (though PRNs 11, 12, and 19 show up as expected).

So if the satellites are transmitting at all, perhaps it’s with a nonstandard code. One way to check is to take a trip back in time and revisit one of the very oldest GNSS receiver techniques, that of squaring a BPSK signal to recover a tone, then driving a PLL with this tone. (The Macrometer V-1000 receiver used this scheme.)

Now Galileo E1 when viewed in a 4 MHz bandwidth is not BPSK—it is, I believe, a three-level signal, {-1,0,1}, being the sum of equal-power E1-B and E1-C with negligible contribution in quadraphase from PRS. But squaring still works on a signal of this type, though not quite as well as with BPSK.

Here is a waterfall plot with several satellites visible, and indeed Galileo PRN 11 is one of them. Its identity is confirmed by acquiring and tracking in the conventional way, then verifying that the Doppler is identical (modulo the factor of 2 from squaring).

squaring

As of September 20, though, there is still nothing from the new satellites. A predicted Doppler curve can be derived from orbital elements, but there is nothing in the spectrogram at the anticipated Doppler. So we wait.

Code for this “receiver” (all of a dozen lines) is at the GitHub repository (squaring.py). Its output can be piped to the indispensable baudline spectrum analysis tool for interactive viewing.

GNSS stamp collecting

Here’s a fun montage of the various GNSS signals-in-space. I think this accounts for all extant open-access signals that are likely to remain so (ruling out Galileo E6 for example) and that are intended for systems having global coverage (so no QZSS or IRNSS). A few comments on the signals:

  • Each waveform is 400 ms in length and is sampled at 1 ms intervals after code and carrier wipeoff
  • Secondary codes have not been removed
  • Carrier-to-noise ratios vary. The weakest signals are Galileo E5a-I and E5a-Q; by eye there is not much to see, but the acquisition metric is clearly well above threshold, and the histograms are clearly bimodal. My L1/L2 antenna receives some L5 but with about 15 dB of attenuation (!). I really need an antenna that covers down to L5. The GPS L5 signals must have been blisteringly strong to come through as well as they did.
  • The pilot signals with no secondary codes, GPS L2CL and GLONASS L2 P, are shown correctly offset from the (undrawn) centerline. I’m not sure much is known about the data modulation on GLONASS P. Apparently L1 has some data at 50 Hz (no secondary/meander code?) and L2 is unmodulated.
  • So far there are two GLONASS satellites transmitting L3OC CDMA signals. The signals shown are from PRN 30, but PRN 33 is also active and of comparable quality.

The tools for acquiring and tracking these signals are in my GitHub repository:

https://github.com/pmonta/GNSS-DSP-tools

They are command-line-ish and not very polished yet.

track-collection

Height-based multipath mitigation

Here’s a crazy idea which I might as well put on the blog.

Multipath is an important error source for GNSS reference stations. Monuments for antennas are nearly always placed close to the Earth’s surface, so the ground will act as a reflector with a grazing geometry that generates short-delay multipath. Usually other objects contribute as well (nearby buildings or fences for example).

Many solutions exist for multipath mitigation, both at the antenna and correlator levels. Another possible system technique, though, would seem to be to move the antenna upwards, far enough away from local objects that any reflected signals have delays larger than the support of the autocorrelation function for any signal of interest.

Conceptually, an antenna could simply be placed at the top of a tall tower a few hundred meters in height. The tower would ideally be transparent to RF (perhaps of lightweight dielectric construction). Of course there are many practical problems with this, but the environment around the antenna would be nearly ideal.

Another possibility is to place the GNSS antenna on a UAV, which would keep station above the reference monument and several auxiliary sensors (whose location and stability are not critical). The UAV would simultaneously maintain links with visible GNSS satellites (aided by an on-board inertial system) and with sensors on the ground using any of a variety of accurate (~0.1 mm say) short-range ranging techniques. In this way the pristine airborne GNSS signal environment is transferred to the reference monument despite the relative movement.

The UAV could execute certain maneuvers to continuously calibrate its antenna, similar to robot absolute antenna calibrations on the ground. The craft could spin slowly around the vertical axis, or tilt slightly, or both. The attitude from the inertial system would become part of the observation stream to close the calibration loop. By contrast, there is great reluctance to move ground-based GNSS reference antennas to carry out any sort of ongoing calibration program on them. With flyers, continuous monitoring comes for free.

Small rotations and tilts on an airborne platform are impossible to completely avoid, and high-wind situations may force some loss of observing time. But for most of the time, the environment should permit an accurate tie from UAV track to ground network. Depending on the choice of flying craft, several may be needed, spelling each other for charging or refueling. Automatic fleet management will probably have many good solutions over the next few years.

It’s hard to know whether there’s a benefit without more detailed study, but the prevailing trends in GNSS system accuracy seem to be increasing the relative importance of multipath. If we assume progressively better satellite orbit and clock estimates and ionosphere and troposphere sensing, then multipath may well loom as the last remaining large, difficult, uncertain systematic error. (Perhaps a UAV could help with estimating the wet troposphere delay as part of normal operation, to the extent that measurements on the very bottom segment of the troposphere are predictive of the full path.)

Finally, I wonder whether UAVs could help with urban canyons or tree-canopy issues. A surveyor might deal with an awkward situation by tossing a UAV into the air and replacing a bad-GNSS-signal problem with a perhaps easier-to-solve UAV-to-ground-sensor problem using vastly stronger optical or RF links.

Semicodeless P(Y)-code processing using high-rate aiding

Present schemes for semicodeless P(Y) processing assume an autonomous receiver that estimates W-code bits directly from the received signal. Given the limited C/N0 available, naturally the signals are noisy, resulting in squaring loss.

One simple way around this is to use more reliable estimates of the W bits acquired with a medium-gain antenna. These estimates can be published continuously in near-real-time by a suitable Internet-based service, then used by any client receiver, which could be a conventional real-time receiver or a recorded-waveform software receiver.

This need not be all that expensive. All that’s required is approximately one medium-gain antenna per satellite per hemisphere. A one-meter dish of 20 dB gain would reduce squaring loss to practically zero. Of course a reasonable Internet uplink is needed of 480 kbit/second for each satellite. Storage costs could be controlled by retaining the W bits for a limited time (a few weeks or months).

Another possibility, in a world where every receiver is uploading full RF waveforms to a central service, is to sum together the signals from many receivers before estimating the W bits. (If the summing is done prior to detection, this is effectively a phased-array antenna.) Quite a few signals would be needed, though, if each receiver has the usual omni antenna. Better to rely on dedicated medium-gain antennas.

The W-code bits would not be of any use to spoofers since they would be tens or hundreds of milliseconds out of date.

The same trick can be played with other unknown codes, such as the GPS M code or the PRS codes on other GNSS services. The bit rates of the published reference waveforms would be much higher than those of W-code, but perhaps the effort would be repaid by observables that could be obtained in no other way, helping with multipath and ambiguity resolution. In the limit of a totally unknown waveform, this is just VLBI.