As you might guess from the name, a moving-base solution differs from the more common fixed-based solutions in that the base station is allowed to move in addition to the rover. Although it could be used to track the distance between two moving rovers it is more commonly used in a configuration with two receivers attached to a single rover and used to determine heading. Since the receivers remain at a fixed distance from each other, the solution in this case becomes a circle with a radius equal to the distance between the receivers. The location on this circle corresponds to the rover’s heading which is easily calculated using a four quadrant arctan of the x and y components of the position. I also used moving base solutions in several of my earliest posts because the circular nature of the solution makes it easier to verify the solution and to measure errors. Since all solution points should be on the circle, any deviation from the circle can be assumed to be error.

To be more exact, everywhere I mention “circle” above I really should say “sphere” instead since the solution has three dimensions, but if the rover is ground-based, the movements in the z-axis will be relatively small and for simplicity we can assume it is a circle.

In fixed-base solutions, the measurement rate of the base station is often lower than the rover both because it’s location is not changing and also because the base data often has to be transmitted over a data link which may be bandwidth limited. In a moving-base solution, since both receivers are moving, and there is usually no need for a data link since they are both attached to the same rover, it makes sense to use the same data rate for both receivers.

For this exercise, I chose to use a data set I discussed previously in my “M8N vs M8T” series of posts. It consists of two receivers, an M8N and an M8T, on top of a moving car and another M8T receiver used as a fixed base station. The car drives on roads with a fairly open sky view for up to a couple kilometers away from the base station. The base station is located next to some sheds and a tree, so is not ideal, but still has fairly open skies. All three receivers ran at 5 Hz sampling rate and both moving receivers have some missing samples. I’m not sure exactly why this is, it may be because I used a single laptop to collect both data streams. Regardless of where they come from, I have found occasional missing samples are fairly common whenever I collect data at higher sample rates and believe the solution should be robust enough to handle them. The rover M8T data also has a simultaneous cycle-slip type receiver glitch near the beginning of the data as described in my last post. Overall, I would consider this a moderately challenging data set but those are often the best kind for testing the limits of RTKLIB.

Having data from three receivers gives us the luxury of being able to calculate three different solutions (base->rover1, base->rover2, and rover1->rover2) and then compare results between them. Since the first two solutions are fixed-base and the third is a moving-base, it also allows us to validate the moving-base solution using a combination of the two fixed-base solutions.

To start with, let’s calculate solutions for the distance between each moving receiver relative to the fixed base station using the demo5 code and my standard config files for the M8N and M8T receivers. The only difference between the two config files is that the GLONASS ambiguity resolution (gloarmode) is set to “fix-and-hold” for the M8N config file and to “on” for the M8T config file for reasons explained in previous posts. I’ve also done the conversion from raw data to RINEX observation files with the TRK_MEAS and STD_SLIP receiver options set to 2 and 4 respectively, again for reasons previously explained. I set the solution mode to “static-start” since I knew the data set started with the rover stationary for long enough to get a first fix but I also could have used “kinematic” mode.

Subtracting the two fixed-base solutions gives us the distance between the two rover receivers which should be equal to a moving-base solution calculated directly between the two rovers. The only difference is that the errors will be larger in the difference of two solutions than they will be in the direct solution because the errors in the combined solutions will accumulate.

Here are the positions and ground track for the difference between the two solutions, using the “1-2” plotting option in RTKPLOT. As expected we get a circle for the ground track. From the radius of the circle we can tell that the two rovers were about 15 cm apart. Usually you would put the two receivers as far apart as possible, since the errors in the heading will decrease as the distance between the rovers increases but in this case I hadn’t intended to use the results this way so had placed the rovers closer together. Still, it might be representative of a configuration on a small drone or other small rover.

Next let’s try to calculate the solution directly between the two moving receivers. RTKLIB does have a special “moving-base” mode but we won’t use this yet. The “kinematic” solution calculates the distance between the two rovers regardless of the location of the base, so for now we can ignore the fact that the base is moving. This will breakdown eventually if the rover gets too far from the base but since in this data set the rover is only a couple kilometers from the base at its farthest point we should be OK.

The only change I made to the config file from the previous M8N run for this run was to reduce the acceleration input parameters “stats-prnaccelh” and “stats-prnaccelv” which are used to describe the acceleration characteristics of the rover in the horizontal and vertical directions relative to the base. In the fixed-base solution, these need to include both the linear accelerations and rotational accelerations since the rover is moving and the base is fixed, but in the moving-base solution, since we care only about differential acceleration between the receivers, we can ignore the linear accelerations and include only the rotational accelerations. I just used a rough guess and reduced the numbers from (1,0.25) to (0.25,0.1) but I could have found more exact numbers by looking at the acceleration plot of an initial run of the solution.

Here’s the solution using this configuration. It looks reasonable except for the occasional large spikes.

After a little debugging, I found that the spikes were occurring wherever there was a missing sample in the base data. When this occurs, RTKLIB just uses the previous base sample. This works fine when the base is not moving, but in this case that’s no longer true, and the previous base measurements are not good estimates of the current position. We can tell RTKLIB to skip these measurements by setting the maximum age of differential to something less than one sample time. This is done with the “pos2-maxage” input parameter. I set it to 0.1 which is half of one sample time.

With this change, I got the following solution for the position. Much better!

The ground track for this solution is shown below on the right, on the left is the previous ground track derived by subtracting the two fixed-base solutions. As expected, the solutions look very similar except the moving-base solution has smaller errors which appear as deviations from a perfect circle.

To further validate this solution we can compare the heading calculated from the moving-base position with the heading determined from the velocity vector of the fixed-base solution. This wouldn’t work if the rover were a boat, drone, or person, but in the case of a car there are no external lateral drifts and the car will move in the direction it is pointed (unless it’s in reverse of course). This won’t work if the velocity is zero or near zero but for reasonably high velocities we should get a good match. The top plot below shows the difference between the two. The blue line is for all velocities and the red is for when the velocity drops below 5 m/s. The bottom plot shows the distance from the base to the rover.

As expected, the errors are large when the velocities are low but we get a good match otherwise. There also appears to be no correlation between the errors and the base to rover distance which suggests we are well below the maximum base to rover distance before we start to see issues with our assumption that the base did not move.

Overall, this solution looks excellent, with 100% fix and based on deviations from the circle, very small errors. In fact, I recommend this configuration over the RTKLIB “moving-base” solution if you are able to live within the maximum baseline constraints. I don’t know how large that is, but it looks like it may be significantly larger than 2 kilometers which is probably large enough for most applications.

In the next post I will explore what happens when the RTKLIB solution mode is set to “movingbase” in more detail but for now let me bring up just one of its effects since it is something we can also do here without invoking “movingbase” mode and it may have some benefit.

RTKLIB uses an extended kalman filter which is designed to handle non-linearities in the system by linearizing around the current operating point. This generally works quite well but as the system becomes more non-linear, the errors introduced by this approximation grow larger. One way to deal with this is to run multiple iterations of the kalman filter every measurement sample to converge on the correct answer. As we get closer to the correct answer, we will operate closer to the point around which the system has been linearized and the errors will be smaller. There is an input parameter in RTKLIB called “pos2-niter” that specifies the number of filter iterations for each sample. The default value is one but when “posmode” is set to “movingbase” two iterations are automatically added to whatever this value is set to. In the default case, we would get three iterations every sample instead of one. Since the kalman filter assumes all velocities are linear and in the moving-base case we have been looking at, they are all rotational and non-linear, it might make sense to do this. In my example, the sample rate is quite high relative to the rate of rotation and I found it did not help, but in other cases where the rate of rotation is higher relative to the sample rate, it might be a good idea.

So, let me finish by summarizing the changes I recommend for moving-base solutions.

- Set measurement sample rate for both rover and base to the same value
- Leave “pos1-posmode” set to “kinematic” or “static-start”
- Set “pos2-maxage” to half the sample time (e.g 0.1 for 5 samples/sec)
- Reduce “stats-prnaccelh” and “stats-prnaccelv” to reflect rotational acceleration only
- Experiment with increasing “pos2-niter” from 1 to 3

These recommendations are based on my fairly limited experience with moving-base solutions so if anybody else has other recommendations, please respond in the Comments section.

I have added the data set I used here to the data sets available on the download page for anyone who would like to experiment further with this data.

Here’s another data set taken from two receivers, one at either end of a kayak. In this case the distance between the two receivers is greater and we see a very clean solution. The two visible deviations from the circle in the plot are caused by rolling the kayak over an embankment at at launch and retrieval. These large z-axis movements violate the assumption that movements are all in the x-y direction and cause the solution to leave the circle but are still on the three dimensional sphere as described above and are not actual errors.

Oh, I didn’t know about the SBAS/GLONASS thing. I have been inputting an obs file, along with a nav, gnav and sbs file and it seemed to be using all the inputs (as the number of satellites I would have would sometimes be 15 or 16?) Not sure what is going on.

I have mainly been using moving-base, as for whatever reason I can’t get kinematic to output anything other than random jumps. Moving-base has worked fairly well.

How should I send you the files? My set-up is basically I have two “stationary” rovers (“stationary” in the sense that I am not moving them, though they are on a moving ice floe) and a “moving” rover that is moving in a grid. I mainly use the two ‘stationary’ ones to track the ice floe rotation/translation so that I can correct the moving one. After correction, using a PPK for enhanced accuracy, I get very good horizontal resolution (<5cm): https://ibb.co/cZy5L5. However, the vertical resolution is still several meters and I'm not sure what to do here.

(by the way, please feel free to email me as these responses may get more detailed).

Hi Jeffrey. Your RTK application sounds very interesting! I think there are advantages and disadvantages in using either the moving base mode or the modified kinematic mode I suggest in this post. For me, the method described here worked better. I would try both and see which works best for you. I never really did figure out why the moving base mode did not work well for my data. If you’d like to send me one of your data sets, I’d be interested in taking a look at it.

I normally do not use the SBAS corrections for a couple reasons. One is that they do not include GLONASS corrections and RTKLIB does not allow a mix of corrected and uncorrected satellites, so enabling SBAS will cause the GLONASS satellites to be ignored. Also, for short baselines the atmospheric errors will be very similar between base and rover and simply cancelling them through differencing may be better than trying to correct them separately.

Hi there. Thanks for your really, really helpful guide! I am working with moving-base solutions (basically, I am roving on a moving ice floe, which is moving relatively fast compared to me walking on it, but at a relatively constant speed). I have been using moving-base primarily in RTKLIB but do you think Kinematic would work better for me? I am doing PPK with data collected at 5Hz on GPS/GLO/SBS.

Also, another question I have been struggling to find an answer to is whether ionosphere/troposhere corrections should be the default saas/brdc mode that is in your conf file or if they should be sbas (as I have a .sbs file – not sure if this is the same thing?)

Thanks so much for all your great work!