An NTP server using GNSS for time

A straightforward project to use an old Raspberry Pi

Similar projects worth following
I finally found a worthwhile use for my ancient Pi 1B from 2012. It will be a time server for my LAN, and perhaps a message server to household displays later


I have a Raspberry Pi 1B from 2012. I thought it would make a nice X terminal but it turned out to be too weak for that. I have also used it as a print server and CD writer server with dubious success. Eventually it got superseded by a Raspberry Pi 2, which was better but still not powerful enough for an X terminal. Also I could not run some x86 software on the ARM processor (the ARM repositories were not as complete then). Eventually I bought a NUC which is also powerful enough to run Virtualbox and is my current desktop.

So the Pi 1B slumbered forlorn in the spares box. I bought a breakout board for it, hoping to use it for hardware experiments in conjunction with WiringPi but soon discovered that 1. it interfaced 3.3V devices so not compatible with old logic chips, and 2. the Arduino was more convenient.

This is not novel

There are heaps of projects connecting to a GNSS module from MCUs including a few on Hackaday. But I decided not to use whatever-duino because it's not so that I can shout "yay, I can show the time from a satellite on the serial console". I want it to be part of home environment and eventually be a low power server for time and messages to devices. And I want to spend little effort on the boring bits and have more fun learning the interesting bits.

GNSS module

I saw u-Blox 7N GNSS modules for sale on eBay cheap. It's the previous generation, the datasheet is from 2014, but it's far more capable than I need anyway. With that I can get accurate time without Internet connectivity. That hardly ever goes down and I don't really need sub-second accuracy, so this project is not essential for accurate time, just an excuse for me to tinker.

But after I received the modules, I discovered that silly me, I should have done more research. What I had purchased was just the chip which is meant to be surface mounted on a board. I thought: I'll just solder some wires for power and the UART lines. Tricky at 1.1 mm spacing but doable. What about the antenna though? It turns out that there is a pin for an external antenna. I should have realised that the chip alone would not receive adequate signals. So I went back to the Internet and ordered a u-Blox 6M GNSS module on a breakout board along with a ceramic antenna. That also solves the soldering problem as the 4 lines are brought out to a pin header. The 6M is two generations behind, but more than adequate for hobbyists. Oh well, the 7N was only a few bucks and maybe someday I'll be able to make use of the 7N modules elsewhere.

DietPi OS

I've decided to use DietPi instead of Raspberry Pi OS (formerly Raspbian) lite to get a small memory footprint. It's also Debian based (currently Buster) and maintained. As it turned out I have plenty of RAM (512MB) but it's also nice to have enough space on the 4GB SD card. (I know, I can get a bigger one cheap.)

gpsd for the server

Eric Raymond (that one) and others maintain gpsd, which unifies access to a variety of GNSS devices from other programs, such as ntpd. It's a standard package in Debian.


I'll write a log for each significant step.


The Raspberry Pi 1B has been successfully fitted with a u-Blox 6M GNSS module through the GPIO port and is now a time source on my intranet with accuracy in the milliseconds which is far more than I need. As mentioned, I really don't even need GNSS time as my Internet connection seldom goes down; this was just an excuse to tinker with GNSS and put an idle piece of gear to use.

  • Chronyd instead of ntpd

    Ken Yap08/27/2020 at 06:48 0 comments

    It seems that my workhorse distro prefers chronyd to ntpd. I was wondering why my ntpd was exiting after a day or two of operation. It turns out that this was due to a clash with chronyd. After reading an article on the Internet I have accepted my distro's judgement that chronyd is an improvement on ntpd, so I have configured it to use my GNSS server in the same way, by adding a server line. Here's the output of chronyc sources -v. You can see there is a wealth of detail.

    # chronyc sources -v
    210 Number of sources = 17
      .-- Source mode  '^' = server, '=' = peer, '#' = local clock.
     / .- Source state '*' = current synced, '+' = combined , '-' = not combined,
    | /   '?' = unreachable, 'x' = time may be in error, '~' = time too variable.
    ||                                                 .- xxxx [ yyyy ] +/- zzzz
    ||      Reachability register (octal) -.           |  xxxx = adjusted offset,
    ||      Log2(Polling interval) --.      |          |  yyyy = measured offset,
    ||                                \     |          |  zzzz = estimated error.
    ||                                 |    |           \
    MS Name/IP address         Stratum Poll Reach LastRx Last sample               
    ^-               4   6   377    32  -3854us[-3670us] +/-   37ms
    ^-                   2   6   377    36  -2552us[-2369us] +/-   56ms
    ^- ec2-13-55-50-68.ap-south>     3   6   377    30  +3660us[+3660us] +/-  109ms
    ^-               4   6   377    35  -3247us[-3064us] +/-   36ms
    ^- dns01.ntl02.privatecloud>     2   6   377    33   -799us[ -615us] +/-   47ms
    ^-      2   6   377    34  -1030us[ -846us] +/-   61ms
    ^+           3   6   377    33  -1061us[ -877us] +/-   15ms
    ^- 220-158-215-20.broadband>     2   6   377    32  +9851us[  +10ms] +/-   38ms
    ^-               4   6   377    32  -1467us[-1283us] +/-   37ms
    ^- dns01.syd01.privatecloud>     2   6   377    32  -3567us[-3382us] +/-   42ms
    ^-        2   6   377    31  -1762us[-1762us] +/-   51ms
    ^-     2   6   377    30  -1399us[-1399us] +/-   42ms
    ^+           3   6   377    35  -1016us[ -833us] +/-   15ms
    ^+     1   6   377    30   +676us[ +676us] +/-   13ms
    ^-                 2   6   337    34   -899us[ -716us] +/-  172ms
    ^-     2   6   377    35  -1745us[-1562us] +/-   38ms
    ^* rpi1.local                    1   6   377    31   +354us[ +538us] +/- 6065us

     I might be able to reduce that 6 ms estimated error by tuning.

  • Marking the project completed

    Ken Yap08/18/2020 at 10:20 0 comments

    The RPi has been running for a while now. The last thing I did was to cut away a panel of the acrylic case so that the GPIO flat cable can enter the box with the cover on. It's strange the makers of the case didn't consider this but I suppose most buyers, including me, ended up using the RPi as a cheap Linux machine and not as a hardware experimentation platform like the Arduino.

    So I'm marking this project completed. If I add other capabilities to the RPi, like bluetooth comms, I'll start a new project.

  • Adding GNSS time to my workhorse

    Ken Yap07/21/2020 at 11:11 0 comments

    Adding my RPi NTP server was simply a matter of adding a server line to my workhorse's /etc/ntp.conf. After doing that running ntpq -p on it gave this:

         remote           refid      st t when poll reach   delay   offset  jitter
    ==============================================================================     3 u   10   64  377   60.369   -0.377   1.405
    + (    2 u    -   64  377   20.083   -0.977   1.474   2 u    6   64  377   14.956   +0.761   1.208
    *rpi1.local      .GPS.            1 u   21   64  377    0.708   -2.365   0.296

    So I will let it run for a while. Hopefully I don't get woken up at the wrong hour in the morning by my computer's alarm 😁, like the programmer who was pranked by friends who changed his computer's clock's timezone or something like that. Actually NTP will throw out outliers so if the GNSS time is wrong it will be disregarded.

  • Fine tuning the clock offset

    Ken Yap07/20/2020 at 01:44 0 comments

    As explained in the time server document, you can get the NTP server to keep statistics on the offset and later compute an adjustment to the fudge line in ntp.conf. The suggested computation had to be modified, probably because I'm running ntpsec instead of ntp. This was what I ended up using:

    awk '/SHM\(0\)/ { sum += $5 * 1000; cnt++; } END { print sum / cnt; }' < /var/log/ntpsec/peerstats

    The original used a search filter of but it seems that ntpsec records GPS readings as SHM(0). Also the output is in milliseconds so has to be translated to seconds for adjusting the time1 value on the fudge line. This is what I have currently.

    fudge time1 0.118 refid GPS

    a difference of 2 milliseconds from the previous value. Here's a typical output of ntpq -p:

         remote           refid      st t when poll reach   delay   offset   jitter
     0.debian.pool.n .POOL.          16 p    -  256    0   0.0000   0.0000   0.0019
     1.debian.pool.n .POOL.          16 p    -  256    0   0.0000   0.0000   0.0019
     2.debian.pool.n .POOL.          16 p    -  256    0   0.0000   0.0000   0.0019
     3.debian.pool.n .POOL.          16 p    -  256    0   0.0000   0.0000   0.0019    .INIT.          16 u    - 1024    0   0.0000   0.0000   0.0019
    *SHM(0)          .GPS.            0 l   49   64  377   0.0000  -1.1543   0.6951
    +time.cloudflare      3 u   42   64  377  15.2933   4.4961   1.1248
    +time.cloudflare      3 u   50   64  377  15.2943   5.6666   0.5611
    -220-158-215-20.    2 u   17  128  377  39.1423  -6.5729   0.8229
    -cosima.470n.act    2 u   15  128  377  18.9447   4.4120   1.0238

    I may fine tune it further after running it for longer, but at this point we are in millisecond territory which is far more accuracy than I need for home purposes. There's no point tweaking it much more given the jitter in the absence of an accurate PPS hardware signal which is coupled into the kernel.

    Next task is to register the RPi as a time server for my workhorse PC.

  • Connecting gpsd to ntpd

    Ken Yap07/17/2020 at 15:54 0 comments

    For this part of the project, I followed the instructions on the page GPSD Time Service HOWTO at the gpsd website in the links.

    You may be wondering how a serial interface with significant delay could convey a very accurate time stamp. In fact it doesn't. Even if you measure from the leading edge of the first character, there is enough jitter in the chain to swamp the accuracy. The solution is a pin on the GNSS module called 1PPS (1 Pulse Per Second) or just PPS. Actually it can be configured to other frequencies, but usually 1 Hz is used. This hardware line is connected directly to the hardware and the CPU takes the edge of this signal as the time conveyed by the serial interface an instant later.

    For this to work, the pulse output from the GNSS module must be connected to the CPU. Unfortunately in the module I have, only two power lines and two serial data lines are used. On custom built GNSS clock rigs, the PPS pin could be connected to a GPIO line.

    So I have to live without PPS.

    Next is how to send the time information from gpsd to ntpd. This is done by gpsd creating a Unix shared memory segment (shm) which ntpd reads. There is a set of magic IP addresses which are used to specify gpsd to ntpd as a "local time server".

    # GPS Serial data reference (NTP0)
    fudge time1 0.12 refid GPS

    The IP address maps to the ID of the shared memory segment.

    A few things. gpsd should be started with the -n argument so that it starts taking a fix even if no client is connected to it. The time1 parameter in the snippet above specifies the total delay in the data feed via the serial interface and chips. It's specified in seconds and can be estimated by watching the output of ntpq -p after the system has settled down. You can see that there is about 120 ms of delay there. Finally, following the recommendation, I replaced the ntp package with ntpsec which is a compatible, more recent, and more secure derivative of ntp. It was just a matter of doing an apt install.

    Here is the output of ntpq -p. The GNSS module has a refid of .GPS. Offset is in ms so not as bad as it looks. I might tune the GNSS delay by and by.

         remote           refid      st t when poll reach   delay   offset   jitter
     0.debian.pool.n .POOL.          16 p    -  256    0   0.0000   0.0000   0.0019
     1.debian.pool.n .POOL.          16 p    -  256    0   0.0000   0.0000   0.0019
     2.debian.pool.n .POOL.          16 p    -  256    0   0.0000   0.0000   0.0019
     3.debian.pool.n .POOL.          16 p    -   64    0   0.0000   0.0000   0.0019    .INIT.          16 u    - 1024    0   0.0000   0.0000   0.0019
    *SHM(0)          .GPS.            0 l   32   64  377   0.0000  -6.3869   5.0497
    -pauseq4vntp1.da    2 u   70  128  377  17.3605   5.0752   0.5022
    +time.cloudflare      3 u   33   64  377  16.8134   5.0694   0.5466    2 u    7   64  377  17.1365   5.6812   0.3424
    -dns01.ntl02.pri    2 u   15  128  377  21.5701   4.7036   0.4717

    So now I have an ntp server that uses GNSS as a source of time, in addition to servers from the Debian ntp pool.

  • Obtained a location fix

    Ken Yap07/15/2020 at 17:28 0 comments

    I shut down the RPi, moved the kit to a window sill and powered it up again. This time running cgps elicited a bunch of messages showing it was getting fixes from the satellites. The readings swung wildly until they stabilised. I've blanked the lat/long in this screenshot.

    So it works. Next I have to make gpsd a source of time for ntpd.

  • Connected up U-blox NEO 6M module

    Ken Yap07/15/2020 at 12:47 0 comments

    The module arrived today. It comprises a small breakout board for the module and a ceramic antenna.

    I soldered a 4 pin header to the board, plugged it into the RPi breakout board and made the power and serial data connections following the instructable in the list of links.

    I had to figure out how to start the gpsd service. At first I thought I had to enable the gpsd service with systemctl. Running the ncurses command line client cgps showed no connection to the module.

    When I tried to run gpsd at the command line it said that the TCP and UDP sockets were already used. A quick look with lsof (which I installed on the fly) showed that systemd was holding them open. Huh?

    A search of the Internet showed that this was indeed supposed to be the case, it's systemd listens on the Unix socket and network sockets and if a connection is made, it starts up gpsd.

    So reenabling and starting the service running cgps eventually elicited a response from the module. There was no location fix, it stayed at mode:1 all the time.


    I will have to move it to a location nearer a window.

  • Tuning while waiting

    Ken Yap06/10/2020 at 23:33 0 comments

    It turns out that I had to do a couple of tweaks to the RPi configuration. The first is to edit /etc/dhcp/dhclient not to request a NTP server in the DHCP request because then ntpd ignores /etc/ntp.conf. In fact later I should switch to a static IP address so that the RPi not reliant on the DHCP server on the LAN being up.

    I also discovered that I had to set the NTP config in DietPi to custom, otherwise every morning it would kill the ntp daemon

    It also helped me discover that my LAN's NTP server wasn't configured to accept NTP requests due to a change in the shipped default configuration. I had to edit out the noquery parameter on the IPv4 and IPv6 restrict lines.

  • Installing DietPi

    Ken Yap06/08/2020 at 10:38 0 comments

    Pretty much by the instructions on the site. I downloaded the distribution, extracted and wrote the image on the 4GB SD card, inserted that into the RPi and powered up. I already had an entry in my DHCP server for the Ethernet interface so it appeared at a known IP address. I ssh'ed in and let it update itself to the latest release, then configured it. Similar to Rasberry Pi OS, it's configured using a ncurses/dialog interface.

    So, nothing to see here, let's move on. Only additional things I did were to install the wiringpi, ntpd, gpsd, nfs-client and autofs packages. The last two are to mount an NFS directory from my workhorse so that I can backup configuration information.

  • Wiring jungle

    Ken Yap06/08/2020 at 10:36 0 comments

    For powering the RPi, I used a wall wart putting out 12V feeding a buck converter to 5V. According to a USB meter in the power lead, it draws 0.41A. So just over 2W consumption.

View all 10 project logs

Enjoy this project?



Similar Projects

Does this project spark your interest?

Become a member to follow this project and never miss any updates