RTKLIB: Tips for using a CORS station as base

Generally, you will get the best RTK/PPK solutions using two matched receivers, one as rover, and one as a local base. This will give you the shortest baseline, maximum number of usable constellations, and no issues with the Glonass hardware biases that come from using unmatched receivers. To do this, you will first need to determine the precise location of your local base receiver. Running a post-processed kinematic (PPK) solution with your own local base receiver as rover and a nearby Continuous Operating Reference Station (CORS) as base is usually the easiest way to do this.

Of course, If you have a CORS station near enough, it is also possible to run solutions directly from your rover to the CORS base but I generally opt for the local base since the solutions will be more accurate and more robust.

For the most part, running a PPK solution against a CORS base is very similar to the local base solutions I describe in many of my other posts but there are a few important differences that I will try to go over in this post. I will describe using CORS stations inside the U.S. with u-blox receivers and the demo5 RTKLIB code since this is what I am most familiar with, but most of the information in this post will apply to other receivers as well as to CORS stations in other countries.

The first step is to find a nearby CORS station. In the U.S, the easiest way to do this is to go to the NOAA User Friendly CORS website and click on the CORS Map link. Zoom into your local area and if you are lucky you should see something like the screen shot below with several nearby choices. If you’re not so lucky you may find only one or two distant choices.

Notice that some stations are GPS only and some are labelled as “GNSS” which indicates that they support multiple constellations, usually GPS and Glonass, but sometimes Galileo as well. Ideally you will find a “GNSS” site within 20 kilometers but if not you may be successful generating fixed solutions with stations up to 100 km or more away. If you are using a dual frequency receiver and solving for a stationary rover you can usually use more distant stations. Be aware that longer baselines will increase accuracy errors so it is a good idea to run longer solutions and average the results from multiple stations if possible. Station sample rate is less important than distance and number of constellations but a higher sample rate is always better if you have the choice.

In addition to the NOAA CORS website, it is also worth checking if your state has its own network of reference stations. These are usually run by the state department of transportation and their data is normally free although you may need to sign up to access it. There is a slightly dated state by state list available here.

If your base station is sampling slower than a 1 second rate then I often find that interpolating between base observation samples improves the solution. In RTKPOST, this is done by setting enabling “Time Interpolation of Base Station Data” in the “Misc” tab of the options menu. In RNX2RTKP, this is done by setting “misc-timeinterp =on” in the config file.

Assuming your rover is stationary, you can run the solution mode as “Static” and this will improve your chance of getting a fixed solution. I prefer to run a “Kinematic” solution if possible, however, since the variation in solution position over time gives some insight into what the accuracy of the solution is, especially if you have collected several hours of observation data.

Since the receiver manufacturers will almost always be different between base and rover in these solutions, you will usually need to deal with the Glonass hardware biases. I describe the different options to account for these in detail in this post so I would recommend reading or reviewing that post for the details. To quickly summarize though, the simplest option is to set “Integer Ambiguity Resolution” in the options menu for Glonass to “Fix-and-Hold” in which case RTKLIB attempts to calibrate the biases. In some cases, this will be good enough to get a fixed solution. However, you are more likely to get a fixed solution if you account for the biases directly. You can do this by setting Glonass ambiguity resolution to the poorly named “Autocal” option and then specifying the biases directly with the “GLO HW bias” option in RTKPOST or “pos2-arthres2” option in the config file. Again, see the above post for the details on how to determine the correct biases and the table of biases by receiver manufacturer. Note that these ambiguity resolution options are only available in the demo5 version of the RTKLIB code.

If the HW biases for both base and rover are close to zero then it’s generally OK to ignore them and set Glonass ambiguity resolution to “on”. This will not be true for a u-blox M8T rover since it’s bias is not zero. However the u-blox F9P bias is close to zero so if you are using it as rover and your base receiver is manufactured by Trimble, Septentrio, Topcon, Spectra Physics, Javad, or Ashtec then go ahead and set Glonass ambiguity resolution to “on”. The receiver manufacturer will be listed in the header of the base rinex file.

The base rinex file header also includes the base antenna type and it’s precise location. Note that the precise location is in the header field labelled “APPROX POSITION XYZ”. Setting the base station position in the options menu to “RINEX Header Position” will cause this value to be used in the solution. If you would also like to include the base station antenna calibration in the solution, then check the “Antenna Type” box and specify “*” in the box below. This tells RTKLIB to use the antenna type listed in the rinex header. You will also need to point to an antenna calibration file in the “Files” tab of the options menu. You can use the “igs14.atx” file that is included with the demo5 binaries for this.

It is important to keep in mind that the accuracy of the solution is going to get worse as the distance to the base station increases as shown in this chart taken from a Novatel tutorial.

If you are using a single frequency receiver then there are usually no good alternatives to using a distant CORS station. The best you can do is collect multiple longer data sets from multiple stations and average the results. If you are using a dual frequency receiver then you also have the option of running a Precise Point Positioning (PPP) solution, either with RTKLIB or through an online service as I describe in this post.

Well, that’s all I can think of at the moment. If anyone else has any additional tips or questions, please add them to the comments below.

RTKLIB Benchmarking: versions 2.4.2, 2.4.3, and demo5

It’s been about four years now since I created the demo5 branch of RTKLIB. During that time I have added a number of features and enhancements to the code with a focus on low-cost receivers (primarily u-blox) and moving rovers while at the same time keeping synced with the latest 2.4.3 code from the official RTKLIB code base. I thought it would be interesting to do a little benchmarking between the the two official versions of RTKLIB (2.4.2 and 2.4.3) and the demo5 code to see to what extent this evolution of the code has affected the results.

First of all, though, it’s probably worth a little discussion about versions 2.4.2 and 2.4.3. Source and executables for both versions are available on Github at https://github.com/tomojitakasu. The source is in the RTKLIB repository and the executables are in the RTKLIB_BIN repository. Both of these repositories default to the “master” branch which is the 2.4.2 code. This is what you will get unless you specifically request the “2.4.3” branch of the code. For several years, almost all the development activity was on the 2.4.3 branch and only very minimal changes were being made to the master branch. In Jan 2018, there was a merge of the 2.4.3 changes back to the master branch although it appears that not all of the changes in 2.4.3 were merged back into the master branch. Since then development has continued on 2.4.3 without another merge back to the master branch.

For most of my data analysis posts, I focus on a single data set, spending a fair bit of time to make sure I am only analyzing the usable parts of the data, possibly tweaking the configuration file for that specific data, and digging into any issues that crop up. In this case I didn’t do that. I picked nine raw data sets, all with u-blox M8T receivers and a moving rover, made no effort to filter out bad data, and used a single generic configuration file for all nine data sets. Eight of the data sets are those that I have previously uploaded to my website at http://rtkexplorer.com/downloads/gps-data/ and the ninth was from my most recent drive around the neighborhood with a u-blox M8T and an antenna on top of the car. I ran solutions for each of the three RTKLIB versions on all nine data sets. For each solution, I converted the data from u-blox binary to rinex using the same code version as I did for the solution, since the different codes will affect this conversion as well as the solution.

I ran the post-processed solutions in “combined” mode, meaning that the solution is run both forward and backward and the results are then combined. Not only does this tend to produce better results, but the results also have higher confidence since RTKLIB compares the forward and backwards solutions, sample by sample, and downgrades any sample where the solutions in both directions are fixed and the results differ by more than four standard deviations. This tends to do a good job of detecting and rejecting any false fixes in the results. However, it is not foolproof. If the solution is fixed in only one direction and float in the other, then there is no additional validation.

I used the same configuration settings for running each of the three RTKLIB versions on each of the data sets with a few exceptions. Versions 2.4.2 and 2.4.3 do not have the “arfilter” feature that automatically holds off new satellites until their phase bias estimates have converged enough to not break the ambiguity resolution so I increased the fixed hold off (arlockcnt) from 0 to 10 for the 2.4.2 and 2.4.3 codes. The outlier detection scheme is also different in the demo5 code from the other two versions, making it necessary to increase the outlier threshold (rejionno). In this case I increased the threshold from 1.0 to 30.0 for versions 2.4.2 and 2.4.3. Lastly, the 2.4.2 code runs very slowly if dynamics is enabled, so I turned dynamics off for this code.

The specific code versions for the experiment were: 2.4.2 p13, 2.4.3 b33 and demo5 b33b2.

I used fix percentage as a metric for the test. This isn’t a perfect metric because it doesn’t take into effect the accuracy of the non-fixed results and it can be affected by false fixes. Overall, though it is probably the best single metric for this kind of test, especially since running the solutions in combined mode should reject many of the false fixes.

The fix percentages for each test are listed below. The data set names correspond to the names of the sample data sets on my website.

TestV 2.4.2V 2.4.3V2.4.3ademo5data set
137.7%93.9%96.3%94.4%union_0705
235.7%9.8%60.6%85.5%niwot2_car
352.4%16.2%89.0%80.2%drone_0414
485.8%51.2%93.8%98.8%m8t_niwot_0606
559.7%21.4%83.7%97.1%swift_m8t_road_0606
663.1%10.0%83.6%99.0%car_1114
754.6%26.6%73.3%94.2%car_0320
839.4%11.2%54.4%98.1%comnav_car
925.0%9.2%53.4%98.1%not uploaded
median52.4%16.2%83.6%97.1%
Experiment results in fix percentage

For the most part, the results match my expectations. Version 2.4.2 has the lowest fix percentage, 2.4.3 is in the middle, and demo5 has the highest. However, I ran into one very significant issue with the 2.4.3 code that I do not fully understand. In the table above, the “V2.4.3” column is the results using version 2.4.3 for both the conversion from raw binary to rinex as well as for the solution. As you can see, the fix percentages were very low for this test for all data sets except the first, significantly lower than even the 2.4.2 results. I did not fully debug this issue but the problem appears to be in the conversion from raw binary to rinex, not in the solution itself.

The V2.4.3a column is the results for running the 2.4.2 code for the raw binary to rinex conversion, and then the 2.4.3 code for the solution. This result is much more within my expectations for the 2.4.3 code. I suspect that the issue with the 2.4.3 rinex conversion is that when it is filtering out low quality observations it is not preserving the cycle-slips. RTKLIB can be very sensitive to unflagged cycle slips.

I am very curious if anyone who is a regular user of the 2.4.3 code can duplicate this result. As you can see from the first data set, it does not always occur, and is much less of a problem if the data does not have a large number of cycle slips, so it would need to be tested on a more challenging data set to see this issue.

Regarding the demo5 results, seven of the nine tests had over 94% fix rate and the median fix percentage was 97.1%. I consider this quite reasonable since all of these were fairly challenging data sets and most of them included at least a small amount of unusable data. The two data sets that did not perform as well (80-86% fix percentage) were both older. One did not include Galileo and the other was from a drone that had one particularly poor quality section of data. However both solutions had well over a 90% fix rate when run in the forward only direction which indicates the fixes in the combined solution were downgraded because of mismatches between the two directions. In one of these cases (test 3), the 2.4.3 solution obtained a higher fix rate than the demo5 code but it only got fixes in the forward direction, not in the backward direction so had no additional validation. Based on some discrete jumps in that solution, I suspect it would have also downgraded the fix percentage if it had achieved fix in the backwards direction.

Looking more closely at the cases where the demo5 solution points were downgraded to float for mismatches, it’s interesting because, at least at first glance, it appears that these were not false fixes, but discrepancies from using different combinations of satellites that were large enough to trigger the four standard deviation threshold. This is a little concerning and worthy of further investigation. Fortunately it only appears to occur when the data quality is fairly poor. However, this does emphasize the importance of insuring the best quality measurements possible, and not over-relying on RTKLIB to reject inaccurate solutions.

As always, I’d like to emphasize that these tests are intended only as one users snapshot of one fairly particular use case of RTKLIB and are not intended to be any kind of comprehensive analysis. Also, it’s important to understand that 99+% of the code in all versions of RTKLIB including the demo5 code are the result of many years of dedicated effort by Tomoji Takasu and his team at Tokyo University of Marine Science and Technology. My only contribution has been to add a few changes on top of this code to make it a little more focused on practical application for specific uses rather than a more generic academic tool.

Lastly, for reference, here’s a partial list of the most important configuration settings I used for this experiment for the demo5 code. The other two codes used the same settings with the exceptions I describe above.

pos1-posmode =kinematic # solution mode
pos1-soltype =combined # solution type (forward, backward, combined)
pos1-frequency =l1 # (l1, l1+l2, l1+l2+l5)
pos1-elmask =15 # min sat elevation to include in solution (deg)
pos1-snrmask_r =off # SNR mask rover (off, on)
pos1-snrmask_b =off # SNR mask base (off, on)
pos1-dynamics =on # add dynamic states to kalman filter (off, on)
pos1-navsys =15 # (1:gps+2:sbas+4:glo+8:gal+16:qzs+32:comp)
pos2-armode =fix-and-hold
pos2-gloarmode =on # Glonass AR mode (off, on, fix-and-hold, autocal)
pos2-bdsarmode =off # Bediou AR mode (off, on)
pos2-aroutcnt =50 # outage count to reset sat ambiguity (samples)
pos2-arminfix =50 # min # of fix samples to enable AR hold (samples)
pos2-rejionno =1.0 # phase bias outlier threshold (m)
pos2-maxage =30 # max age of differential (secs)
pos2-arthres =3.0 # minimum AR ratio for fix (m)
pos2-arthres1 =0.1 # max variance of position states to attempt AR (m)
pos2-varholdamb =0.1 # variance of fix-and-hold tracking feedback (cyc^2)

pos2-arfilter =on # automatic hold off for adding new sats to AR
pos2-arlockcnt =0 # fixed hold off for adding new sats to AR (samples)
pos2-minfixsats =4 # min sats required for fix
pos2-minholdsats =5 # min sats required for AR hold
pos2-mindropsats =10 # min sats required to drop sats from AR
stats-eratio1 =300 # ratio of input stdev of code to phase observations
stats-eratio2 =300 # ratio of input stdev of code to phase observations

Updates to RTKLIB for the u-blox F9P receiver

I recently released a new version of the demo5 RTKLIB code (demo5 b33b2) with some more updates for the u-blox dual-frequency F9P receiver. Its taken a lot longer than I would have hoped, but for the first time, I feel like the demo5 code now has reasonably complete support for this receiver. Many of these improvements should help with other receivers as well, especially other dual-frequency receivers. In this post, I’ll describe some of the recent changes to the code. If you are more interested in a getting-started guide to using RTKLIB with the F9P rather than the latest code changes, then you might prefer this earlier post.

Here are the most important code changes to be aware of. They include changes made in the last couple of demo5 code versions.

  1. Support for F9P constellations/frequencies:
    I believe the latest code now supports all the F9P L1, L2, E1, E5b, B1, and B2 codes for the GPS, Glonass, Galileo, and Beidou constellations, both for u-blox raw binary formats and for the RTCM3 MSM7 messages. Raw and RTCM3 files are correctly converted to rinex format and processed in the position solution for all these codes. However, I did recently notice that there are still a few issues with RTKPLOT correctly plotting all the different codes, particularly the Beidou B2 observations.
  2. Frequency selection:
    Another change in the new code is a slight shuffling of the frequency grouping between constellations. Since RTKLIB memory use and CPU load increases significantly when enabling additional frequencies, I eliminated Galileo E5b processing in RTKLIB as a separate frequency and it is now processed with the other “L2” frequencies. This means to run a dual-frequency solution for all constellations on the F9P, you need to only enable “L1+L2” as the frequency option. Galileo E5a is still part of the “L5” frequencies so if you are using a receiver that outputs E5a instead of E5b you will need to enable “L1+L2+L5” as the frequency option when running an RTKLIB solution. Running dual frequency solutions still take longer than running single frequency solutions but the difference is smaller than it was.
  3. Receiver Configuration:
    A more significant change to the new code is that RTKLIB now much more fully supports configuration of the F9P receiver using a “.cmd” file. In the previous code, some commands still worked on the F9P such as turning on or off receiver messages or changing the observation output rate but other commands, particularly those to enable or disable constellations were broken since the format of these commands changed when going from the M8T to the F9P. In addition, only a small subset of the total commands available for either the u-blox M8T or F9P receivers were ever supported by RTKLIB.

    To make things more complicated, with the F9P, u-blox is also transitioning from the legacy UBX-CFG messages to a new configuration protocol. Although most of the legacy configuration messages still work on the F9P, they are recommending switching to the new format as stated in this quote from the F9P Integration Manual:

    “3.1.6 Legacy configuration interface compatibility There is some backwards-compatibility for the legacy UBX-CFG configuration messages. It is strongly recommended to adopt the new configuration interface, as the legacy configuration messages support will be removed in the future.

    Fortunately the latest demo5 code now fully supports the new configuration interface, thanks to code contributed by Nagarjun Redla. Under the new interface, instead of having different commands, each with its own format and various numbers of input parameters, configuration parameters are set individually using the VALSET command . For example, the legacy CFG-RATE command had 3 input parameters to set time source, measurement period, and navigation rate. Under the new interface each of these parameters has its own key and each is set with a call to the VALSET command with parameter key and value. The two examples below set the measurement rate to 200 msec and enable the Galileo observations.

    !UBX CFG-VALSET 0 1 0 0 CFG-RATE-MEAS 200
    !UBX CFG-VALSET 0 1 0 0 CFG-SIGNAL-GAL_ENA 1

    Currently, only the second of the first four numeric parameters is used and that is set to to the configuration layer to write to. In this case “1” writes to the RAM layer. Use “4” to write to the flash layer or “5” to write to both flash and RAM. “2” is used to write to the BBR (battery-backed up RAM). See section 6 of the F9P Interface Description document for a list of all configuration key values as well as for more details on the VALSET command.

    All of the available key values are supported by RTKLIB, so any receiver configuration parameter that can be modified from u-center can now be modified from a command file in RTKLIB and the results can be saved to either RAM, flash, or both. The legacy commands that RTKLIB previously supported have not been removed so they will all still work as well.

    The one exception I am aware of is that the key values to set the individual frequencies do not seem to work from either u-center or RTKLIB. This is either a bug in the F9P or a misunderstanding on my part. However, in the Generation 9 Configuration View in u-center, the “Advanced Configuration” command is used for all parameters except GNSS configuration which uses the legacy command. This makes me believe it is actually a bug. Since RTKLIB does not support the legacy GNSS configuration command for dual-frequencies, this means that RTKLIB can enable and disable constellations but not individual frequencies within a constellation.
  4. Ambiguity Resolution:
    Although as I demonstrated in an earlier post, the demo5 code was working well for solutions with moving rovers, the results were less consistent for stationary rover solutions. In some examples, especially those with moderate amounts of multipath in the rover observations, the RTKLIB solution was not getting a fix even after several minutes, while the real-time internal solution and even the single frequency RTKLIB solutions were converging much more quickly.

    Most of my experiments focus on moving rovers and I tend to think of them as more challenging than stationary rovers because of the large number of cycle slips and the continuously changing set of satellites available as the rover’s sky view keeps changing and different satellites come in and out of view behind obstructions. However, stationary rovers have their own set of challenges and in particular multipath is a greater challenge in the stationary rover case than it is in the moving rover case.

    This is because, for the stationary rover, the paths from satellites to receiver antenna stay relatively constant and change only slowly as the satellites move across the sky. This means that the errors introduced by the combination of direct and indirect paths (reflections) between satellite and antenna have very long time constants. In the moving rover case, the errors are still present but are changing much more quickly with rover movement, have much shorter time constants, and average out to zero much more quickly.

    The existing partial ambiguity resolution algorithm in the demo5 code turned out to be more sensitive to the long time constant multipath when the number of observations increased with the dual frequency receivers. I had previously added a step to the ambiguity resolution algorithm to exclude a different satellite each epoch whenever the number of satellites was above a defined minimum (Min Drop Sats) to try and detect “bad” satellites. To minimize the increased risk of false fixes while the kalman filter was still converging, I had only done this after first fix. It turns out that with large numbers of observations, it becomes important to extend this test to before the first fix, and this is the change I made to the code.

    False fixes appear to be much less common with the F9P than the M8T, presumably due to the increased number of observations, so the increased vulnerability while the filter is converging is not much of a concern with the F9P. However if using the latest code with the single frequency M8T, you may need to adjust the configuration parameters slightly to avoid increasing the risk of false fixes while the kalman filter is converging. The best way to do this is usually to adjust the maximum position variance threshold (Max Pos Var for AR) to a slightly lower value. This will delay the start of ambiguity resolution attempts until after the filter is better converged. The trade-off is that if set too low it can delay time to first fix so it may require a little experimentation for best results. I might suggest starting at a value of 0.05 meter for the M8T and 0.1 meter for the F9T but optimal values will vary with configuration so adjust as needed. Time to first fix is less important if you are post-processing with a “combined” solution so you can tighten these numbers even more in this case. For post-processing high quality observations with the M8T I usually set this value to 0.004 meters.
  5. Precise ephemeris:
    This last change is not related to the F9P receiver but is worth mentioning anyways. I don’t use precise ephemeris files very often but other people do, especially for PPP solutions. Use of the MGEX files has become much more popular since they include the Galileo and Beidou orbital data as well as GPS and Glonass. These files usually have capitals in their extension unlike the older files. (.SP3 vs .sp3). Due to a bug in the RTKLIB code it was rejecting the “.SP3” files without reporting an error which could be very confusing to the user. The newest code now accepts files with “.SP3” extensions as well as “.sp3” extensions.

I think those are the most important changes but you can always review the demo5 Github repository for more details on code changes or if you want to build the code for linux platforms.

I do my best to test the code before I release it but I don’t have the time or resources to do this properly on all the different variations that RTKLIB can support so please treat new releases as beta, and if you see results that don’t make sense, it’s a good idea to compare results between the newest version of code and an older version that you have confidence in. If you do find degraded results in the new version, please let me know, and if possible, send logs of the observations and config file along with a detailed description of the issue. I rely on users to validate the code and treat any reported issues where a newer version of the demo5 code performs poorly compared to either a previous version or the official 2.4.3 code with the highest priority I can.

One last thing. You may notice that I have added an optional contribution button to the code download page. I do this work to promote low-cost precision GNSS and because I enjoy it, not to get rich and I want to emphasize that contributions are completely optional. However, if you are a regular user of the demo5 code, find value in the software, and would like to share that value, then any contribution is much appreciated.   I have also added a field to the contribute page to describe any feature or bug fix that you would like to see added to the demo5 code and will try to prioritize popular requests when making future code updates.

A first look at the Broadcom BCM47755 dual-frequency receiver

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

Rover observations for B47755

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.

RTKLIB documentation tips

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.

navi1

Here is an image of  a few of the various monitor windows I frequently find useful.

navi3

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.

navi2

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.

str2str_1

str2str_1a

Now let’s bring up the embedded help message in STR2STR by typing “str2str -h” and we get the following:

str2str_2

str2str_2a

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.

options

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.

A first look at the u-blox ZED-F9T dual frequency receiver

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.

f9t_1

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.

f9t_3

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.

f9t_2

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.

 

 

Build issue with downloaded demo5 b31 executables

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.