RTK - base/rover with U-blox GNSS receivers
After some tests with Precise Point Positioning ( see below 1-5 ) and in detailed with (4) I describe this time a setup with a base station sending RTCM (Radio Technical Commission for Maritime Services) data to a rover station. For both ( base and rover ) I use Raspberry Pi’s with a GNSS pi-hat with the latest Debian OS trixie Version 13 and the latest Version of https://gitlab.com/gpsd/gpsd. I also use github.com/rtklibexplorer/RTKLIB written by Jens Reimann. It’s not necessary to have this tool on these servers. Most of the time I use this from a third server as well as the gpsd package where I use “ubxtool” remotely.
Base station is a Raspberry Pi5 with a ZED-X20P from U-blox. The pi-hat is from sparkfun.
Rover is a Raspberry Pi4 with a ZED-F9P from U-blox. The pi-hat is from uputronics.
In both cases I use the second interface UART2 to communicate between base and rover for the RTCM traffic. How to setup I described in second interface for u-blox receiver
In advance I want to say that this combination with ZED-X20P and ZED-F9P is not perfect but possible. The reasons are multiple: ZED-F9P can handle only the L1 and L2 band. ZED-X20P is designed for L1/L2/L5/E6/B3/L. Another reason is that ZED-X20P cannot handle GLONASS (Globalnaja nawigazionnaja sputnikowaja sistema) at the moment. (And maybe never) And the Navigation Indian Constellation (NavIC) can only be used by ZED-X20P. Independant of that I don’t see any Indian satellite here in Vienna ( 48N 16E ). Therefore there are left 3 GNSS: GPS, Galileo and BeiDou as least common multiple and common source.
Below you can find 2 scripts. The first one is to setup the base station which is a little bit more laborious. The second one is for the rover. These scripts require certain prerequisites. For example there is a server with hostname “base” and “rover” or at least an DNS CNAME for it. SSH should be possible with password.
functubxtool_ksh defines a function “ubxtool” like this
export UBXOPTS='-P 27.50'
/usr/local/bin/ubxtool $@ rover:gpsd:/dev/serial0
This is to avoid to add each time “rover:gpsd:/dev/serial0” as aditional argument
setup_base_sh
#!/usr/bin/env bash
. functubxtool_ksh base
# ident setup_base_sh
setup_initial(){
# Setup Script for u-blox Base (X20P)
logger -p user.debug "setup_base_sh setup_initial "
# this is the initial setup to prepare the base sation for it function
# make sure in advance that baudrate for uart1 is high enough
if test -z "`ubxtool -g CFG-UART1-BAUDRATE | grep UART1-BAUDRATE | head -1 | grep 921600`"
then
echo $0: UART1-BAUDRATE,921600 failed
exit 1
fi
ubxtool -z CFG-UART2-BAUDRATE,921600 | grep UBX-ACK-ACK:
if test $? -ne 0
then
echo $0: UART1-BAUDRATE,921600 failed
exit 1
fi
# make sure that no Survey-In is running
ubxtool -z CFG-TMODE-MODE,0 | grep UBX-ACK-ACK:
# reference cooridnates set to ECEF
ubxtool -z CFG-TMODE-POS_TYPE,0 | grep UBX-ACK-ACK:
# position of the base station , unit is cm
# 48.1493013022 16.2838442507 288.08
ubxtool -z CFG-TMODE-ECEF_X,409252331 | grep UBX-ACK-ACK:
ubxtool -z CFG-TMODE-ECEF_Y,119548502 | grep UBX-ACK-ACK:
ubxtool -z CFG-TMODE-ECEF_Z,472818312 | grep UBX-ACK-ACK:
# set High-Precision Register to zero
ubxtool -z CFG-TMODE-ECEF_X_HP,0 | grep UBX-ACK-ACK:
ubxtool -z CFG-TMODE-ECEF_Y_HP,0 | grep UBX-ACK-ACK:
ubxtool -z CFG-TMODE-ECEF_Z_HP,0 | grep UBX-ACK-ACK:
# RTCM data (1 Hz Intervall, 1230 each 5 Sek)
# activate RTCM3 output on UART2 (Port 2)
# 1005: Station ID & Position, 1077-1207: MSM7 Nachrichten for GPS, GLO, GAL, BDS
for msg in 1005 1077 1087 1097 1124 1127 ; do
ubxtool -z CFG-MSGOUT-RTCM_3X_TYPE${msg}_UART2,1 | grep UBX-ACK-ACK:
done
ubxtool -z CFG-MSGOUT-RTCM_3X_TYPE1230_USB,5 | grep UBX-ACK-ACK:
# FIXED MODE schalten
ubxtool -z CFG-TMODE-MODE,2 | grep UBX-ACK-ACK:
ubxtool -z CFG-SIGNAL-PLAN,1 | grep UBX-ACK-ACK:
ubxtool -z CFG-SIGNAL-GPS_ENA,1 | grep UBX-ACK-ACK:
ubxtool -z CFG-SIGNAL-SBAS_ENA,0 | grep UBX-ACK-ACK:
ubxtool -z CFG-SIGNAL-GAL_ENA,1 | grep UBX-ACK-ACK:
ubxtool -z CFG-SIGNAL-BDS_ENA,1 | grep UBX-ACK-ACK:
ubxtool -z CFG-SIGNAL-QZSS_ENA,0 | grep UBX-ACK-ACK:
ubxtool -z CFG-SIGNAL-GLO_ENA,0 | grep UBX-ACK-ACK:
ubxtool -z CFG-SIGNAL-NAVIC_ENA,0 | grep UBX-ACK-ACK:
ubxtool -z CFG-SIGNAL-BDS_B2A_ENA,1 | grep UBX-ACK-ACK:
ubxtool -z CFG-SIGNAL-GPS_L1CA_ENA,1 | grep UBX-ACK-ACK:
ubxtool -z CFG-SIGNAL-GPS_L2C_ENA,1 | grep UBX-ACK-ACK:
ubxtool -z CFG-SIGNAL-GPS_L5_ENA,1 | grep UBX-ACK-ACK:
ubxtool -z CFG-SIGNAL-SBAS_L1CA_ENA,0 | grep UBX-ACK-ACK:
ubxtool -z CFG-SIGNAL-GAL_E1_ENA,1 | grep UBX-ACK-ACK:
ubxtool -z CFG-SIGNAL-GAL_E5A_ENA,1 | grep UBX-ACK-ACK:
ubxtool -z CFG-SIGNAL-GAL_E5B_ENA,0 | grep UBX-ACK-ACK:
ubxtool -z CFG-SIGNAL-GAL_E6_ENA,1 | grep UBX-ACK-ACK:
ubxtool -z CFG-SIGNAL-BDS_B1_ENA,1 | grep UBX-ACK-ACK:
ubxtool -z CFG-SIGNAL-BDS_B2_ENA,1 | grep UBX-ACK-ACK:
ubxtool -z CFG-SIGNAL-BDS_B1C_ENA,0 | grep UBX-ACK-ACK:
ubxtool -z CFG-SIGNAL-BDS_B3_ENA,0 | 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_L5_ENA,0 | grep UBX-ACK-ACK:
ubxtool -z CFG-SIGNAL-GLO_L1_ENA,0 | grep UBX-ACK-ACK:
ubxtool -z CFG-SIGNAL-GLO_L2_ENA,0 | grep UBX-ACK-ACK:
ubxtool -z CFG-SIGNAL-NAVIC_L5_ENA,0 | grep UBX-ACK-ACK:
# kill a possible running process
ssh base pkill str2str
# send the RTCM date to the rover
ssh base "str2str -in serial://ttyAMA3:921600:8:n:1:off -out tcpsvr://:42101 --deamon"
}
usage(){
echo "usage: $0 setup_initial "
exit 1
}
case "$1" in
setup_initial ) setup_initial ;;
* ) usage ;;
esac
setup_rover_sh
#!/usr/bin/env bash
# ident setup_rover_sh
. functubxtool_ksh rover
setup_initial(){
logger -p user.debug "setup_rover_sh setup_initial "
# this is the initial setup to prepare the rover sation for it function
# make sure in advance that baudrate for uart1 is high enough
if test -z "`ubxtool -g CFG-UART1-BAUDRATE | grep UART1-BAUDRATE | head -1 | grep 921600`"
then
echo $0: UART1-BAUDRATE,921600 failed
exit 1
fi
ubxtool -z CFG-UART2-BAUDRATE,921600 | grep UBX-ACK-ACK:
if test $? -ne 0
then
echo $0: UART1-BAUDRATE,921600 failed
exit 1
fi
# is set per default layer 7
ubxtool -z CFG-UART2INPROT-RTCM3X,1 | grep UBX-ACK-ACK:
# disable not usable GNSS
ubxtool -z CFG-SIGNAL-SBAS_ENA,0
ubxtool -z CFG-SIGNAL-QZSS_ENA,0
ubxtool -z CFG-SIGNAL-GLO_ENA,0
ubxtool -z CFG-SIGNAL-SBAS_L1CA_ENA,0
ubxtool -z CFG-SIGNAL-QZSS_L1CA_ENA,0
ubxtool -z CFG-SIGNAL-QZSS_L1S_ENA,0
ubxtool -z CFG-SIGNAL-QZSS_L2C_ENA,0
ubxtool -z CFG-SIGNAL-GLO_L1_ENA,0
ubxtool -z CFG-SIGNAL-GLO_L2_ENA,0
# kill a possible running str2str
ssh rover pkill str2str
# start a new one - this is the communication to the base for RTCM traffic
ssh rover "str2str -in tcpcli://192.168.241.93:42101 -out serial://ttyAMA5:921600:8:n:1:off --deamon"
ubxtool -z CFG-MSGOUT-UBX_RXM_RTCM_UART1,1 | grep UBX-ACK-ACK:
# set high precision mode
ubxtool -z CFG-NMEA-HIGHPREC,1 | grep UBX-ACK-ACK:
ubxtool -z CFG-MSGOUT-NMEA_ID_GGA_UART1,1 | grep UBX-ACK-ACK:
}
nmea_pipe(){
logger -p user.debug "setup_rover_sh nmea_pipe with argument $1 "
# this pipe is for monitoring with rtkplot_q
case "$1" in
stop )
# kill a possible running gpspipe
ssh rover "pkill -f 'socat EXEC:gpspipe -r TCP-LISTEN:10001,reuseaddr,fork'"
;;
start )
# start a gpspipe for monitoring with rtkplot_qt / option -r is NMEA output
ssh rover nohup "socat EXEC:'gpspipe -r' TCP-LISTEN:10001,reuseaddr,fork > /dev/null 2>&1 & disown "
echo pipe ready at port 10001
;;
"" )
nmea_pipe stop ; nmea_pipe start
;;
esac
}
raw_pipe(){
logger -p user.debug "setup_rover_sh raw_pipe with argument $1 "
case "$1" in
stop )
# kill a possible running gpspipe
ssh rover "pkill -f 'socat EXEC:gpspipe -RB TCP-LISTEN:10002,reuseaddr,fork'"
;;
start )
# start a gpspipe for logging with strsvr_qt or str2str
ssh rover nohup "socat EXEC:'gpspipe -RB' TCP-LISTEN:10002,reuseaddr,fork > /dev/null 2>&1 & disown "
echo for example its possible to start now: str2str -in tcpcli://rover:10002 -out file://log_%Y%m%d%h%M.ubx
;;
"" )
raw_pipe stop ; raw_pipe start
;;
esac
}
sat_used(){
logger -p user.debug "setup_rover_sh sat_used "
# NAVSAT=`ubxtool -p NAV-SAT -v 2 | sed -n -e '/^UBX-NAV-SAT:/,/^$/ p' | awk -v RS= 'NR==2' | grep -B 2 -A 2 -i rtcm | egrep 'flags'`
NAVSAT=`ubxtool -p NAV-SAT -v 2 | sed -n -e '/^UBX-NAV-SAT:/,/^$/ p' | awk -v RS= 'NR==2' `
echo " satellites total seen : " `echo "$NAVSAT" | grep -c gnssId `
echo " satellites used : " `echo "$NAVSAT" | grep 'flags(' | grep -c svUsed `
echo " satellites used with rtcm coorection : " `echo "$NAVSAT" | grep -c rtcm `
echo " satellites with pseudorange corrections : " `echo "$NAVSAT" | grep -c prCorrUsed `
echo "satellites with carrier range corrections : " `echo "$NAVSAT" | grep -c crCorrUsed `
SYST=`echo "$NAVSAT" | grep -B 2 crCorrUsed | grep gnssId | awk '{ print ( $2 ) }' | uniq -c`
echo $SYST | awk '{ print ( "GPS: " $1 " Galileo: " $3 " BeiDou: " $5 ) }'
}
navpvt(){
logger -p user.debug "setup_rover_sh navpvt "
ubxtool -p NAV-PVT -v 2 | sed -n -e '/^UBX-NAV-PVT:/,/^$/ p' | awk -v RS= 'NR==2'
}
usage(){
echo "usage: $0 setup_initial | nmea_pipe | raw_pipe | sat_used | navpvt "
exit 1
}
case "$1" in
setup_initial ) setup_initial ;;
nmea_pipe ) nmea_pipe "$2" ;;
raw_pipe ) raw_pipe "$2" ;;
sat_used ) sat_used ;;
navpvt ) navpvt ;;
* ) usage ;;
esac
(1) PPP - Precise Point Positioning with averaging
(2) PPP with gpsrinex, CSRS-PPP and ECTT
(3) PPP with RTKlib and local correction
(4) PPP with NTRIP source for u-blox GNSS receiver over gpsd
(5) PPP with NTRIP source and rtknavi_qt