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