XinCTO/Raspberry NTP stratum 1

Écrit Sun, 17 May 2020 17:10:02 +0000

Raspberry NTP stratum 1

Mes machines se synchronisaient sur des serveurs NTP d’internet, avec des écarts de quelques millisecondes. J’ai voulu mon propre serveur de temps. Voici comment le construire avec un Raspberry Pi et un GPS.

L’objet de ce billet est de vous donner les clés de configuration d’un raspberry pi afin de le transformer en serveur NTP (RFC 5905) fiable de stratum 1, c’est-à-dire en haut de la hiérarchie du NTP. Ce dispositif n’a pas la précision d’un serveur connecté à une source atomique présentant une fréquence d’oscillation très fiable, mais comme la plupart des serveurs stratum 1 il utilise une source GPS déjà de bonne qualité.

Principe de base

Le système de géolocalisation GPS (idem pour Galileo) se base sur une triangulation des signaux de plusieurs satellites afin de déterminer finement la distance du récepteur à chacun de ces satellites. On devrait parler normalement de trilatération (le GPS mesure des distances, pas des angles). Les signaux reçus des satellites comportent l’heure absolue et leur position respective est connue sur la base d’éphémérides. Pour déterminer finement la position du récepteur on doit donc utiliser l’heure présentée par chaque satellite dont le signal arrive avec un léger décalage en fonction de leur distance et utiliser l’éphéméride pour positionner le satellite à l’heure estimée. Plus l’heure estimée est fiabilisée, plus la position de chaque satellite est précise et la position du récepteur précise également.

Ce qui nous intéresse plus particulièrement dans ce principe de positionnement est l’heure que le récepteur va déterminer pour calculer sa position. La position en l’occurrence n’est pas importante pour notre serveur NTP.

En complément de l’heure calculée par le récepteur GPS ce qui est très intéressant ici est d’utiliser la source fournie par le composant du récepteur et permettant de fournir un oscillateur fiable (PPS). C’est à partir de celui-ci que nous obtiendrons la fiabilité de notre horloge et pas depuis l’heure du GPS. Les ordres de grandeur en termes de fiabilité sont de l’ordre de 100 ms d’écart pour l’heure du GPS contre 200 ns pour le PPS.

Architecture

Pour reproduire cette installation il vous faudra un Raspberry Pi et une carte GPS avec une sortie PPS, j’utilise un chip de type Neo 6M de chez uBlox, on en trouve aux alentours de 10€ à souder ou autour de 30€ sur un pi-hat.

Le GPS pousse ses informations brutes sur le port série /dev/ttyAMA0 qu’il faut donc dédier à cet usage (on supprimera la console standard).

Le signal PPS est repositionné sur un GPIO, j’ai choisi le PWM0 qui est géré par le matériel directement, positionné sur la broche 12 et généralement nommé BCM18 (cf. https://pinout.xyz/). On fera analyser et utiliser le signal PPS par le noyau du Raspbian afin d’en faire une discipline et la proposer dans le user space pour les logiciels qui en auraient besoin, ici ce sera gpsd.

Le logiciel gpsd interprète les signaux en provenance du port série et les normalisent dans un segment de mémoire partagée. Idem pour le PPS qui est également partagé dans un segment de mémoire.

Le serveur NTP, chrony, utilisera alors les 2 segments de mémoire afin de récupérer l’heure du GPS et la discipline PPS et s’en servir pour proposer le service NTP au reste des utilisateurs du réseau IP.

Installation

A partir d’une installation neuve sur base de raspbian on installe quelques packages complémentaires. Je suis parti sur une base de version 10.3 pour ce blog.

apt-get -y update
apt-get -y upgrade
apt-get install -y gpsd gpsd-clients python-gps pps-tools chrony

Cet ajout consiste principalement en :

  • la gestion du GPS permettant de récupérer l’heure et la source PPS
  • la librairie et les outils pour le PPS
  • le serveur de temps chrony

1- le PPS

Le module pps-gpio est chargé dans le noyau, on le configure pour un chargement automatique dans le fichier /etc/modules :

pps-gpio

Puis on le raccroche au GPIO sur lequel nous avons accroché le PPS de la carte GPS, dans /boot/config.txt:

dtoverlay=pps-gpio,gpiopin=18

Après le prochain boot on peut vérifier la présence de la source PPS avec la commande ppstest qui va valider que l’on dispose bien d’un PPS, c’est cette source qui sera consommée par gpsd.

> 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 1589711839.999999633, sequence: 6672 - clear  0.000000000, sequence: 0
source 0 - assert 1589711840.999998902, sequence: 6673 - clear  0.000000000, sequence: 0

2- GPSD

Le daemon gpsd interprète les informations transmises en texte sur le port série et utilise le PPS pour fiabiliser la dérive. On lui spécifie le port série sur lequel il va trouver ses informations de positionnement et d’heure dans son fichier de configuration /etc/default/gpsd

DEVICES="/dev/ttyAMA0"
GPSD_OPTIONS="-n"

Une fois le service gpsd démarré, on peut vérifier les informations collectées avec la commande cgps qui affichera entre autres les informations de position, d’heure et les satellites présents et sélectionnés.

> cgps -s
│    Time:       2020-05-17T12:00:30.000Z   ││PRN:   Elev:  Azim:  SNR:  Used: │
│    Latitude:    48.XX       N             ││   5    13    052    30      Y   │
│    Longitude:    2.YY       E             ││  16    17    292    29      Y   │
│    Altitude:   35.054 m                   ││  21    43    111    26      Y   │
│    Speed:      0.36 kph                   ││  25    28    112    27      Y   │
│    Heading:    58.7 deg (true)            ││  26    50    294    35      Y   │
│    Climb:      14.82 m/min                ││  29    59    056    30      Y   │
│    Status:     3D DIFF FIX (41 secs)      ││  31    57    220    46      Y   │
│    Longitude Err:   +/- 2 m               ││ 136    34    136    28      Y   │
│    Latitude Err:    +/- 3 m               ││   2    02    033    22      N   │
│    Altitude Err:    +/- 10 m              ││   4    10    318    33      N   │
│    Course Err:      n/a                   ││  18    45    159    47      N   │
│    Speed Err:       +/- 0 kph             ││ 123    27    143    32      N   │
│    Time offset:     0.137                 ││ 125    31    203    46      N   │
│    Grid Square:     JNXXX                 ││                                 │

Ici, 8 satellites utilisés pour la triangulation, la zone du récepteur est grossièrement donnée en coordonnées Grid (cf https://www.qrz.com/gridmapper).

Une vérification sur le PPS de l’écart avec ppswatch, vous pourrez alors avoir quelque chose comme:

> ppswatch -a /dev/pps0
timestamp: 1589717422, sequence: 12242, offset:  -1464
timestamp: 1589717423, sequence: 12243, offset:     61
timestamp: 1589717424, sequence: 12244, offset:  -2060
timestamp: 1589717425, sequence: 12245, offset:   -692
^C
 
Total number of PPS signals: 84
Maximum divergence: 3027
Mean value: -1052.42
Standard deviation: 907.399

3- chrony

Le serveur NTP utilisera les informations en provenance des 2 segments de mémoire partagée afin de récupérer les informations mises à disposition par gpsd concernant le GPS et le PPS. On configure ces 2 sources dans /etc/chrony/chrony.conf :

refclock SHM 0 offset 0.5 delay 0.2 refid GPS noselect
refclock SHM 1 refid PPS precision 1e-9

Une fois démarré, chrony va utiliser l’ensemble des sources dont il dispose, vous pouvez utiliser quelques sources de serveurs NTP sur internet afin de comparer les valeurs. Quelques outils à regarder avant de mettre en mode serveur pour les autres utilisateurs du réseau.

Les sources :

> chronyc sources
MS Name/IP address         Stratum Poll Reach LastRx Last sample
===============================================================================
#? GPS                           0   4   377    18   -367ms[ -367ms] +/-  100ms
#* PPS                           0   4   377    19     +6ns[ -102ns] +/-  208ns
^- 2001:bc8:255e:100::1          2  10   377   450  -2302us[-2264us] +/-   54ms
^- 2001:41d0:a:268f::53          2  10   377   510  +1446us[+1493us] +/- 8450us
^- server2.websters-compute>     2  10   377   182   -667us[ -665us] +/-   45ms
^- yotsuba.fkraiem.org           2  10   377   694  -1314us[-1265us] +/-   48ms
^- ntp-1.arkena.net              2  10   377   418  +2954us[+2987us] +/-   31ms
^- wahe.diwi.org                 2  10   377   498  +1356us[+1401us] +/-   29ms
^- www.almaprovence.fr           3   8   377   645    +11ms[  +11ms] +/-   62ms
^- ns3003772.ip-37-59-47.eu      2  10   377   202   -425us[ -422us] +/- 5508us
^- ts2.aco.net                   1  10   377   518  -1931us[-1884us] +/-   20ms
^- ts2.aco.net                   1  10   377   212  -3486us[-3483us] +/-   15ms

Ici des sources de différents stratum, on évitera dans la vraie vie d’aller directement chercher des sources de stratum 1, le protocole est assez fiable pour se limiter à des sources plus nombreuses même si un peu moins fiables. On retrouve GPS comme notre source de temps et PPS comme notre source d’horloge, c’est celle-ci qui est préférée, directement attachée et d’une fiabilité bien meilleure, sous la microseconde.

> chronyc sourcestats
Name/IP Address            NP  NR  Span  Frequency  Freq Skew  Offset  Std Dev
==============================================================================
GPS                        16   7   240     +2.448      9.234   -367ms   765us
PPS                         6   3    80     -0.001      0.024     -6ns   190ns
2001:bc8:255e:100::1       34  15  143m     +0.334      0.116  -1544us   404us
2001:41d0:a:268f::53       21  12  127m     +0.450      0.183  +1218us   406us
server2.websters-compute>  33  15  147m     +0.337      0.151   +347us   527us
yotsuba.fkraiem.org        15  11  116m     +0.395      0.178   -157us   313us
ntp-1.arkena.net           17   6  118m     +0.284      0.328  +4144us   610us
wahe.diwi.org              33  16  142m     +0.437      0.214  +1083us   619us
www.almaprovence.fr         8   7   38m     +0.093      0.562    +11ms   108us
ns3003772.ip-37-59-47.eu    6   3   77m     +0.255      0.464   -231us   248us
ts2.aco.net                34  19  142m     +0.525      0.375  +1284us  1091us
ts2.aco.net                34  15  147m     +0.485      0.127  -2708us   483us
> chronyc tracking
Reference ID    : 50505300 (PPS)
Stratum         : 1
Ref time (UTC)  : Sun May 17 12:17:52 2020
System time     : 0.000000267 seconds slow of NTP time
Last offset     : -0.000000383 seconds
RMS offset      : 0.000000437 seconds
Frequency       : 1.942 ppm fast
Residual freq   : -0.003 ppm
Skew            : 0.026 ppm
Root delay      : 0.000000001 seconds
Root dispersion : 0.000014458 seconds
Update interval : 16.0 seconds
Leap status     : Normal

Sur 2 clients du réseau on peut constater quelques écarts et la précision relative du raspberry

un client ntpd :

> ntpq -p
     remote           refid      st t when poll reach   delay   offset  jitter 
============================================================================== 
*raspberrypi.hom .PPS.            1 u   30  512  377    1.083    0.350   0.567 
+ts2.aco.net     .GPS.            1 u  510  512  377   34.653   -0.004   0.231 
-time.cloudflare 10.19.9.101      3 u  515  512  377    2.286   -2.071   0.178 
+nio.nucli.net   131.188.3.222    2 u   14  512  377    2.057    0.147   0.307 
+2a01:e0a:334:10 .GPS.            1 u  524  512  377   12.961    0.319   0.460 
-www.almaprovenc 37.187.174.185   3 u   23  512  377    1.236  -10.238   0.663 
+serveur-secours 212.83.158.83    3 u  514  512  377    2.138    0.308   0.368 

un client chrony :

> chronyc tracking
Reference ID    : C0A810F1 (raspberrypi.home)
Stratum         : 2
Ref time (UTC)  : Sun May 17 12:01:41 2020
System time     : 0.000027616 seconds slow of NTP time
Last offset     : -0.000105767 seconds
RMS offset      : 0.003665681 seconds
Frequency       : 719.454 ppm fast
Residual freq   : -0.002 ppm
Skew            : 0.078 ppm
Root delay      : 0.001195393 seconds
Root dispersion : 0.001225245 seconds
Update interval : 1024.9 seconds
Leap status     : Normal

et

> chronyc sources
210 Number of sources = 8
MS Name/IP address         Stratum Poll Reach LastRx Last sample               
===============================================================================
^x clock.stdtime.gov.tw          4   6   377    37    +31ms[  +31ms] +/-   24ms
=* raspberrypi.home              1  10   377  1133    -69us[ -175us] +/-  628us
^- 2001:41d0:b:816::1            2  10   377   924  +1622us[+1622us] +/-   33ms
^- 2001:41d0:1:90a7::123         2   8   377    24   +340us[ +340us] +/- 6969us
^- ntp2.ds.network               2  10   377   811   -303us[ -303us] +/-   14ms
^- time-b.as43289.net            2   7   377    93   -154ms[ -154ms] +/-  334ms
^- ts2.aco.net                   1  10   377   814    +74us[  +74us] +/-   18ms
^- ts2.aco.net                   1  10   377   749  -3545us[-3545us] +/-   15ms

Conclusion

Avec peu d’investissement on peut disposer d’un serveur NTP disposant de sa propre horloge d’un niveau de précision et fiabilité assez élevé. Un bon moyen de rester autonome, voire de proposer ce serveur à la communauté via le pool NTP. Seul réel inconvénient, disposer d’un endroit pour le positionner avec son antenne en visibilité directe des satellites, ce qui est de loin le plus complexe dans cette opération.


Photo de Fabrizio Verrecchia