LinuxPPS support

From EnneEnneWiki

Contents

If you like LinuxPPS make a donation with PayPal - it's fast, free and secure!

Note: this documentation has been updated to the LinuxPPS version 5.x.x-rc1

Old versions are currently NOT supported anymore due several incompatibilities even if an old documentation page is still available at LinuxPPS support (old API).

Introduction

If you wish using a PPS (Pulse Per Second) source on your GNU/Linux system (versions 2.6) you may try using my implementation. :)

This API uses char devices to exchange data to the userland and a well defined kernel API (by exported symbols) to register new PPS sources (client).

The benefits of this solution are:

  • by using a char device for each PPS source we can divide the PPS API from a particular driver or device: for example you don't need to modify the serial driver so heavy as in PPSkit-light-PPSAPI-alpha-1610m-2.6.5, just add some functions calls.
  • the PPS API can be the same for all PPS source: currently in PPSkit-light-PPSAPI-alpha-1610m-2.6.5 there are duplicated functions that do the same things: do_clock_gettime(), pps_update_event(), etc..
  • by using the exported kernel symbols we can manage the PPS source with a few and simple functions calls. See the example below.

Data and Performance Monitoring

Just to help you in choosing my solution or another one let me suggest to you this performance monitoring site by Steven Bjork where you can see several PPS implementation in action.

Also you may take a look at Steven Bjork's main site to learn more on the Network Time Protocol.

Getting help

Currently there is a mail list focused on LinuxPPS where you can find other people interested into the project.

The main documentation is reported here but on the mail list you can ask about you specific problems.

Downloading the code

The latest code is at my GITWeb and you can find some patches against some Linux versions here but you are warned that some of these patches may implement old LinuxPPS version! Refer to the README file in order to see which LinuxPPS version implements each patch.

You can also find also a very old and deprecated version of the software for kernel series 2.4 and 2.6 on my HTTP/FTP site but you are warned in using that code.

Cloning my repository

As already said the latest version of LinuxPPS code is on my GITWeb and I strongly suggest you in using such tool since it is simple to use. :)

First and simple way (but time consuming) is to clone my repository. This step is mandatory for further use of the GIT commands, the drawback is that you must download whole repository which is about 120MB...

Ok, once you have found enough space on your hard disk you should use the command:

giometti@zaigor:~$ git clone git://git.enneenne.com/linuxpps

after the clone is finished into directory linuxpps you can find the code.

At this point whenever new patches are added into my repository you may update your own repository just giving the command:

giometti@zaigor:~$ git pull git://git.enneenne.com/linuxpps

When finished your repository will be again a copy of mine.

If your internet connection is slow and you wish to get only changes between an already kernel tree, you may read this page Merging an existent kernel tree with a remote one.

Using a patch

If you don't know GIT and you wish using the old-style way of obtaining the code you can just download a patch.

Note that I provide a patch only for few LinuxPPS versions and only against specific kernel versions!

You can get the patches here, but before using them refer to the README file who explains which versions of LinuxPPS are available.

Once you have chosen a patch you can do the job with the command:

giometti@zaigor:~/linux-2.6.23-rc2$ patch -p1 < ntp-pps-2.6.23-rc2.diff

Compiling the code

The kernel code

Once you have downloaded the code form my GITweb site you may decide to use my kernel (which derives from vanilla one) or try to get a patch to apply at your preferred tree (please refere to the GIT tools in learn how to do this or look here for already available patches).

In the first case simply run the kernel compilation tool to configure your new PPS support into the "Device Drivers" section. So, for example, use the command make menuconfig and then open the "PPS support" menu. Setup your PPS configuration and don't forget to choose a PPS client! If you wish enable the serial line support I suggest you to set something as follow:

        <M> PPS support                                                       
        [ ]   PPS debugging messages                                          
        ---   PPS clients support                                             
        <M>   Kernel timer client (Testing client, use for debug)             
        [*]   UART serial support                                             
        [*]   Parallel printer support                                 

Note that "PPS support" code is currently marked as experimental so you will need to enable prompt for experimental drivers in the "Code maturity level options" section:

        [*] Prompt for development and/or incomplete code/drivers             
        [*]   Select only drivers expected to compile cleanly (NEW)                            

Also note that the "UART serial support" option may be forced off if you selected "PPS support" as a module and "8250/16550 and compatible serial support", into "Serial drivers" section, as built in, it needs to be a module too (similar behavior is for "Parallel printer support" and the same menu entry into "Character devices"). See below for an example:

        <M> PPS support                                                       
        [*]   PPS debugging messages                                          
        ---   PPS clients support                                             
        <M>   Kernel timer client (Testing client, use for debug)             
        ---   UART serial support (forced off)                                
        ---   Parallel printer support (forced off)            

Well, now recompile the kernel and reboot.

If you decide to use a kernel patch just patch the kernel and then configure and compile the new kernel as above.

Note: each kernel patch adds a LinuxPPS snapshot at time of patch creation, so if you wish having latest code you should refer to the GITweb site.

The userland tools

After the kernel has been compiled and rebooted, before continuing, it's time to compile the userland tools by setting up the compilation environment.

In order to correctly compile the userland programs we need add our new kernel to the glibc, so go to the /usr/include directory and substitute the directories linux, asm and asm-generic as follows:

giometti@zaigor:/usr/include# mv linux linux.old
giometti@zaigor:/usr/include# mv asm asm.old
giometti@zaigor:/usr/include# mv asm-generic asm-generic.old
giometti@zaigor:/usr/include# ln -s /lib/modules/$(uname -r)/build/include/linux .
giometti@zaigor:/usr/include# ln -s /lib/modules/$(uname -r)/build/include/asm .
giometti@zaigor:/usr/include# ln -s /lib/modules/$(uname -r)/build/include/asm-generic .

then we need to add the file timepps.h under the directory /usr/include, you can do it by using the command:

giometti@zaigor:/usr/include# ln -s /lib/modules/$(uname -r)/build/Documentation/pps/timepps.h .

ppstest and ppsctl

You can find the userland tools into linux/kernel/Documentation/pps/ directory, you can compile them with the command:

giometti@zaigor:~/linux/kernel/Documentation/pps$ make
cc -Wall -O2 -D_GNU_SOURCE -M ppstest.c ppsctl.c > .depend
cc -Wall -O2 -D_GNU_SOURCE    ppstest.c   -o ppstest
cc -Wall -O2 -D_GNU_SOURCE    ppsctl.c   -o ppsctl

If you get no errors your compilation environment should be ok.

setserial

If you wish using setserial to manage your serial ports you have to patch it with the following patch in order to support the hardpps parameter (at least version 2.17 has no such support):

--- setserial.c.old	2007-02-17 08:47:45.000000000 +0100
+++ setserial.c	2007-02-17 08:57:26.000000000 +0100
@@ -127,6 +127,7 @@
 	CMD_FLAG,	"hup_notify",	ASYNC_HUP_NOTIFY, ASYNC_HUP_NOTIFY, 0, FLAG_CAN_INVERT,
 	CMD_FLAG,	"skip_test",	ASYNC_SKIP_TEST,ASYNC_SKIP_TEST,2, FLAG_CAN_INVERT,
 	CMD_FLAG,	"auto_irq",	ASYNC_AUTO_IRQ,	ASYNC_AUTO_IRQ,	2, FLAG_CAN_INVERT,
+	CMD_FLAG,	"hardpps",	ASYNC_HARDPPS_CD, ASYNC_HARDPPS_CD, 0, FLAG_CAN_INVERT,
 	CMD_FLAG,	"split_termios", ASYNC_SPLIT_TERMIOS, ASYNC_SPLIT_TERMIOS, 2, FLAG_CAN_INVERT,
 	CMD_FLAG,	"session_lockout", ASYNC_SESSION_LOCKOUT, ASYNC_SESSION_LOCKOUT, 2, FLAG_CAN_INVERT,
 	CMD_FLAG,	"pgrp_lockout", ASYNC_PGRP_LOCKOUT, ASYNC_PGRP_LOCKOUT, 2, FLAG_CAN_INVERT,
@@ -725,6 +726,7 @@
 	fprintf(stderr, "\t^ fourport\tconfigure the port as an AST Fourport\n");
 	fprintf(stderr, "\t  autoconfig\tautomatically configure the serial port\n");
 	fprintf(stderr, "\t^ auto_irq\ttry to determine irq during autoconfiguration\n");
+	fprintf(stderr, "\t^ hardpps\tmanage PPS signal when CD changes status\n");
 	fprintf(stderr, "\t^ skip_test\tskip UART test during autoconfiguration\n");
 	fprintf(stderr, "\n");
 	fprintf(stderr, "\t^ sak\t\tset the break key as the Secure Attention Key\n");

So download the sources from the setserial home site or directly from your distribution. As example on my Debian I simply do:

giometti@zaigor:/tmp$ apt-get source setserial

then recompile the setserial tool as described into its documentation or, if you use Debian, use the command:

giometti@zaigor:/tmp/setserial-2.17$ fakeroot debian/rules binary

Checking the new system

After the system restart you should get the new directory /sys/class/pps and digging inside that directory you should see all your enabled clients:

giometti@zaigor:~$ tree /sys/class/pps
/sys/class/pps
|-- pps0
|   |-- assert
|   |-- clear
|   |-- dev
|   |-- device -> ../../../devices/platform/serial8250
|   |-- echo
|   |-- mode
|   |-- name
|   |-- path
|   |-- power
|   |   `-- wakeup
|   |-- subsystem -> ../../../class/pps
|   `-- uevent
`-- pps1
    |-- assert
    |-- clear
    |-- dev
    |-- echo
    |-- mode
    |-- name
    |-- path
    |-- power
    |   `-- wakeup
    |-- subsystem -> ../../../class/pps
    `-- uevent

If you see nothing maybe you should load some client module and/or enable the PPS support for a specified client, for example for the 8250 serial line support, after insmoding the kernel module:

giometti@zaigor:~$ sudo modprobe 8250

use the commands:

giometti@zaigor:~$ setserial /dev/ttyS0 hardpps 
giometti@zaigor:~$ cat /sys/class/pps/pps0/path 
/dev/ttyS0

to enable the PPS support for the serial line /dev/ttyS0, or, if you don't wish modify your current setserial, you can use my ppsctl as follow:

giometti@zaigor:~/linux/kernel/Documentation/pps$ ./ppsctl /dev/ttyS0 enable

Ok, now the PPS support is active and you can try using the ppstest (as root) into in order to verify if you get the PPS signal.

Before doing it you should verify if your system has proper char device files into /dev directory, if so you should have something like this:

giometti@zaigor:~/linux/kernel/Documentation/pps$ ls -l /dev/pps*
crw-rw-r-- 1 root dialout 253, 0 Aug  8 18:58 /dev/pps0
crw-rw-r-- 1 root dialout 253, 1 Aug  8 18:58 /dev/pps1

if not you need to configure your udev utility properly or doing the device files by hand with the mknod tool (please refer to the relative manual pages). However, into my system, I just add the following line into udev configuration file:

SUBSYSTEM=="pps", MODE="0664" GROUP="dialout"

Ok, once you have such files you can use ppstest as follow (as example see my test below using the debugging PPS client ktimer):

giometti@zaigor:~/linux/kernel/Documentation/pps$ sudo ./ppstest /dev/pps1
trying PPS source "/dev/pps1"
found PPS source "/dev/pps1"
ok, found 1 source(s), now start fetching data...
source 0 - assert 1186592699.388832443, sequence: 364 - clear  0.000000000, sequence: 0
source 0 - assert 1186592700.388931295, sequence: 365 - clear  0.000000000, sequence: 0
source 0 - assert 1186592701.389032765, sequence: 366 - clear  0.000000000, sequence: 0

If ktimer has a different ID from 1 into your system, you can get the right ID by using the utility ppsfind as follow:

giometti@zaigor:~/linux/kernel/Documentation/pps$ ./ppsfind ktimer
pps0: name=ktimer path=

You can also test if the PPS events are cached by the system by using the sysfs support. In the /sys/class/pps directory you can find a dedicated directory for each PPS source registered into the system:

giometti@jeeg:~$ ls -l /sys/class/pps/
total 0
drwxr-xr-x 3 root root 0 Aug  8 18:59 pps0/
drwxr-xr-x 3 root root 0 Aug  8 18:59 pps1/

Each directory contains both files assert and clear but depending on your PPS source capability one of them may be always empty, in the above example I get:

giometti@jeeg:~$ ls /sys/class/pps/*/{assert,clear}
/sys/class/pps/pps0/assert  /sys/class/pps/pps1/assert
/sys/class/pps/pps0/clear   /sys/class/pps/pps1/clear

but since ktimer client supports only assert events file clear is empty. Reading these files you may get last assert or clear event time and sequence number:

giometti@jeeg:~$ cat /sys/class/pps/pps1/assert
1148727816.24772000#326
giometti@jeeg:~$ cat /sys/class/pps/pps1/assert 
1148727816.24772000#326
giometti@jeeg:~$ cat /sys/class/pps/pps1/assert 
1148727817.24872000#327

if the PPS source is generating the events, and your system correctly collects them, you should read different values each time you read these files.

NTPD support

Currently you need some patches in order to get LinuxPPS to work, even if we are working to put these in the NTPD's main tree.

Here a list of currently tested reference clock with relative patches (when needed).

Reference clock Driver Contact Docs Patches
Generic NMEA GPS Receiver (nmea) 20 Udo van den Heuvel NTPD driver 20 ntp-4.2.4p2
Atom PPS Clock Discipline (atom) 22 None NTPD driver 22 None
Motorola Oncore GPS receiver (oncore) 30 None NTPD driver 30 None
Rockwell Jupiter GPS receiver (jupiter) 31 Peter Szegedi

Lichtenberger János

NTPD driver 31 ntp-4.2.4p4

Generic NMEA GPS Receiver (refclock_nmea - driver 20)

This driver implements reference clock support for NTPD package that can read time data from a Generic NMEA GPS receiver and optionally its PPS signal.

Installation

Patch refclock_nmea.c with the supplied patch file and then run the configure command disabling know LinuxPPS-buggy reference clocks (please refere to NTPD documentation in order to know which other compilation options will fit your needs):

giometti@zaigor:~/ntp$ ./configure --disable-all-clocks --disable-parse-clocks\
                                   --enable-NMEA --enable-LOCAL-CLOCK

Then compile with make.

If everything goes well you should now install the new NTDP daemon into your system following NTPD documentation and then start it.

Udo's configuration file ntp.conf appear as follow:

server 127.127.20.0 prefer minpoll 4
fudge 127.127.20.0 flag3 1 flag2 0 time1 0.0

Note

For further info on this reference clock support, please, refer to the file driver20.html in the NTP source tree.

The Generic NMEA GPS Receiver was tested successfully with a Garmin GPS 18 LVC and NTP ntp-4.2.4p2.

You can find here an useful instructional e-mail about how to use NPD patched with latest NMEA patch from ntp bugs list (hope this e-mail may become an HOWTO...).

Atom PPS Clock Discipline (refclock_atom - driver 22)

This driver implements reference clock support for NTPD package that can read time data from a Atom PPS Clock Discipline.

Installation

As mentioned in the NTPD documentation, this is a secondary PPS-input driver that can be used with any other time source, either a local reference clock or a network peer.

Because a PPS signal does not give a complete time, it must be attached to another time source which can disambiguate the seconds. If that time source is valid and within ±0.4 seconds of the PPS edge, the PPS clock will "take over" and provide the time measurements that NTPD uses.

Note that it is entirely normal for the PPS time source to use the same serial port as a serial time code. For serial ports, the PPS time source uses the edges of the DCD input signal.

The disambiguating time source is marked with the keyword prefer on its server or peer line. The PPS clock will not begin sampling until this clock is detected, so do not worry about not seeing events detected immediately.

An example PPS clock configuration is:

server 127.127.22.1 minpoll 4 maxpoll 4
fudge 127.127.22.1 flag flag2 1

The prefix 127.127.22 specifies the ATOM driver. The last number 1 specifies PPS source #1, which you should see in /sys/class/pps/ directory.

The fudge 127.127.22.1 flag2 1 specifies that the "clear" transtion of the PPS signal (/sys/class/pps/pps1/clear) should be used as a time source. The default, if flag2 is 0, is to use the assert edge.

PPS pulses are generally short, and the leading edge is the on-time mark, so by looking at the time of two adjacent edges with cat /sys/class/pps/pps1/{assert,clear}, you can see which one leads the other.

Note

For further info on this reference clock support, please, refer to the file driver22.html in the NTP source tree.

If you do not have a local reference clock, one rude hack which is possible is to mark the PPS source as prefer as well as a network clock. Then, the network clock is required for NTPD to start, but once it has started, as long as it doesn't lose track of the PPS signal, it can keep on by itself. However, this can result in an NTP server claiming to have good time when it is off by some seconds, and so is not recommended.

Motorola Oncore GPS receiver (refclock_oncore - driver 30)

This driver implements reference clock support for NTPD package that reads time data and PPS signals from a Motorola Oncore GPS receiver.

Note

For further info on this reference clock support, please, refer to the file driver30.html into NTP source tree.

Rockwell Jupiter GPS receiver (refclock_jupiter - driver 31)

This driver implements reference clock support for NTPD package that can read time data and PPS signal from a Rockwell Jupiter GPS receiver.

Installation

Patch refclock_jupiter.c with the supplied patch file and then run the configure command with the following parameters:

./configure --disable-all-clocks --disable-parse-clocks --enable-JUPITER \
            --enable-LOCAL-CLOCK --enable-HAVE_PPSAPI --enable-HAVE_TIMEPPS_H

Then compile with make.

There have to be a /dev/gps0 file which is a symlink to a /dev/ttySx file.

The ntp.conf file should contain the following line:

server 127.127.31.0

Note

For further info on this reference clock support, please, refer to the file driver31.html in the NTP source tree.

The Rockwell Jupiter receiver was tested with a Jupiter12 and ntp-4.2.4p4.

How to contribute

If you wish contribute to this project, first of all, you should subscribe to the LinuxPPS's mail list.

Then if you wish to modify the source code I suggest you to get LinuxPPS's GIT repository (see above) and send back your patches.

Just use the command:

giometti@zaigor:~/linuxpps$ git diff linux-vanilla master > my_patch

this produces a patch against the linux vanilla code holded in the repository. Please, refer to the GIT documentation in order to know how to update current Linux vanilla into your LinuxPPS repository.

Then, if you are a good boy/girl who improved LinuxPPS or just who found a bug, you may send the patch to the mail list (adding the string "[PATCH]" in the subject) by using the command:

giometti@zaigor:~/linuxpps$ git diff > patch_file

Please, refere to the GIT manuals in order to have further information on how to use the git-diff command.

On the other hands, if you wish to suggest modifications to this documentation, please, use the following procedure:

1) by using the commands:

$ lynx --dump 'http://wiki.enneenne.com/index.php/LinuxPPS_support' > LinuxPPS.txt
$ cp LinuxPPS.txt LinuxPPS.txt.orig

you can get a text copy of this WEB page.

2) then modify the file LinuxPPS.txt and send back the output of the command:

$ diff -u LinuxPPS.txt.orig LinuxPPS.txt

in this manner I can read very easily what you changed (again, don't forget the string "[PATCH]" into the subject).

Hacking the code

Adding a new LinuxPPS source

To register a PPS source into the kernel you should define a struct pps_source_info as follow:

static struct pps_source_info pps_ktimer_info = {
        name            : "ktimer",
        path            : "",
        mode            : PPS_CAPTUREASSERT | PPS_OFFSETASSERT | \
                          PPS_ECHOASSERT | \
                          PPS_CANWAIT | PPS_TSFMT_TSPEC,
        echo            : pps_ktimer_echo,
        owner           : THIS_MODULE,
};

and then calling the function pps_register_source() in your intialization routine:

source = pps_register_source(&pps_ktimer_info,
                 PPS_CAPTUREASSERT | PPS_OFFSETASSERT);

The pps_register_source() prototype is:

int pps_register_source(struct pps_source_info_s *info, int default_params)

where info is a pointer to a structure that describes a particular PPS source, default_params tells the system what the initial default parameters for the device should be (is obvious that these parameters must be a subset of ones defined into the struct pps_source_info which describe the capabilities of the driver).

Once you have registered a new PPS source into the system you can signal an assert event (for example in the interrupt handler routine) just using:

pps_event(source, PPS_CAPTUREASSERT, ptr);

The same function may also run the defined echo function (pps_ktimer_echo(), passing to it the ptr pointer) if the user asked for that... etc..

Please see the file drivers/pps/clients/ktimer.c for an example code.

Modifing a reference clock to work with LinuxPPS

While implementing a PPS API as RFC 2783 defines and using an embedded CPU GPIO-Pin as physical link to the signal, I encountered a deeper problem:

  At startup it needs a file descriptor as argument for the function
  time_pps_create().

This implies that the source has a /dev/... entry. This assumption is ok for the serial and parallel port, where you can do something useful besides(!) the gathering of timestamps as it is the central task for a PPS-API. But this assumption does not work for a single purpose GPIO line. In this case even basic file-related functionality (like read() and write()) makes no sense at all and should not be a precondition for the use of a PPS-API.

The problem can be simply solved if you consider that a PPS source is not always connected with a GPS data source.

So your programs should check if the GPS data source (the serial port for instance) is a PPS source too, otherwise they should provide the possibility to open another device as PPS source.

In LinuxPPS the PPS sources are simply char devices usually mapped into files /dev/pps0, /dev/pps1, etc..

In the directory refclocks you can find patches for some refclocks that already support the LinuxPPS API.

FAQs

Q1

Running my NTPD driver I see in the log messages:
[ntpd] refclock_nmea: time_pps_kcbind failed: Operation not supported
Is that an implementation bug?

A1

No. The function time_pps_kcbind() is defined as «optional» into RFC 2783 and, simply, LinuxPPS doesn't implement it! :)

Q2

How can I verify that my NTPD (or just the ppstest program) is reading the PPS data from LinuxPPS?

A2

If you enable the kernel debugging messages you should see something as follow:
pps_core: capture assert seq #292 for source 0
8250: serial8250: PPS assert event at 420817
This is the capture event from the GPS and saved by LinuxPPS.
pps_core: capture clear seq #292 for source 0
8250: serial8250: PPS clear event at 420942
This is the clear event from the GPS and saved by LinuxPPS. Also we can see that 420942-420817=125 which is ok if PPS duty cycle is 50% and the linux's clock (HZ variable) is set to 250 ticks per second.
pps_core: New message from PID 5205 (flags 0)
pps_core: PPS_FETCH: source 0
Here the userland application with PID 5205 (NTPD or ppstest) is reading PPS data from LinuxPPS.

Note: sometimes (and especially for serial ports) you may need to keep opened the serial line in order to enable kernel interrupts registration, in order to do that you may use the command "cat /dev/ttyS0" on a different terminal.


Q3

OK, I see /dev/lp0 and/or /dev/ttyS0 down in /sys/class/pps, but I thought there also was supposed to be an entry in /proc. I don't see anything there! Why?

A3

No /proc support for Linux 2.6. It's deprecated!

Q4

There is an entry in /sys/class/pps/pps0/name of serial0 what is this? The actual name of the device is in path.

A4

Into name there is the driver's name while into path there is the device's path name.

LinuxPPS based projects

Here a list of LinuxPPS based projects around the WEB:

  • Dedicated Short Range Communications (DSRC) radio synchronization used by US-DOT's VII [1] [2].

If you have a new project, and you wish adding it here, please send a message to me.

PayPal donations

If you like LinuxPPS make a donation with PayPal - it's fast, free and secure!

Thanks to:

  • Kenji Hiranuma
Personal tools