Over the last few months, readers have sent me several data sets collected from cell phones using the Broadcom BCM47755 dual-frequency GNSS chip. In most cases, however, the quality of the data was low and the number of cycle slips made it difficult to do any meaningful analysis. More recently I was sent some BCM4755 data collected from a Xiaomi Mi8 phone mounted on a tripod with a ground plane underneath that was noticeably better quality than the previous data sets. This data came from Julian who is trying to use the phone for forestry applications and has an open project he is working on here. In this post, I will describe my experiences attempting to use RTKLIB to analyze this data.
This is the first time I have worked with L5 data with RTKLIB so I wasn’t quite sure what to expect. Fortunately, with a few minor updates to the code handling code in RTKLIB which I have included in the most recent b33 version of the demo5 code, the code was able to see and process the L5 observations. Both rover and base observation files were sent to me in rinex format.
According to the rover file header, the rover rinex file was generated by Geo++ RINEX Logger App for Android (Version 2.1.3). It contains L1/E1/B1 observations for GPS, Glonass, Galileo, and Bediou and L5/E5a observations for GPS and Galileo. Like L2C, not all of the GPS satellites support L5 yet. In this case, only two of the six GPS satellites supported L5 data. All four of the Galileo satellites supported E1 and E5a observations but the rover was unable to pick up both frequencies for the one satellite between 10 and 15 degrees elevation. Below is a plot of the rover observations using RTKPLOT. Satellites in gray are below 10 degrees elevation. The rest of the colors indicate the frequencies of the observations according to the color key at the bottom of the plot. There is an error in the Galileo plot colors in which E5a-only observations are being plotted in green rather than gray. Overall though, the data is of reasonably good quality and has only a small number of receiver-reported cycle slips (red ticks).
The base data was from a CORS station about 20 km away and had matching signals for all the rover observations plus many more. Here is a plot of the base observations. This data is very clean.
For my initial attempt to run an RTKLIB solution on this data, I used the same config file I use for u-blox L1-only solutions except I changed the frequency mode from “l1” to “l1+l2+l5” Even though, there is no L2 data in these observations, RTKLIB does not allow you to individually select frequencies, just the number of frequencies, so valid choices are “l1”, “l1+l2”, and “l1+l2+l5”.
This run did not go well and digging into the trace file I found that RTKLIB was detecting many false cycle slips. The code attempts to detect cycle slips using the geometry-free linear combination of the L1 and L5 phase measurements. Either the threshold ( pos2-slipthres) is too tight for this data, or there is something wrong with this check. For the time being, I increased the slip threshold from the default of 5 cm to 50 cm and this eliminated the false slip detections.
Even with this change however, the solution was still very poor. Digging back into the trace debug file, I found more problems with cycle slips. This time I was finding that real cycle slips were not being flagged by the rover receiver in the rinex data. These unreported cycle slips were introducing large errors into the bias states in the kalman filter and preventing convergence. RTKLIB has always had trouble dealing with unflagged cycle slips. The u-blox receivers are very good at consistently flagging cycle slips which is why RTKLIB tends to work better with u-blox receivers than many other receiver types.
RTKLIB has some code to detect and reject outliers, but this code has never worked very well and I have generally recommended setting the outlier threshold (pos2-rejionno) to 1000 meters to effectively disable this feature.
For the BCM47755 receiver however, it was clear that this was not going to work. So I made some changes to the RTKLIB code to more fully ignore the outliers, and to reset the phase bias estimates when an outlier was detected. I also changed the way this threshold is interpreted for phase and code observations. Previously the threshold was used without adjustment for both phase and code observations. Since the code errors are much larger than the phase errors, this meant that the limit had to be set large enough so as to catch code outliers only. For the b33 demo5 code I changed this so that the unadjusted threshold is still used for the phase observations but the threshold is multiplied by the error ratio between phase and code observations (pos2-eratio1) before being used with code observations. This means that it can be set much lower and now becomes useful for detecting cycle slips instead of just code errors.
I have actually been using this fix for custom versions of RTKLIB for a while and usually get good results setting this to roughly one GPS L1 cycle or 20 centimeters. In this case though the rover data appears to be too noisy for this and I had to set the threshold to 50 centimeters (pos2-rejionno=0.50) to avoid triggering an excessive number of outliers.
With this change, the solution was much better and provided a solid fix after about four minutes with a “forward” solution as shown in the plot below. Even though the rover was static, I ran the solution as kinematic to get a better indication of the unfiltered errors. I also verified that this solution is using all the available measurements in the rinex file.
With a “combined” solution, the result was 100% fix.
This appears to be a fairly promising start for the BCM47755. It is still not nearly as solid as a u-blox receiver but some of this can be attributed to the fact that this data was collected with the internal phone antenna which is likely to be fairly low quality.
Unfortunately, other data sets from the same experimental setup did not generate solutions as solid as this. In particular the Galileo observations sometimes were not consistent with observations from the other constellations and prevented the kalman filter from converging.
Overall, my impression is that using BCM47755 and RTKLIB together for PPK solutions is still fairly immature and not ready for any real applications but hopefully this will change with time.
I am very interested in anyone else’s experiences with the BCM47755 for RTK or PPK solutions, particularly in combination with RTKLIB and hoping anyone with experience with this chip will add a comment below.
With previous generations of u-blox receivers there has been a lower priced option available without an internal RTK engine, such as the popular M8T in the generation 8 modules. This does not appear to be the case with the new dual-frequency generation 9 modules, as the F9T, without internal RTK solution, is currently priced higher than the F9P with internal solution.
As the u-blox internal RTK solution in the F9P appears to be very robust, there is probably no good reason to ever use RTKLIB for real-time solutions with the F9P. However, it often still makes sense to use RTKLIB to post-process raw data previously collected by the F9P since the F9P is not capable of post-processing solutions.
Post-processed (PPK) solutions have several advantages over real-time solutions. The rover hardware is simpler, less expensive, lighter, and lower power since post-processing does not require a real-time data link between base and rover. Post-processed solutions also tend to be more robust than real-time solutions, both because they are not subject to data dropouts in the base data link and because they allow for solution techniques that take advantage of both past and future observations, not just past observations. When the solution is not required in real-time, it often makes more sense to collect the data first and then process it later.
Collecting data and processing RTK solutions for the dual-frquency F9P with RTKLIB is not very different for doing this for the single-frequency u-blox M8T, and if you are already familiar with doing that, you will probably not have much trouble adapting to the F9P. However, since it’s been a long time since I did a post on this subject, I thought it would be worth going over again with some updated tips for the new receiver.
Step 1: Configuring the receiver:
To process an RTKLIB solution, we will need raw observation messages from both rover and base receivers and navigation data messages from one of the receivers. The receivers do not output these messages by default so we will need to configure them to do this. With the u-blox M8T it was possible to do this directly with RTKLIB using a command file but this is not an option with the F9P as RTKLIB does not currently support the new F9P configuration messages.
Instead we will download the u-blox u-center app and use this to configure the receivers, then save the results to the on-board flash. There are detailed instructions on how to do this in the F9P documentation available on the u-blox website but here’s a quick summary of the process.
Plug the receiver into a Windows PC using a USB cable if the board supports USB or with an FTDI serial/USB converter if the receiver only has a UART port.
Start the “u-center” app and connect to the receiver with the “Connection” command on the “Receiver” tab. If it is a USB connection, baud rate won’t matter, but if it is a UART->USB connection through FTDI, then you will have to set the correct baud rate from the “Receiver” tab. If all is well, you should see the green connection indicator flashing at the bottom of the screen
From the “View” tab, open the “Messages”, Configure”, “Gen 9 Configure”, and “Packet Console” windows
If using the UART port, click on “PRT (Ports)” from the “Configure” window, set the Target to “1-UART1” and “Baudrate” to the desired baud rate, and click on “Send”. I typically set this to 115200 baud. You will then need to change the baud rate in u-center to the new baud rate. If you are using the USB port directly, you can skip this step.
From the “Configure” window, click on “RATE”, and set “Measurement Period” to the desired time between observation samples, then click on “Send”. I typically set this to 200 ms which gives a 5 Hz sample rate.
From the “Gen 9 Configure” window, select “GNSS Configuration”, enable the desired constellations and signals, select “RAM” and “Flash” under “Layer Selection”, then click on “Send Configuration”. The F9P supports GPS L1C/A and L2C, Glonass L1 and L2, Galileo E1 and E5b, BediDou B1 and B2, and QZSS L1C/A.
From the “Messages” window, right click on “NMEA” and then click on “Disable Child Messages” to disable all the NMEA messages. None of these are needed for an RTK solution but if you want any of the messages for other reasons you can then individually enable the ones you need.
From the “Messages” window, double click on “UBX” then “RXM”. Right click and enable “RAWX” to enable raw observation messages and “SFRBX” to enable navigation messages. Alternatively, you can enable the RTCM3 messages from the “Gen 9 Configure” window. In this case you will want to enable the 1077,1087,1097,and 1127 messages. I have occasionally had trouble enabling the RTCM3 messages on the F9P and have had to use the “Revert to default configuration” option under the “CFG” command first to get this working.
If an antenna is connected to the receiver and is not completely blocked, verify that you see RAWX and SFRBX messages appear in the “Packet” window.
From the “Configure” window, select “CFG”, then “Save current configuration” then “Send” to save these settings to the flash on-board the F9P module.
Repeat this procedure for the base receiver except set the “Measurement Period” under “RATE” to “1000 ms” for a 1 Hz sample rate. You will only need one set of navigation data so you can choose not to enable the SFRBX messages on the base. I tend to leave them enabled just because it makes plotting slightly easier later if each set of observations has its own navigation data.
If you have any trouble with the above summary, you might find this YouTube video from Robo Roby useful. It is intended for setting up the F9P for real-time solutions, not post-processing, but there is a lot of overlap between the two.
In the descriptions below STRSVR, RTKCONV, RTKPLOT, and RTKPOST are all RTKLIB GUI apps. They can be opened individually or you can start by opening RTKLAUNCH and run the individual apps from there. I do not believe the official 2.4.2 or 2.4.3 versions of RTKLIB fully support the F9P receiver yet so I would recommend using the demo5 version of RTKLIB available here.
Step 2: Collecting the data:
For this exercise I will connect both base and rover directly to a Windows PC through the USB port. You can connect both receivers to one PC or each to a separate PC.
Launch two instances of STRSVR, one for each receiver
Set the input stream to “Serial”, click on the input “Opt” button and set the port and baud rate. Set the output stream to file and click on the output “Opt” to set the file name. Click on the “?” to get a list of keyword replacements for the file name. I like to add “_%h%M” to the end of the file name which will append the hour and minute of the data to the file name. If you are collecting long data sets you might want to set the “Swap Intv” to break up the data into manageable file sizes. Note that you will need to use the keywords in this case to avoid overwriting the same file repeatedly. Give the file name a “.ubx” extension to let RTKLIB know that it is u-blox binary data.
Click “Start” to start collecting data.
Step 3: Convert the observation data to rinex format:
Start the RTKCONV app
Click on the “…” button to the right of the “RTCM, RCV RAW or RINEX OBS” field and select the observation file created in the previous step.
If the file extension is not “.ubx” set the “Format” to “u-blox”, otherwise leave as “Auto”
Click on the “Options” button and select “L1”, “L2/E5b”, and all GNSS constellations collected (usually “GPS”,”GLO”,”GAL”, and possibly “BDS” (Bediou) depending on your location. Then close the options menu.
Click on “Convert” to convert from binary to rinex format.
Step 4: Review the observation data:
Before processing the solution, it is a good idea to look at the data first and make sure it is complete, of reasonable quality, and at the right sample rate.
From the RTKCONV main window, click on “Plot” to plot the observations you just converted.
Verify there are observations from all constellations. Green indicates dual frequency measurements, yellow is single frequency. The GPS observations will be a mix of single and dual frequency since only about half of the satellites currently support L2C used by the F9P, but the other constellations should be nearly all dual frequency.
Red ticks indicate cycle slips. Too many of these will make it difficult to get a decent solution. Gaps in the data usually indicate the receiver lost lock and these are not good unless they are in the low elevation satellites.
If all the satellites are in gray, this usually indicates you are missing the navigation data. The previous step should have generated a “.nav” file as well as a “.obs” file. If just a few satellites are in gray, this normally indicates that they are below the elevation threshold which can be adjusted in the options menu selected in the top right corner with the star-like icon.
Check both rover and base observations.
In some cases you may only have one set of navigation data and so not have a matching “.nav” file for one of your observation files. In that case you can manually specify the navigation data with the “Open Nav Data…” option in the “File” tab.
Step 5: Generate the position solution
From the “…” buttons on the right hand side of the GUI, select the rover observation file, the base observation file, and the navigation file as shown in the example below.
Click on the “Options” button and then the “Load” button. Select the “demo5_m8t_5hz.conf” file from the same folder as the demo5 RTKLIB executables, and then click on “Open”
From the “Setting1” tab in the “Options” menu, enable “Galileo” and if applicable “Bediou”. “GPS” and “GLO” should already be enabled.
From the “Setting2” tab in the “Options” menu, set “Integer Ambiguity Res (GLO)” to “On”. We are able to use the “On” setting in this case because the receivers are identical and so the Glonass hardware biases cancel. If you are not using an F9P receiver for base, then leave this field set to “Fix-and-Hold” which will automatically calibrate out the biases.
From the “Setting1” tab in the “Options” menu, change the “Frequencies” from “L1” to “L1+L2”. This is the only change you should need to make to switch from processing single-frequency data to dual-frequency data for the F9P. The Galileo second frequency for the F9P is actually E5b not L2 but to simplify and improve the processing speed, I have modified the demo5 code to include “E5b” processing as Galileo’s second frequency. This won’t be the case for the 2.4.2 or 2.4.3 code. I don’t believe it’s currently possible to include the E5b data with these versions of RTKLIB but if I’m wrong please let me know
Click on “OK” to close the Options menu.
Click on “Execute” to run the solution. The bar at the bottom of the GUI will show the solution status as it runs and will report any errors. You should see a mix of Q=1 and Q=2 as the solution runs. If you see only Q=0, something is wrong. In this case, open the “Options” window, select the “Output” tab and set “Output Debug Trace” to “Level3”, exit the Options menu, and rerun the solution. Then open the “.trace” file in the solution folder for additional information on what went wrong.
Click on “Plot” to plot the solution with RTKPLOT
This was just meant to be a quick summary of the process. For more details please see the references below.
Unfortunately, error reporting is not one of RTKLIB’s strengths. Sometimes it will halt with an error message that is less than helpful, other times it will just continue through the entire solution with Q=0 or Q=2 without ever getting a fix because of a minor error in the input file configuration or parameters. I suspect anyone who has used RTKLIB with any regularity has run into cases like this innumerable times. It still happens to me all the time, almost always because I have made a silly mistake somewhere. For a new RTKLIB user, it can be quite difficult to figure out why they are not getting a valid solution in cases like this.
In this post, I will demonstrate some examples of common errors and some techniques to help identify them. First let’s take a quick look at a couple of real-time solution examples with RTKNAVI, then I will jump to post-processing cases for the rest of the examples.
In my experience, the most common error when using RTKNAVI is to improperly specify the base location. To demonstrate this, I go to the “Positions” tab on the “Options” menu in RTKNAVI and switch the Base Station position type from “Average of Single Position” to “RTCM Position”. In this example, my base stream is u-blox binary and contains no RTCM messages so RTKLIB will not get any valid base location information and can not calculate a solution. However, rather than halting and letting the user know there is a problem, RTKNAVI just runs through the whole solution without ever reporting either an error or a valid position. To help understand what happened in a case like this, the first step is to click on the tiny unlabeled square above the “Start/Stop” button. This will bring up the RTK Monitor window. Choose “Error/Warning” from the Monitor options as shown in the image below to bring up additional error and warning information.
In this case, every sample reports an “initial base station position error”. This should be enough information to track down the problem fairly quickly. Often this will be the case. If the additional information in this window doesn’t help, then the next step is to enable the debug trace and look at the trace file. This is covered in more detail in the post-processing examples further below.
In my experience, the second most common reason to get no solution with RTKNAVI is that navigation messages haven’t been enabled for either base or rover receiver. If this is the case, then all the SNR bars will be gray. When navigation data is available, the bars will be colored as in the image above. Lack of SNR bar color is the easiest way to detect this problem but another symptom is that the “Error/Warning” window will report “No common satellites” for every sample even though satellites are appearing in the SNR window.
A more general troubleshooting technique that will work for either real-time or post-processing and for GUI or command line RTKLIB applications is the debug trace file. I will demonstrate a couple of examples of using this with RTKPOST but the same basic technique can also be used with RTKNAVI, RNX2RTKOP, RTKCONV, CONVBIN, or STRSVR.
To enable the debug trace, set “Output Debug Trace” in the “Options” menu to something other than “OFF”. For simple errors, level 2 is enough and will produce a less overwhelming amount of information. For more detailed information about the GNSS solution, especially the ambiguity resolution, level 3 is better. For more details about the communication streams, level 4 can be useful. Level 5 includes many of the solution matrices and is too detailed for most general debug. Level 1 only contains the error messages available in the error window and is not useful. The image below from the RTKPOST Options menu shows the trace level set to 3. I usually set it to level 3 since I am often looking at the details of the ambiguity resolution, but for simple debug I would recommend starting with level 2 and that is what I will use in the examples below.
Next, I will intentionally make some silly mistakes and show how they appear in the error window and the trace file.
Let’s start with an example where RTKPOST actually does a good job of reporting the error to demonstrate how it should work, and occasionally does. To keep the example simple I will use files in the root directory, with names: rover.obs, base.obs, and rover.nav.
In this first case, I have made a typo in the name of the .nav file. Now, when I click on “Execute” to start the solution, it almost immediately halts and reports “Error: no nav data” in the error window near the bottom of the window. If only RTKLIB would always be as straightforward as this!
Next, I intentionally make a typo in the rover observation file and click on “Execute”. Again, the solution halts almost immediately but in this case the error message in the error window is much less useful. Not only does it not tell me which file has a problem, but it also suggests there is something specific wrong with the input data, not that the file is completely missing. Fortunately, clicking on another tiny unlabeled square (circled in red below) brings up the trace file, assuming trace debug was enabled as described above. This tells us exactly what went wrong, that the input rover observation file could not be opened.
For the next example, I will correct the typo in the file name but then set the location in the base rinex file header to all zeros. This is another common problem and usually happens because the rinex file was generated by RTKCONV or other application from a binary receiver output file that did not contain navigation messages. Without navigation information, RTKCONV can not estimate the receiver location and sets the approximate position field in the rinex header to all zeros. Again, the solution halts almost immediately, and the error message is identical to the previous case. However, looking at the trace file, there is now no mention of a file open error. To understand the difference between these two trace files, it’s important to know that the entries in the trace file beginning with 2 are warnings, and those beginning with 1 are errors. RTKLIB treats the missing file as a warning since there may be other observation files that are present, and doesn’t get an actual error until it looks for the base position information. With just the error, it is impossible to tell these two case apart, but by looking at the warnings as well, it is fairly easy to differentiate them.
Another fairly common error is to use the wrong base observation file. If the base observations do not overlap the rover observations in time, then the solution will run all the way through the data, without error, but the solution status will always be Q=0. Here’s the trace output for this example. I aborted the run before it completed, after it was obvious that something is wrong which is why the error windows shows “aborted”. The “age of differential” is the time between rover observation and closest base observation so again in this case, looking at the trace file makes the problem much more obvious
If the wrong navigation file is used, the solution will again run to the end with no error, and the status will remain Q=0, just as above, but this time the trace file will report “no common satellites”
Another mistake I make fairly often, is when I am specifying the precise base location rather than using the approximate location in the rinex header, and I forget to change it when moving to a different data set with a different base location. In this case, the solution will run without error, but the fix status will remain in float (Q=2) for the entire solution. There are several reasons why you might never get a fix, including just poor quality data, but in this case when I look at the trace file I see extremely large outliers right from the first sample. If all the satellites are reporting outliers and they are as large as this, that almost always means that the base location has been specified incorrectly.
These are only a few examples but probably cover over 80% of cases I look at where people are unable to get a solution. Most of the rest are caused by low quality data.
If you have other examples of common failures that I haven’t covered here, please describe them in a comment, along with the signature seen in the trace file.
The RTKLIB software package has an enormous amount of capability and flexibility. Between all of the different applications, the configuration parameters and input options, it is very powerful, but it can also be a little difficult to navigate at times. I get a lot of questions on how to do various tasks in RTKLIB so I thought it would be worth going over the available documentation as well as mentioning a couple of tips and tricks for finding information on some of the less documented features.
The first place to look is the official RTKLIB manual . It is well written and fairly thorough, at least in many parts. If you are using RTKLIB and have not been through the manual recently (or have never read it), it is well worth going back and taking another look. Almost every time I go back to it, I find some detail I have missed in the past, not because it is difficult to find, but just because there is so much information in there. Especially when using the GUI apps there is a tendency to think the manual is not necessary since everything should be intuitive, but in this case there are many features that are not so obvious. For example, at least to me, it is not overly intuitive that the tiny little unlabeled square (the RTK Monitor Window) above the Start/Stop button in the image below is one of the most important buttons in RTKNAVI and will give you access to up to 35 different windows of useful information.
Here is an image of a few of the various monitor windows I frequently find useful.
Click on a few more of the small inconspicuous triangles, squares and rectangles on the RTKNAVI, and you can change the solution coordinates to pitch/yaw, open up sky views for the base and rover, open up a baseline compass heading plot, and many other things. If you haven’t read the manual, it’s easy to miss some of these options.
Unfortunately, although the manual has lots of useful information, both for the command line and GUI apps, it has not been updated in six years, so some of the newer features are not included.
This is especially true for the linux command line apps like STR2STR and RTKRCV. One tip for getting more up to date information for any of the command line apps is to use the embedded help messages, since, for the most part, these have been kept up to date. For example, let’s first look in the manual at the stream options and command line options for the STR2STR app. We find that there are 6 stream options and 13 command line options listed.
Now let’s bring up the embedded help message in STR2STR by typing “str2str -h” and we get the following:
Notice that the number of stream options has increased from 6 to 8 and the number of command line options from 13 to 24. This is a significant number of additional options to work with!
Another thing to be aware of is that all of the RTKLIB apps are using a common core code, and for the most part it is only the user interfaces that are different. This means that if a feature is available in one app, say for example RTKNAVI, then it is very likely also available in another app, say RTKRCV. It may just be harder to find information on how to enable it there. However, if you know what you are looking for, and you know that it’s most likely there somewhere, then this usually makes it easier to find.
Another resource that can be helpful is this post I wrote a while back which includes description of most of the additional features I have added to the demo5 version of RTKLIB to optimize it for low cost receivers, as well as additional information on all of the previously existing config parameters that I typically find useful to modify. This post is not guaranteed to be fully up to date but I do try and update it with new information as I add new features to the demo5 code.
The above resources will be enough to answer many questions but sometimes they will not be enough and you will need to dig a little deeper. The next step is to look at the source code which is available on Github, both for the official and the demo5 versions of RTKLIB. The first place to look is the “options.c” file as it lists all of the supported input configuration parameters as well as their valid input values in a format that is easy to read. Here is the beginning of the tables of valid option values and parameters.
Going through these tables, I found about a dozen parameters in the official code that are not mentioned in the manual and a few more in the demo5 code that are not mentioned in my documentation.
If you still need more information, the next level is to look at the top-level source file for each app. For example in the “rnx2rtkp.c” file you will find the help menu listed at the top and then in the main() function lower down you will see where the options are actually parsed. This should answer most questions. You can go even deeper into the code if you have to, but I will warn you, things do get a bit more challenging once you get to the next layer.
The last tip that I can offer is that I have written about a number of RTKLIB features in my posts over the last few years, so using the search window at the top right corner of this page will occasionally bring up some useful information.
Anyway, hope at least some of this is helpful to those of you trying to learn a little bit more about some of RTKLIB’s less well documented features.
Back in November last year, I wrote a post on my first experiments with a dual frequency u-blox F9P based receiver. At the time it was quite difficult for those without good connections to u-blox to get a hold of the F9P and even now, nearly three months later, it still is not readily available. Ardusimple, the lowest price provider of F9P receivers still has all their receivers on back order till next month and low cost dual frequency antennas are even harder to get. Hopefully all that will change fairly soon though.
Meanwhile, thanks to “clive1” and “cynfab” from the u-blox forum, I have been lucky enough to have been given a prototype receiver based on the dual frequency u-blox F9T, the next product from u-blox in the Generation 9 series. Like the previous generation M8T, this is intended for timing uses and does not include an internal RTK engine. Otherwise I believe the F9T hardware is nearly identical to the F9P. In theory it should be less expensive than the F9P, just as the M8T is less expensive than the M8P but meaningful pricing is not yet available.
In many of my posts, I have focused on post-processing short baseline data sets using a local base station and identical receivers for base and rover. For this particular combination, I have shown that the differences between a single frequency solution and a dual frequency solution are typically fairly small. This assumes that the single frequency solution includes Galileo and possibly SBAS while the dual-frequency solution includes only GPS and Glonass. This makes the total number of observations fairly similar between the two cases. At least until very recently this has been a reasonable assumption given that most existing CORS or other reference base stations and reasonably priced dual frequency receivers offered only GPS and Glonass. It’s also true that time to first fix is longer in the single frequency solutions but post-processing with a combined solution generally eliminates the need for a fast fix.
However there are many other cases where there are definite advantages to using a dual frequency solution. In particular the most important advantages occur for:
Longer baselines where linear combinations of L1 and L2 can cancel ionospheric errors
Use of an existing CORS or other reference base station which typically has only GPS and Glonass and hence is not an ideal match-up with a single frequency receiver using additional constellations
Real-time solutions where time to first fix is more critical
PPP (Precise Point Positioning) solutions for the same reasons as the long baseline cases.
So for my initial experiments with the F9T I focused on including some of these conditions. In particular I ran two experiments, the first a real-time RTK solution with an existing UNAVCO reference base (P041) located 17 km away. For the second experiment I compared an online PPP solution from the Canadian Spatial Reference System (CSRS) with an RTKLIB SSR based PPP solution.
For the first experiment, I connected the F9T receiver to the dual frequency antenna on my roof and ran a quick five minute RTKLIB real-time solution against the UNAVCO base station using the demo5 b31 RTKLIB code. Other than changing the frequency mode from L1 to L1+L2 I used the exact same configuration file I normally use for the u-blox M8T single frequency receiver. Even though the rover was stationary in this case, I ran the solution as kinematic for better visibility to any variation in the solution. Here’s the result.
Overall the solution looked excellent. First fix occurred within a few seconds, fix rate was 100% after first fix, horizontal variation was roughly +/-0.5 cm and vertical variation was roughly +/-1 cm.
The solution residuals, both pseudorange and carrier-phase also looked very clean.
I only made a brief look at the raw observations but did not see anything unusual there either. At only five minutes of data, it is not much more than a quick sanity check, but so far, so good.
For the second experiment I collected four hours of raw observations, again with the F9T receiver and my rooftop antenna, a ComNav AT330. I then submitted this data to CSRS for their online PPP solution as well as running an RTKLIB SSR solution as I described in this post. Below are the results for both solutions. The plots are all relative to my best estimate of the location of the rooftop antenna based on previous PPP solutions with Swift and ComNav receivers as well as RTK solutions from nearby CORS stations. The left plots shows the first hour of solution with a +/-0.25 meter vertical scale. The right plot shows the second through fourth hours with a +/-0.06 meter vertical scale.
Both solutions get to below 6 cm of error in each axis after 1 hour and below 3 cm of error after four hours. The CSRS solution gets down to almost zero error in all three axes after four hours but I don’t believe my reference is this accurate so I think this was partially luck. The reported accuracies (95%) for the CSRS solution were 1 cm, 4 cm, and 5 cm for latitude, longitude, and height respectively. My previous experience running RTKLIB SSR PPP solutions with other low cost dual frequency receivers is that after running many solutions, they generally all fall within +/-6 cm accuracies in all axes after four hours. Both solutions include only GPS and Glonass observations because both the SSR correction stream I used from the CLK93 source, and the CSRS online PPP algorithm use only GPS and Glonass.
Being able to run accurate PPP static solutions can be a big advantage since it can make it much simpler to precisely locate a base station for RTK solutions with a dynamic rover, especially in more remote areas where there may not be any nearby CORS or other reference stations to run an RTK solution against.
As always, this post is intended to be just a quick snapshot and not an extended analysis of any type, but so far I have been very impressed with both the F9P and F9T and with their compatibility with RTKLIB.
If you have downloaded the b31 demo5 binaries from the rtkexplorer.com website before today, please download them again. The b31 source code is fine but I had a build issue caused by a combination of not doing a build from scratch and changes to the project file that I ported in from the 2.4.3 code. I know this can cause RTKCONV to crash for an illegal memory access. I don’t know if it can cause any other issues.
Sorry about that!
Also, I just added an update to the end of my previous post with some further analysis. It doesn’t change the overall conclusion, but it does impact some of the details, so you might want to re-read the post while you are downloading the new code.
The new low cost dual frequency receiver from u-blox, the ZED-F9P, is just now becoming available for purchase for those not lucky enough to get early eval samples from u-blox. CSGShop has a ZED-F9P receiver in stock for $260 which seems quite reasonable, given that it is only $20 more than their NEO-M8P single frequency receiver.
Even better, Ardusimple is advertising an F9P receiver for 158 euros (~$180) + 20 euros shipping , although their boards won’t ship until January. As far as I’m aware of, this is actually less than anybody today is selling the M8P receiver for today!
Of course, this is still a fair bit more than a u-blox M8T single frequency receiver without an internal RTK engine, which is available from CSGShop for $75, but the F9T will be coming out next year also without internal RTK engine, which should bring down the price for the lowest cost dual frequency receivers.
Unfortunately I am not one of the lucky ones who got eval boards directly from u-blox yet. However, I do have two prototype boards from Gumstix, given to me by them for evaluation. Gumstix offers both off-the-shelf boards and semi-custom boards designed from their libraries of circuits. I haven’t worked with them directly but it looks like an interesting and useful concept. The F9P boards from Gumstix won’t be available for sale until at least Feburary next year but I thought I would share the results of some initial testing. From a performance perspective, I would expect these boards to be similar to F9P boards from other suppliers.
For a first look, I chose to compare the F9P to an M8T for one of my typical driving-around-the-neighborhood exercises. I looked at both the internal real-time F9P solution and the RTKLIB solutions, both real-time and post-processed.
For the base stations, I connected a CSGShop M8T receiver and a Gumstix F9P receiver through an antenna splitter to a ComNav AT330 dual frequency antenna on my roof. Since RTKLIB doesn’t yet fully support the receiver commands needed to setup the F9P, I used the most recent version (18.08) of the u-blox u-Center app run on a Windows laptop to configure the F9P receiver using the documentation on the u-blox website. I then saved the settings to flash. The receivers were connected to a laptop with USB cables and I broadcast the base observations over the internet on a couple of NTRIP streams using STRSVR and RTK2GO.com as I’ve described previously. I configured the F9P to send RTCM3 1005, 1077, 1087, 1097, 1127, and 1230 messages which include base location, raw observations, and GLONASS biases.
For the most part the u-blox documentation is well written and this exercise was fairly straightforward, but I did run into a couple of issues. First of all, when I plugged the F9P receiver into the laptop, Windows chose the standard Windows COM port driver instead of the u-blox GNSS COM port driver that it chose for the M8T receiver. You can see this in the screen snapshot below where COM17 is the M8T and COM21 is the F9P.
Both drivers allow the user to set the baudrate in the properties menu available by right clicking on the device name. With the u-blox driver, the baudrate setting doesn’t seem to matter which makes sense since it is a USB connection. I have always left the u-blox driver baudrate at the default of 9600 baud without any issue. With the windows driver, however, I found that I had to increase the baudrate setting to 115200 to avoid data loss issues. I have run into a similar problem before for sample rates greater than 5 Hz when the M8T is accessed through it’s UART interface and an FTDI converter is used to translate to USB, rather than communicating directly through it’s USB interface. I verified though, that in this case the board is using the USB interface on the receiver and not the UART interface. Not a big deal, and it may be unique to this board, but something to be aware of in case you run into a similar problem.
The second problem I ran into is that the F9P module seems to be sensitive to my antenna splitter, a standard SMA DC block and tee which I have used on many other receivers before without issue. It works fine if the F9P power is blocked but if the M8T power is blocked, the F9P seems to detect the tee and shut off the antenna power. Again, not a big deal, but something to be aware of.
For the rovers, I used a u-blox ANN-MB-00 dual-frequency antenna for the F9P receiver. This is the antenna u-blox provides with its F9P eval units. I had planned to split this antenna signal to both receivers as I usually do, but I ran into the problem described above, and not fully understanding the issue yet, ended up using a separate Tallysman TW4721 L1 antenna for the M8T receiver. Both antennas were attached directly to the car roof which acted as a large ground plane.
I used a hot spot on my cellphone to stream the NTRIP base station observations from the phone to a laptop and then to the F9P receiver and to two instances of RTKNAVI, one for each rover receiver.
Streaming the base observations to the F9P, while simultaneously logging the internal RTK solution and the raw rover observations, and also sending the raw rover observations to RTKNAVI, all over a single serial port can be challenging since only a single application can be connected to the serial port at one time. Fortunately RTKLIB has a little trick to deal with this. If the “Output Received Stream to TCP Port” box is checked in STRSVR and a port number specified as shown below, all data coming from the other direction on the serial port will be redirected to a local TCP/IP port. This data can then be accessed by any of the other RTKLIB apps as a TCP Client with server address “localhost” using the specified port number.
I set up the F9P rover to output both raw observation and navigation messages (UBX-RXM-RAWX/ UBX-RXM-SFRBX) and solution position messages( NMEA-GNGGA). RTKNAVI then logged all of these messages to a single log file. RTKCONV and RTKPLOT can both extract the messages they need from this file and ignore the rest so combining them was not an issue.
The NMEA-GNGGA messages from the F9P default to a resolution of 1e-7 degrees of latitude and longitude which works out to roughly 1 cm. For higher resolution you can increase the resolution of the GNGGA message by setting a bit in the UBX-CFG-NMEA message. Unfortunately I did not realize the resolution issue until after I collected the data and so my results for the internal F9P solution for this experiment were slightly deteriorated by the lower resolution.
I used the most recent demo5 b31 code to calculate all of the RTKLIB solutions. Both the demo5 and the 2.4.3 versions of RTKLIB have been updated to translate the new dual frequency u-blox binary messages. The demo5 solution code will process all the dual frequency observations but I don’t believe 2.4.3 code is able to process the E5b Galileo measurements yet. The RTKLIB 2.4.2 code however does not have any of these updates.
The demo5 code updates made in the recent B30/B31 versions are based on the updates from the 2.4.3 B30 code but include some modifications to the u-blox cycle slip handling that I had previously added to the demo5 code for the M8T. Since the demo5 code is primarily aimed at low cost receivers I also made some changes to make the E5b frequency a little easier to specify and faster to process.
To run the RTKNAVI F9P real-time solution, the only significant change I needed to make to the default M8T config file was to change the frequency option from “L1” to “L1+L2+E5b”. I should have also changed the base station position to “RTCM Antenna Position” to take advantage of the F9 base station RTCM 1005 base location messages but neglected to do this. This caused the RTKNAVI solution to differ from the F9P solution by small constant values due to the approximate base location used in the RTKNAVI solution. I later used exact base locations for the RTKLIB post-processing solutions to verify that the different solutions did in fact all match.
Once I had everything set up, I then drove around the local neighborhood, emphasizing the streets with most challenging sky views since I knew both receivers would perform well and be difficult to distinguish if the conditions were not challenging enough.
I first converted the binary log files to observation files using RTKCONV and verified that the F9P was logging both L1 and L2 measurements for GPS, GLONASS, and Galileo. I had the Bediou constellation enabled as well but as I verified later, there were no fully operational Bediou satellites overhead when I collected the data.
Here is an plot of the L1 observations for the M8T on the left and the F9P on the right. I have zoomed into just two minutes during some of the more difficult conditions to compare the two. The red ticks are cycle slips and the grey ticks are half cycle ambiguities.
First, notice that the F9P does not log observations for the SBAS satellites, while the M8T does, giving the M8T a couple more satellites to work with. However, what’s also interesting, and I don’t know why, is that the F9P collected quite good measurements from the Galileo E27 satellite, while the M8T did not pick up this one at all. Of course the F9P also got a second set of measurements from the second frequency on each satellite and so overall ended up with nearly twice as many raw observations as the M8T.
Also notice that the F9P reports somewhat less cycle slips and many less half cycle ambiguities than the M8T. Some of this might be because of the different antennas, but particularly the large difference in half cycle ambiguities suggests that u-blox has made other improvements to the new module besides just adding the second frequencies.
Another thing to notice is the number of Galileo satellites. If you compare these plots to earlier experiments I’ve posted, you’ll notice there are more Galileo satellites now as more and more of them are starting to come online. The extra satellites really help the M8T solutions because as you can see, they tend to have the highest quality observations through the most difficult times. Again I don’t know why this is. It doesn’t appear to be as true for the F9P though.
Next I looked at the real-time solutions. First, the RTKLIB solutions with RTKNAVI for both receivers. For the full driving route, the M8T solution had a 77.3% fix rate and the F9P solution had a 96.4% fix rate. Here is a zoom into the most challenging part of the drive, an older neighborhood with narrower streets and larger trees, the M8T is on the left, and the F9P on the right. Fixed solutions are in green and float in yellow. Clearly here the F9P significantly outperformed the M8T.
The F9P internal solution did even better with a 99.2% fix rate, as shown in the plot below. All three solutions agreed within 2 cm horizontal, a little more in vertical, and none of them showed any sign of any false fixes.
I didn’t do any static testing to characterize time to first fix as I sometimes do, but for this one run the RTKLIB time to first fix for the M8T was 18 seconds while the RTKLIB F9P solution reached first fix in 6 seconds. In both cases, RTKLIB was started after the hardware had time to lock to the satellites and acquire navigation data for all satellites. The demo5 RTKLIB code has an additional fix constraint based on the kalman filter position variance to minimize false fixes while the filter is converging and so this can sometimes affect time to first fix. I had increased this parameter to 0.1 meter for this experiment since the large number of measurements reduces the chance of a false fix. This constraint did not limit the M8T time to first fix but it did so for the F9P, meaning the F9P would have reached first fix even faster if this constraint were opened up more. I can’t tell what the equivalent number would be for the internal F9P solution from this data since it had already been running and achieved a fix before I started logging the data but generally the F9P seems to acquire first fix very quickly.
Next I post-processed both data sets with RTKLIB using the combined-mode setting to run the kalman filter both forwards and backwards over the data. This noticeably improved the results, bringing the fix rate for the M8T up from 77.3% to 96.1% and the F9P fix rate from 96.4% to 98.8%.
Obviously this is not enough data to make any definitive conclusions, but so far I am very impressed with the F9P! Both the raw observations and the internal RTK solutions for the F9P look as good as anything I’ve seen from receivers costing many times what this one cost.
If anybody would like to look at the data from this experiment more closely, I have uploaded it to here. I should mention that all the fix rates I specify in this post and other posts won’t exactly match the fix rates in the raw solutions, since I adjust the data start and end times to be consistent between data sets and to start after all solutions have achieved first fix. I believe this is the fairest way to compare multiple solutions, especially when there is a mix of internal and RTKLIB solutions
Also, I’d like to thank Gumstix again for making these modules available to me for evaluation!
Reviewing the config files I used for this experiment I discovered that, while I had intended the real-time and post-processing config files to be identical, there were in fact some small differences between them. One difference in particular, that appears to have affected the results as described above, is that I reduced the minimum number of consecutive samples required to hold ambiguities (pos2-arminfix) from 100 to 20 for the post-processed config files. A value of 100 corresponds to 20 seconds at the experiment’s 5 Hz sample rate which is a value I have typically used. However, with lower ambiguity tracking gain (pos2-varholdamb=0.1) and the increase in observations coming from including Galileo, the chances of false fixes is reduced and I have been tending to use lower values of arminfix in more recent experiments. Reducing this value appears to explain a large part of the jump in percent fix for the M8T between real-time and post-processing, rather than the switch from forward-only to combined that I attribute it to above. These differences only affect the comparisons between RTKLIB real-time and post-processed results, and not between the M8T and the F9P since the config files were consistent between the two receivers.
This was only intended to be a quick first look at the F9P. It will require more data and more analysis to properly characterize the F9P so I won’t try to do that here but I will share the table shown below which includes a few cases I have run since the original post. I hope to dig into the details in future posts.
One last point worth making is that while at first glance the post-process fix percent increase from M8T=96.0% to F9P=99.3% may not sound that significant, it is in fact a factor of nearly six if you consider it as a decrease in float from 4.0% to 0.7%.