1 /*
2 * Watch a specified serial port for transitions that might be 1PPS.
3 *
4 * Each output line is the second and nanosecond parts of a timestamp
5 * followed by the names of handshake signals then asserted. Off
6 * transitions may generate lines with no signals aserted.
7 *
8 * If you don't see output within a second, use gpsmon or some other
9 * equivalent tool to check that your device has satellite lock and is
10 * getting fixes before giving up on the possibility of 1PPS.
11 *
12 * Also, check your cable. Cheap DB9 to DB9 cables such as those
13 * issued with UPSes often carry TXD/RXD/GND only, omitting handshake
14 * lines such as DCD. Suspect this especially if the cable jacket
15 * looks too skinny to hold more than three leads!
16 *
17 * This code requires only ANSI/POSIX. If it doesn't compile and run
18 * on your Unix there is something very wrong with your Unix.
19 *
20 * This code by ESR, Copyright (C) 2013, under BSD terms.
21 * This file is Copyright (c)2013-2018 by the GPSD project
22 * SPDX-License-Identifier: BSD-2-clause
23 */
24
25 #include "gpsd_config.h" /* must be before all includes */
26
27 #include <errno.h>
28 #include <fcntl.h> /* needed for open() and friends */
29 #include <stdio.h>
30 #include <stdlib.h>
31 #include <string.h>
32 #include <sys/ioctl.h>
33 #include <sys/stat.h>
34 #include <sys/types.h>
35 #include <time.h>
36 #include <unistd.h>
37
38 #include "revision.h"
39 #include "timespec.h"
40
41 struct assoc {
42 int mask;
43 char *string;
44 };
45
46 /*
47 * Possible pins for PPS: DCD, CTS, RI, DSR. Pinouts:
48 *
49 * DB9 DB25 Name Full name
50 * --- ---- ---- --------------------
51 * 3 2 TXD --> Transmit Data
52 * 2 3 RXD <-- Receive Data
53 * 7 4 RTS --> Request To Send
54 * 8 5 CTS <-- Clear To Send
55 * 6 6 DSR <-- Data Set Ready
56 * 4 20 DTR --> Data Terminal Ready
57 * 1 8 DCD <-- Data Carrier Detect
58 * 9 22 RI <-- Ring Indicator
59 * 5 7 GND Signal ground
60 *
61 * Note that it only makes sense to wait on handshake lines
62 * activated from the receive side (DCE->DTE) here; in this
63 * context "DCE" is the GPS. {CD,RI,CTS,DSR} is the
64 * entire set of these.
65 */
66 static const struct assoc hlines[] = {
67 {TIOCM_CD, "TIOCM_CD"},
68 {TIOCM_RI, "TIOCM_RI"},
69 {TIOCM_DSR, "TIOCM_DSR"},
70 {TIOCM_CTS, "TIOCM_CTS"},
71 };
72
usage(void)73 static void usage(void)
74 {
75 (void)fprintf(stderr, "usage: ppscheck [-h] [ -V] <device>\n");
76 (void)fprintf(stderr, " -h print usage\n");
77 (void)fprintf(stderr, " -V print cwVersion\n");
78 exit(1);
79 }
80
main(int argc,char * argv[])81 int main(int argc, char *argv[])
82 {
83 struct timespec ts;
84 int fd;
85 int c;
86 char ts_buf[TIMESPEC_LEN];
87
88 while((c = getopt(argc, argv, "hV")) != -1) {
89 switch(c){
90 case 'h':
91 default:
92 usage();
93 exit(0);
94 case 'V':
95 (void)printf("%s: %s\n", argv[0], REVISION);
96 exit(EXIT_SUCCESS);
97 }
98 }
99 argc -= optind;
100 argv += optind;
101
102 if (argc != 1)
103 usage();
104
105 fd = open(argv[0], O_RDONLY);
106
107 if (fd == -1) {
108 (void)fprintf(stderr,
109 "open(%s) failed: %d %.40s\n",
110 argv[0], errno, strerror(errno));
111 exit(1);
112 }
113
114 (void)fprintf(stdout, "# Seconds nanoSecs Signals\n");
115 for (;;) {
116 if (ioctl(fd, TIOCMIWAIT, TIOCM_CD|TIOCM_DSR|TIOCM_RI|TIOCM_CTS) != 0) {
117 (void)fprintf(stderr,
118 "PPS ioctl(TIOCMIWAIT) failed: %d %.40s\n",
119 errno, strerror(errno));
120 break;
121 } else {
122 const struct assoc *sp;
123 int handshakes;
124
125 (void)clock_gettime(CLOCK_REALTIME, &ts);
126 (void)ioctl(fd, TIOCMGET, &handshakes);
127 (void)fprintf(stdout, "%s",
128 timespec_str(&ts, ts_buf, sizeof(ts_buf)));
129 for (sp = hlines;
130 sp < hlines + sizeof(hlines)/sizeof(hlines[0]);
131 sp++)
132 if ((handshakes & sp->mask) != 0) {
133 (void)fputc(' ', stdout);
134 (void)fputs(sp->string, stdout);
135 }
136 (void)fputc('\n', stdout);
137 }
138 }
139
140 exit(EXIT_SUCCESS);
141 }
142
143 /* end */
144