ppscheck.c 4.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173
  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 asserted.
  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 2013, under BSD terms.
  21. * This file is Copyright 2013 by the GPSD project
  22. * SPDX-License-Identifier: BSD-2-clause
  23. */
  24. #include "../include/gpsd_config.h" /* must be before all includes */
  25. #include <errno.h>
  26. #include <fcntl.h> /* needed for open() and friends */
  27. #ifdef HAVE_GETOPT_LONG
  28. #include <getopt.h>
  29. #endif
  30. #include <stdio.h>
  31. #include <stdlib.h>
  32. #include <string.h>
  33. #include <sys/ioctl.h>
  34. #include <sys/stat.h>
  35. #include <sys/types.h>
  36. #include <time.h>
  37. #include <unistd.h>
  38. #include "../include/timespec.h"
  39. struct assoc {
  40. int mask;
  41. char *string;
  42. };
  43. /*
  44. * Possible pins for PPS: DCD, CTS, RI, DSR. Pinouts:
  45. *
  46. * DB9 DB25 Name Full name
  47. * --- ---- ---- --------------------
  48. * 3 2 TXD --> Transmit Data
  49. * 2 3 RXD <-- Receive Data
  50. * 7 4 RTS --> Request To Send
  51. * 8 5 CTS <-- Clear To Send
  52. * 6 6 DSR <-- Data Set Ready
  53. * 4 20 DTR --> Data Terminal Ready
  54. * 1 8 DCD <-- Data Carrier Detect
  55. * 9 22 RI <-- Ring Indicator
  56. * 5 7 GND Signal ground
  57. *
  58. * Note that it only makes sense to wait on handshake lines
  59. * activated from the receive side (DCE->DTE) here; in this
  60. * context "DCE" is the GPS. {CD,RI,CTS,DSR} is the
  61. * entire set of these.
  62. */
  63. static const struct assoc hlines[] = {
  64. {TIOCM_CD, "TIOCM_CD"},
  65. {TIOCM_RI, "TIOCM_RI"},
  66. {TIOCM_DSR, "TIOCM_DSR"},
  67. {TIOCM_CTS, "TIOCM_CTS"},
  68. };
  69. static void usage(void)
  70. {
  71. (void)fprintf(stderr,
  72. "usage: ppscheck [OPTIONS] <device>\n\n"
  73. #ifdef HAVE_GETOPT_LONG
  74. " --help Show this help, then exit.\n"
  75. " --version Show version, then exit.\n"
  76. #endif
  77. " -? Show this help, then exit.\n"
  78. " -h Show this help, then exit.\n"
  79. " -V Show version, then exit.\n");
  80. exit(1);
  81. }
  82. int main(int argc, char *argv[])
  83. {
  84. struct timespec ts;
  85. int fd;
  86. int ch;
  87. char ts_buf[TIMESPEC_LEN];
  88. const char *optstring = "?hV";
  89. #ifdef HAVE_GETOPT_LONG
  90. int option_index = 0;
  91. static struct option long_options[] = {
  92. {"help", no_argument, NULL, 'h'},
  93. {"version", no_argument, NULL, 'V' },
  94. {NULL, 0, NULL, 0},
  95. };
  96. #endif
  97. while (1) {
  98. #ifdef HAVE_GETOPT_LONG
  99. ch = getopt_long(argc, argv, optstring, long_options, &option_index);
  100. #else
  101. ch = getopt(argc, argv, optstring);
  102. #endif
  103. if (ch == -1) {
  104. break;
  105. }
  106. switch(ch){
  107. case '?':
  108. case 'h':
  109. default:
  110. usage();
  111. exit(0);
  112. case 'V':
  113. (void)printf("%s: %s\n", argv[0], REVISION);
  114. exit(EXIT_SUCCESS);
  115. }
  116. }
  117. argc -= optind;
  118. argv += optind;
  119. if (argc != 1)
  120. usage();
  121. fd = open(argv[0], O_RDONLY);
  122. if (fd == -1) {
  123. (void)fprintf(stderr,
  124. "open(%s) failed: %d %.40s\n",
  125. argv[0], errno, strerror(errno));
  126. exit(1);
  127. }
  128. (void)fprintf(stdout, "# Seconds nanoSecs Signals\n");
  129. for (;;) {
  130. if (ioctl(fd, TIOCMIWAIT, TIOCM_CD|TIOCM_DSR|TIOCM_RI|TIOCM_CTS) != 0) {
  131. (void)fprintf(stderr,
  132. "PPS ioctl(TIOCMIWAIT) failed: %d %.40s\n",
  133. errno, strerror(errno));
  134. break;
  135. } else {
  136. const struct assoc *sp;
  137. int handshakes;
  138. (void)clock_gettime(CLOCK_REALTIME, &ts);
  139. (void)ioctl(fd, TIOCMGET, &handshakes);
  140. (void)fprintf(stdout, "%s",
  141. timespec_str(&ts, ts_buf, sizeof(ts_buf)));
  142. for (sp = hlines;
  143. sp < hlines + sizeof(hlines)/sizeof(hlines[0]);
  144. sp++)
  145. if ((handshakes & sp->mask) != 0) {
  146. (void)fputc(' ', stdout);
  147. (void)fputs(sp->string, stdout);
  148. }
  149. (void)fputc('\n', stdout);
  150. }
  151. }
  152. exit(EXIT_SUCCESS);
  153. }
  154. /* end */
  155. // vim: set expandtab shiftwidth=4