FreeBSD 14 USB - BYO-NTP/recipes GitHub Wiki
This page is a build recipe for Stratum 1 NTP servers on any USB-equipped server with FreeBSD 14, using one of the 4 GNSS listed in the table and any of the NTP daemons listed. For modern computers without a UART (serial port) or GPIO, USB + PPS is capable of precision within single digit microseconds (~/- .000009 seconds). For a Stratum 0 NTP clock, that is excessively adequate.
date | server | os | gnss | daemon | 🗣️ |
---|---|---|---|---|---|
2025-07 | Chuwi Larkbox X Raspberry Pi 4 Raspberry Pi 5 Xeon E5 |
FreeBSD 14 |
M8T M8Q 7M U7 6M |
chrony NTPsec ntp |
link |
FreeBSD install instructions for Pi 4, Pi 5, or Chuwi Larkbox. For everything else, just follow the FreeBSD handbook.
Follow the assembly instructions for your GNSS on the Assemble USB TTL page.
If you don't use the mouse on your server, avoid potential interference from the USB driver detecting the GNSS serial adapter as a mouse:
sysrc devmatch_blocklist+="ums"
Plug the USB adapter into the FreeBSD server and verify it is present:
usbconfig | egrep -i 'uart|serial|blox|gps'
# FTDI USB to TTL adapter
ugen1.2: <FT232 Serial (UART) IC Future Technology Devices International, Ltd> at usbus1, cfg=0 md=HOST spd=FULL (12Mbps) pwr=ON (90mA)
# VK-162
ugen1.6: <u-blox AG - www.u-blox.com u-blox 7 - GPS/GNSS Receiver> at usbus1
umodem0: <u-blox AG - www.u-blox.com u-blox 7 - GPS/GNSS Receiver, class 2/0, rev 1.10/1.00, addr 5> on usbus1
FreeBSD has recognized the USB device, the uftdi or umodem driver has configured it, and ucom will have presented it as a tty.
Find the new TTY:
ls /dev/cuaU?
/dev/cuaU0
There it is at cuaU0
. Export it so all the subsequent commands can find it:
export GNSS_TTY_DEVICE=cuaU0
Configure the TTY and read some data from it (Control-C to disconnect):
stty -f /dev/$GNSS_TTY_DEVICE.init cs8 clocal -cstopb -parenb -echo raw 115200
cat /dev/$GNSS_TTY_DEVICE
$GPRMC,145934.00,A,4746.15234,N,12219.54351,W,0.056,,050625,,*09
$GPZDA,145934.00,05,06,2025,00,00*6E
$GPRMC,145935.00,A,4746.15237,N,12219.54346,W,0.056,,050625,,*0D
$GPZDA,145935.00,05,06,2025,00,00*6F
$GPRMC,145936.00,A,4746.15241,N,12219.54341,W,0.058,,050625,,*06
$GPZDA,145936.00,05,06,2025,00,00*6C
^C
It works! Make the serial port change permanent:
test -d /usr/local/etc/devd || mkdir -p /usr/local/etc/devd
cat > /usr/local/etc/devd/gps_tty_init.conf <<EO_GPS_TTY
notify 0 {
match "cdev" "cuaU[0-1]";
action "stty -f /dev/$cdev.init cs8 clocal -cstopb -parenb -echo raw 115200";
};
EO_GPS_TTY
service devd restart
The NTP daemons look to /dev/gps0
by default, so link gps0
to the USB serial port using devfs:
test -L /dev/gps0 && rm /dev/gps0
grep -q gps0 /etc/devfs.conf && sed -i '' -e '/^link.*gps0$/d' /etc/devfs.conf
echo "link $GNSS_TTY_DEVICE gps0" >> /etc/devfs.conf
service devfs restart
Note: VK-162 devices get to skip this step.
Enable PPS on the UART CTS pin to match the wiring. See ucom for details.
sysctl hw.usb.ucom.pps_mode=1
grep -q pps_mode /boot/loader.conf || echo 'hw.usb.ucom.pps_mode=1' >> /boot/loader.conf
Create /dev/pps0
for the NTP daemons using devfs links:
test -L /dev/pps0 && rm /dev/pps0
grep -q pps0 /etc/devfs.conf && sed -i '' -e '/^link.*pps0$/d' /etc/devfs.conf
cat >> /etc/devfs.conf <<EOL
link $GNSS_TTY_DEVICE pps0
link $GNSS_TTY_DEVICE gpspps0
EOL
service devfs restart
The latency of each GNSS requires a custom offset (in milliseconds), so export the GNSS specific values from the table (replace the ?
s):
gnss | NMEA | PPS |
---|---|---|
M8T | 0.14 | 0.1 |
M8Q | 0.09 | 0.1 |
7M | 0.11 | 0.1 |
U7 | 0.05 | |
6M | 0.7 | 0.1 |
export GNSS_OFFSET_NMEA="0.??"
export GNSS_OFFSET_PPS="0.??"
If needed, each section heading is a link with many more details about installing, configuring, and verifying that particular NTP daemon. Otherwise, choose the commands for the NTP daemon you want and the installer will do the rest.
export NTP_REFCLOCKS=$(cat <<EO_CHRONY
refclock SHM 0 refid NMEA precision 3e-2 poll 4 offset $GNSS_OFFSET_NMEA prefer
refclock PPS /dev/pps0 refid PPS precision 5e-6 poll 2 offset $GNSS_OFFSET_PPS minsamples 32 lock NMEA trust
EO_CHRONY
)
curl -sS https://byo-ntp.github.io/tools/chrony/install.sh | sh
Note: minsamples 32
and a quick poll time (1 or 2) smoothes out much of the USB-induced jitter.
Note: NTPsec's NMEA+PPS has a pathological case when the GPS fix is lost and PPS drops. The accuracy then is worse than using only the NMEA source. Increasing the poll frequency to minpoll 3 maxpoll 5
improves it but the gpsd driver handles that case much better. In every other case, reducing the polling time improves accuracy.
NTPsec nmea
export NTP_REFCLOCKS=$(cat <<EO_NTPSEC_NMEA
refclock nmea refid NMEA minpoll 4 maxpoll 5 time2 $GNSS_OFFSET_NMEA prefer mode 8 baud 115200
refclock pps refid PPS minpoll 1 maxpoll 2 time1 $GNSS_OFFSET_PPS prefer
EO_NTPSEC_NMEA
)
curl -sS https://byo-ntp.github.io/tools/ntpsec/install.sh | sh
Alternatively, via NTPsec's GPSD JSON driver:
export NTP_REFCLOCKS=$(cat <<EO_NTPSEC_GPSD
refclock gpsd unit 0 refid NMEA minpoll 3 maxpoll 5 time2 $GNSS_OFFSET_NMEA prefer
refclock gpsd unit 128 refid PPS minpoll 1 maxpoll 2 time1 0.0 flag1 1 prefer
EO_NTPSEC_GPSD
)
curl -sS https://byo-ntp.github.io/tools/ntpsec/install.sh | sh
export NTP_REFCLOCKS=$(cat <<EO_NTP
server 127.127.20.0 minpoll 4 maxpoll 5 mode 88 prefer
fudge 127.127.20.0 refid NMEA time2 $GNSS_OFFSET_NMEA minjitter 0.03
server 127.127.22.0 minpoll 3 maxpoll 4
fudge 127.127.22.0 refid PPS time1 $GNSS_OFFSET_PPS minjitter 0.005
EO_NTP
)
curl -sS https://byo-ntp.github.io/tools/ntp/install.sh | sh
Alternatively, via gpsd using ntp's GPSD-NG driver:
export NTP_REFCLOCKS=$(cat <<EO_NTP_NG
server 127.127.46.0 minpoll 3 maxpoll 4 mode 0 prefer
fudge 127.127.46.0 refid NMEA time2 $GNSS_OFFSET_NMEA minjitter 0.05
server 127.127.46.128 minpoll 3 maxpoll 4 prefer
fudge 127.127.46.128 refid PPS flag1 1 time1 0.0
EO_NTP_NG
)
curl -sS https://byo-ntp.github.io/tools/ntp/install.sh | sh
- watch the status in near real time (requires installing gnu-watch):
- printf '\e[8;9;80t'; ssh -t pi5 gnu-watch -n2 chronyc sources
- printf '\e[8;9;80t'; ssh -t pi5 gnu-watch -n2 ntpq -c peer
- Measure the offset
- Gather statistics with telegraf + influxdb + grafana or similar.
- BYO-NTP, GNSS, NTP daemons, USB
- FreeBSD umodem
- ntpq, ntp, ntpsec
- Austins Nerdy Things - Revisiting Microsecond Accurate NTP
- Sven Reifschneider, High Precision Time with GPS on Pi
gnss | chrony | NTPsec | ntp |
---|---|---|---|
M8T | 3.3 µs |
88 µs |
70 µs |
M8Q | 1.9 µs |
62 µs |
61 µs |
7M | ? µs |
? µs |
143 µs |
VK-172 | 2600 µs |
5800 µs |
6800 µs |
6M | 4.5 µs |
25 µs |
22 µs |