]> CyberLeo.Net >> Repos - FreeBSD/releng/10.0.git/blob - contrib/ntp/ntpd/refclock_chronolog.c
- Copy stable/10 (r259064) to releng/10.0 as part of the
[FreeBSD/releng/10.0.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         got_good=0;
219         if (sscanf(pp->a_lastcode, "Y %d/%d/%d", &up->year,&up->month,&up->day))
220         {
221             /*
222              * Y2K convert the 2-digit year
223              */
224             up->year = up->year >= 69 ? up->year : up->year + 100;
225             return;
226         }
227         if (sscanf(pp->a_lastcode,"Z %02d:%02d:%02d",
228                    &hours,&minutes,&seconds) == 3)
229         {
230 #ifdef GET_LOCALTIME
231             struct tm  local;
232             struct tm *gmtp;
233             time_t     unixtime;
234             int        adjyear;
235             int        adjmon;
236
237             /*
238              * Convert to GMT for sites that distribute localtime.  This
239              * means we have to do Y2K conversion on the 2-digit year;
240              * otherwise, we get the time wrong.
241              */
242             
243             local.tm_year  = up->year;
244             local.tm_mon   = up->month-1;
245             local.tm_mday  = up->day;
246             local.tm_hour  = hours;
247             local.tm_min   = minutes;
248             local.tm_sec   = seconds;
249             local.tm_isdst = -1;
250
251             unixtime = mktime (&local);
252             if ((gmtp = gmtime (&unixtime)) == NULL)
253             {
254                 refclock_report (peer, CEVNT_FAULT);
255                 return;
256             }
257             adjyear = gmtp->tm_year+1900;
258             adjmon  = gmtp->tm_mon+1;
259             pp->day = ymd2yd (adjyear, adjmon, gmtp->tm_mday);
260             pp->hour   = gmtp->tm_hour;
261             pp->minute = gmtp->tm_min;
262             pp->second = gmtp->tm_sec;
263 #ifdef DEBUG
264             if (debug)
265                 printf ("time is %04d/%02d/%02d %02d:%02d:%02d UTC\n",
266                         adjyear,adjmon,gmtp->tm_mday,pp->hour,pp->minute,
267                         pp->second);
268 #endif
269             
270 #else
271             /*
272              * For more rational sites distributing UTC
273              */
274             pp->day    = ymd2yd(year+1900,month,day);
275             pp->hour   = hours;
276             pp->minute = minutes;
277             pp->second = seconds;
278
279 #endif
280             got_good=1;
281         }
282
283         if (!got_good)
284             return;
285
286
287         /*
288          * Process the new sample in the median filter and determine the
289          * timecode timestamp.
290          */
291         if (!refclock_process(pp)) {
292                 refclock_report(peer, CEVNT_BADTIME);
293                 return;
294         }
295         pp->lastref = pp->lastrec;
296         refclock_receive(peer);
297         record_clock_stats(&peer->srcadr, pp->a_lastcode);
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 */