LinuxPPS support
From EnneEnneWiki
| 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 | 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
ppstestprogram) 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=125which is ok if PPS duty cycle is 50% and the linux's clock (HZvariable) 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/lp0and/or/dev/ttyS0down 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
/procsupport for Linux 2.6. It's deprecated!
Q4
- There is an entry in
/sys/class/pps/pps0/nameofserial0what is this? The actual name of the device is inpath.
A4
- Into
namethere is the driver's name while intopaththere is the device's path name.
LinuxPPS based projects
Here a list of LinuxPPS based projects around the WEB:
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
