This is a follow-up to a post I made eight years ago on using RTKLIB to generate real-time PPP solutions using SSR corrections. I’d suggest reading or re-reading that post before continuing since I will assume you are already familiar with everything I described there. In that post, I described using the real-time SSR correction stream CLK93 from CNES (the French Space agency) for real-time PPP solutions with RTKLIB.
In general, if I need a PPP solution, but it doesn’t need to be real-time, then I prefer to upload my raw observations to one of the free online PPP services such as CSRS since they are simple to use and generally provide very high-quality solutions. However, these services are not suitable for real-time solutions since they typically have a latency for processing current data. For example the table below is from the CSRS website:

For real-time PPP solutions, using RTKLIB with SSR corrections can be a useful option.
The CLK93 stream I used in my previous post has been renamed using the new naming convention and is now called the SSRA00CNE0 stream. This stream contains orbit and clock corrections as well as code and phase biases for all four major GNSS constellations as well as ionospheric corrections. This stream and many others can be accessed with an account from BKG or several other locations listed at the BKG link. More information about these streams is also available here. I used the BKG site rather than the CDDIS option because the CDDIS site uses SSL certificates which RTKLIB doesn’t support. It is still possible to use the CDDIS streams by downloading the free BKG Ntrip Client and using that to pipe the streams to a TCP port but that is obviously not an ideal choice.
In my previous post I focused on multiple hour PPP solutions to get cm-level accuracy but for this exercise I was more interested in shorter solutions (15-30 minutes) with dm-level accuracy since typically a real-time user does not want to wait hours for a solution. My own use-case for this is to process and collect raw observations from a static receiver with unknown location while simultaneously using it as a base station in a real-time RTK solution for a second receiver. The real-time base-station PPP solution would be used in conjunction with the RTK solution to quickly provide dm-level absolute accuracy for the real-time user while the final post-processed data would have cm-level accuracy using a base station position generated from an online post-processed PPP solution of the full base station dataset.
There have been several PPP and SSR related improvements made to the RTKLIB-EX code since the 2.5.0 release, so you will want to use the 2.5.1 or newer code for any of your own testing. It is available on Github as a pre-release now and should become a final release soon. The most relevant improvements include fixes to the parsing of orbit and clock correction messages for the BeiDou constellation, better outlier detection, improved matching of timestamps between ephemeris messages and SSR messages, and support for the ionosphere correction messages. Some of these changes were mine and some were contributed by “Our Air Quality” from Australia and Andre Hauschild from DLR in Germany.
SSR solutions would normally be used only for real-time solutions, not post-processed solutions, since more accurate corrections are available later in file format. This means they will generally be run with the RTKLIB real-time apps (RTKNAVI, RTKNAVI-QT, or RTKRCV). However, it is also possible to run post-processed SSR solutions in RTKLIB because the post-processing apps accept support files in RTCM3 format.
For analysis and evaluation purposes, it is more convenient to record the data and then post-process the same input data multiple times under different configurations rather than performing multiple real-time runs with different input data each time. When I did this analysis, I had forgotten that RTKPOST accepts RTCM3 correction files, so I chose to use RTKNAVI with time-tagged input files instead. This also had the advantage that numerous debug screens are available from the “Monitor” button, including one that displays the SSR correction values as shown below.

Although RTKNAVI is primarily designed for real-time data, receiver and SSR correction streams can be logged with RTKLIB time-tags enabled and later replayed by enabling the time-tag options for the input files. When running in this mode, you can also select the playback speed. The dropdown menu includes playback speeds up to 10X, but you can manually enter larger values if you want to run faster. On my laptop, I am able to run at 100X without issue, but if I run much faster than that I start to see the satellite count drop, indicating that the processing can no longer keep up with the incoming data.
PPP solutions in RTKLIB can be run either using ionosphere-free linear combinations of observations of two frequencies or with the uncombined observations. The results are fairly similar for either choice but I usually find the ionosphere-free solutions to converge a little faster than the uncombined solutions so I will focus on them for this exercise. If a two frequency solution (L1+L2) is selected, then the ionospheric-free combinations will use L1 and L2, if a three frequency solution (L1+L2+L3) is selected, the the ionospheric-free combinations will use L1 and L3 and the L2 observations will be ignored. The L1 frequencies are L1/G1/E1/B1, L2 frequencies are L2/G2/E5b/B2b and the L3 frequencies are L1/G3/E5a/B2a.
To test the performance of the SSR PPP solutions, I logged six one-hour data sets with time-tags using a rooftop antenna connected with an RF splitter to two different low-cost receivers, a u-blox F9P and a Quectel LG290P. The F9P provides dual-frequency observations for GPS, GLONASS, and Galileo. while the Quectel LG290P provides triple-frequency observations for all four constellations. I also simultaneously logged the SSRA00CNE0 correction stream. There are numerous other SSR correction stream choices available but the SSRA00CNE0 stream is a good choice because in includes both clock and orbit corrections for GPS, GLONASS, Galileo, and BeiDou. It also seems to do some validation on the satellite quality. In my data sets, one of the GLONASS satellites appeared to have some clock stability issues which showed up as oscillations in the phase residuals. The SSRA00CNE0 stream did not include corrections for this satellite which meant it was automatically excluded from the solution. Another stream I was evaluating did include the corrections from this satellite resulting in a poorer solution.
The “A” in the fourth character of the stream name indicates the corrections are relative to the satellite antenna phase centers rather than the center of masses. Correction streams relative to the satellite center-of-masses will have a “C” in the fourth character. RTKLIB will work with either convention, you just need to set the Satellite Ephemeris/Clock input configuration option appropriately and include an antenna correction file if you choose the CoM option. The zero at the end of the stream name indicates the messages are in RTCM3 format.
I used RTKNAVI to post-process the logged data with time-tags enabled, connecting the rover input stream to the receiver log file and the correction input stream to the corrections log file. Usually you will want to leave the base station input stream blank but if for any reason your receiver data does not include navigation messages, then you can can use this stream to inject them into the solution either from a local receiver or from a navigation stream such as RTCM3EPH. I found this useful when running solutions on some real-time IGS station observation streams since these seemed to only send navigation messages when they changed which may only happen once every couple of hours for some constellations.
Another thing to be aware of regarding navigation messages is that RTKNAVI retains navigation messages from one run to the next and saves and retrieves them when you exit and reenter the program. This is useful for minimizing solution convergence time but can be confusing because you get different behavior from the same input data depending on the state of the navigation message buffer.
I processed three solutions for each data set, an L1/L2 solution for the F9P, an L1/L2 solution for the LG290P, and an L1/L3 solution for the LG290P. The results are shown in the plots below. On the left are the East, North, and Up errors for the F9P L1/L2 solutions, in the middle are the LG290P L1/L2 solutions, and on the right are the LG290P L1/L3 solutions. The three solution sets are fairly similar, usually showing less than two dm errors after 15 minutes in each component, and less than one dm error after 30 minutes. Because I optimized the input parameters for fast convergence rather than maximum final accuracy, the results don’t improve much after 30 or 40 minutes. I used a CSRS online post-processed solution as the reference position for the origin of these plots.

I’ve included the config file I used to generate these solutions in the 2.5.1 release under the filename ppp_ssr_fast.conf. If you compared this file to the one I used for the post from eight years ago, you would see a few differences but there are three that I believe are most significant.
First of all, I increased the one sigma phase bias process noise by a factor of five to make the filter converge more quickly at the expense of long term stability. This allows the ambiguity states to adapt more quickly during convergence but also increases their long-term variability.
Second, I reduced the outlier threshold for code and phase residuals. Previously they were both set to 30 meters but with the new code I am able to reduce these to 10 meters and 2 meters and could probably have reduced them more. The reason I can do this is because in the old code, the outlier thresholds were applied before the receiver clock errors were removed but now they are not applied until after. The old method worked fine for RTK/PPK solutions because the clock errors are removed by the double-differencing before the outlier thresholds are applied, but this is not true in PPP solutions. Without this fix, the outlier thresholds had to be set so high to avoid detecting clock errors that they were effectively disabled.
The last change was to increase the ratio between code and phase measurement error sigmas (eratio). For RTK/PPK solutions, I typically set this to 100-300 which probably reflects the true differences in the measurement qualities. However, for PPP solutions I have always found that increasing this value improves the solution, especially the convergence speed. I suspect this may be because of uncorrected non-zero biases in the code measurements. In the previous SSR post, I had increased this value to 1000 but was uncomfortable making it larger. However, after more data evaluation, and looking at the details a little more closely, I now believe that larger values are OK. The concern was that the phase measurements are only relative, not absolute, so by effectively ignoring the code measurements with very large eratio values, we might find false solutions far from the correct position. I believe there are two things that prevent this. First of all, each epoch starts from an initial position estimate that is derived from the code measurements, so the solution remains constrained even when the code observations receive very little weight in the filter. Also, the phase bias states are changing continuously with satellite geometry so any false solution would be unstable and quickly collapse. For the new solutions I am using an eratio of 10,000 which is large enough to be effectively equivalent to infinity and means we are relying almost entirely on the phase measurements and ignoring the code measurements.
One last test I did was to pick one of the six data sets and run all three solutions both with the 2.5.0 code and the 2.5.1 code using the new config file to validate the new code and try to quantify any improvements. Since the 2.5.0 code does not have the outlier threshold improvements, I did have to revert back to the original outlier thresholds for that code but otherwise the config files were identical. The results are plotted below. You can see all three solutions improved with the new code. The F9P solution improved less than the LG290P solutions, presumably because the F9P data did not have dual-frequency BeiDou observations, but still the improvement is significant in all three cases. In all cases I also noticed that the number of satellites used was higher with the new code. I think this comes from the improvements in matching the timestamps between ephemeris and corrections.

Overall, I was pleasantly surprised by how well these solutions performed. While RTK still remains the better choice when a local base station or network corrections are available, I think these results show that SSR-based PPP can be a useful option in certain situations. I expect both the correction products and the receiver hardware to continue improving, so it will be interesting to revisit these results again in a few years.












































