PPP with RTKLIB
This is now my third attempt to get an exact position of my fixed mounted GNSS antenna at the roof of my house.
You can find the methods I used previously in my blogs here:
(1) PPP - Precise Point Positioning
(2) PPP with gpsrinex
As GNSS receiver I used again my u-blox ZED-F9P
to manage this device I use the gpsd package.
This time I used RTKlib to make the post-processing by myself. There are different versions available, I used this: github.com/rtklibexplorer/RTKLIB
The method is about this:
1) create an UBX file over several hours.
2) apply the correction with a RINEX file offered from an external service provider.
Within a shell script I prepared my ZED-F9P with the following settings:
check if baud-rate is high enough
ubxtool -g CFG-UART1-BAUDRATE | grep CFG-UART1-BAUDRATE
I use 921600 bd. This is the highest value I can use. 38400 would be the default.
disable NMEA protocol
ubxtool -d NMEA | grep UBX-ACK-ACK: ubxtool -z CFG-UART1OUTPROT-NMEA,0 | grep UBX-ACK-ACK: ubxtool -g CFG-UART1OUTPROT-NMEA | grep CFG-UART1OUTPROT-NMEA | head -1
enable or disable different satellites
ubxtool -z CFG-SIGNAL-SBAS_L1CA_ENA,1 | grep UBX-ACK-ACK: ubxtool -z CFG-SIGNAL-SBAS_ENA,1 | grep UBX-ACK-ACK: ubxtool -z CFG-SIGNAL-QZSS_L1CA_ENA,0 | grep UBX-ACK-ACK: ubxtool -z CFG-SIGNAL-QZSS_L1S_ENA,0 | grep UBX-ACK-ACK: ubxtool -z CFG-SIGNAL-QZSS_L2C_ENA,0 | grep UBX-ACK-ACK: ubxtool -z CFG-SIGNAL-QZSS_ENA,0 | grep UBX-ACK-ACK: ubxtool -g CFG-SIGNAL | sed -n -e '/^UBX-CFG-VALGET:/,/^$/ p' | awk -v RS= 'NR==1'
enable raw data
ubxtool -e RAWX | grep UBX-ACK-ACK: ubxtool | grep RAWX
to get satellite track data
ubxtool -e SFRBX | grep UBX-ACK-ACK: ubxtool -z CFG-MSGOUT-UBX_RXM_SFRBX_UART1,1 | grep UBX-ACK-ACK:
SECS=50400 DAT=`date '+%Y%j%H%M00'` FILE=`echo result$DAT` gpspipe -x $SECS -R gpsdhost:gpsd:/dev/serial0 > $FILE.ubx
gpspipe will run 14 hours.
to generate $FILE.obs , $FILE.nav and $FILE.sbs run convbin
convbin $FILE.ubx
Instead of convbin one can run the GUI rtkconv_qt & interactive.
If files $FILE.obs and $FILE.nav are available one can view some graphs with
rtkplot_qt -r $FILE.obs $FILE.nav &
For example the skyplot for Galileo

Now comes the part of preparing the correction.
For this we need the RINEX (Receiver Independent Exchange Format) data from a reference station. I use the APOS-PP from BEV. BEV is the Federal Office of Metrology and Surveying ( Bundesamt für Eich- und Vermessungswesen ) in Austria and APOS-PP is a free service they offer. APOS-PP is the Austrian Position Service Post Processing. See APOS
The free available RINEX files are accessible short time after 00:00 UTC, so I fetched them next day. As there are about 50 reference stations one has to select a station which is near to the own position. In my case it’s WIEN00AUT. As each file has only 1 hour of data multiple files are needed. The files have type .crx.gz. To get a .rnx run first gunzip and than CRX2RNX. CRX2RNX is also free available and should have at least version : ver.4.2.0. Older Versions are buggy. Finally all these files have to be combined to one file. This is done with gfzrnx which is also free available. Current available VERSION: gfzrnx-2.2.0 at GFZ.de
Now comes the real part of correction. There are 2 possibilities:
- a GUI
rtkpost_qt - a command line tool:
rnx2rtkp
As first step I run the GUI. The reason is that some settings can be saved in a configuration file which can be used with the command line tool as argument. I configured under “Setting1” position mode “Static” and selected Navigation Systems: GPS, Galileo, Glonass and BDS. In selector “Positions” I entered below “Base Station” the position in X/Y/Z-ECEF format. The values can be found in the final .rnx file in line marked with APPROX POSITION XYZ. Don’t worry, it’s not approximately, it is exact. In case of WIEN00AUT it is 4085097.7110 1200224.1682 4733306.7362. It’s important to enter the exact values, otherwise the calculated result is wrong. Now press the “Save” button and give the configuration file a name, e.g. “myconfig.conf”. Back to the main window enter the necessary file names. It is the .obs file for Rover, the .rnx file for the basestation and also needed .nav and .sbs file. In the solutions sections you get a proposal for the final position file name. Press the “Execute” button. After a while you get the result. Press the “Plot” button. It will start rtkplot_qt with the .pos file. If “Ground Track” is selected we can see that the position goes around only few centimeter during the last hours. Better to see using the “Position” tab.

As we can see the curve swings in. As value I don’t use the last line of the result file, I use the average of the last 3 hours. Especially don’t use the first 2 lines for calculating the average.
Instead of using the GUI with rtkpost_qt it’s also possible to use the command line tool rnx2rtkp. As we have now a config file we can apply this as argument with option -k calling rnx2rtkp. A typical call looks like this:
rnx2rtkp -k myconfig.conf -o $FILE.pos $FILE.obs $FILE.rnx $FILE.nav $FILE.sbs
I run this scenario over several days. And this is the result
| date | longitude | latitude |
|---|---|---|
| result2026031081521.pos | 48.149286929965 | 16.283835338478 |
| result2026035020026.pos | 48.149286770863 | 16.283834924148 |
| result2026036020000.pos | 48.149286798081 | 16.283835460302 |
| result2026037020000.pos | 48.149286919252 | 16.283835496834 |
| result2026038020000.pos | 48.149286670760 | 16.283835066090 |
| result2026039020000.pos | 48.149286846305 | 16.283835422324 |
| result2026040020000.pos | 48.149286713159 | 16.283835438211 |
| result2026041020000.pos | 48.149286842099 | 16.283835050953 |
| average | 48.149286811309 | 16.283835274668 |
All results are within a circle with a radius of 26 mm. The maximum distance between 2 points is 46 mm.
But when I compare this result with the result I got using gpsrinex (Method 2) then there is an offset of about 77 cm.
Comparing this with the averaging method 1 I see an offset of 1.09 meters.