LinuxPPS support (old APIs)
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 3.1.0 to 3.2.0.
Note: version series from 4.0.0-rc1 and 4.0.x are not documented anymore due they short life.
Old versions are currently NOT supported anymore due several incompatibilities, while newer versions starting from 5.0.0-rc1 are documented in LinuxPPS support page. This page is still available only for backward compatibility.
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 the Linux's netlink layer 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 the netlink layer 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 but you can find some patches against some Linux versions here but you are warned that some of these patches may implement old LinuxPPS version! Refere 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.
Merging my repository with your current Linux Vanilla one
Ok, my internet connection is slow and you have no time to waste in waiting for code downloading... but if you have your own Linux Vanilla repository you are lucky!
In fact, another way to get LinuxPPS is just to create a new branch for it starting from your preferred linux version (for example 2.6.18-rc4):
git checkout -b linuxpps
then download and merge only LinuxPPS changes from my server on master branch into your current local branch:
git pull git://git.enneenne.com/linuxpps master
This example came from Wikipedia (Great! We are on Wikipedia!!!)
If you wish cloning your local Linux Vanilla repository in order to have multiple copies of it, you can use the command:
git clone --reference /path/to/linux/clone
If you have no Linux Vanilla repository to clone from you can use rsync to grab one (since the trees on kernel.org are available by all of http, git and rsync and go from there. You do need the --partial option to rsync (use -avrPSH). Once you get a rsync you can use it as a --bare clone. Clone from that (use an ssh url) and then use that clone for the --reference (thanks to James Cloos).
As above, 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
Please note that is this case you must have checked out the linuxpps branch and not the master one as in the first example (repository cloning).
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.21$ patch -p1 < ntp-pps-2.6.21.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 ppstime.h under the directory /usr/include.
ppstest and ppsctl
Now you can get the userland tools here and compiling them with the command:
giometti@zaigor:~/linuxpps/test$ 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:~/linuxpps$ tree /sys/class/pps
/sys/class/pps
|-- 00
| |-- assert
| |-- clear
| |-- echo
| |-- mode
| |-- name
| |-- path
| |-- subsystem -> ../../../class/pps
| `-- uevent
`-- 01
|-- assert
|-- echo
|-- mode
|-- name
|-- path
|-- 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:~/linuxpps$ sudo modprobe 8250
use the commands:
giometti@zaigor:~/linuxpps$ setserial /dev/ttyS1 hardpps giometti@zaigor:~/linuxpps$ cat /sys/class/pps/02/path /dev/ttyS1
to enable the PPS support for the serial line /dev/ttyS1, or, if you don't wish modify your current setserial, you can use my ppsctl as follow:
giometti@zaigor:~/linuxpps$ ppsctl /dev/ttyS1 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. As example see my test below using the debugging PPS client ktimer:
giometti@zaigor:~/linuxpps$ sudo ./test/ppstest found PPS source #0 "ktimer" on "" Assert timestamp: 1138812799.797267675, sequence: 477 Assert timestamp: 1138812800.797337675, sequence: 478 Assert timestamp: 1138812801.797392675, sequence: 479 Assert timestamp: 1138812802.797463675, sequence: 480 Assert timestamp: 1138812803.797522675, sequence: 481
If ktimer has a different id from 0 into your system, you can run ppstest as follow:
giometti@zaigor:~/linuxpps$ cat /sys/class/pps/01/name ktimer
giometti@zaigor:~/linuxpps$ sudo ./test/ppstest 1 found PPS source #1 "ktimer" on "" Assert timestamp: 1139092418.968589675, sequence: 514 Assert timestamp: 1139092419.968671675, sequence: 515 Assert timestamp: 1139092420.968718675, sequence: 516 Assert timestamp: 1139092421.968785675, sequence: 517
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/ drwxr-xr-x 2 root root 0 Feb 18 20:08 00/ drwxr-xr-x 2 root root 0 Feb 18 20:08 01/ drwxr-xr-x 2 root root 0 Feb 18 20:11 02/
Each directory may contain file assert and/or clear depending on your PPS source capability, in the above example I get:
giometti@jeeg:~$ ls /sys/class/pps/??/{assert,clear}
/sys/class/pps/00/assert /sys/class/pps/01/assert /sys/class/pps/02/clear
/sys/class/pps/00/clear /sys/class/pps/02/assert
since ktimer client supports only assert events and 8250 supports both. Reading this file you may get last assert or clear event time and sequence number:
giometti@jeeg:~$ cat /sys/class/pps/01/assert 1148727816.24772000 #326 giometti@jeeg:~$ cat /sys/class/pps/01/assert 1148727816.24772000 #326 giometti@jeeg:~$ cat /sys/class/pps/01/assert 1148727817.24872000 #327 giometti@jeeg:~$ cat /sys/class/pps/01/assert 1148727817.24872000 #327 giometti@jeeg:~$ cat /sys/class/pps/01/assert 1148727818.24945000 #328 giometti@jeeg:~$ cat /sys/class/pps/01/assert 1148727818.24945000 #328 giometti@jeeg:~$ cat /sys/class/pps/01/assert 1148727819.25003000 #329
if the PPS source is generating the events and you system sees them you should read different values each time you read the file.
NTPD support
Currently ntpd doesn't support LinuxPPS so you have to recompile it. To do it consider the specific instructions for your reference clock into the following list of (currently) tested reference clocks with LinuxPPS and after running the configure tool verify that the PPS support has been enabled by checking:
giometti@zaigor:~/ntp$ grep HAVE_PPSAPI config.h #define HAVE_PPSAPI 1
| Reference clock | Driver | Contact | PPS source | Docs | Patches |
|---|---|---|---|---|---|
Generic NMEA GPS Receiver (nmea)
| 20 | Udo van den Heuvel | pps_8250
| NTPD driver 20 | here for ntp-4.2.0.a.20050816-14 |
Atom PPS Clock Discipline (atom)
| 22 | linux at horizon dot com | pps_8250
| NTPD driver 22 | here for ntp-4.2.2+dfsg.2 |
Motorola Oncore GPS receiver (oncore)
| 30 | Reg Clemens | pps_8250
| NTPD driver 30 | none for ntp-dev-4.2.0b-rc1-20060306 or later |
RIPE NCC interface for Trimble Palisade (ripencc)
| 43 | Rodolfo Giometti | pps_mpc8xx
| NTPD driver 43 | here for ntp-4.2.0a+stable |
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 the daemon:
giometti@zaigor:~/ntp$ 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 4.2.0.a.20050816-14 & 4.2.0.a.20060224 for Fedora Core 5.
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/01/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/01/{assert,clear}, you can see which one leads the other.
Note
For further info on this reference clock support, please, refere 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.
Installation
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-dev-4.2.0b-rc1-20060306$ ./configure --disable-ATOM \ --disable-JUPITER --disable-NMEA
Then compile the daemon:
giometti@zaigor:~/ntp-4.2.0a+stable$ make
If everything goes well you should now install the new NTDP daemon into your system following the NTPD documentation and then start it.
Note
For further info on this reference clock support, please, refere to the file driver30.html into NTP source tree.
RIPE NCC interface for Trimble Palisad (refclock_ripencc - driver 43)
This driver implements reference clock support for NTPD package that can read time data from a Trimble Acutime 2000 GPS antenna (or by a generic GPS device connected to the serial line that supports the TSIP protocol).
Here you can find some patches for the existing file refclock_ripencc.c distribuited with NTP package version 4.2.0a+stable in order to make it functional.
I fixed the big/little endian support and the PPS support in such a way that you may use this reference clock with or without PPS support in your kernel.
I tested this reference clock with and without PPS support by using it on a MPC8xx board (ppc) runnig linux 2.4.24. The PPS signal was connected to special CPU's input pin (not with the serial line DCD).
Installation
Then unpack the code and patch it:
giometti@zaigor:~$ tar xfz ntp_4.2.0a+stable.tgz giometti@zaigor:~$ cd ntp-4.2.0a+stable/ giometti@zaigor:~/ntp-4.2.0a+stable$ patch -p1 < ../patch.cross_compiling patching file configure.in Hunk #1 succeeded at 26 (offset 1 line). patching file ntpdc/Makefile.am giometti@zaigor:~/ntp-4.2.0a+stable$ patch -p1 < ../patch.ntp-4.2.0a+stable-configure.in patching file configure.in giometti@zaigor:~/ntp-4.2.0a+stable$ patch -p1 < ../patch.ntp-4.2.0a+stable-refclock_ripencc.c patching file ntpd/refclock_ripencc.c
then rerun the auto configuration tools:
giometti@zaigor:~/ntp-4.2.0a+stable$ aclocal && autoconf && automake
Now 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-4.2.0a+stable$ ./configure --enable-RIPENCC \ --disable-ATOM --disable-JUPITER --disable-NMEA
Then compile the daemon:
giometti@zaigor:~/ntp-4.2.0a+stable$ make
If everything goes well you should now install the new NTDP daemon into your system following the NTPD documentation and then start it.
Note
For further info on this reference clock support, please, refere to the file driver43.html in the NTP source tree.
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_s as follow:
static struct pps_source_info_s pps_ktimer_info = {
name : "ktimer",
path : "",
mode : PPS_CAPTUREASSERT|PPS_OFFSETASSERT|PPS_ECHOASSERT| \
PPS_CANWAIT|PPS_TSFMT_TSPEC,
echo : pps_ktimer_echo,
};
and then calling the function pps_register_source() in your intialization routine:
source = pps_register_source(&pps_ktimer_info,
PPS_CAPTUREASSERT|PPS_OFFSETASSERT,
-1 /* is up to the system */);
The pps_register_source() prototype is:
int pps_register_source(struct pps_source_info_s *info, int default_params, int try_id)
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_s which describe the capabilities of the driver) and try_id can be used to force a particular ID for your device into the system (just use -1 if you wish the system chooses one for you).
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
The RFC 2783 says that
- pps_handle_t type is an opaque scalar type used to represent a PPS source within the API
In my implementation I intentionally want to separate the concept of file descriptor to the concept of the PPS source since some devices do not have such association (some devices are directly connected to a dedicated interrupt line for example).
If your receiver is connected to a serial line then everything works well but, if this is not true, you have no filedes to pass to the function time_pps_create(). That's why I added a new function time_pps_findsource() in order to find a generic PPS source (note that
this function is protected by the PPS_HAVE_FINDSOURCE define).
So, my opinion is that RFC 2783 should say that
- pps_handle_t type is an opaque variable used to represent a PPS source within the API
and programs should not access it directly to it due to its opacity.
Also note that the RFC 2783 does not say that programs should check the pps_handle_t variable before calling the time_pps_*() functions, since each function should do this job internally.
According the above considerations you have two ways to make a generic reference clock working with LinuxPPS support.
The first one (and the oldest one) is by using the function time_pps_findsource() in order to get a PPS source ID to pass to the function time_pps_create():
+#ifdef PPS_HAVE_FINDSOURCE
+ /* Try to find the source (by using "index = -1" we ask just for a
+ * generic source) */
+ fd = time_pps_findsource(-1, path, PPS_MAX_NAME_LEN, id, PPS_MAX_NAME_LEN);
+ if (fd < 0) {
+ msyslog(LOG_ERR, "refclock: no available PPS source in the system");
+ return 1;
+ }
+ msyslog(LOG_INFO, "refclock: found PPS source #%d \"%s\" on \"%s\"", fd, path, id);
+#endif /* PPS_HAVE_FINDSOURCE */
+
+
if (time_pps_create(fd, &pps_handle) < 0) {
- pps_handle = 0;
msyslog(LOG_ERR, "refclock: time_pps_create failed: %m");
}
In this manner fd is not a file descriptor but it is a PPS source ID. In an older program, by using the PPS_HAVE_FINDSOURCE define, everything should continue working the same as it did before.
The second way (and the most powerful one) is by using the function time_pps_findpath() with the function time_pps_readlink():
+#ifdef PPS_HAVE_FINDPATH
+ /* Get the PPS source's real name */
+ time_pps_readlink(link, LENPPS, path, PPS_MAX_NAME_LEN);
+
+ /* Try to find the source */
+ fd = time_pps_findpath(path, PPS_MAX_NAME_LEN, id, PPS_MAX_NAME_LEN);
+ if (fd < 0) {
+ msyslog(LOG_ERR, "refclock: cannot find PPS source \"%s\" in the system", path);
+ return 1;
+ }
+ msyslog(LOG_INFO, "refclock: found PPS source #%d \"%s\" on \"%s\"", fd, path, id);
+#endif /* PPS_HAVE_FINDPATH */
+
+
if (time_pps_create(fd, &pps_handle) < 0) {
- pps_handle = 0;
msyslog(LOG_ERR, "refclock: time_pps_create failed: %m");
}
The variable link may be initialized with the string "/dev/gps0", in this manner we can surely address the right PPS device as show below:
giometti@jeeg:~/linuxpps$ cat /sys/class/pps/01/name serial1 giometti@jeeg:~/linuxpps$ cat /sys/class/pps/01/path /dev/ttyS1 giometti at jeeg:~/linuxpps/test$ sudo ln -sf /dev/ttyS1 /dev/gps0 giometti at jeeg:~/linuxpps/test$ sudo ./ppstest /dev/gps0 found PPS source #2 "serial1" on "/dev/ttyS1" giometti at jeeg:~/linuxpps/test$ sudo ln -sf ktimer /dev/gps0 giometti at jeeg:~/linuxpps/test$ sudo ./ppstest /dev/gps0 found PPS source #0 "ktimer" on ""
Note that sources without a path can be addressed by their name.
Note: Just calling time_pps_create() function may NOT enable PPS signal generation! It depends on your particular client. For instance ktimer client enables PPS signal generation automatically but client 8250 does not, so, in this case, you also need to open the serial device, and, depending on your GPS antenna, you need to send some additional command(s) to it before you can get the PPS signal. NTPD reference clocks should do it all automagically...
Regarding the time_pps_destroy(), we have:
#ifdef HAVE_PPSAPI
- if (pps_handle) {
- time_pps_destroy(pps_handle);
- pps_handle = 0;
- }
+ time_pps_destroy(pps_handle);
#endif /* HAVE_PPSAPI */
Here we do not need to check the pps_handle status before calling the time_pps_destroy() since the function checks it internally.
In the directory refclocks you can find patches for some refclocks that already support the LinuxPPS API.
FAQs
Q1
-
time_pps_create()first parameter should be "...an already-open UNIX file descriptor, for an appropriate special file...". This could be problematic for example by accessing the ktimer source, which has no device. - My idea: The PPS-API should create it's own special devices
/dev/pps? and use those for identifying the expected source. Anything wrong with this idea?
A1
- I avoid using special devices by using netlink API, that was my real intention! If I define a char device (for instance) I can use it to send/receive data from kernel without the netlink layer.
- Using a char device (
/dev/ppsfor instance) can rise the problem when you have a PPS source connected with a serial line or another char device. Which file descriptor should I send to the PPS functions? The one associated with/dev/ppsor the one associated with/dev/whatever?
- I decided to use netlink layer to avoid specific association between a PPS source and a char/block device. In this nammer I support well both PPS source which is connected with a char device and PPS source which has no devices connected to.
- However I don't think
filedescould be a real problem, since my API simply doesn't use it and the PPS API use it only intime_pps_create()andtime_pps_destroy(). :)
Q2
-
pps_handle_tis defined as struct with two members, source and socket. But the RFC says that "...pps_handle_ttype is an opaque scalar type...". The NTPD uses handles of this type with operators like==and!=, which wont work on structs. How I can resolve the problem?
A2
- Here is the real problem with RFC 2783!
- I need both a socket for the netlink layer and a source index to identify the PPS source.
- I know what RFC specifies but I see that in pratical use there are no reasons to test if the handle is
0or not, since all functions should return error (EBADF) if they get a wrong handle.
- Usually the NTPD drivers test the handle just to know if they already opened it (here a code sample from
refclock_ripencc.c):
if (up->handle != 0)
time_pps_destroy(up->handle);
- But you can replace it with
time_pps_destroy(up->handle);
- since even if the handle is not valid you simply wish to destroy it and nobody tests the return value. :)
- I think that RFC 2783 says that
pps_handle_tshould be a scalar type since it takes in account a specific implementation involving char devices, but in my opinion this solution is reductive. Implementations should use typepps_handle_tas they wish since it should be an opaque type.
Q3
- 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?
A3
- No. The function
time_pps_kcbind()is defined as «optional» into RFC 2783 and, simply, LinuxPPS doesn't implement it! :)
Q4
- How can I verify that my NTPD (or just the
ppstestprogram) is reading the PPS data from LinuxPPS?
A4
- 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.
Q5
- 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?
A5
- No
/procsupport for Linux 2.6. It's deprecated!
Q6
- There is an entry in
/sys/class/pps/00/nameofserial0what is this? The actual name of the device is inpath.
A6
- Into
namethere is the driver's name while intopaththere is the device's path name.
| If you like LinuxPPS make a donation with PayPal - it's fast, free and secure! |
