Dual-frequency PPK solutions with RTKLIB and the u-blox F9P

With previous generations of u-blox receivers there has been a lower priced option available without an internal RTK engine, such as the popular M8T in the generation 8 modules. This does not appear to be the case with the new dual-frequency generation 9 modules, as the F9T, without internal RTK solution, is currently priced higher than the F9P with internal solution.

As the u-blox internal RTK solution in the F9P appears to be very robust, there is probably no good reason to ever use RTKLIB for real-time solutions with the F9P. However, it often still makes sense to use RTKLIB to post-process raw data previously collected by the F9P since the F9P is not capable of post-processing solutions.

Post-processed (PPK) solutions have several advantages over real-time solutions. The rover hardware is simpler, less expensive, lighter, and lower power since post-processing does not require a real-time data link between base and rover. Post-processed solutions also tend to be more robust than real-time solutions, both because they are not subject to data dropouts in the base data link and because they allow for solution techniques that take advantage of both past and future observations, not just past observations. When the solution is not required in real-time, it often makes more sense to collect the data first and then process it later.

Collecting data and processing RTK solutions for the dual-frquency F9P with RTKLIB is not very different for doing this for the single-frequency u-blox M8T, and if you are already familiar with doing that, you will probably not have much trouble adapting to the F9P. However, since it’s been a long time since I did a post on this subject, I thought it would be worth going over again with some updated tips for the new receiver.

Step 1: Configuring the receiver:

To process an RTKLIB solution, we will need raw observation messages from both rover and base receivers and navigation data messages from one of the receivers. The receivers do not output these messages by default so we will need to configure them to do this. With the u-blox M8T it was possible to do this directly with RTKLIB using a command file but this is not an option with the F9P as RTKLIB does not currently support the new F9P configuration messages.

Instead we will download the u-blox u-center app and use this to configure the receivers, then save the results to the on-board flash. There are detailed instructions on how to do this in the F9P documentation available on the u-blox website but here’s a quick summary of the process.

  1. Plug the receiver into a Windows PC using a USB cable if the board supports USB or with an FTDI serial/USB converter if the receiver only has a UART port.
  2. Start the “u-center” app and connect to the receiver with the “Connection” command on the “Receiver” tab. If it is a USB connection, baud rate won’t matter, but if it is a UART->USB connection through FTDI, then you will have to set the correct baud rate from the “Receiver” tab. If all is well, you should see the green connection indicator flashing at the bottom of the screen
  3. From the “View” tab, open the “Messages”, Configure”, “Gen 9 Configure”, and “Packet Console” windows
  4. If using the UART port, click on “PRT (Ports)” from the “Configure” window, set the Target to “1-UART1” and “Baudrate” to the desired baud rate, and click on “Send”. I typically set this to 115200 baud. You will then need to change the baud rate in u-center to the new baud rate. If you are using the USB port directly, you can skip this step.
  5. From the “Configure” window, click on “RATE”, and set “Measurement Period” to the desired time between observation samples, then click on “Send”. I typically set this to 200 ms which gives a 5 Hz sample rate.
  6. From the “Gen 9 Configure” window, select “GNSS Configuration”, enable the desired constellations and signals, select “RAM” and “Flash” under “Layer Selection”, then click on “Send Configuration”. The F9P supports GPS L1C/A and L2C, Glonass L1 and L2, Galileo E1 and E5b, BediDou B1 and B2, and QZSS L1C/A.
  7. From the “Messages” window, right click on “NMEA” and then click on “Disable Child Messages” to disable all the NMEA messages. None of these are needed for an RTK solution but if you want any of the messages for other reasons you can then individually enable the ones you need.
  8. From the “Messages” window, double click on “UBX” then “RXM”. Right click and enable “RAWX” to enable raw observation messages and “SFRBX” to enable navigation messages. Alternatively, you can enable the RTCM3 messages from the “Gen 9 Configure” window. In this case you will want to enable the 1077,1087,1097,and 1127 messages. I have occasionally had trouble enabling the RTCM3 messages on the F9P and have had to use the “Revert to default configuration” option under the “CFG” command first to get this working.
  9. If an antenna is connected to the receiver and is not completely blocked, verify that you see RAWX and SFRBX messages appear in the “Packet” window.
  10. From the “Configure” window, select “CFG”, then “Save current configuration” then “Send” to save these settings to the flash on-board the F9P module.
  11. Repeat this procedure for the base receiver except set the “Measurement Period” under “RATE” to “1000 ms” for a 1 Hz sample rate. You will only need one set of navigation data so you can choose not to enable the SFRBX messages on the base. I tend to leave them enabled just because it makes plotting slightly easier later if each set of observations has its own navigation data.

If you have any trouble with the above summary, you might find this YouTube video from Robo Roby useful. It is intended for setting up the F9P for real-time solutions, not post-processing, but there is a lot of overlap between the two.

In the descriptions below STRSVR, RTKCONV, RTKPLOT, and RTKPOST are all RTKLIB GUI apps. They can be opened individually or you can start by opening RTKLAUNCH and run the individual apps from there. I do not believe the official 2.4.2 or 2.4.3 versions of RTKLIB fully support the F9P receiver yet so I would recommend using the demo5 version of RTKLIB available here.

RTKLAUNCH used to open the different RTKLIB apps

Step 2: Collecting the data:

  1. For this exercise I will connect both base and rover directly to a Windows PC through the USB port. You can connect both receivers to one PC or each to a separate PC.
  2. Launch two instances of STRSVR, one for each receiver
  3. Set the input stream to “Serial”, click on the input “Opt” button and set the port and baud rate. Set the output stream to file and click on the output “Opt” to set the file name. Click on the “?” to get a list of keyword replacements for the file name. I like to add “_%h%M” to the end of the file name which will append the hour and minute of the data to the file name. If you are collecting long data sets you might want to set the “Swap Intv” to break up the data into manageable file sizes. Note that you will need to use the keywords in this case to avoid overwriting the same file repeatedly. Give the file name a “.ubx” extension to let RTKLIB know that it is u-blox binary data.
  4. Click “Start” to start collecting data.
STRSVR used to collect the raw data

Step 3: Convert the observation data to rinex format:

  1. Start the RTKCONV app
  2. Click on the “…” button to the right of the “RTCM, RCV RAW or RINEX OBS” field and select the observation file created in the previous step.
  3. If the file extension is not “.ubx” set the “Format” to “u-blox”, otherwise leave as “Auto”
  4. Click on the “Options” button and select “L1”, “L2/E5b”, and all GNSS constellations collected (usually “GPS”,”GLO”,”GAL”, and possibly “BDS” (Bediou) depending on your location. Then close the options menu.
  5. Click on “Convert” to convert from binary to rinex format.
RTKCONV used to convert the raw data from binary format to rinex text format

Step 4: Review the observation data:

  1. Before processing the solution, it is a good idea to look at the data first and make sure it is complete, of reasonable quality, and at the right sample rate.
  2. From the RTKCONV main window, click on “Plot” to plot the observations you just converted.
  3. Verify there are observations from all constellations. Green indicates dual frequency measurements, yellow is single frequency. The GPS observations will be a mix of single and dual frequency since only about half of the satellites currently support L2C used by the F9P, but the other constellations should be nearly all dual frequency.
  4. Red ticks indicate cycle slips. Too many of these will make it difficult to get a decent solution. Gaps in the data usually indicate the receiver lost lock and these are not good unless they are in the low elevation satellites.
  5. If all the satellites are in gray, this usually indicates you are missing the navigation data. The previous step should have generated a “.nav” file as well as a “.obs” file. If just a few satellites are in gray, this normally indicates that they are below the elevation threshold which can be adjusted in the options menu selected in the top right corner with the star-like icon.
  6. Check both rover and base observations.
  7. In some cases you may only have one set of navigation data and so not have a matching “.nav” file for one of your observation files. In that case you can manually specify the navigation data with the “Open Nav Data…” option in the “File” tab.
Plot of raw observations

Step 5: Generate the position solution

  1. Open RTKPOST
  2. From the “…” buttons on the right hand side of the GUI, select the rover observation file, the base observation file, and the navigation file as shown in the example below.
  3. Click on the “Options” button and then the “Load” button. Select the “demo5_m8t_5hz.conf” file from the same folder as the demo5 RTKLIB executables, and then click on “Open”
  4. From the “Setting1” tab in the “Options” menu, enable “Galileo” and if applicable “Bediou”. “GPS” and “GLO” should already be enabled.
  5. From the “Setting2” tab in the “Options” menu, set “Integer Ambiguity Res (GLO)” to “On”. We are able to use the “On” setting in this case because the receivers are identical and so the Glonass hardware biases cancel. If you are not using an F9P receiver for base, then leave this field set to “Fix-and-Hold” which will automatically calibrate out the biases.
  6. From the “Setting1” tab in the “Options” menu, change the “Frequencies” from “L1” to “L1+L2”. This is the only change you should need to make to switch from processing single-frequency data to dual-frequency data for the F9P. The Galileo second frequency for the F9P is actually E5b not L2 but to simplify and improve the processing speed, I have modified the demo5 code to include “E5b” processing as Galileo’s second frequency. This won’t be the case for the 2.4.2 or 2.4.3 code. I don’t believe it’s currently possible to include the E5b data with these versions of RTKLIB but if I’m wrong please let me know
  7. Click on “OK” to close the Options menu.
  8. Click on “Execute” to run the solution. The bar at the bottom of the GUI will show the solution status as it runs and will report any errors. You should see a mix of Q=1 and Q=2 as the solution runs. If you see only Q=0, something is wrong. In this case, open the “Options” window, select the “Output” tab and set “Output Debug Trace” to “Level3”, exit the Options menu, and rerun the solution. Then open the “.trace” file in the solution folder for additional information on what went wrong.
  9. Click on “Plot” to plot the solution with RTKPLOT
RTKPOST used to generate a PPK solution
Plot RTKPOST generated PPK solution

This was just meant to be a quick summary of the process. For more details please see the references below.


  1. u-center User Guide
  2. u-blox F9P Interface Description
  3. RTKLIB manual
  4. Updated guide to the RTKLIB configuration file

37 thoughts on “Dual-frequency PPK solutions with RTKLIB and the u-blox F9P”

  1. I am trying to follow this post with the new version of u-center 19.11.01 and your new version of RTKLIB demo5 b33b2 with no luck. I am not getting any obs or nav files output when I try to convert the UBX log file. One difference I notice is my log from previous (I believe successful attempts) is coded as such I cannot view in Notepad++ but the new UBX log file is easily readable with $GNRMC $GNVTG $GNGGA messages. Thanks


    1. Hi Jay. The NMEA text messages and UBX binary messages both go into the same ubx log file, so just because there are NMEA messages in the log file, doesn’t mean there are not any UBX binary messages. IF you see just text messages in the log file, however, and no binary gibberish, then the RXM-RAWX and RXM-SFRBX binary messages were not enabled when you collected the data. You can enable these from u-center.


      1. Ok. I will try again but I swear I have been enabling them. I always hit the default settings button before I try to reset everything too. Thanks!


  2. Hi, I am astonished with my firt postprocessing with this demo5_b33b version of rtklib. To my test I Did everything you said and results are perfect. on 30min not moving (both Zed f9p) Base and Rover, 1.3m apart, I get more than 90% Q=1, with only 3mm difference on Heigth, and 0mm horizontal. Simply amazing
    Then I tried to restrain Time Start and Time End on RTKPOST, and no more Q=1, all were Q=2. Solution was to close and reopen RTKPOST with no changes on options it works fine.
    The only thing not normal about my data is that there is a lapse of 15min of no logging :computer suspended 🙂 in the middle of the file.
    Thank you very much.

    one final note for the inexperienced begginers like me: when I first was changing F9P Base configuration I enabled all RTCM messages to the USB port and the rover stopped making corrections. So I got back and enabled to the USB only those who are enabled to the radio (UART2).
    With current Base and Rover configuration I can do both RTK and Postprocessing without changing any settings. Just have to log the base.


    1. Hi Paulo. I just released the b33b version a couple of days ago so it is not well tested and it’s possible there is a bug in it. If you are able to email me your base and rover raw files and config file I will take a look and see if I can replicate the issue.

      Regarding different RTCM messages from the u-blox receiver to the USB and UART ports, this can be a problem. The RTCM format includes a flag for the last message of each epoch. The F9P does not set these flags independently for the two ports so both ports must both have the same last message in each epoch.


  3. Hello Tim. Thank you for your excellent work.
    I have a set of Zed F9Ps and Im wondering whether theres a method of obtaining the raw pseudorange-rates, pseudoranges and carrier-phase measurements in RTKLib. I realise that the rinex obtained from the ubx logs has pseuodorange but im unsure of pseudorange rates and carrier phase, and whether this is readily accessible. Would be grateful if you could point me in the right direction.


    1. Hi Suraj. The F9P can be configured to output raw pseudorange, carrier-phase, and doppler observations by either enabling the appropriate RTCM3 messages or the UBX-RXM-RAWX messages. The easiest way to enable these messages is using the configuration menus in the u-center app from u-blox. The raw binary or RTCM3 files can then be converted to rinex with the RTKCONV app in RTKLIB. Pseudorange-rate can be derived from the doppler measurement.


      1. Hello Tim. Thanks for your reply. My apologies, I should have been more specific.
        I’ve already enabled raw messages in UBX-RXM-RAWX plus im converting to RINEX using RTKCONV as mentioned in your blog posts.
        Im simply wondering if theres an option to visualize pseudorange, carrier-phase and doppler in RTKplot, or to export these variables to a text file (similar to how theres an option for exporting carrier to noise and multipath).
        Is this possible in RTKLIB?


  4. Dear Tim! In the most recent b33a release it seems like you have changed some of the compiler settings, since I got the following error code when calling rnx2rtkp:
    “The code execution cannot proceed because VCRUNTIME140.dll was not found.” I suspect this is related to Visual Studio, on the same computer b33 works just fine. Also I do not find the usual 64 bit rnx2rtkp_win64 executable, so that also supports my suspect that you have changed compiler settings. It would be great to have some sort of fix for this. Thanks, Balint


    1. Hi Balint. I did make some updates to the Embarcadero compiler project files for the newer version of the Embarcadero compiler for the GUI apps but these should not affect builds with the VS compiler. You can download VCRUNTIM140.dll as part of the MS VS++ Redistributable packages here. Another suggestion that helps in general with VS compiler errors that may or may not help in your particular case is to go to the properties menu in the VS compiler and select the most recent options listed for “Windows SDK Version” and “Platform Toolset” from the “General” tab.

      I don’t think I’ve included the 64 bit version of rnx2rtkp in any of the demo5 releases except for b31 but I will consider adding it to future releases.

      Liked by 1 person

      1. Dear Tim! Thanks for the reply! Actually I copied the VCRUNTIM140.dll file to the folder and it gave me a different error that time “The application was unable to start correctly (0xc000007b)”. On the other hand I built the rnx2rtkp_win64 and rnx2rtkp files with embarcadero and seen a 38% speed increase in processing (64 bit vs 32 bit).


  5. After running RTKconv on F9P data logging RAWX and SFRBX messages, I end up with nav files for every GNSS system: gnav(GLO), lnav(GAL), and nav(GPS). Is there any reason to splice these together in a combined file for RTKpost? Or put them all into RTKpost?
    I am just curious if the additional nav files can be used with RTKpost, and if they improve anything if used.


    1. Hi Adam. The options menu in RTKCONV allows you to select different versions of Rinex format. If you select a 2.xx version, the nav files will be separate. If you select a 3.xx version, the nav data will be combined into a single file. I would suggest using version 3.03 and using the single navigation file for RTKPOST. If you stick with the 2.xx version, you will need to provide all the nav files to RTKPOS for the constellations you are using in the solution.


      1. Thanks for the response!
        As a follow up question, is there a simple way to know which satellite was used as the reference satellite for PPK? I see the documentation states it will use the highest elevation satellite, but there I do not see any indication of what it choose, and I do not see skyplots for the PPK solutions.


        1. Hi Adam. As you mentioned, RTKLIB will choose the highest elevation satellite from each constellation as reference for the double differencing. You can deduce this from elevations in the skyplot or if you want to see this specified explicitly, you can enable the debug trace to level 3 and look in the trace file. Each double difference will be listed.


    1. Hi Aleksandr. RTKLIB does not support a single solution from multiple bases. Depending on exactly what you are trying to do, you may be able to add a layer on top of RTKLIB to either call the command line executables and combine the solutions, or use the API to internal function calls as described in the online RTKLIB manual.


    1. Hi JJ. In RTKPOST, the output format (LLH or ENU) is specified in the Options/Output menu under “Solution Format”. Note that in RTKNAVI this option is grayed out and you need to specify the solution format from the Output menu.


  6. Hi Tim,

    Great post! It was exactly this kind of information that I had wished was more readily available when first starting to work with RTKLIB.

    For PPK, you’re recording both the UBX-RXM-RAWX and UBX-RXM-SFRBX messages. Can you confirm if these are the only two messages required for PPP as well? Do any of the other messages provide any use when creating a RINEX file from the raw collected data?



    1. Hi Adam. For online PPP solutions you need only the observation messages (RXM-RAWX). For RTKLIB PPP solutions you will need more precise ephemeris/clock information which I usually get from the SSR correction messages as I describe in this post. For RTK/PPK solutions using surveyed base stations, you might want to collect base position/antenna info messages as well. RTKLIB supports the 1005, 1006, 1007, 1008, and 1033 messages.


      1. Thanks, Tim!

        I collected some data with the ZED-F9P and submitted a RINEX file to NRCan’s CSRS-PPP tool the other day.
        I receive the message: “Warning : Some or all of the GPS signal(s) in your RINEX file are not currently supported by CSRS-PPP. A dual-frequency GLONASS only solution has been processed. The currently supported signals for GPS are: C1C L1C C2C L2C C1W L1W C2W L2W and for GLONASS: C1C L1C C2C L2C C1P L1P C2P L2P. Other modulations are planned for support once specific code biases become available.”

        Does this mean that PPP tool does not support the L1C/A signal as of yet? I’m curious if you received a similar message.



        1. Hi Adam. I believe it is the GPS L2C codes (C2L/L2L) that CSRS is complaining about. If you set the options in RTKCONV to use Rinex version 2.11 instead of 3.03 when generating the Rinex observation files, then the codes in the file header are listed as C2/L2 instead of C2L/L2L. In this case, CSRS will include the GPS observations in the PPP solution. The errors in the solution caused by the missing code biases should be very small.


  7. hi, i have a m8t and im planning to buy a second antenna to make a rover/base rtk rig, what would you reccomend, the base is going to be stationary and the rover goes in a moving tractor, should i buy a second m8t and use rtklib or a z9p? in this case should i use the z9p as a rover or base?


    1. Hi Diego. A dual-frequency solution will require dual frequency base and rover receivers so if you want to use an existing M8T your only real option is another M8T. A F9P would work but the solution would only be single frequency so there would be no advantage in using the more expensive receiver.


  8. Do to know if there’s any way to run your demo5 code on an Android tablet?

    I’m also wondering if there’s a way to log position events with the f9p. The aim is to have a single f9p on a pole as a rover connected to an Android tablet via Bluetooth. I want to be able to log the raw observations and also save position events and then post process it against a local CORS station. Is this viable?


  9. wow how incredibly helpful! Thank you so much for creating this. Your postings related to rtklib have been invaluable in helping me understand how to get the most out of RTKLIB and my ublox 8t.

    I do not own a f9 but have been looking into it. What do you suggest as the best antenna for the f9? one that might produce the best results in a canopy and canyon constrained environment?


        1. Hi JJ. For PPP solutions on the OPUS website, you should specify “NONE” for the antenna option when using the ANN-MB-00. I am not aware of any calibration data available for the ANN-MB-00. The adjustments are usually very small in the horizontal axes (a few mms) and a little larger in the vertical axis. If you need more precise solutions than this, you will need to use a survey grade antenna from the dropdown list on the OPUS page.


          1. Thanks, Do you have a survey grade antenna you can recommend? I see some of them are tough to find and some are not L1 L2/E5b


          2. Hi JJ. A more complete set of antennas with calibration data than what is in the igs14.atx file is available at https://geodesy.noaa.gov/ANTCAL/#. I would suggest one of the Harxon antennas, either GPS500, GPS600, or GPS1000. SwiftNav has sold GPS500 and GP1000 antennas with their receivers. The GPS1000 antenna is available new from the SwiftNav wibsite for $425 but you may be able to find better prices elsewhere or you can look for used antennas.


          3. I am also getting an error when trying to rtkpost my obs of no position in rinex header. I’m assuming it is in my obs file not the unavco station file. Can i manually enter the position in the header?


          4. Hi JJ. The “No position in rinex header” error can be deceptive as it often means that RTKLIB just can’t find the observation file at all. See this post for more details on getting better debug info from RTKLIB. You could manually enter the base location in the rinex file header but normally it should not be missing in files from any kind of CORS reference station. For non-CORS base rinex files, it would usually be simpler to add the location to the config file under the antenna settings than modifying the rinex files.


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 )

Google photo

You are commenting using your Google 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.