Comparing real-time precise (RTK) solutions for the u-blox F9P

The u-blox F9P internal RTK solution engine is very good and I have been recommending using it rather than RTKLIB for real-time RTK solutions, saving RTKLIB for F9P post-processing solutions. However, in this post, I will take another look at that recommendation.

Tomoji Takasu, the creator of RTKLIB, recently published some data also confirming the high quality of the F9P RTK solutions. He ran comparisons of the F9P against several other receivers of various price and quality. You can see his results in the 2021/07/01 entry of his online diary. His conclusion (at least the Google translation of the original Japanese) is that “Considering the price, there is no reason to choose other than F9P at this time.”

He did his comparisons by splitting an antenna output, feeding it to the two receivers being compared, then cycled the antenna output on and off. He fed the output of the receivers to RTKPLOT to produce plots like the example shown below from his diary which show acquire times and consistency of the fix location. In this case the upper plot is a Bynav C1-8S receiver and the lower plot is a u-blox F9P. The yellow intervals in the plot indicate float status and show the acquire times for each signal cycle. The green intervals indicate fix status and show the consistency of the fix location. Clearly the F9P outperformed the C1-8S in this example.

Receiver comparison data from Tomoji Takasu online diary

Inspired by his comparisons and the recent changes I incorporated into the demo5 b34c code, particularly the variable AR ratio threshold option described in my previous post, I decided it was time to do a similar head to head comparison between the internal F9P RTK solution and the latest demo5 RTKNAVI RTK solution.

To setup this experiment I used u-center to configure an Ardusimple F9P receiver to output both NMEA position messages (GGA) and raw observation messages (RAWX, SFRBX) to the USB port. I then setup STRSVR and RTKNAVI on my laptop as shown in the image below.

RTKLIB configuration for the comparison

The first instance of STRSVR is configured to stream NTRIP data from a nearby UNAVCO reference base (P041) to both the u-blox receiver and a local TCP/IP port. The output of the u-blox receiver is streamed to a second local TCP/IP port by selecting the “Output Received Stream to TCP Port” in the “Serial Options” window. The second instance of STRSVR then streams the second TCP/IP port containing the output of the u-blox receiver to a file for logging the internal F9P solution. This file will contain the raw observations in binary format as well as the receiver solution but RTKPLOT will ignore the binary data and plot just the NMEA message data.

The demo5_b34c version of RTKNAVI is configured to use the two TCP/IP ports configured above, one from the receiver, and one from the UNAVCO base, as inputs for rover and base. I also configured two instances of RTKPLOT so that I could see the real-time status of both solutions in addition to saving the results to files.

To setup the RTKNAVI config file for this experiment, I started with the PPK config file from the U-blox F9P kinematic PPP data set 12/24/20 data set and made a few changes to make it more suitable for a real-time solution. Time to first fix is not as important in post-processed solutions since they are usually run in both directions, so the config parameters in that file are set to minimize the chance of a false fix at the expense of relatively long acquire times. To shift this balance towards faster acquire times, I increased the maximum filter variance allowed for fix (Max Pos Var for AR / pos2-arthres1) from 0.1 to 1.0 and decreased the number of fix samples required for hold (Min Fix / pos2-arminfix) from 20 to 10. I also changed the solution type from combined to forward since combined mode is not possible for real-time solutions.

Most importantly, I enabled the new variable AR ratio threshold described in my previous post by setting the AR ratio threshold min and max (pos2-arthresmin, pos2-arthresmax) to 1.5 and 10. I left the nominal AR ratio threshold at 3.0. This means that instead of using a fixed AR ratio threshold of 3.0 for all epochs, the AR ratio threshold used in each epoch is selected from the blue curve below, with the limitation that it can not drop below the specified minimum of 1.5

AR ratio threshold curves

This will allow the AR ratio to drop well below 3.0 when there are many available satellites and the model strength is very high, while still protecting against false fixes by increasing the AR ratio when the number of satellites and the model strength are low.

To run the experiment, I connected the antenna to the receiver, waited until both solutions had acquired a fix, then disconnected the antenna for 10-20 seconds to force the receiver to lose lock to all the satellites, then reconnected the antenna again. I repeated this sequence for about 15 cycles.

I ran this experiment twice. The first time, I connected the receiver to a Harxon geodetic GPS-500 antenna mounted on my roof with a reasonably good view of the sky. The second time, I connected the receiver to a smaller, less expensive u-blox ANN-MB antenna with ground plane on a tripod in my backyard in a moderately challenging environment with the sky view partially blocked by the house and nearby trees. In both cases, the base station (P041) was 17 kms away and included Galileo E1 signals in addition to GPS and GLONASS.

Below is the result from the first experiment. As previously, yellow is float, and green is fix. The internal F9Psolution is on the top and the RTKNAVI solution is below. The blue in the F9P plot indicates a dead reckoning solution which only occurs when the antenna is disconnected, so can be ignored.. To reduce clutter in the plots I am showing only the U/D axis. In this case I know the precise location of my roof antenna so the y-axis values are absolute errors.

F9P vs RTKNAVI, GPS-500 antenna on roof

Here is a zoomed in version of the same plot. Both solutions acquire first fix fairly quickly in most cases. The absolute errors during fix are small for both solutions but do appear to be a little larger in the internal F9P solution. None of the errors are large enough to be considered false fixes.

F9P vs RTKNAVI, GPS-500 antenna on roof (zoomed in)

Below is the same plot for the second experiment where the smaller ANN-MB antenna is located in a more challenging environment. Again, both solutions tend to acquire first fix quite quickly, and again the internal F9P errors appear to be larger than the RTKNAVI errors. In this case I don’t know the precise location of the antenna so the errors are relative.

F9P vs RTKNAVI, ANN-MB antenna in backyard (zoomed in)

Here is a summary of the acquire times for both solutions measured from the above plots. The plots don’t show precisely when the antenna was reconnected so I measured both acquire times starting from the first solution output sample after the disconnect gap, regardless of which solution it came from. The first column in the table is the number of acquisitions measured, the other columns show the minimum, maximum, mean, and standard deviations of the time to first fix in seconds.

GPS-500 antenna on roof
ANN-MB antenna in backyard
Time to first fix comparison between F9P and RTKNAVI

Both solutions performed quite well in both experiments. The RTKNAVI solution outperformed the F9P internal solution in both cases, but given the very small amount of data and testing conditions, I would be reluctant to declare RTKNAVI the winner. I does suggest though, that the RTKLIB solution has closed the gap and should be considered at least roughly equal in performance to the internal RTK engine for real-time solutions.

In many cases it will still be simpler for real-time solutions to use the internal solution than RTKNAVI since it requires less configuration. There are cases, however, where it makes more sense to use an external solution even in real-time. For example, one user recently shared data with me where the rover is measuring real-time buoy activity. In order to avoid needing a bi-directional radio link, he sends the rover raw observations back to shore and calculates the solution there. Otherwise he would need to send the raw base observations to the buoy, and then send the resulting solution back to shore.

If anyone else runs a similar experiment comparing RTKNAVI to the internal F9P solution and wants to share their results, please leave a comment below.

18 thoughts on “Comparing real-time precise (RTK) solutions for the u-blox F9P”

  1. Hello
    This article of yours has inspired me a lot and have to admit that the F9P is a great low cost receiver that achieves great positioning results in computationally constrained situations. My comparison of the F9P and RTKLIB is very good Interested, do you have more comparative information about them? For example, a comparative test in a scenario such as a mobile car.

    Liked by 1 person

    1. No, I haven’t written up a direct comparison between the F9P internal RTK solution and an RTKLIB PPK solution, at least in a while. The F9P internal solution is very good and my perception is that, like the static case, in the moving rover case in the most challenging environments the internal solution will have a little higher fix rate albeit with a slightly higher false fix rate. Also, in extremely challenging environments where RTK is not achievable it will degrade a little more gracefully. That said, I do receive raw data sets from clients in which the internal solution failed to achieve a fix or found a false fix, which I am able to recover with the PPK solution, often with a little tweaking of the config parameters. I will add this comparison to my list of ideas for future posts.

      Liked by 1 person

  2. hello,
    I was wondering if when using F9P with RTKNavi (and with rtklib rtk engine) you were setting L1+L2+L5
    or only L1+L2 frequency ?


        1. Just for clarification for other readers, L1+L2+L5 in RTKLIB is not useful for solutions using the F9P since the F9P does not support L5 but this mode is useful for any receiver that does support L5.


  3. Hello again Tim.

    I have tested my ZED-F9P in different environments, and I am pretty sure that it is a powerful receiver. Although I can get a fix in a few seconds, and the internal solution is consistent, the “Sat Vis” plot of every observation data, shows a cyclic pattern where fixed satellites seem to “disconnect” periodically. An example can be seen here:

    Not sure if this is normal, but want to be sure if I have a connection issue, or maybe a problem with my antenna.



    1. Hello.

      I think I found the solution to my problem, so I am posting it here to help other people dealing with this.

      My setup comprises a Sparkfun GPS RTK2 module and I Raspberry Pi 3. I use the Pi for streaming RTCM corrections to the Sparkfun board. Also, I use the Pi for saving raw observations and send NMEA messages to a TCP port, so I can use them in my Android phone and save real-time data.

      This sounds pretty good, but in my setup, I was using the USB port of the Sparkfun module for everything (e.g. sending and receiving data), so I guess I overloaded the port with the resulting missing data behavior. I decided to set the UART port of the Pi to send RTCM corrections to the UART1 of the Sparkfun board, and now I don’t see missing data in two OBS files I generated for testing. The troubleshoot was pretty painful, but the reward was nice too.



      1. Hi Jose. Your example plot is not normal, it should not have those gaps but it sounds like you already figured out what was wrong, so thanks for sharing your description of the problem and solution.


  4. Hello again.

    I have a working rover using ZED-F9P chip using internal solutions. I want to get better precision, so I activated the “High precision mode” in the UBX-CFG>CFG-NMEA-DATA2 page in the u-center. After doing so, the messages sent by the receiver were unconsistent. I decided not to save the configuration and leave it without the “High precision mode” unticked.

    So, my question is: How do you achieve better precision when using the internal solution?


    José Martínez.


    1. Hi Jose. I have not had any issue with the high precision NMEA messages with the F9P and so that is what I use. The only reason I can think that you would have trouble with them is that you are right on the edge for UART port bandwidth at your baud rate and the longer messages are pushing you over the edge. Another option is to use the UBX-NAV-PVT message but this is not supported by RTKLIB so you would need to decode it yourself.


      1. Hi Tim.

        Thank you for your response.

        I configured again the receiver using the high precision mode option, and now I can see consistent NMEA messages. I guess I made a mistake while activating the high precision mode, maybe I also activated the compatibility mode at the same time, which produced the inconsistency. However, I wanted to be sure before trying with the precision mode again, so thanks for your advice.




  5. Hello.

    Congratulations for your blog. I’ve been following your posts since ~2019, and I found your insights very useful.

    I want to ask you, did you purchase your ZED-F9P unit from GNSS Store (formerly CSGSHOP)? If yes, since GNSS Store is based in Europe, did you pay customs duties when the item arrived in the US?



  6. you are a great specialist rtklib etc.! I wish you success and new, interesting projects!


Leave a Reply

Fill in your details below or click an icon to log in: Logo

You are commenting using your account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s

This site uses Akismet to reduce spam. Learn how your comment data is processed.

%d bloggers like this: