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.


    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.


39 thoughts on “Updates to RTKLIB for the u-blox F9P receiver”

  1. Hi. I am trying to convert ublox file to obs and nav data through RTKLIB but no output appears. Can you help troubleshoot please.
    receiver: ublox M8P
    RTKLIB version 2.4.3


  2. Thanks for the blog, always full of interesting info.

    I’ve just come across something weird running some data using a f9p base station. On the GPS unit it is set to TMode = fixed, and the lat long and height are set. The height is set to 377.93m in the f9p’s config.

    When I ran rtkconv (b33b) on the ubx log from this gps, then used rkvpost (b33b) to process the log from a rover, it says the ref pos height (set from RINEX header position) was only 365.4253, leading to all the heights being out by the difference between this height and 377.93m. The lat and long of the ref pos come through correctly.

    I tried redoing it with b34c, and it gave a ref pos of 369.84 and all the heights were again off by the difference between that and 377.93.

    I processed the rover data using another CORS station (not mine) and the positions all ended up correct, so the rover data is good.

    What could be going on here?


    1. Hi Glenn. The F9P does not output the base location in the UBX messages, at least not the standard obs and nav messages that RTKLIB is able to parse. It outputs this info in the 1005 RTCM3 message. If you have configured the F9P to output UBX messages and not RTCM3 messages, then I suspect the “APPROX POSITION XYZ” field in the rinex file header you created with RTKCONV will have all zeros for the position. In this case, RTKPOST will generate an approximate base position from the base observation data. I suspect this is what is happening in your case. To resolve this, you can switch the F9P to output RTCM3 messages, or you can manually enter the precise base location in the options menu in either RTKCONV or RTKPOST. Be sure to set the “Base Station” location type in the RTKPOST options menu “Positions” tab to the appropriate choice.


      1. Thanks for that. I have the F9P base outputting RTCM3 messages as well as the UBX messages, but I ended up getting a higher percentage of the positions with a float fix rather than rtk using the RTCM3 log compared to the UBX one, so figured the UBX one would be better to use. The UBX log has an APPROX POSITION XYZ value in the header which when converted to lat/long/height are correct, so I just wasn’t sure why after processing with rtkpost I would get an incorrect value for the ref position.


        1. OK, re-reading your original question more closely, I realize that you mentioned the lat and long in the base reference position were correct and only the height was wrong. Are the antenna height offsets in both the rinex file and the config file set to zero? If that is not the issue, I’d be happy to take a quick look at your data to better understand what is going on. You can send me the base and rover rinex files and the config file you are using to rtklibexplorer@gmail.com


  3. With my F9P, after measuring several points in mountain, I did calculation versus several permanent reference stations in the vicinity (<50 km). I observed that there was a bias in the elevation depending on the used reference station. After examination, I discovered that it was related to the elevation of the reference station (about 0.1 mm per meter). I attributed it to an incorrect troposphere delay estimation. So, I changed the “Tropo Correction” from “Saastamoinen” to “Estimate ZTD”. It reduced (and inverted) the bias to -0.04 mm per meter.
    Indeed, our national geographic institute is publishing hourly tropospheric models (http://rgp.ign.fr/PRODUITS/tropo.php). I would like to use it. Or if you have any other sources, I may be interested. I also see in RTKPOST an “Input ZTD” option but I can’t find any information on it. Any tip would be welcome.


    1. Hi Eric. That sounds like an interesting experiment … I may try to duplicate it at some point. In general it is a good practice to minimize elevation difference between base and rover since the atmospheric error cancellation derived by differencing base and rover observations will be less effective if the two are at different altitudes. There is some information on the tropospheric model options in the RTKLIB manual (see appendix E.5) but it is not something I have worked with much. RTK solutions rely primarily on observation differencing to remove the atmospheric errors and some of the more advanced options described in the manual I believe are only fully supported in PPP mode in RTKLIB, not in RTK/PPK mode.


      1. Indeed, I knew that differences in elevation were a problem. Long time ago, I read a report on GPS measurements on Île de la Réunion volcano. They were in trouble not only because difference in elevation but also in humidity and temperature between the different side of the volcano.
        In my case, I don’t like the present workaround (i.e. estimate ZTD) because it is introducing new unknown parameters in the solver, resulting in more difficult fix in some conditions. ZTD is evaluated twice at reference and at rover. Looking in the Solution Status File, the two values are changing over time but are correlated each other (i.e. the difference is quite constant and much smaller than overall changes). So I would like to impose fixed values that I would evaluate by other methods (like NRCAN PPP). But I don’t know how to set them.
        Concerning the Saastamoinen model used in RTKLIB, it is relaying on a standard atmosphere with parameters at sea level : 1013 hPa, +15°C, humidity 70%. At my measurement time, it was more +35°C, humidity 30% (at 200 m elevation).


        1. Hi, Eric,
          you wrote “…So I would like to impose fixed values that I would evaluate by other methods (like NRCAN PPP). But I don’t know how to set them.”
          Did you find a way to solve this issue?
          Some variables can be changed in the file, but this will require the programs to be recompiled.
          Perhaps you have found another possibility to enter ZTD or model?
          Regards, Aleks


          1. Hi Aleks,
            Sorry, I didn’t investigated further on how to introduce ZTD values in RTKLib. Overall, it is more critical when there is a high elevation difference between base and rover, but, in that case, having precise elevation evaluation is not that critical. In opposite, in flat area, where I may need accurate elevation determination (for hydrology for instance), ZTD should have less influence on results.


  4. Thanks for your work on this. The blog is a great resource.

    Was trying out b33c binaries, and noticed rtkpost.exe when it launches is a very small window and the bottom row of buttons are not visible. Tried out b33b and rtkpost.exe works as expected.


    1. Hi Michael,

      Yes, going from b33b to b33c, I switched from 32 bit binaries to 64 bit binaries. For some reason that I don’t understand, this can cause problems with Windows 10 display scaling. Setting display scaling to 100% by following these instructions usually fixes the problem.


  5. Hi, very interesting ! I have a question concerning the conversion of ubx files from ZED F9P with RTKCON (same problem with all versions ); I can’t choose the interval, and if I do I have the result which is not saved in the output folder (there are no conversion errors). Maybe you can help me solve this problem ?


    1. Hi RZ. Unlike most receivers, the u-blox receivers attempt to adjust the timestamps by the clock error. There is no real advantage to this because RTKLIB maintains its own estimate of clock error but it does cause an issue with the interval option since RTKLIB is expecting the samples to fall on nice round numbers like 0.0, 0.2. It rejects any samples that fall outside the time tolerance option set in the Options menu. Increasing the time tolerance value should fix your problem.


  6. Hi Tim,

    thanks for your tireless work on the low-cost RTK scene! I am still thinking about the OSM-like precise mapping project but if I do it I need to keep it as simple as possible. I plan to use Raspberry Pi as base bacause I need its camera. That means compiling RTKLIB on Pi (I havent studied that yet). With the new ZED-F9P I am thinking about doing things without RTKLIB and using the internal solution which should be good enough. Looks like F9P receives RTCM input messages on serial port, but for VRS I need to input send back the rover position somehow…

    Do you think when working on Raspberry Pi platform it may be easier to compile RTKLIB for it (and probably be all set) and use simple commnad line tools with scripts or is it worth trying to avoid RTKLIB?

    Thanks again!


    1. Hi Kozuch. The RTKRCV and STR2STR apps in RTKLIB compile and run fine on a Pi as long as you run them from a console but the F9P internal solution is very good and usually gives better results than RTKLIB for real-time solutions. You can use STR2STR to send back position messages to the base for VRS applications although I would guess it is possible to do this directly with the F9P as well.


    1. Hi Juan,

      The u-blox receivers include a quality metric with each raw observation which RTKLIB uses to reject low quality observations. The default threshold for rejecting observations is 5 which I find is usually appropriate for the M8T but too low for the F9P. Since RTKLIB doesn’t differentiate between the different u-blox receivers it is not possible to automatically set this differently for the M8T and the F9P receiver. I suggest starting with “-MAX_STD_CP=8” in the receiver options box in the Options menu for RTKCONV or other RKTLIB apps when working with the F9P. I don’t have a lot of experience with this option so you may want to try adjusting it yourself to the optimal setting for your environment. In general this will only affect conditions where you are seeing a lot of cycle slips in the raw observations. Increasing this value will reject less of the lower quality observations, so will give you more raw data but of lower quality. The trick is to find the right balance between quantity and quality.


  7. One more difficulty in my exploration of F9P employment ;-).
    So, mostly, I’m doing static measurements with the F9P. Back home, I try to calculate position versus permanent GNSS stations. Most of them are GPS+Glonass only and I get good fixes. In opposite, for stations with Galileo in addition, fix is difficult, even with small baseline (<10 km). I need to deactivate Galileo in RTKCONV options to get easy fixes. An idea why adding Galileo degrades the fix?


    1. Hi Eric,

      Have you tried GPS+Galileo solutions as well? My guess is that the problem is too many satellites rather than anything specific about the Galileo satellites. I have seen this problem as well for static solutions with RTKLIB when calculating dual-frequency solutions with many constellations. Multipath rejection is more challenging for static solutions since the time constants of the errors are long unlike moving rovers where rover movement randomizes the errors. RTKLIB was having trouble rejecting the multipath when the number of observations got too large. I put some improvements into the demo5 b33c code to try and deal with this, have you tried this code?


      1. Late answer… but with much more experience. I’m using the last version of code (b33c). I always give the ANTEX file of the base station. I get overall better fix. Indeed, usually, I set the Interger Ambiguity Resolution for GLONASS to ON with good results (remembering that with my F9P+UBlox antenna, GLO bias should be equal to 0 mm). Indeed, now, I usually get better fix with GPS+GLO+Galileo 35 km away than with GPS+GLO 15 km away. All in one, Galileo helps a lot to get fix.


  8. Hello Tim,

    I am struggling with the configuration of my F9P with RTKLIB demo5 b33c. Actually I use the manual configuration with u-center and then it goes to RTKLIB via USB. But how I generate a .cfg-file for the direct use? Or do you have any example file for disable NMEA and enable UBX, then set all constellation an frequencies to “on” and set the raw-rate to 10 Hz?
    Thanks for your action,



      1. Hello Tim,

        I know the manual configuration with u-center. There was a mistake of my part because I mean the “.cmd”-file like you describe at point 3 above… I don’t find a useful example in the Interface Description.


        1. Hi Paul. I have not put together a .cmd file for the F9P yet since I usually use u-center to configure my receivers these days, but if you use my example .cmd file for the M8T (part of the demo5 executables download), most of the commands will still work on the F9P. However, you will need to replace the constellation configuration commands with new commands similar to the example I give in the post. Section 6 of the Interface Description describes the new configuration interface, I’ve updated the post to reflect this and fix the broken link to the interface document.


  9. It seems that I have an issue with RTKPLOT.

    I have the sparkfun F9P board. I use it connected with USB to u-center. I can see all four constellations in u-center. I recorded UBX files in u-center after setting parameters as suggested. I can play back the UBX file in u-center and see all constellations. In details, I can see them both in UBX-RXM-RAWX and UBX-RXM-SFRBX messages. Then, using RTKLaunch, I started RTKCONV. I selected all constellations and all frequencies. I converted. When I plot in RTKPLOT, I can’t see C sats, only G, R and E. Indeed, looking inside .obs and .nav files, several C, R, E and G sats are visibles. So, I think that the issue is in RTKPLOT (demo5 b33b2). Any hint on how to see beidou sats in RTKPLOT?


    1. New investigations. If I open the .obs file converted with demo5, with “official” RTKLIB version (still 2.4.3 b33), I can see the four constellations in RTKPLOT. Indeed, Galileo and Beidou are single-frequency. Conversion and plotting both in demo5 is showing dual-frequency Galileo (and no Beidou). Conversion and plotting in “official” may show (using RINEX 2.10) fiew dual-frequency Beidou but all Galileo as single-frequency.


      1. Hi Eric. The RTKPLOT options menu (select from the top right corner) allows you to enable and disable each constellation. It’s very possible that the 2.4.3 code defaults to enabling Beidou and the demo5 defaults to disabling it. I would check that first.


        1. You are right. I just dicovered that Beidou was unckecked in RTKPLOT options.
          BTW, I have a suggestion. On the DOP/NSat view, use two different Y scales for DOPs and NSat respectively. DOPs are around 1 m while NSat may be around 30.


  10. Hello,

    Thank you for a very interesting blog!

    Could you list the minimum (ubx) messages required for bs and rover receivers for moving baseline rtk with F9P or are there listed in some other post?

    I have been using F8T for many years and it has worked like charm, but I seem to not be getting any fix with F9P. The comms work I guess, as rtknavi plots the satellites for both receivers, as well as single position fix is almost instant. There must be some message missing as when switching to moving baseline the fix never comes as even “float”.


      1. Thanks, I followed this point by point and still seem to have some issue left. Also I noticed that in the rtknavi-window, satellite bars alternate between gray and coloured all the time with F9, with F8 they stay constantly “lit” all the time.

        My settings are almost 100% same between F8 and F9, I even tried F9 with only L1 enabled, and it does not make much difference.


      2. I also raised the rate/interval from default 1000ms -> 200ms. But it does not have much effect.

        Antennas and test position is the same as with F8T, so that can’t be the issue. And only minutes apart.


        1. Hi Andy. If you are getting float solutions with the F9P then you are close since this means you are getting observations from both receivers. The satellite bars should stay colored though, gray usually means that you are missing navigation data. I would log the raw observations and then plot them with RTKPLOT to see if they look OK there. My best guess is that you may be missing some raw messages. Because the F9P is dual frequency it is transmitting more raw data than the M8T and your data link may be losing some of this data because the baud rate is too low or other limitation. RAWX and SFRBX messages are the only messages you need to enable. Moving base solutions are a little trickier than kinematic, I would verify everything is working first in kinematic mode then read my most recent post on movingbase solutions for some tips.


    1. Or is it simply enough to enable:


      In order to get RTK-fix? And what is the rate needed, 1 Hz for both or?

      I see some progress, now I can get F9P to get “float” fix somewhat to right direction, but I can only get “fix” with F8T so far… Must be something small as I feel I am close.


  11. Thank you for once again an interesting blog.

    I have been playing around with F9P. One thing I do not understand is that why the sign of the doppler observations in the RTCM 1077 , 1987… messages seems to be negative compared to the messages from other receivers?
    Should be positive for rising satellites and negative for descending ones? And F9P observations are the other way round.

    I wonder how this might affect to the solution accuracy etc when working in a system where one has receivers from different brands?


    1. Hi Esko. Yes, that’s true. The doppler is inverted in the F9P RTCM3 messages relative to other receivers but not in the F9P binary RAWX messages. I don’t know why this is. Fortunately it seems to have only a very small effect on the PPK/RTK RTKLIB solutions. In general, I prefer to use the u-blox binary messages rather than the RTCM3 messages especially for the rover. In addition to the doppler reversal, the RTCM3 messages are missing some carrier-phase filtering done by RTKLIB to reject low quality observations. RTKLIB relies on the quality metric info in the binary messages that is not available in the RTCM3 messages. Another issue with RTCM3 not unique to the F9P is that observation messages are grouped by constellation not epoch. RTKLIB handles the case where an entire epoch of observation data is missing better than it does when just one constellation is missing. I see a couple of percent lower fix rate when comparing F9P RTCM3 based solutions to F9P binary data solutions as well as larger position errors due to the unfiltered observations.


  12. Hello Sir,

    Thank you for your version of RTKPost. Is it possible to control, through your code, how much CPU resources RTKPost will use when processing? I just imagine many users may be like myself: just waiting for the progress bar to go from left to right while doing nothing else. I’d have no problem with seeing 100% CPU usage for a while if it meant significantly faster processing.

    Thanks again for your version of RTKPost.


    1. Hi Marion. Not directly. You can increase processing speed by making sure all unused constellations and frequencies are disabled in the configuration. The 64 bit version of rtkpost (rtkpost_win64) will run faster than rtkpost. Starting with the b33b2 version of the demo5 code, this is the default when run from rtklaunch. If you need to run solutions for multiple data sets, you can run multiple instances of rtkpost simultaneously which will use more CPU resources and will complete much more quickly than running the solutions one after the other. Long data sets logged with RTKLIB can be automatically broken up into smaller files using the “swap interval” option and then processed in parallel.


Leave a Reply

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

WordPress.com Logo

You are commenting using your WordPress.com 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: