A friend from NIST recently told me about a Raspberry Pi Stratum-1 NTP server project, and that reminded me of the experiments I did with the Navspark dual GPS+Beidou receiver module. Navspark is a small, Arduino-compatible module that besides GPS can also receive data from China’s Beidou 北斗 satellite navigation system , that is currently being built. I thought it would be fun to build a Beidou-powered Stratum-1 NTP server to see how does it compare to GPS.
To have a really good really good, satellite-powered reference clock, I have to have access to a 1-pulse-per-second (1PPS) signal from the receiver. The pure USB-connected receivers don’t really seem to do that yet (looks like plenty of opportunities there!), instead I have to use separate hardware for it.
The Navspark module has a 1PPS pin (GPO3 below), and the only other pin I’ll really need is a serial pin to receive the NMEA stream of the satellite lock data (TXD1 below).
These pins have to be connected to a Raspberry Pi 2’s GPIO pins.
Based on the pinout above, and some digging around, the connections I needed to make between the Pi (left) and Navspark (right) were:
- 5V ↔ BATTERY
- Ground ↔ GND
- GPIO15 (UART0_RX) ↔ TXD1
- GPIO8 ↔ P1PPS (GPO3)
Some things to remember here, that on serial connections one side’s RX line is connected to the other’s TX and vice versa. Also, as much as I gather any other GPIO pins on the Pi could be used to receive the 1PPS signal, I used GPIO8 because it’s been used by other projects and playing it safe…
The adapter board was easy enough to put together (though single-side pads are a pain when the components are on both sides, as it is the case here).
The adapter works well enough, though I’m sure that it could be done nicer.
I found a lot of examples and help from other people’s similar projects but there were still some differences to be figured along the way. The overall setup was to be as follows:
- ArchLinux ARM for base platform
- gpsd for interpreting the Navspark NMEA messages
- LinuxPPS for 1PPS signal passing from GPIO to the userspace
- chrony as time server (as opposed to the usual ntpd)
Installing the system was straightforward enough (well, because I’ve done it so many times, but I digress), and added a few required basic packages: gpsd, chrony, git, the base-devel group, avahi (for easier network discovery).
UART0 serial communication is done through the /dev/ttyAMA0 device in Pi, though the default settings needed some change for gpsd to be able to receive the data from Navspark.
Most of the changes our outline in a blogpost where I have learned from as well. First remove all config settings from /boot/cmdline.txt, basically these parts:
Next disable the automatic serial console on that device with removing relevant systemd unit with
sudo systemctl disable serial-getty@ttyAMA0.service
Then have to set up the serial channel’s baud rate to enable communication, and here I’ve done that with udev: create a new rule file at /etc/udev/rules.d/AMA0_serial.rules with the content of
KERNEL=="ttyAMA0", RUN+="/bin/stty -F /dev/ttyAMA0 115200 raw -echo"
which will set the 115200 baud rate used by Navspark when the ttyAMA0 device comes up.
Finally have to add gpsd’s default settings in the /etc/gpsd file:
# Default settings for gpsd.
Now it’s ready, and should be able to run gpsd and see some communication.
sudo systemctl start gpsd
and if want to start it on boot, then run the same command with “enable” instead “start”. Use gpsmon to see what NMEA data Navspark sends, and hopefully it will lock on soon too (mind the sky visibility!). Can also use cgps to monitor which satellites the system is locked onto.
For 1PPS I had to add an item to /boot/config.txt:
This adds the relevant device tree overlay to enable the pps-gpio driver on GPIO8. Upon boot there was a /dev/pps0 device that can be poked at e.g. with ppstest from pps-tools. Since pps-tools did not have a package on ArchLinux, I made one, and the PKGBUILD is available on Github. That package also adds the timepps.h header file which is required for the chrony PPS driver to work.
The pps signal can be checked by running ppstest:
$ sudo ppstest /dev/pps0
trying PPS source "/dev/pps0"
found PPS source "/dev/pps0"
ok, found 1 source(s), now start fetching data...
source 0 - assert 1448436177.999366423, sequence: 199 - clear 0.000000000, sequence: 0
source 0 - assert 1448436178.999373610, sequence: 200 - clear 0.000000000, sequence: 0
but it needs a 3D fix for the signal to show up.
Next up is the time server itself. The satellite time will be pulled in using two “reference clock”: the GPS timestamps provided by gpsd through a memory interface, and the PPS data that locks those timestamps. Unfortunately the PPS driver is not built in by default because it needs the timepps.h header. This means after installing pps-tools chrony needs to be recompiled.
Update the chrony configuration by editing the /etc/chrony.conf file. I usually leave the defaults alone, and add my modifications to the end of the file for clarity. Here’s my current setup:
log measurements statistics tracking refclocks
# being a server
server 2.tw.pool.ntp.org iburst
server 3.asia.pool.ntp.org iburst
server 2.asia.pool.ntp.org iburst
server 1.europe.pool.ntp.org iburst
# on-boot time setup, from https://news.ycombinator.com/item?id=9796885
makestep 0.1 1
# from https://www.raspberrypi.org/forums/viewtopic.php?f=41&t=1970&start=275
refclock PPS /dev/pps0 lock GPS prefer refid PPS0
# If Beidou, there is a day offset somehow
refclock SHM 0 offset -86400 delay 0.1 refid GPS noselect
It uses NTP pool servers for initial time setup and a quick on-boot step (as we don’t want to be stuck at 1970, do we?). The time service related settings are the “refclock” parts. The first line sets the 1PPS lock-on and I tell chrony to prefer that over other sources so we’ll switch over to that when there’s good-enough data. The second refclock section uses gpsd’s NMEA data. The large offset there is due to some issues where the reference time shows up with 24h offset. That I still need to investigate. The delay time set needs some fine-tuning too (I think), though from comments 0.1-0.3 (that is seconds) seems to be suitable? I also tell chrony not to use that for reference.
I also set leap seconds timezones (with the “right/…” TAI time zone, see e.g. the NTP support page and a discussion on the Debian mailing list). And I enable the server on the local network so other can connect to it and get the time.
I’ve enabled logging of the tracking data (so there’s offset and other information available) and refclocks (tailing /var/log/chrony/refclocks.log continuously is a handy way to see when the lock is lost).
Start chrony (or enable if you want to start it on boot)
sudo systemctl start chrony
and the results can be monitored with chronyc.
All together now
If everything worked well, gpsmon should show a good locked signal after not too much time, something like this:
From the interface above I can also see that Navspark has a 3D lock (Mode A3) to satellites number 209, 207, and 206, which are Beidou birds. And that the time offset is 86399.96 seconds (~24h), that is what the chrony configuration compensates for.
Checking the results in chrony I the system time it’s locked to PPS0 where the last sample had 410ns offset (pretty good). Also printed the sourcestats.
And this is pretty much all that is needed in general to have a Stratum-1 (atomic clock-locked) NTP server in general.
Some extra references:
- NavSpark GPS+GLONASS Stratum-1 NTP Time Source with a Raspberry Pi
- Hackspherelab: NTP GPS Time Server
- GPSD Time Service HOWTO
Navspark has a GNSS Viewer program (available for download from their website) which can also set the module’s parameters, for example forcing it to only listen to the GPS signal or only Beidou signal. There are a lot of settings, and I think most don’t need any change, but there’s still a bit to explore, and it’s definitely a handy tool.
Running the NTP lock and looking at the tracking lock there are immediately a few lessons in the ~1 day I’ve been playing with the system:
Environment matters a lot: having wind blowing over the system and being exposed to temperature changes (e.g. outdoor setting) is quite bad for frequency/time stability. One could say duh! but it’s good to see. Even a curtain and placing it indoors helps too, though having it in a well air conditioned space is even better. Too bad that in that case the sky visibility and thus the signal strength suffers a lot, but as long as there’s enough signal to have a lock it is worth it.
PPS is lost when 3D fix is lost: this took a while to figure out why did I lose lock every now and then, especially because once chrony loses the PPS signal it won’t pick it up again.
Indoor can work: indoor lock with the default antenna can work, but the building structure matters a lot.
GPS vs Beidou
Of course the most interesting part is comparing GPS and Beidou. What follows here is not very well qualified yet, just first impressions and need more work.
One of the biggest advantage of GPS is having a lot more satellites than Beidou. I wouldn’t be surprised if they are transmitting at a higher power too, or some other ways easier to receive. When locked onto GPS I could have rock-solid indoors time lock with 4-9 satellites visible at any one time. The time precision is judged by the offset frequency distribution that chrony reports (and would love to hear if there’s a better way to do that!), and that had a standard deviation of about 1µs by experience (in an air-conditioned office).
When I’ve enabled both GPS and Beidou then Navspark seemed to have generally locked onto the Beidou signal. When i was lucky I got 5-6 satellites, but generally it’s around 3 – just on the threshold of having a 3D fix, and the source of the PPS-unlock mentioned above. The offset stability on the other hand can be really good, I had runs that had about 5x smaller time offset standard deviation than GPS had (~0.2µs), and that’s quite a bit of improvement!
The issue is, that when I set the Navspark module GPS+Beidou then when there was a low Beidou satellite count (2-3), even though it still kept a (fragile) 3D fix, the time offset stability was pretty much the same as GPS on its own, though with some intervals of improved readings….
This below is a ~35minutes run of GPS+Beidou setup in an air-conditioned office with limited sky-view and 3 more stories above, using the default 12dB antenna. Using cpgs I could see that in general the lock was provided by 2-3 Beidou satellites, and in some intervals switched over to GPS without losing PPS signal (until it did). Thus it’s not the best performance that I’ve seen but still pretty good. Offset is plotted with the reported standard deviation as error bars. The central line is the mean value of the offset readings, and the two bars are the standard deviation calculated from the offsets.
The ideal setup would be a well air-conditioned roof-top location (I think it would be possible to reliably see 6-7 satellites that way), and switching to Beidou only measurements to clearly see the difference between the two satellite network.
Improved reception could probably be achieved by a different Navspark antenna, which provides 25±3dB gain (that’s an extra ~13dB above the current antenna), and should help indoor reception a lot. It’s currently on pre-sale and expected by the end of the year, so will keep an eye on it!
For more advanced improvements, the adapter board would be easy enough to redo on PCB (instead of surface mount soldering), add maybe some more voltage stabilization for the module to improve the noise figure, and probably have cleaner 1PPS signal too (compared to flying wires). Will definitely give it a try as it is so easy to get the boards printed these days, e.g. with Seeed Studio.
Finally some more setting adjustments could improve the final result, such as tweaking the 1PPS pulse length, using the surveying profile for higher precision (though have to figure out what does it mean first, but looks promision), and other little tweaks….
It looks to me that if the satellite reception issues are cleaned up, the Navspark + Beidou 1PPS signal should be interesting to try with laboratory frequency references for long term stability (such as a Rubidium Clock). And of course to run the Pi+Navspark as a local time reference and provide a NTP service, e.g. from the Tapei Hackerspace…. Plenty to do. :)