/contrib/ntp/ntpd/refclock_trak.c

https://bitbucket.org/freebsd/freebsd-head/ · C · 359 lines · 197 code · 31 blank · 131 comment · 25 complexity · 3baf4a12b188ebe2ebdb9a9c7454fca3 MD5 · raw file

  1. /*
  2. * refclock_trak - clock driver for the TRAK 8810 GPS Station Clock
  3. *
  4. * Tomoaki TSURUOKA <tsuruoka@nc.fukuoka-u.ac.jp>
  5. * original version Dec, 1993
  6. * revised version Sep, 1994 for ntp3.4e or later
  7. */
  8. #ifdef HAVE_CONFIG_H
  9. #include <config.h>
  10. #endif
  11. #if defined(REFCLOCK) && defined(CLOCK_TRAK) && defined(PPS)
  12. #include "ntpd.h"
  13. #include "ntp_io.h"
  14. #include "ntp_refclock.h"
  15. #include "ntp_stdlib.h"
  16. #include "ntp_unixtime.h"
  17. #include <stdio.h>
  18. #include <ctype.h>
  19. #ifdef HAVE_SYS_TERMIOS_H
  20. # include <sys/termios.h>
  21. #endif
  22. #ifdef HAVE_SYS_PPSCLOCK_H
  23. # include <sys/ppsclock.h>
  24. #endif
  25. /*
  26. * This driver supports the TRAK 8810/8820 GPS Station Clock. The claimed
  27. * accuracy at the 1-pps output is 200-300 ns relative to the broadcast
  28. * signal; however, in most cases the actual accuracy is limited by the
  29. * precision of the timecode and the latencies of the serial interface
  30. * and operating system.
  31. *
  32. * For best accuracy, this radio requires the LDISC_ACTS line
  33. * discipline, which captures a timestamp at the '*' on-time character
  34. * of the timecode. Using this discipline the jitter is in the order of
  35. * 1 ms and systematic error about 0.5 ms. If unavailable, the buffer
  36. * timestamp is used, which is captured at the \r ending the timecode
  37. * message. This introduces a systematic error of 23 character times, or
  38. * about 24 ms at 9600 bps, together with a jitter well over 8 ms on Sun
  39. * IPC-class machines.
  40. *
  41. * Using the memus, the radio should be set for 9600 bps, one stop bit
  42. * and no parity. It should be set to operate in computer (no echo)
  43. * mode. The timecode format includes neither the year nor leap-second
  44. * warning. No provisions are included in this preliminary version of
  45. * the driver to read and record detailed internal radio status.
  46. *
  47. * In operation, this driver sends a RQTS\r request to the radio at
  48. * initialization in order to put it in continuous time output mode. The
  49. * radio then sends the following message once each second:
  50. *
  51. * *RQTS U,ddd:hh:mm:ss.0,q<cr><lf>
  52. *
  53. * on-time = '*' * ddd = day of year
  54. * hh:mm:ss = hours, minutes, seconds
  55. * q = quality indicator (phase error), 0-6:
  56. * 0 > 20 us
  57. * 6 > 10 us
  58. * 5 > 1 us
  59. * 4 > 100 ns
  60. * 3 > 10 ns
  61. * 2 < 10 ns
  62. *
  63. * The alarm condition is indicated by '0' at Q, which means the radio
  64. * has a phase error than 20 usec relative to the broadcast time. The
  65. * absence of year, DST and leap-second warning in this format is also
  66. * alarming.
  67. *
  68. * The continuous time mode is disabled using the RQTX<cr> request,
  69. * following which the radio sends a RQTX DONE<cr><lf> response. In the
  70. * normal mode, other control and status requests are effective,
  71. * including the leap-second status request RQLS<cr>. The radio responds
  72. * wtih RQLS yy,mm,dd<cr><lf>, where yy,mm,dd are the year, month and
  73. * day. Presumably, this gives the epoch of the next leap second,
  74. * RQLS 00,00,00 if none is specified in the GPS message. Specified in
  75. * this form, the information is generally useless and is ignored by
  76. * the driver.
  77. *
  78. * Fudge Factors
  79. *
  80. * There are no special fudge factors other than the generic.
  81. */
  82. /*
  83. * Interface definitions
  84. */
  85. #define DEVICE "/dev/trak%d" /* device name and unit */
  86. #define SPEED232 B9600 /* uart speed (9600 baud) */
  87. #define PRECISION (-20) /* precision assumed (about 1 us) */
  88. #define REFID "GPS\0" /* reference ID */
  89. #define DESCRIPTION "TRACK 8810/8820 Station Clock" /* WRU */
  90. #define LENTRAK 24 /* timecode length */
  91. #define C_CTO "RQTS\r" /* start continuous time output */
  92. /*
  93. * Unit control structure
  94. */
  95. struct trakunit {
  96. int polled; /* poll message flag */
  97. l_fp tstamp; /* timestamp of last poll */
  98. };
  99. /*
  100. * Function prototypes
  101. */
  102. static int trak_start P((int, struct peer *));
  103. static void trak_shutdown P((int, struct peer *));
  104. static void trak_receive P((struct recvbuf *));
  105. static void trak_poll P((int, struct peer *));
  106. /*
  107. * Transfer vector
  108. */
  109. struct refclock refclock_trak = {
  110. trak_start, /* start up driver */
  111. trak_shutdown, /* shut down driver */
  112. trak_poll, /* transmit poll message */
  113. noentry, /* not used (old trak_control) */
  114. noentry, /* initialize driver (not used) */
  115. noentry, /* not used (old trak_buginfo) */
  116. NOFLAGS /* not used */
  117. };
  118. /*
  119. * trak_start - open the devices and initialize data for processing
  120. */
  121. static int
  122. trak_start(
  123. int unit,
  124. struct peer *peer
  125. )
  126. {
  127. register struct trakunit *up;
  128. struct refclockproc *pp;
  129. int fd;
  130. char device[20];
  131. /*
  132. * Open serial port. The LDISC_ACTS line discipline inserts a
  133. * timestamp following the "*" on-time character of the
  134. * timecode.
  135. */
  136. (void)sprintf(device, DEVICE, unit);
  137. if (
  138. #ifdef PPS
  139. !(fd = refclock_open(device, SPEED232, LDISC_CLK))
  140. #else
  141. !(fd = refclock_open(device, SPEED232, 0))
  142. #endif /* PPS */
  143. )
  144. return (0);
  145. /*
  146. * Allocate and initialize unit structure
  147. */
  148. if (!(up = (struct trakunit *)
  149. emalloc(sizeof(struct trakunit)))) {
  150. (void) close(fd);
  151. return (0);
  152. }
  153. memset((char *)up, 0, sizeof(struct trakunit));
  154. pp = peer->procptr;
  155. pp->io.clock_recv = trak_receive;
  156. pp->io.srcclock = (caddr_t)peer;
  157. pp->io.datalen = 0;
  158. pp->io.fd = fd;
  159. if (!io_addclock(&pp->io)) {
  160. (void) close(fd);
  161. free(up);
  162. return (0);
  163. }
  164. pp->unitptr = (caddr_t)up;
  165. /*
  166. * Initialize miscellaneous variables
  167. */
  168. peer->precision = PRECISION;
  169. pp->clockdesc = DESCRIPTION;
  170. memcpy((char *)&pp->refid, REFID, 4);
  171. up->polled = 0;
  172. /*
  173. * Start continuous time output. If something breaks, fold the
  174. * tent and go home.
  175. */
  176. if (write(pp->io.fd, C_CTO, sizeof(C_CTO)) != sizeof(C_CTO)) {
  177. refclock_report(peer, CEVNT_FAULT);
  178. (void) close(fd);
  179. free(up);
  180. return (0);
  181. }
  182. return (1);
  183. }
  184. /*
  185. * trak_shutdown - shut down the clock
  186. */
  187. static void
  188. trak_shutdown(
  189. int unit,
  190. struct peer *peer
  191. )
  192. {
  193. register struct trakunit *up;
  194. struct refclockproc *pp;
  195. pp = peer->procptr;
  196. up = (struct trakunit *)pp->unitptr;
  197. io_closeclock(&pp->io);
  198. free(up);
  199. }
  200. /*
  201. * trak_receive - receive data from the serial interface
  202. */
  203. static void
  204. trak_receive(
  205. struct recvbuf *rbufp
  206. )
  207. {
  208. register struct trakunit *up;
  209. struct refclockproc *pp;
  210. struct peer *peer;
  211. l_fp trtmp;
  212. char *dpt, *dpend;
  213. char qchar;
  214. #ifdef PPS
  215. struct ppsclockev ppsev;
  216. int request;
  217. #ifdef HAVE_CIOGETEV
  218. request = CIOGETEV;
  219. #endif
  220. #ifdef HAVE_TIOCGPPSEV
  221. request = TIOCGPPSEV;
  222. #endif
  223. #endif /* PPS */
  224. /*
  225. * Initialize pointers and read the timecode and timestamp. We
  226. * then chuck out everything, including runts, except one
  227. * message each poll interval.
  228. */
  229. peer = (struct peer *)rbufp->recv_srcclock;
  230. pp = peer->procptr;
  231. up = (struct trakunit *)pp->unitptr;
  232. pp->lencode = refclock_gtlin(rbufp, pp->a_lastcode, BMAX,
  233. &pp->lastrec);
  234. /*
  235. * We get a buffer and timestamp following the '*' on-time
  236. * character. If a valid timestamp, we use that in place of the
  237. * buffer timestamp and edit out the timestamp for prettyprint
  238. * billboards.
  239. */
  240. dpt = pp->a_lastcode;
  241. dpend = dpt + pp->lencode;
  242. if (*dpt == '*' && buftvtots(dpt + 1, &trtmp)) {
  243. if (trtmp.l_i == pp->lastrec.l_i || trtmp.l_i ==
  244. pp->lastrec.l_i + 1) {
  245. pp->lastrec = trtmp;
  246. dpt += 9;
  247. while (dpt < dpend) {
  248. *(dpt - 8) = *dpt;
  249. ++dpt;
  250. }
  251. }
  252. }
  253. if (up->polled == 0) return;
  254. up->polled = 0;
  255. #ifndef PPS
  256. get_systime(&up->tstamp);
  257. #endif
  258. record_clock_stats(&peer->srcadr, pp->a_lastcode);
  259. #ifdef DEBUG
  260. if (debug)
  261. printf("trak: timecode %d %s\n", pp->lencode,
  262. pp->a_lastcode);
  263. #endif
  264. /*
  265. * We get down to business, check the timecode format and decode
  266. * its contents. If the timecode has invalid length or is not in
  267. * proper format, we declare bad format and exit.
  268. */
  269. if (pp->lencode < LENTRAK) {
  270. refclock_report(peer, CEVNT_BADREPLY);
  271. return;
  272. }
  273. /*
  274. * Timecode format: "*RQTS U,ddd:hh:mm:ss.0,q"
  275. */
  276. if (sscanf(pp->a_lastcode, "*RQTS U,%3d:%2d:%2d:%2d.0,%c",
  277. &pp->day, &pp->hour, &pp->minute, &pp->second, &qchar) != 5) {
  278. refclock_report(peer, CEVNT_BADREPLY);
  279. return;
  280. }
  281. /*
  282. * Decode quality and leap characters. If unsynchronized, set
  283. * the leap bits accordingly and exit.
  284. */
  285. if (qchar == '0') {
  286. pp->leap = LEAP_NOTINSYNC;
  287. return;
  288. }
  289. #ifdef PPS
  290. if(ioctl(fdpps,request,(caddr_t) &ppsev) >=0) {
  291. ppsev.tv.tv_sec += (u_int32) JAN_1970;
  292. TVTOTS(&ppsev.tv,&up->tstamp);
  293. }
  294. #endif /* PPS */
  295. /* record the last ppsclock event time stamp */
  296. pp->lastrec = up->tstamp;
  297. if (!refclock_process(pp)) {
  298. refclock_report(peer, CEVNT_BADTIME);
  299. return;
  300. }
  301. pp->lastref = pp->lastrec;
  302. refclock_receive(peer);
  303. }
  304. /*
  305. * trak_poll - called by the transmit procedure
  306. */
  307. static void
  308. trak_poll(
  309. int unit,
  310. struct peer *peer
  311. )
  312. {
  313. register struct trakunit *up;
  314. struct refclockproc *pp;
  315. /*
  316. * We don't really do anything here, except arm the receiving
  317. * side to capture a sample and check for timeouts.
  318. */
  319. pp = peer->procptr;
  320. up = (struct trakunit *)pp->unitptr;
  321. if (up->polled)
  322. refclock_report(peer, CEVNT_TIMEOUT);
  323. pp->polls++;
  324. up->polled = 1;
  325. }
  326. #else
  327. int refclock_trak_bs;
  328. #endif /* REFCLOCK */