Improving RTKLIB solution: (AR lock count and elevation mask)

In the previous post, after a couple changes to the input parameters, we ended up with the following solution for the difference between two stationary, then moving Ublox receivers:

During the time when the receivers were stationary (17:25 to 17:45), we were able to hold a fixed solution (green in plot) continuously after the initial convergence, but started to revert to float solution (yellow in plot) fairly frequently once the receivers started moving.

Let’s look at the data a little closer to see if we can discern why we are reverting back to the float solution. If we switch to the “Nsat” view in RTKPLOT plotted below, we can see a couple of useful plots.


The top plot shows the number of satellites used for the solution and the bottom plot show the AR ratio factor. The AR ratio factor is the residuals ratio between the best solution and the second best solution while attempting to resolve the integer cycle ambiguities in the carrier-phase data. In general, the larger this number, the higher confidence we have in the fixed solution. If the AR ratio exceeds a set threshold, then the fixed solution is used, otherwise the float solution is used. I have left this threshold at its default value of 3.0.

Notice in the plot that each time we revert to the fixed solution, it happens when the number of valid satellites increases. At first this seems counter-intuitive, you would think that more information would improve the solution, not degrade it. What is happening though, is that the solution is not using the satellite data directly, but rather, the kalman filter estimate of the data. This estimate improves as the number of samples increases. This is also why the solution takes some time to converge at the beginning of the data. By using the data from the new satellite before the kalman filter has had time to converge, we are adding large errors to the solution, forcing us back to the float solution.

Fortunately, RTKLIB has a parameter in the input configuration file to address this issue. “Pos2-arlockcnt” sets the minimum lock count which has to be exceeded before using a satellite for integer ambiguity resolution. This value defaults to zero, but let’s change it to 20 and see what happens. While we are it, we will also change the related parameter “pos2-arelmask” which excludes satellites with elevations lower than this number from being used. We will change this from it’s default of 0 to 15 degrees.

Unfortunately, re-running the solution with these changes makes almost no difference as seen below. The jumps back to float solutions still occur immediately after a new satellite is included, and the additional satellites are being included at the same epochs. Something is clearly wrong.


Digging into the code a bit reveals the problem. First, the arlockcnt input parameter is copied to a variable called “rtk->opt.minlock” Then the relevant lines of code from rtkpos.c are:


if (rtk->ssat[i-k].lock[f]>0&&!(rtk->ssat[i-k].slip[f]&2)&&
     rtk->ssat[i-k].azel[1]>=rtk->opt.elmaskar) {
     rtk->ssat[i-k].fix[f]=2; /* fix */
else rtk->ssat[i-k].fix[f]=1;

So far, everything looks OK, but when we look at the definition for the structure member “lock” in rtklib.h we find:

unsigned int lock [NFREQ]; /* lock counter of phase */

This won’t work. We are assigning a negative number (-rtk->opt.minlock) to an unsigned variable, then checking if the unsigned variable is greater than zero, which by definition, will always be true. Hence, satellites are always used immediately, regardless of lock count.

We fix this bug by changing the definition of lock from unsigned to signed:

int lock [NFREQ]; /* lock counter of phase */

Rerunning after rebuilding the code, gives us the following solution:

Much better! All but a couple of the jumps back to float solutions have been eliminated.

Looking at the plot of the AR ratio for this solution, we can see that the kalman filter requires more than 20 seconds (the value we chose for arlockcnt) to re-converge. Two minutes looks like a more reasonable value from the plot, so let’s try rerunning the solution with arlockcnt=120.

Even better.  That eliminates the last couple of jumps back to the float solution and also significantly reduces the number of jumps in the AR ratio plot as shown below. The green line in the bottom plot is arlockcnt=20 and the blue line is arlockcnt=120.


It’s possible we will need to revisit this number after looking at more data, but for now we will use 120 secs.

As always, please leave a comment if you have any comments, discussion points, or questions.

Update 4/11/16:  

  • In the above discussion, my data is one epoch/sec and I equate epochs with seconds.  Arlockcnt is specified in epochs, not seconds, so if your data is at a different sample rate you will need to adjust accordingly.
  • The suggestion above to set arlock to 120 secs is probably too conservative for more challenging environments.  If there are few satellites and/or many cycle slips for example it is probably better to start using a just-locked satellite before it is fully converged.  I have started using 30 secs rather than 120 secs for my solutions.
  • You can pull this code fix from my github repository either by itself from the arlockcnt branch or as part of a larger set in the demo1 branch

33 thoughts on “Improving RTKLIB solution: (AR lock count and elevation mask)”

  1. Although this post is nearly 4 years old, I think it is still relevant 😉 So, I ask my question here.

    Mostly, I want to record precisely cave entrance coordinates using F9P and post-processing. Indeed, some caves/springs are a the bottom of cliffs so I will end up with a big obstruction of the sky in one direction. I found that one can define a “elevation mask pattern” and use it in RTKPLOT. Indeed, I would like use it at others steps i.e. with RTKCONV and/or RTKPOS. Do you know if it is possible? If yes, how?


    1. Hi Eric. There is no azimuth dependent elevation mask option in RTKCONV or RTKPOST. To some extent, the cliffs will act as a mask by obstructing the signals from that part of the sky. If your concern is low quality satellite signals from satellites that are partially obstructed by the cliffs then including an SNR mask to exclude satellites with SNR below some threshold may be helpful.


      1. Thanks for your answer. Indeed, I’ve been experimenting from my balcony in a building that could simulate a cliff, with logging for several hours. When looking at the skyplot, satellite tracks obstructed by my building (and also the one in front of me) are straight forward because they are full of cycle slips. In opposite, cycle slips are nearly absent for satellites in open sky. Looking at SNR level for individual sat, it seems that a 40 dB threshold should remove sats without direct view. But, overall, fix is difficult. One will have to withdraw from cliff as much as possible and to end up with another method like a surveyor’s chain (if possible but cave entrance may be trapped between two cliffs 😉 ).


        1. Hi Eric. Locations adjacent to buildings can be difficult due to reflections of the satellite signals off of the building which cause multipath. If the cliff is less reflective than the building you may find somewhat better results there.


    1. Hi Cocute. I have just updated my demo5 branch to rtklib 2.4.3 b18 and verified that RTKNAVI does build. I also added “Static-start” and GLONASS “fix-and-hold” to the RTKNAVI GUI input options but be aware that I have not added any of my new input parameters (eg minfixsats, minholdsats, arfilter,etc) to the GUI. That is a bigger job because it means re-doing the GUI layouts but I hope to get to that fairly soon. They will run using the default parameters which can be changed in rtkcmn.c if you want to adjust them.

      I have done all my work with post-processing using the rnx2rtkp CUI so I have never tested RTKNAVI with my changes. You will probably be the first person to try it. Let me know how it goes.

      I have also uploaded executables for the demo5 code to here


      1. Hi, there thank you for all of the research you are doing on the Ublox, I have a couple of M8Ts that I updated to the M8N firmware 3.01 that allows galileo. I probably should have kept them with the original firmware that kept the timing function but oh well.

        Anyhow, just wanted to let you know that tried your compiled RTKnavi binary and it seems to be working. It took a minute to get it started since I tried to use the rtknavi.ini file I had been using with the beta release, for some reason, when I used this .ini file I could not get any solution. The satellite bars showed good strength but no solution.

        I deleted the file and went through and set up rtknavi again and this time, it worked. Right now I have my antenna set in the window in my office and am streaming RTCM3 corrections from a station that is about 50 miles away so not the most ideal. But I have periods when good satellites are in view that a fix is held really well under static and giving me seemingly accurate results. I will try more this weekend with some of the other settings, i.e. kinematic and static start, plus base things on setting up over a known point with a much shorter baseline.

        Thank you for all the research and sharing it with everybody.


        1. Hi Reldridge. Glad to hear the updated RTKNAVI is working for you. I’ve played with a little now and have also had good results. I’ve also now added all of my additional input parameters (minfixsats, minholdsats, arthres1, and ARFilter) to both the RTKNAVI and RTKPOST GUIs. I’ve uploaded the code to my demo5 Github repository and uploaded the executables here. The rtknavi.ini file contains the input configuration I am using for M8N receivers with GPS and GLONASS enabled at 5 Hz.


  2. Hi rtklibexplorer.
    First of all, i want to thank you for your amazing work in this blog. Nowadays, i´m working with rtklib demo3 and a modification of your conf file obtaning quite good results. The problem comes when a day i realized that the ratio factor for AR validation was decreasing along the time, reaching the point that around 35 minutes after i start the measures its value is under the treshold avoiding fix solutions ¿Do you know what can be the reason?


    1. Hi Al. It’s possible you have a false fix. In my experience that is the most likely cause of a continuously decreasing AR ratio. Sometimes you can also see movement in the z-axis that does not make sense, usually a gradual drift when you expect constant elevation.

      False fixes are more likely with moving rovers. If your rover is moving I would suggest keeping it stationary long enough at the beginning of the data set to get an initial fix. I would also suggest switching from the demo3 code to the demo4 code, and possibly using the “Static-start” feature I described a couple posts back.


  3. @Giorgos
    I never used Novatel but I think you have to pay attention also to the startup commands and the way the receiver is connected to the Android device. If you connect it using usb port, please keep in mind that some FTDI chipsets are buggy and at high rates you’ll lose data. If you use BT to connect you may have the unpleasant surprise to notice that the chip in your device is not as fast as the RTKGPS+ needs.


  4. @Giorgos
    I’m a surveyor too, so I understand your needs. I use NVS based receivers and Tallysman antennas.
    Here are some of my settings:
    Nav sys GPS+GLO
    Elev mask 10
    SNR mask 35
    Rec dynamics OFF
    IAR continuos
    Glonass IAR On
    Min ratio to fix amb 1,5 (I know, it sounds crazy but I get great results)
    Min lock to fix amb 60
    Min elev to fix amb 0

    Liked by 1 person

    1. @Dinu
      Min ratio to fix amb = 1,5 ?? I wouldn’t dare to set it that low!
      Also the Min lock to fix amb = 60 i think is too high for a stop ‘n go rover.
      Anyway my email is if you want to exchange some info about our setups.


  5. Ok thanks, I am looking forward to test this version also.
    Is it possible to add a save-load settings option in RTKGPS+ (if it is easy to do of course)?
    That would be handy!

    Liked by 1 person

    1. @Giorgos
      The RTKGPS+ settings are stored in /data/user/0/gpsplus.rtkgps/shared_pref folder in xml format. On rooted devices is very easy to replace/edit those files. Good luck!


      1. Thanks Dinu, will have this in mind.
        As for the fixed version 2.4.2_p11 the “min lock to fix ambiguity” works now but I don’t see any better results in maintaining the fix. Of course I have to do some more testing.
        My application is surveying so I move the rover from point to point and the delay in fixing ambiguity is no good for me.
        So for now I have the best results in fix-n-hold mode by setting the “SNR (db) mask” to 35 and “min elevation to fix ambiguity” to 15. I will try also the Receiver Dynamics and RAIM FDE options to see if makes any difference.
        Dinu, the x86 version in which devices it works? Is it for tablets with Intel processor only?Unfortunately I don’t own android device with Intel cpu so I can’t test it.


  6. @Dinu,
    thanks for the links!
    Have you included the fix in code this article tells us about?
    I downloaded a version of rtkgps+ 2.4.2 with patch 11 a year ago. What is the difference in this version?
    Thanks again!

    Liked by 1 person

    1. @Giorgos
      Yes, this new build includes the fix. I’ll upload a new RTKGPS+ version based on demo3 code from here:


    1. Hi Dinu,
      I am very much interested in your source code changes to RTKGPS+. I would like to either fork or use the changes you applied to RTKGPS+. Can you post them, or let me know what repository?


  7. After some testing I had better results by raising the SNR (db) mask to 35-40.
    The solution is more stable and for me the fix-n-hold works better. I don’t know if this has to do with the receivers I use (survey grade) but it works more stable.
    Also tried the VRS and MAX option on my CORS and it fixed in seconds using the network solution in a distance >10km from the nearest base station. In one occasion the MAX option provided fixed solution and the VRS solution remained float. All the tests were compared to the solution from the L1/L2 receiver I have in the same CORS and the results was within 1-2cm.


  8. Hi,
    I read your article and understand why my receivers don’t maintain fix for a long time.
    I have a pair of Novatel OEMV L1 GNSS receivers with survey grade antennas.
    However I use the rtklib 2.4.2 patch 11 ported to android and can’t make changes to the code.
    The initial fix from a near base station (about 500m-1km away) is very very fast (a few seconds) but when new sats come into play it looses the fix. I found that the fix-n-hod solution maintains the fix better but it also is a bit unstable.
    In the android version I can change the parameters “min lock to fix ambiguity” and “min elev to fix ambiguity” but as i understand from the article it would not make difference if the code is left as is. right?
    Anyway I will try it and report back.

    P.S. You are not interested in the android version of the RTKlib?


    1. Hi Giorgos. Yes, that’s correct. The min lock to fix ambiguity parameter is broken and the min elev to fix ambiguity parameter works but probably won’t fix your problem.

      I don’t have any tools to build the Android version but if somebody can send a compiled version with the fix I can post it with my other code.


  9. I could improve my postprocessing solutions with your changes on Windows and Linux (raspberry/ARM). Also I have compiled the GUI versions with your changes and had very good results on realtime processing.
    Thank you


  10. Hi Tim, thx.
    I got it working and have some observations after first tests. I use config you provided in data folder.
    1. I got fake fix on fix-and-hold armode and switched to continuous immediately. I performed tests on continuous mode without any fake so far.
    2. First test gave me stable fix after 15 minutes. Second test started 12hrs later was much worse. I got fix after 54 minutes.
    The amount of valid satellites were same in both cases. Could this be improved somehow?
    3. You use comments in C++ style “//” which make compilation error under GCC. I used -std=c99 to got it compiled.
    4. I confirm, that fix once reached is very stable. No matter how many valid satellites is currently shown, I do not lose fix. AR validation is also high, like never before. This is huge improvement. Great work Tim!!!
    5. I had to set dynamics to off as I got very high CPU usage when was set to ON.
    6. Do you think using Linear Algebra PACKage (DLAPACK) might help somehow to obtain first fix faster? Mr. Takasu pointed out this library in System Requirements on first page of rtklib.
    7. I found you use navsys=5, shouldn’t be gloarmode set to ON in this case?


    1. Hi Andrew. Thanks for trying this out on your data! In response to your points:

      1,2: If you could possibly share your data with me, I’d love to take a closer look and understand what you are seeing. I’m especially concerned with the false fixes you see with fix-and-hold turned on. If the solution can’t reject false fixes reliably then it’s hard to trust any of the data. I’m currently playing with a new feature I’m calling “fix-and-bump” which is a less aggressive version of “fix-and-hold” that I am hoping will give most of the benefit of fix-and-hold while minimizing the risks of false fixes.
      3: Thanks for figuring that out. I didn’t realize traditional C doesn’t allow “//”. I’ll fix this next time I’m checking in code.
      4: Great! It’s good to know it helps on another data set.
      5: I’m curious … how many epochs per second are you processing? Is it possible to go lower to at least evaluate the potential improvement from turning dynamics on, turning it off just feels wrong because you are possibly throwing away a lot of information.
      6: If you have LAPACK available on your system, using it may make RTKLIB process each epoch faster because it is using more efficient matrix algebra functions, but I don’t believe it should affect time to first fix unless the faster computations allow you to enable other features (like dynamics).
      7: Setting navsys=5 but leaving gloarmode off enables use of the GLONASS satellites for the float solutions but disables their use for the fixed solutions. The additional GLONASS satellites should improve the float solutions but will break the fixed solutions unless the inter-channel biases that come from use of FDMA in the GLONASS system are accounted for. RTKLIB has a calibration mode for the IC biases (gloarmode=2) but I have not had any luck using it.


  11. Hi. I also confirm this issue exists on original rtklib. I have seen it many times when more sats became visible.
    I have permanent station with two very good antennas (GPS+ Glonass ) connected to tiny Embedded Linux SoCs where I do tests all the time. Unfortunately, I’m unable to test your changes as there is a known bug to build it under gcc for 2.4.3 version.
    Here is the fix that worked for me
    Would u maybe try to patch your repo as well?
    I can serve logged data if needed in advance.


    1. An update to my previous post.
      Looks like the problem is not related to gcc. Patch from above link works fine on 2.4.3 Takasu’s version and I got it working whilst I have compilation errors on rtklibexplorer one. I will provide some compiler logs on the github.


  12. I saw this happened multiple times, when I got fixed solution with 5 sat but jump to float with 7 sat.
    My workaround is to increase the elevation mask (to ~30), sometime it works, sometime not. Your improvement is much better.
    By the way, when using the survey GPS. I often leave the elevation mask at 20.


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 )

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.