Pseudorange corrections for the u-blox TRK-MEAS command

When configuring u-blox GPS receivers to output raw pseudorange and carrier phase data, we normally use the RXM-RAWX command for the M8T receiver. For the M8N receiver, since it does not support the RXM-RAWX command, we normally use the undocumented TRK-MEAS command instead. These are what the “xxx.cmd” scripts in RTKNAVI or STR2STR are doing when starting up the receivers.

Something I did not realize until fairly recently was that the M8T also supports the TRK_MEAS command and furthermore, both commands can be enabled simultaneously.

This is normally not a good thing to do since it confuses the RTKLIB decode apps and will result in a cycle slip reported for every epoch. In fact I first discovered the capability to run both at once accidentally by running the M8N “cmd” script on an M8T after previously running the M8N script. I then spent an embarrassingly long time figuring out why the data was so garbled. If you ever see two outputs in your observation file for every epoch, this is probably the cause.

But, for learning purposes, this is actually quite an interesting configuration because it allows us to directly compare the TRK-MEAS output to the RXM-RAWX output and verify if the two are providing the same information.

To do this experiment I first combined the two command scripts to create one that would enable both raw measurement commands. I then collected some data with an M8T receiver using this script to configure the receiver. Running an unmodified version of RTKCONV on the resulting raw output file allowed me to confirm I was collecting output from both commands. Here’s one epoch from the observation file with output from both commands. I’ve deleted some of the satellites to make it easier to read. The last column is SNR which is reported with a resolution of 0.25 by the TRK-MEAS command and 1.0 by the RXM-RAWX command which makes results from the two commands easy to distinguish.


Psuedorange and carrier phase are the two 13 digit fields. At first glance, the two command outputs look quite different. This is at least in part because the TRK-MEAS command reports transmission time rather than pseudorange which RTKLIB converts to a sort of pseudo-pseudorange by subtracting an arbitrary constant time off each satellite in the epoch. This error will be removed later as part of the receiver clock offset so it can be ignored. It will be constant for all satellites so we can determine it’s value by removing the common offset between all satellites.

My analysis tools don’t have an easy way to separate the two sets of data from the combined observation file and I also wanted to run solutions on each data set separately so rather than work with the combined file I chose to modify RTKCONV to separate the two sets of data. I compiled two temporary versions of this app, one with the TRK-MEAS decode function disabled and one with the RXM-RAWX decode function disabled. I then ran each on the combined raw data file to create two observation files, one with the TRK-MEAS output and one with the RXM-RAWX output.

Here’s the difference between the pseudorange measurements from the two commands, the top plot is the GPS and SBAS satellites and the bottom plot is the GLONASS satellites. In both cases they are actually differenced with a reference satellite (GPS) to remove the common clock error mentioned above.


What this plot shows is that once the common error is removed, the pseudorange outputs of TRK_MEAS and RXM-RAWX are identical for the GPS and SBAS satellites but differ by constant integer number of meters for the GLONASS satellites.

Halfway through this exercise I remembered someone had left a comment a few months ago with a link referencing something similar. At the time I hadn’t fully understand the significance of the comment but going back to it I realized they were talking about exactly the same thing. The link was to the OpenStreetMap project and this is the table from the link. Apparently someone there must have done a very similar experiment and their results match mine exactly.


It turns out, not surprisingly, that the integer offsets correspond directly to the carrier frequency of each GLONASS satellite, and the above table can be expressed more concisely that way. They also found that the integer offsets are different between the 2.30 u-blox firmware and the 3.01 firmware which is what the column titles refer to.  My M8T receivers are running the 2.30 firmware.


I find the arbitrariness of the numbers in this table quite strange but the data suggests very strongly that they are real. If they are real and unchanging then they can quite easily be corrected in the RTKLIB TRK-MEAS decode function. Of course, the data only shows they are different, it doesn’t show which is correct. It seems more likely that the RXM-RAWX would be more accurate since it is the documented command, but we can verify this.

To do that, I ran solutions for both data sets. In the plot below, the yellow/green trace is the solution for the RXM-RAWX data, and the green/blue trace is the solution for the TRK-MEAS data. The pseudorange measurements will have a steadily diminishing effect on the solution as the kalman filter converges and the carrier-phase measurements start to dominate. What this plot shows is that the initial errors, when psuedorange dominates, are smaller in all 3 axes with the RXM-RAWX data and presumably because of this, the fix occurs earlier with this data.


I then modified the TRK-MEAS decode function in ublox.c to include an adjustment to the pseudorange measurements based on the values in the table above. Rerunning the solutions with this change gave the following result. As you can see the two commands now give virtually identical solutions.



It would not be very useful if this correction to the TRK-MEAS results applied only to the M8T and not to the M8N, but since the two modules share the same hardware, it is very likely that the corrections apply to both.

I had collected some data with an M8N receiver running 2.01 firmware at the same time I collected the M8T data above, so let’s take a look at that data next to see if we can confirm this. I converted the M8N TRK-MEAS raw data to two sets of observation files using the unmodified and modified version of RTKCONV to give uncorrected and corrected measurements. I then ran solutions on both sets of data.

In the plot below, the yellow/green trace is the solution for the corrected measurements and the green/blue trace is the uncorrected measurements. Again all three axes show smaller initial errors with the corrected measurements, although in this case it didn’t result in an earlier fix. On average, smaller position errors result in earlier fixes, but the correlation is not 100% and the fix times tend to be quite discrete so I believe the reduced initial position errors are significant even though they did not result in an earlier fix in this particular example.


Just to be sure though, let’s dig in a little deeper to see if we can find some more convincing data.  Let’s look at the pseudorange residuals first.  We would expect that reducing errors in the GLONASS pseudorange measurements should be most visible in the pseudorange residuals.  Plotting the pseudorange residuals for just the GLONASS satellites, uncorrected on the left, corrected on the right confirms this.


The same plot for the GPS satellite pseudorange residuals shows little change between corrected and uncorrected, again as we would expect since we have not corrected those.


All of this is quite encouraging and suggests it would be worthwhile to add this correction feature to the code. So I’ve gone ahead and done this to the demo5 RTKLIB code by adding a receiver option for the u-blox receivers. Receiver options are entered from the “Options” menu from RTKCONV or RTKNAVI and from the command line with CONVBIN. Below is an example of setting the new option with RTKCONV. The name of the option is “-TRKM_ADJ” and it is set to “2” to apply the 2.01 or 2.30 firmware offsets and “3” to apply the 3.01 firmware corrections. (Note:  the second dash in the option specified in the image below is incorrect, it should be an underscore.)


The options used by RTKLIB during decoding are listed in the header of the observation file so you can verify which options were used by looking there.

This change should help the M8N solutions get to a first fix quicker and more reliably.

I’ve already uploaded the source code changes to my Github page and plan to update the executables soon.



New website

My previous post is actually part of a new website I am in the process of building to eventually replace this blog.  It’s been nearly a year since I started this blog and while it has been a good experience, I am starting to feel the blog format can be somewhat limiting.  A website should make it easier to organize and maintain the existing information and also allows for  better upload and download capabilities for code and data.   It will be intended more as a general resource and less of a description of my own personal experiences but I will also continue to post what I’m up doing to my blog, either as part of the website, or link to it from there.   I haven’t figured out exactly how to integrate the two yet.

However, the most important reason I’m making the change is to try and migrate to a much more user-interactive experience.  A place where every reader can have their own page and can describe what they are doing and communicate with others rather than just read about what I am doing.  The only similar forum with high activity that specializes in low-cost precision GPS that I am aware of is the Emlid forum, and while it is very good, it is obviously intended just for users of their products.  I’d like to create something somewhat similar, but to serve a broader audience to include any hardware, software, or application related to low-cost precision GPS.  I want to find out what everyone else is doing with RTKLIB.

The website is not quite ready for prime time but it is getting close.  If you’d like to check it out you can go to and let me know if you have any ideas or suggestions to improve it.  I took the “lib” out of the name partially to make it easier to remember but also to broaden the potential scope beyond RTKLIB.


Selecting a GPS receiver (M8N vs M8T)

[Update 6/7/17: The newest version of the M8N does not provide access to raw observations, so the M8T is now the lowest cost option available from u-blox that is still usable with RTKLIB.  See this post for details.]

Selecting a GPS receiver

The first thing you will need to begin your journey into low-cost precision GPS is a receiver that provides access to the raw GPS position signals;  pseudorange and carrier phase.  There are only a few low cost GPS chips that provide these signals.  I chose the u-blox receivers because they seem to be the most available and lowest cost option out there.  Also I was able to find examples of other people successfully using them with RTKLIB, including Tomoji Takasu, the author of RTKLIB (see here).

The NEO-M8 series is the latest generation from Ublox.  There are three basic versions of the chip, the NEO-M8N,  the NEO-M8T, and the NEO-M8P.  The NEO-M8P uses u-blox’s own integerated RTK (real-time kinematics) solution and is significantly more expensive than the other two.  I have not worked with this version and don’t know anything about it.  Assuming you plan to use the RTKLIB open-source software to process the raw GPS signals, then you will want to choose between the M8T and the M8N.


The NEO-M8T is more capable and more expensive than the NEO-M8N.  Unlike the M8N, it is specifically intended to be used for precision positioning and officially supports output of the raw signals.  The current firmware supports the GPS and GLONASS satellite systems and newer firmware should be available soon to also support the Galileo system.

The best source for a M8T based receiver that I am aware of is from CSG Shop for $75.  It has a USB interface which means it can easily be connected directly to a computer without any kind of adapter.  It does not come with an antenna but CSG also sells a u-blox antenna for an additional $20.  I have had good results with this antenna and would recommend it.  Assuming you buy two units, one for a base and one for a rover, this setup will cost just over $200 with shipping.  If you are looking for maximum performance and easy setup, and the $200 is within your budget, I would recommend this choice over the M8N.  Here’s a photo of the receiver and antenna from CSG.

UBLOX NEO-M8N GPS GNSS receiver board with SMA for UAV, Robots

Another M8T-based option, if you are looking for a more integrated solution and willing to spend a little more is the  Emlid Reach.  This is a pair of M8T receivers combined with Intel Edison SBCs with wireless and bluetooth as well as higher quality Tallysman antennas for $570 for the pair.  It uses RTKLIB for the GPS solutions but also includes an additional layer of code to make setup and use easier for the average user.


If you’d prefer a less expensive choice than the M8T and are willing to accept a few compromises then you should consider the M8N.  The NEO-M8N chip does not officially support output of the raw GPS signals but can be configured to do so with undocumented and unsupported commands over the serial port.  These commands are no longer available in the latest firmware (3.01).  However most units shipping today still have the older 2.01 firmware and so still work.  Also, the firmware can be downgraded from 3.01 to 2.01 if you did end up with a receiver with the newer firmware.  [Update 6/7/17: This is no longer true if the M8N comes with ROM version 3.01] The older firmware does not support Galileo.  Going forward, as that system becomes more capable,  this will become a more significant disadvantage of the M8N receiver.

Performance-wise, the M8N and M8T are based on the same core and for the most part are very similar.  There is one noticeable difference however in the way the M8N processes the GLONASS measurements.  Without getting into too many of the details, the issue is that normally when using two identical receivers, the GLONASS satellites can be used to solve the integer ambiguities, but with the M8N this is generally not true because of some additional error terms.  I have added a partial fix to my public branch of RTKLIB to calibrate out these errors after first fix.  If you are using the standard 2.4.3 version of RTKLIB, though, you will not have this capability, and either way you will not have this for the initial acquisition which means it will take a little longer with the M8N to get a good fix.

Most of the inexpensive M8N receivers are intended for use in drones and use a UART interface rather a USB interface.  This means you will need an FTDI type adapter to translate UART to USB and most likely will need to solder a few wires to get this hooked up.  You will find many choices available online for $15 to $40 per receiver including shipping.  These usually include an inexpensive, lower-performance antenna.  You will have to add $5-$15 for the FTDI adapter.  Still, you should be able to put together a pair of receivers fairly easily for under $75, less than half the cost of using the M8Ts.  If you are willing to wait for parts from China, you could do it for less than $50 for the pair.  Although not quite as capable as the M8Ts, if you are careful to collect good quality data and include a little more time for initial acquire, much of the time the results will be indistinguishable between the M8N and the M8T.

I have experience with three different M8N based receivers and have gotten good results with all three.  The first was from CSG Shop and while a perfectly good receiver I would not recommend it because the price is only ten dollars less than the M8T so if you are going to go that route, get the M8T.

The second receiver I have used is intended for drones and is marked as a GY-GPSV3-NEOM8N.  It is available from several suppliers, I bought it on Ebay for $25.78 including antenna and shipping.

The third type of receiver I have used is very similar but includes an on-board magnetometer.  It is labeled as GY-GPSV5-NEOM8N and sells for about $5 more than the GY-GPSV3-NEOM8N and is also available from multiple sources.  The magnetometer can be useful for collecting additional information about heading and orientation but I have not used it much yet.

Here’s a couple of other M8N receivers worth considering.  I have ordered both of them but have not had time to evaluate them yet.  For the very lowest cost and with an integrated receiver/antenna package, the unit on the left from ebay, shipped from China is  $16.65 including shipping.   The Reyax RY835AI unit on the right includes an accelerometer, gyroscope, and magnetometer with onboard antenna, all for $18.99 from Amazon Prime  (thanks to Ken McGuire for this suggestion).

[Update 12/6/16:  I have not tested the unit on the left yet but have verified the M8N module is counterfeit based on inconsistencies between the labels on this module and my other modules.  Preliminary testing of the Reyax unit was disappointing for me with low satellite count and low SNRs.  I suspect it may be because the antenna is passive unlike my other receivers that all have active antennas, but Ken has shown data with his receiver that looks much better so I’m not sure why the differences.  He has extended the ground plane on his unit but that doesn’t usually have a large enough effect to explain the differences I see.]


In summary, I would recommend the M8T receiver with a u-blox antenna for someone that has a specific application in mind and is looking for maximum performance and ease of setup.  However, the M8N with included antennas is how I got started and I still think it is a good choice for anyone that just wants to explore the capability of precision GPS without spending a lot of money.  It could also be a good choice for someone planning on building multiple units for a more price-sensitive application and is willing to work within it’s constraints.   Combining the M8N with a u-blox or other external antenna is another possibility that will put you somewhere in the middle for both capability and cost.

Demo5 code features

I’ve had a couple questions about what’s different between my demo5 code and the 2.4.3 release code. Here’s a list of what I think are the significant differences.  Any changes that I have made but have then been ported back to the release code are not included here.

 1 Calculation of GPS Solution (RTKNAVI, RTKPOST, RNX2RTKP)

1.1 Significant Functional Differences

(1) The kalman filter state update has been re-written to remove all the zero-element multiplies. This significantly reduces the computation time when receiver dynamics is enabled.

(2) Code has been added to calibrate and manage inter-channel biases for the GLONASS and SBAS satellites based on an extension of the fix-and-hold method (direct feedback from the ambiguity resolution results to the kalman filter states).  This enables use of the GLONASS and SBAS satellites for ambiguity resolution with the M8N receiver or when the base and rover receivers are not identical.

(3) Integer ambiguity resolution has been enhanced to include up to three attempts per sample to resolve the ambiguities using different combinations of satellites. Additional attempts may remove GLONASS and SBAS satellites and/or remove newly acquired satellites depending on various conditions.

(4) Additional adjustable constraints for ambiguity resolution have been added to help minimize the chance of false fixes.

(5) The common component of the phase-biases has been moved to a separate variable instead of spreading it out among all the satellites. This makes the phase-bias states easier to interpret during debug and also removes some issues with improper adding of cycles from satellites with different carrier wavelengths.

(6) Comments have been added to most of the core positioning algorithm code.

(7) The trace output has been enhanced and modified to provide more relevant information for debugging poor solutions.

1.2 Additional Input Parameters and Options

(1) pos1-posmode = static-start: Begins solution in “static” mode but switches to “kinematic” mode after first fix. This mode will normally acquire first fix faster than kinematic mode if the rover is stationary but will fail if the rover starts moving before the first fix.

(2) pos2-gloarmode = fix-and-hold: Extends the fix-and-hold method to calibrate the inter-channel biases for the GLONASS satellites and similar errors for the SBAS satellites. Note that fix-and-hold feedback only occurs after the first fix.

(3) pos2-arfilter = on,off: Rejects new or recovered satellites from being used in ambiguity resolution if the AR ratio was significantly degraded by their addition. This is an alternative or enhancement to using the blind delay of “arlockcnt” since the satellite will only be left out of the solution as long as necessary instead of for a fixed length of time.

(4) pos2-arthres1 = x: Integer ambiguity resolution is delayed until the variance of the position state has reached this threshold. It is intended to avoid false fixes before the kalman filter has had time to converge. If you see AR ratios of zero extending too far into your solution, you may need to increase this value. The “arthres1” option exists in the release code config file but is not used for anything.

(5) pos2-minfixsats = n: Minimum number of sats necessary to get a fix. Used to avoid false fixes from a very small number of satellites, especially during periods of frequent cycle-slips.

(6) pos2-minholdsats = n: Minimum number of sats necessary to hold an integer ambiguity result. Used to avoid false holds from a very small number of satellites, especially during periods of frequent cycle-slips.


2 Conversion from raw data to RINEX (RTKCONV, CONVBIN, RTKNAVI)

2.1 Significant Functional Differences

(1) The M8T raw output to cycle-slip translation algorithm has been modified to more closely match that of the M8N. This insures that a cycle-slip is flagged after every loss-of-lock and is not flagged until the carrier phase measurement quality is sufficient for resetting the phase-bias estimate.

(2) The half-cycle invalid bit is set for the SBAS satellites based on lock-time since the half-cycle invalid bit from the receiver is not functional for these satellites. This change has been made for both the M8N and M8T in the demo5 code but has only been ported back to the 2.4.3 code for the M8N receiver.

(3) The receiver measurement quality metrics for each sample are recorded in the RINEX observation files. The single character SNR field in the pseudorange and carrier-phase measurements are used for this purpose. The M8T reports quality metrics for both pseudorange and carrier-phase, the M8N reports quality metrics only for the carrier-phase. This is for information purposes only, RTKLIB does not use these fields.

2.2 Receiver Specific Options (U-blox)

(1) -STD_SLIP = x: Carrier phase measurements are flagged as cycle-slips if the standard deviation of the carrier phase measurement (as reported by the receiver) is greater or equal to x (scale=0.04 meters/count). This feature now exists in both  the 2.4.3 and the demo5 codes but is not listed in the 2.4.3 documentation. It is only used for the M8T receiver. If this option is not set, then the same fixed threshold will be used for cycle-slips as for valid carrier-phase measurements, which in the release code can cause cycle-slips to fail to be flagged after the phase-bias estimate is valid.

Issue with u-blox USB drivers

[Update 5/19/2018: See this tutorial for a more direct and up-to-date description of how to install u-blox USB drivers for Windows]

I ran into an issue with the u-blox USB drivers on Windows yesterday that ended up costing me a fair bit of time.  I thought I’d share it here in the hope it may help others that may be running into similar problems.

U-blox has recently switched from using a COM port driver to using a “sensor device driver” on Windows.  This seems to have caused some problems and does not appear to be fully functional yet.   They have published a flowchart on their website to help users decide whether to use the new drivers or not and how to rollback to the standard windows drivers if necessary.  It is not so easy to find it by going directly to their website, at least it wasn’t for me.  I only found this document through a Google search while looking for other people who had also run into this problem.

Here is a copy of the document but it may be too small to read here and may be out of date by the time you read this so I suggest you click on this link to take you to the original document.


I don’t understand all the implications of this change but I can explain what happened to me and what I had to do to fix it.

I first ran into the problem when working with two CSG M8T receivers, both with USB interfaces.  I had previously connected one of them to my computer but not the other one.  I wanted to be able to communicate with both receivers using the u-center eval software and RTKNAVI.   After plugging in USB cables from both receivers into the computer, the receiver that had been previously connected appeared as it usually does as a COM port but I was unable to see the new receiver with either program.

[Update 11/29/16:  u-center actually does see the receiver but it is listed under “Sensor API” instead of “Ports” in the Receiver menu]

I am using Windows 10, and it appears that it had automatically downloaded the new sensor device driver for the new receiver but continued to use the old COM port driver for the receiver that had been previously connected.   Neither RTKNAVI or u-center seemed to be able to see the new receiver since it had not been assigned a COM port.

To access the new receiver I had to follow a process similar to what is described in the blue circle above.  First disconnect from the internet so Windows can’t find the new driver.  Then plug in the GPS receiver to the USB port.  If it hasn’t been plugged in before Windows will probably assign the standard COM port driver to it.  If it has been plugged in before but only recently, the new driver has probably already been downloaded and it will use the new driver.  If so, open Windows Device Manager.  The receiver will appear under “Sensors” instead of under “Ports” as it used to do.  Uninstall the driver using Device Manager, being sure to click the box that deletes the driver files.  Windows will then revert to the standard COM port driver and the receiver will appear in the Device Manager list under “Ports”.

This is what we want but if we leave it like it is, Windows will download the new driver as soon as you connect back to the internet again.  To prevent this from happening we need to tell Window not to do this.   Do this by connecting to the internet, waiting for the new driver to download and the receiver to appear back under “Sensors”.  Then use Device Manager to “Rollback” the driver to the previous version.  This option is in the “Driver” tab in the Device Manager.

After doing this, both receivers appeared as COM ports and I was able to communicate with them with RTKNAVI but I still had trouble with u-center.  Unfortunately I am a little less clear about what I had to do to get this working again.  I suspect rebooting may have been enough but I ended up also downloading the u-blox Virtual COM Port driver and installing that (as describe in a different step in the flowchart), so it’s possible that is also necessary.

If anybody else has a more complete understanding of what’s going on, maybe they can add some insight in the comments section.

[Update 11/29/16:  After going through this procedure again, I understand it a little better. Rebooting after modifying the drivers does appear to be enough to get u-center to see the receiver again under “Ports”.  Installing the u-blox Virtual COM Port driver is also another option instead of using the Windows COM port driver. However that option works with only one receiver at at time.  If you want to hook up two receivers simultaneously you will need the Windows COM port driver.]

Another Raspberry Pi RTKLIB project

After describing my simple Pi based data logger in my last post, I stumbled across this Raspberry Pi based GPS project on  It uses an M8T receiver and includes a touchscreen GUI to manage RTKLIB.  It has downloadable files for 3D printing the case as well as an image file with all the code for the SD card (something I should probably provide with mine).

I haven’t looked at in great detail but it seems very intriguing and could be a good alternative to what I described in my last post if you are looking for something with greater capability.

Here’s a photo of the completed project.


Pi Zero Based GPS data logger


The easiest way to collect GPS data from the u-blox receivers is simply to plug the receiver into a laptop PC through an FTDI serial to USB converter and run STRSVR or RTKNAVI as I’ve previously described. Of course, if you want to collect base and rover data, that means lugging around two laptops, which if added to the cost of the system, start to make it not look so low cost and also not so convenient since the laptops are so much larger than the receivers.

An inexpensive alternative to the laptop is to configure one of the many linux-based single board computers that are available to collect the data. They can also be used to process the data but in this post I will focus on collecting the data for post-processing later. I have used both Beaglebone and Pi boards to do this and find they each have their advantages.

In this post I will describe setting up a Pi Zero for this purpose. The advantages of the Pi Zero over the Beaglebone are it’s lower price and smaller size. One of the disadvantages is that it does require soldering since there are no host USB ports or headers on the board. The other is that it only has a single UART port, so does not have the flexibility of the Beaglebone. This is important for example if you want to add a radio link for real-time data collection. Like the Beaglebone, it does have an I2C port which can be used to collect magnetometer (digital compass) data if your receiver includes that feature.

The Pi Zero is supposedly a $5 computer but at least at the moment you can only order one at at time and the shipping costs are significant relative to the board price. I paid $13.50 including shipping for the last one I bought from Adafruit. Still, this is less than half the price of the least expensive version of the Beaglebone. Here’s a stock photo of one in case you haven’t already seen it.


In addition to the Pi Zero, you will need a microSD card to use as non-volatile storage for the operating system, programs, and data. I used an 8 GB card available for $5 or less from many sources. I believe any size 4 GB or larger will work. The last things you will need are a USB portable charger (battery pack) to power it and a USB cable. I have bought 10000 mAh units for $10 on Amazon and 2600 mAh units for $5. Both include USB cables but they are power only, you will need a power and data cable for communication between PC and Zero. The 2600 mAh units are sufficiently large to run the Zero and GPS receiver for many hours.

The total cost for a basic logger with an M8N receiver and basic patch antenna is just under $50, using an M8T receiver and/or higher quality antenna will increase the price.

I will describe the process I went through to connect the receiver to the Zero and to configure the Zero to automatically start collecting data when power is applied. I did this using a Windows PC but I imagine it would not be very different if you are using another machine.  I am no expert in linux so I relied heavily on cutting and pasting various pieces of code from different tutorials I found online.

To get started I downloaded three open-source applications to my PC: The first is Putty to communicate with the Zero through SSH over a USB cable and avoid having to hook up a keyboard or HDMI monitor to the Zero. The second is WinSCP to transfer files between the PC and the Zero. WinSCP also includes a convenient file editor that makes it easy to edit files on the Zero directly with a standard GUI interface. The third is WinDiskImager to transfer memory images between the PC and the SD card.

The next step is to copy an operating system onto the microSD card. I used the “2016-09-23-raspbian-jessie-lite.img” version available here, which is currently the most recent stable version of Raspbian Jessie Lite. Raspbian Jessie Lite is a minimal image of linux based on Debian Jessie.

To get the image onto your SD card, download the image to your computer, decompress it, and copy it to the SD card using the “Write” button on WinDiskImager.


Once you have done that, the next step is to configure the operating system on the microSD card for SSH access over USB. This is done with the card plugged into the laptop using a microSD to SD adapter. There is an excellent tutorial here on how to do this by modifying the config.txt and cmdline.txt files in the boot folder so I won’t explain the details here. In the comments section, it also describes how to link the Zero to your network to get internet access. Although not required for this exercise, it is nice to have. Also note that it does require Bonjour, iTunes or Quicktime to be installed on your PC to translate the Zero’s SSH address. If you don’t already have one of these installed, you will need to do that as well.

While you are editing the config.txt and cmdline.txt files in the previous step, there are two more changes you need to make.  First,  in the config.txt file, add the following line to the end of the file: “init_uart_clock=6000000”. This increases the speed of the UART clock. Without this change you will not be able to run the UART faster than 115200 bps.

Next, in the cmdline.txt file you will find a reference to either serial0 or ttyAMA0. If you are using the same image as I did it will be in the form of: “console=serial0,115200”. Delete this command from the line, then save and close the file. This will free up the UART from it default use as a debug terminal.

Now that the operating system is configured, plug the microSD card into the Zero and connect the Zero to your PC using a USB cable that you know has the data lines hooked up (many USB cables only have power). Verify that you can communicate with the Zero through SSH using Putty (Host Name=raspberrypi.local). There are two micro-USB connectors on the Zero, one is power only, so be sure you use the one labeled USB. You can login to the Zero using the default username=pi, and password=raspberry. Once you are sure this is working, disconnect the Zero for the next step.

Now, we will connect the GPS receiver to the Zero through the UART pins with soldered wires. It is a little more complicated to change the baud rate on the u-blox receiver after it is connected to the Zero, so I would suggest setting the receiver baud rate before doing this step. Be sure to follow up by issuing the CFG-CFG command to save it to flash.

Below is the pin-out of the Zero. TX and RX need to be swapped whenever hooking up a UART so connect TX from the GPS receiver to RXDO (pin 10) on the Zero, and RX from the GPS receiver to TXD0 (pin 8). The solder pads on the edge connector are normally labeled on the GPS receiver. If your receiver has a digital compass it’s output will most likely be on an I2C interface using SDA and SCL pins. If that’s the case hook those up to pins 3 and 5 on the Zero (these don’t get swapped). I won’t talk about collecting data from the compass in this post but at least you will have them hooked up for later.


Here are two data loggers I have built (without antenna or power pack). The receiver on the left is a GY-GPSV3-NEO which does not have a digital compass so there are only four wires. I have used double-stick foam tape to connect the two boards. This unit is set up to run with the antenna included with the receiver which has a very short lead. I like to run the lead through a hole in the ground plane so I need to make sure access to the uFL connector is not blocked by the Zero.

The receiver on the right is a GYGPSV5-NEO which does have a digital compass so there are six wires. In this case I connected the two boards using a machine screw with nylon spacer. This one also has a uFL to SMA adapter cable to connect to my more expensive antennas with SMA connectors. The adapter cable is fairly fragile so I have secured it with a simple L bracket to the receiver board. Note that his board has a DF13 connector, I could have chosen to use that instead of soldering directly to the pads on that end.


Now that the hardware is complete, reconnect the Zero to the PC with the USB cable and re-open a SSH window with Putty. Also start WinSCP and login to the Zero, again using Host Name=raspberrypi.local and username/password = pi/raspberry. When WinSCP connects, the left window will list files on your PC and the right window files on the Zero. Be aware that WinSCP only has permission to copy files into your user space, if you want to copy files anywhere else on the Zero, you will need to login in using that users name and password.

Now we need to copy the RTKLIB source code onto the Zero. You can do this with WinSCP. Copy the rtklib folder from your computer to “/home/pi/rtklib”. Note that none of the windows executables will run on the Zero so there is no need to copy them. We do need to build code though so you will need to copy all the source files. Also copy the “.cmd” file you use to configure the GPS receiver at startup. Put this in a new folder called gps (/home/pi/gps).  You can create a new folder on the Zero by right-clicking on the right window.


The demo5 version of the RTKLIB code is available here and there are some sample “.cmd” files for the M8N and M8T receivers in the binaries zip file here.

Once you’ve copied everything over, go to the Putty window, switch to the /home/pi/rtklib/app/str2str/gcc folder and run “make” to build the STR2STR app on the Zero. Copy the STR2STR executable to /usr/local/bin and change its permissions to make it executable.  You can do all that by executing these commands from the command prompt:

cd rtklib/app/str2str/gcc
sudo cp str2str /usr/local/bin/str2str
sudo chmod +x /usr/local/bin/str2str

You may get a warning from the make process that the build may not be complete because we didn’t update the date and time on the Zero, but you can ignore this.

You should now be able to run the STR2STR app to make sure everything you’ve done so far is working. I use the following command but you may need to adjust it for your baud rate file names/locations etc. Details of the STR2STR command options are in the RTKLIB manual.

str2str -in serial://ttyAMA0:230400#ubx -out rover.ubx -c /home/pi/gps/m8n_gpsglo_5hz.cmd

The output from this command should look something like this:


The output should be in the file rover.ubx.  You can use WinSCP to copy this file to your PC and convert and plot it with RTKCONV. If that works fine, you are almost done.

The last step is to create an auto-run script that will run at power-up and start collecting data. We will use the systemctl command to do this. I used this tutorial as an example on how to do it.

First we’ll create the actual script to collect the data. I first tried to do this in python but had an issue with it not working when called during the boot sequence, so switched it to a shell script. Create a new file by maneuvering to your user folder (/home/pi) in WinsSCP then right-click and select New then File. Copy the following text into the file then save as “/home/pi/” and exit. You may need to modify the path and str2str command lines in this file to match your file names, locations and baud rate.



# find unused file name
while [ -f $fname ]; do
     let “i=i+1”


# start task to collect rover data at 5 Hz
str2str -in serial://ttyAMA0:230400#ubx -out tcpsvr://:128 -out $fname -c     home/pi/gps/m8n_rover_5hz.cmd &

# check for error by looking for output file
sleep 1
ls $fname
if [ $? -ne 0 ]
     echo Exit on error

# blink LEDs to let user know all is OK and we are collecting data
while [ $X -le 0 ]
     echo 0 > /sys/class/leds/led0/brightness
     sleep 1
     echo 1 > /sys/class/leds/led0/brightness
     sleep 1

Now create another new file in the /home/pi folder named gpslog.service.  Copy the following text into that file then save and exit. This will run our script during the power up sequence.

Description=Startup Script Service



To move the second file into the correct system folder and enable both files, use the following commands:

cd /home/pi
sudo chmod +x
sudo mv gpslog.service /lib/systemd/system
sudo systemctl daemon-reload
sudo systemctl enable gpslog.service

To manually test the startup script first without using the autorun task, use the following commands:

sudo systemctl start gpslog.service
sudo systemctl status gpslog.service

The output should look similar to when we ran STR2STR directly.  If not, make sure the baud rate and “.cmd” file name and location in the script are correct.

If the output looks OK, then power-cycle the Zero.  It should come up and automatically start collecting data and flashing the LED on-board the Zero. You can reconnect with Putty and check the status with the previous command;

sudo systemctl status gpslog.service

The raw output files will be saved in the /home/pi/gps folder and will be named roverxx.ubx where “xx” will increment to the next unused number each run. Use WinSCP to copy this over to your laptop for analysis.

And that’s it, you should now have a fully functional GPS data logger!

Below is a little more info on using your new data logger.

If you look carefully at the script above you will see in the str2str options, that in addition to directing the output to a file, I also directed it to a tcp server (-out tcpsvr://:128). If the Zero is plugged into the laptop, you can see the receiver output real-time using RTKNAVI or the u-blox u-center evaluation software. Instead of connecting to a serial port, connect to a network connection using the address “tcp://” where is the ip address of your Zero. You can get this number by issuing a “ifconfig” command from the Putty window and then using the inet address of usb0.

Unfortunately this connection does not work in both directions so if you want to change the receiver baud rate using u-center you will need to use “socat” to create a virtual port.

Use the following command to install “socat” on your Zero. You will need to be connected to the internet to do this.

sudo apt-get install socat

[Note 12/8/16: For a more complete tutorial on connecting your Zero to the internet, see this tutorial (thanks to Jernej Tekavec for pointing me to this link) ]

Then use the following command to open the virtual port, then connect with u-center as described above. Now the connection will work in both directions and you should be able to change the baud rate with u-center.

sudo socat tcp-listen:128,reuseaddr /dev/ttyAMA0,b38400,raw,echo=0

Here is a complete but unassembled logger on the left with ground plane (aluminum foil glued to cardboard) and waterproof case (Rubbermaid food container). On the right it is assembled but not in the case. I place the receiver/antenna unit face down in the container, put the lid on, then flip it over so the antenna points upwards. If I wanted to make it smaller I could use a smaller battery pack and smaller ground plane, but for my uses this size is fine.




Here is a link to a sample data set I collected with this data logger.  It was on a small sailboat while sailing on a lake and got consistent fixes for the duration of the data.