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