/contrib/ntp/ntpd/refclock_chronolog.c

https://bitbucket.org/freebsd/freebsd-head/ · C · 341 lines · 212 code · 38 blank · 91 comment · 25 complexity · 82808f8bca25dc4c5686799b1e656ec1 MD5 · raw file

  1. /*
  2. * refclock_chronolog - clock driver for Chronolog K-series WWVB receiver.
  3. */
  4. /*
  5. * Must interpolate back to local time. Very annoying.
  6. */
  7. #define GET_LOCALTIME
  8. #ifdef HAVE_CONFIG_H
  9. #include <config.h>
  10. #endif
  11. #if defined(REFCLOCK) && defined(CLOCK_CHRONOLOG)
  12. #include "ntpd.h"
  13. #include "ntp_io.h"
  14. #include "ntp_refclock.h"
  15. #include "ntp_calendar.h"
  16. #include "ntp_stdlib.h"
  17. #include <stdio.h>
  18. #include <ctype.h>
  19. /*
  20. * This driver supports the Chronolog K-series WWVB receiver.
  21. *
  22. * Input format:
  23. *
  24. * Y YY/MM/DD<cr><lf>
  25. * Z hh:mm:ss<cr><lf>
  26. *
  27. * YY/MM/DD -- what you'd expect. This arrives a few seconds before the
  28. * timestamp.
  29. * hh:mm:ss -- what you'd expect. We take time on the <cr>.
  30. *
  31. * Our Chronolog writes time out at 2400 bps 8/N/1, but it can be configured
  32. * otherwise. The clock seems to appear every 60 seconds, which doesn't make
  33. * for good statistics collection.
  34. *
  35. * The original source of this module was the WWVB module.
  36. */
  37. /*
  38. * Interface definitions
  39. */
  40. #define DEVICE "/dev/chronolog%d" /* device name and unit */
  41. #define SPEED232 B2400 /* uart speed (2400 baud) */
  42. #define PRECISION (-13) /* precision assumed (about 100 us) */
  43. #define REFID "chronolog" /* reference ID */
  44. #define DESCRIPTION "Chrono-log K" /* WRU */
  45. #define MONLIN 15 /* number of monitoring lines */
  46. /*
  47. * Chrono-log unit control structure
  48. */
  49. struct chronolog_unit {
  50. u_char tcswitch; /* timecode switch */
  51. l_fp laststamp; /* last receive timestamp */
  52. u_char lasthour; /* last hour (for monitor) */
  53. int year; /* Y2K-adjusted year */
  54. int day; /* day-of-month */
  55. int month; /* month-of-year */
  56. };
  57. /*
  58. * Function prototypes
  59. */
  60. static int chronolog_start P((int, struct peer *));
  61. static void chronolog_shutdown P((int, struct peer *));
  62. static void chronolog_receive P((struct recvbuf *));
  63. static void chronolog_poll P((int, struct peer *));
  64. /*
  65. * Transfer vector
  66. */
  67. struct refclock refclock_chronolog = {
  68. chronolog_start, /* start up driver */
  69. chronolog_shutdown, /* shut down driver */
  70. chronolog_poll, /* poll the driver -- a nice fabrication */
  71. noentry, /* not used */
  72. noentry, /* not used */
  73. noentry, /* not used */
  74. NOFLAGS /* not used */
  75. };
  76. /*
  77. * chronolog_start - open the devices and initialize data for processing
  78. */
  79. static int
  80. chronolog_start(
  81. int unit,
  82. struct peer *peer
  83. )
  84. {
  85. register struct chronolog_unit *up;
  86. struct refclockproc *pp;
  87. int fd;
  88. char device[20];
  89. /*
  90. * Open serial port. Don't bother with CLK line discipline, since
  91. * it's not available.
  92. */
  93. (void)sprintf(device, DEVICE, unit);
  94. #ifdef DEBUG
  95. if (debug)
  96. printf ("starting Chronolog with device %s\n",device);
  97. #endif
  98. if (!(fd = refclock_open(device, SPEED232, 0)))
  99. return (0);
  100. /*
  101. * Allocate and initialize unit structure
  102. */
  103. if (!(up = (struct chronolog_unit *)
  104. emalloc(sizeof(struct chronolog_unit)))) {
  105. (void) close(fd);
  106. return (0);
  107. }
  108. memset((char *)up, 0, sizeof(struct chronolog_unit));
  109. pp = peer->procptr;
  110. pp->unitptr = (caddr_t)up;
  111. pp->io.clock_recv = chronolog_receive;
  112. pp->io.srcclock = (caddr_t)peer;
  113. pp->io.datalen = 0;
  114. pp->io.fd = fd;
  115. if (!io_addclock(&pp->io)) {
  116. (void) close(fd);
  117. free(up);
  118. return (0);
  119. }
  120. /*
  121. * Initialize miscellaneous variables
  122. */
  123. peer->precision = PRECISION;
  124. pp->clockdesc = DESCRIPTION;
  125. memcpy((char *)&pp->refid, REFID, 4);
  126. return (1);
  127. }
  128. /*
  129. * chronolog_shutdown - shut down the clock
  130. */
  131. static void
  132. chronolog_shutdown(
  133. int unit,
  134. struct peer *peer
  135. )
  136. {
  137. register struct chronolog_unit *up;
  138. struct refclockproc *pp;
  139. pp = peer->procptr;
  140. up = (struct chronolog_unit *)pp->unitptr;
  141. io_closeclock(&pp->io);
  142. free(up);
  143. }
  144. /*
  145. * chronolog_receive - receive data from the serial interface
  146. */
  147. static void
  148. chronolog_receive(
  149. struct recvbuf *rbufp
  150. )
  151. {
  152. struct chronolog_unit *up;
  153. struct refclockproc *pp;
  154. struct peer *peer;
  155. l_fp trtmp; /* arrival timestamp */
  156. int hours; /* hour-of-day */
  157. int minutes; /* minutes-past-the-hour */
  158. int seconds; /* seconds */
  159. int temp; /* int temp */
  160. int got_good; /* got a good time flag */
  161. /*
  162. * Initialize pointers and read the timecode and timestamp
  163. */
  164. peer = (struct peer *)rbufp->recv_srcclock;
  165. pp = peer->procptr;
  166. up = (struct chronolog_unit *)pp->unitptr;
  167. temp = refclock_gtlin(rbufp, pp->a_lastcode, BMAX, &trtmp);
  168. if (temp == 0) {
  169. if (up->tcswitch == 0) {
  170. up->tcswitch = 1;
  171. up->laststamp = trtmp;
  172. } else
  173. up->tcswitch = 0;
  174. return;
  175. }
  176. pp->lencode = temp;
  177. pp->lastrec = up->laststamp;
  178. up->laststamp = trtmp;
  179. up->tcswitch = 1;
  180. #ifdef DEBUG
  181. if (debug)
  182. printf("chronolog: timecode %d %s\n", pp->lencode,
  183. pp->a_lastcode);
  184. #endif
  185. /*
  186. * We get down to business. Check the timecode format and decode
  187. * its contents. This code uses the first character to see whether
  188. * we're looking at a date or a time. We store data data across
  189. * calls since it is transmitted a few seconds ahead of the
  190. * timestamp.
  191. */
  192. got_good=0;
  193. if (sscanf(pp->a_lastcode, "Y %d/%d/%d", &up->year,&up->month,&up->day))
  194. {
  195. /*
  196. * Y2K convert the 2-digit year
  197. */
  198. up->year = up->year >= 69 ? up->year : up->year + 100;
  199. return;
  200. }
  201. if (sscanf(pp->a_lastcode,"Z %02d:%02d:%02d",
  202. &hours,&minutes,&seconds) == 3)
  203. {
  204. #ifdef GET_LOCALTIME
  205. struct tm local;
  206. struct tm *gmtp;
  207. time_t unixtime;
  208. int adjyear;
  209. int adjmon;
  210. /*
  211. * Convert to GMT for sites that distribute localtime. This
  212. * means we have to do Y2K conversion on the 2-digit year;
  213. * otherwise, we get the time wrong.
  214. */
  215. local.tm_year = up->year;
  216. local.tm_mon = up->month-1;
  217. local.tm_mday = up->day;
  218. local.tm_hour = hours;
  219. local.tm_min = minutes;
  220. local.tm_sec = seconds;
  221. local.tm_isdst = -1;
  222. unixtime = mktime (&local);
  223. if ((gmtp = gmtime (&unixtime)) == NULL)
  224. {
  225. refclock_report (peer, CEVNT_FAULT);
  226. return;
  227. }
  228. adjyear = gmtp->tm_year+1900;
  229. adjmon = gmtp->tm_mon+1;
  230. pp->day = ymd2yd (adjyear, adjmon, gmtp->tm_mday);
  231. pp->hour = gmtp->tm_hour;
  232. pp->minute = gmtp->tm_min;
  233. pp->second = gmtp->tm_sec;
  234. #ifdef DEBUG
  235. if (debug)
  236. printf ("time is %04d/%02d/%02d %02d:%02d:%02d UTC\n",
  237. adjyear,adjmon,gmtp->tm_mday,pp->hour,pp->minute,
  238. pp->second);
  239. #endif
  240. #else
  241. /*
  242. * For more rational sites distributing UTC
  243. */
  244. pp->day = ymd2yd(year+1900,month,day);
  245. pp->hour = hours;
  246. pp->minute = minutes;
  247. pp->second = seconds;
  248. #endif
  249. got_good=1;
  250. }
  251. if (!got_good)
  252. return;
  253. /*
  254. * Process the new sample in the median filter and determine the
  255. * timecode timestamp.
  256. */
  257. if (!refclock_process(pp)) {
  258. refclock_report(peer, CEVNT_BADTIME);
  259. return;
  260. }
  261. pp->lastref = pp->lastrec;
  262. refclock_receive(peer);
  263. record_clock_stats(&peer->srcadr, pp->a_lastcode);
  264. up->lasthour = pp->hour;
  265. }
  266. /*
  267. * chronolog_poll - called by the transmit procedure
  268. */
  269. static void
  270. chronolog_poll(
  271. int unit,
  272. struct peer *peer
  273. )
  274. {
  275. /*
  276. * Time to poll the clock. The Chrono-log clock is supposed to
  277. * respond to a 'T' by returning a timecode in the format(s)
  278. * specified above. Ours does (can?) not, but this seems to be
  279. * an installation-specific problem. This code is dyked out,
  280. * but may be re-enabled if anyone ever finds a Chrono-log that
  281. * actually listens to this command.
  282. */
  283. #if 0
  284. register struct chronolog_unit *up;
  285. struct refclockproc *pp;
  286. char pollchar;
  287. pp = peer->procptr;
  288. up = (struct chronolog_unit *)pp->unitptr;
  289. if (peer->burst == 0 && peer->reach == 0)
  290. refclock_report(peer, CEVNT_TIMEOUT);
  291. if (up->linect > 0)
  292. pollchar = 'R';
  293. else
  294. pollchar = 'T';
  295. if (write(pp->io.fd, &pollchar, 1) != 1)
  296. refclock_report(peer, CEVNT_FAULT);
  297. else
  298. pp->polls++;
  299. #endif
  300. }
  301. #else
  302. int refclock_chronolog_bs;
  303. #endif /* REFCLOCK */