Creating an NTP Server Using GPS/PPS

You can build your own Network Time Protocol (NTP) server using GPS and PPS on your ODROID. This system gives you very accurate time which can be very useful for specific use cases. The atomic clocks in GPS satellites are monitored and compared to ‘master clocks’ by the GPS Operational Control Segment; this GPS time is steered to within one microsecond of Universal Time. Our GPS receiver provides 1 Pulse Per Second, or PPS, output signal but you need to do a bit of wire soldering to expose this pin. This pulse has a rising edge aligned with the GPS second, and is used to discipline local clocks to maintain synchronisation with Universal Time (UT). As a result, our local server can have a very accurate time with less than 10 microseconds of tolerance. Before you start, you will need to expose the PPS signal from the GPS receiver.

Exposing the PPS signal
First, disassemble the GPS module by removing the 4 screws on the back of the GPS module. They are covered by a sticker on the bottom of the receiver. You should find out where they are by rubbing along the sticker feeling for divots. There should be 2 at the top and bottom ends of the sticker. After find the screw holes, cut the sticker and detach the cut part to unscrew the 4 screws.

Figure 1 – Tools you will need
Figure 2 – Peel off sticker to access screws
Figure 3 - Peel off sticker to access screws
Figure 3 – Peel off sticker to access screws
Figure 4 – All screws removed

Uncover the module to reveal the PCB board, which is what you have to solder in order to expose the PPS.

Figure 5 – Cover removed

This is a very important part of this guide, since you have to solder a jumper cable to a specific pin of the chip. The location of the PPS pin that we need to expose is shown in Figure 6. Be very careful not to create a short circuit.

Figure 6 – PPS pin on main PCB

If you’ve done so, you may clean up the cable as shown in Figure 7.

Figure 7 – Attached breakout cable

Next, we need to assemble and connect everything back together. Place The PCB back into the housing like before and screw the case back together again.

Figure 8 – Place PCB back into case
Figure 9 – Screw cover back on case

Connect the jump cable to the GPIO pin #18 of the XU4 shifter shield. Note that this needs to be the shifter shield to ensure that the voltage levels from the PPS pin match what is expected.

Figure 10 – PPS pin attached to XU4

Connect the USB cable to the ODROID-XU4, and connect LAN, power cable as well.

Figure 11 – Pin and USB connected to XU4

Our mainline kernel doesn’t fully support PPS from GPIO. Some required software setup should be done by building your own kernel on your ODROID-XU4. To start, prepare the Linux kernel source from Github, and install the needed tools to build a new kernel:

$ sudo apt update && sudo apt install git gcc g++ build-essential libncurses5-dev bc

Get the Linux kernel source from our official Github repository at

$ git clone --depth 1 https://github.  com/hardkernel/linux.git -b odroidxu4-4.14.y odroidxu4-4.14.y
$ cd odroidxu4-4.14.y

Add PPS support by editing the file arch/arm/boot/dts/exynos5422-odroidxu4.dts file to add a new device which gets PPS from GPIO #18:

$ vi arch/arm/boot/dts/exynos5422-odroidxu4.dts

Add the following contents to the file:

dummy_codec : spdif-transmitter {};
/* add for pps-gpio */
pps {
  compatible = "pps-gpio";
  gpios = <&gpx1 2 GPIO_ACTIVE_HIGH>;
  status = "okay";

Next, make a custom menuconfig:

$ make odroidxu4_defconfig
$ make menuconfig

Find and enable with the space key, as shown in Figure 12, then save and exit.

Figure 12 – Enable PPS Support

If your custom settings works well, that would make new devices at /dev. Let’s check them:

$ ls -al /dev/{ttyACM*,gps*,pps*}

crw------- 1 root root    248, 0 Jan 31 14:21 /dev/pps0
crw-rw---- 1 root dialout 166, 0 Jan 31 14:53 /dev/ttyACM0
lrwxrwxrwx 1 root root         7 Jan 31 14:21 /dev/ttyACM99 -> ttySAC0

If any one of the items in the above example above doesn’t exist, you’ve done something wrong, and you should try to configure and build the kernel again. If all of them exist, make soft link files to use later:

$ sudo ln -sF /dev/ttyACM0 /dev/gps0
$ sudo ln -sF /dev/pps0 /dev/gpspps0
$ ls -al /dev/{ttyACM*,gps*,pps*}

lrwxrwxrwx 1 root root        12 Jan 31 15:50 /dev/gps0 -> ttyACM0
lrwxrwxrwx 1 root root         9 Jan 31 15:51 /dev/gpspps0 -> /dev/pps0
crw------- 1 root root    248, 0 Jan 31 15:50 /dev/pps0
crw-rw---- 1 root dialout 166, 0 Jan 31 15:50 /dev/ttyACM0
lrwxrwxrwx 1 root root         7 Jan 31 15:50 /dev/ttyACM99 -> ttySAC0

Make sure your result looks like the above example, theninstall the GPS related packages and configure them:

$ sudo apt install gpsd gpsd-clients
$ sudo dpkg-reconfigure gpsd

Next, you will need to test them:

$ sudo gpsmon /dev/gps0

An example screenshot using “gpsmon /dev/gps0” is shown in Figure 13. Wait more than 5 minutes to get the GPS information properly.

Figure 13 – NMEA data

Next, install PPS tools, then test our ppstest on /dev/gpspps0:

$ sudo apt install pps-tools
$ sudo ppstest /dev/gpspps0

trying PPS source "/dev/gpspps0"
found PPS source "/dev/gpspps0"
ok, found 1 source(s), now start fetching data...
source 0 - assert 1517363638.431673232, sequence: 130 - clear  0.000000000, sequence: 0
source 0 - assert 1517363639.431676649, sequence: 131 - clear  0.000000000, sequence: 0

A new row starting with “source 0 – assert …” will be added for every each second. Next, install the NTP service:

$ sudo apt install ntp

Edit the /etc/ntp.conf file to use GPS/PPS. Backup the original file, and create a new configuration file using the options below:

$ sudo mv /etc/ntp.conf /etc/ntp.conf.bak
$ sudo vi /etc/ntp.conf

# /etc/ntp.conf, configuration for ntpd; see ntp.conf(5) for help

# Drift file to remember clock rate across restarts
driftfile /var/lib/ntp/ntp.drift

# Server from generic NMEA GPS Receiver
# server: NMEA serial port (/dev/gps0), mode 16 = 9600 baud + 2 = $GPGGA
# fudge:  flag 1 for use PPS (/dev/gpspps0), time2 for calibration time offset
server mode 18 minpoll 3 maxpoll 3 prefer
fudge flag1 1 time2 0.000 refid gPPS

Note that the time2 parameter (0.000) is for editing time offset for calibrating the result time. Finally, restart the NTP service.

$ sudo service ntp restart
$ sudo service ntp status

● ntp.service - LSB: Start NTP daemon
   Loaded: loaded (/etc/init.d/ntp; bad; vendor preset: enabled)
   Active: active (running) since Wed 2018-01-31 17:44:58 KST; 3s ago
     Docs: man:systemd-sysv-generator(8)
  Process: 744 ExecStop=/etc/init.d/ntp stop (code=exited, status=0/SUCCESS)
  Process: 754 ExecStart=/etc/init.d/ntp start (code=exited, status=0/SUCCESS)
   CGroup: /system.slice/ntp.service
           └─765 /usr/sbin/ntpd -p /var/run/ -g -u 111:115
Jan 31 17:44:58 odroid ntp[754]:    ...done.
Jan 31 17:44:58 odroid systemd[1]: Started LSB: Start NTP daemon.
Jan 31 17:44:58 odroid ntpd[765]: proto: precision = 1.375 usec (-19)
Jan 31 17:44:58 odroid ntpd[765]: Listen and drop on 0 v6wildcard [::]:123
Jan 31 17:44:58 odroid ntpd[765]: Listen and drop on 1 v4wildcard
Jan 31 17:44:58 odroid ntpd[765]: Listen normally on 2 lo
Jan 31 17:44:58 odroid ntpd[765]: Listen normally on 3 eth0
Jan 31 17:44:58 odroid ntpd[765]: Listen normally on 4 lo [::1]:123
Jan 31 17:44:58 odroid ntpd[765]: Listen normally on 5 eth0 [fe80::4db2:ce0b:48f3:26af%2]:123
Jan 31 17:44:58 odroid ntpd[765]: Listening on routing socket on fd #22 for interface updates

Wait for about minutes for the GPS to stabilize, then check that you are getting an accurate time from the GPS/PPS. The PPS output is enabled only when it gets several stable satellite signals. You can see the results like below, Check that the “o” character exists before IP numbering and reach value is increasing up to 377.

$ ntpq -p

     remote           refid      st t when poll reach   delay   offset  jitter
oGPS_NMEA(0)     .gPPS.      0 l    1    8  377    0.000    0.008   0.002
$ ntptime
Check that estimated error is just 1 us(Microsecond).
ntp_gettime() returns code 0 (OK)
  time de1bee1d.49adfb50  Wed, Jan 31 2018 16:26:21.287, (.287811636),
  maximum error 2000 us, estimated error 1 us, TAI offset 0
ntp_adjtime() returns code 0 (OK)
  modes 0x0 (),
  offset -3.606 us, frequency 1.000 ppm, interval 1 s,
  maximum error 2000 us, estimated error 1 us,
  status 0x2001 (PLL,NANO),
  time constant 3, precision 0.001 us, tolerance 500 ppm,

To view the original Wiki posting, please visit

Be the first to comment

Leave a Reply