]> CyberLeo.Net >> Repos - FreeBSD/releng/10.2.git/blob - contrib/ntp/ntpd/refclock_chronolog.c
- Copy stable/10@285827 to releng/10.2 in preparation for 10.2-RC1
[FreeBSD/releng/10.2.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         (int, struct peer *);
71 static  void    chronolog_shutdown      (int, struct peer *);
72 static  void    chronolog_receive       (struct recvbuf *);
73 static  void    chronolog_poll          (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         snprintf(device, sizeof(device), DEVICE, unit);
108 #ifdef DEBUG
109         if (debug)
110                 printf ("starting Chronolog with device %s\n",device);
111 #endif
112         fd = refclock_open(device, SPEED232, 0);
113         if (fd <= 0)
114                 return (0);
115
116         /*
117          * Allocate and initialize unit structure
118          */
119         up = emalloc_zero(sizeof(*up));
120         pp = peer->procptr;
121         pp->unitptr = up;
122         pp->io.clock_recv = chronolog_receive;
123         pp->io.srcclock = peer;
124         pp->io.datalen = 0;
125         pp->io.fd = fd;
126         if (!io_addclock(&pp->io)) {
127                 close(fd);
128                 pp->io.fd = -1;
129                 free(up);
130                 pp->unitptr = NULL;
131                 return (0);
132         }
133
134         /*
135          * Initialize miscellaneous variables
136          */
137         peer->precision = PRECISION;
138         pp->clockdesc = DESCRIPTION;
139         memcpy((char *)&pp->refid, REFID, 4);
140         return (1);
141 }
142
143
144 /*
145  * chronolog_shutdown - shut down the clock
146  */
147 static void
148 chronolog_shutdown(
149         int unit,
150         struct peer *peer
151         )
152 {
153         register struct chronolog_unit *up;
154         struct refclockproc *pp;
155
156         pp = peer->procptr;
157         up = pp->unitptr;
158         if (-1 != pp->io.fd)
159                 io_closeclock(&pp->io);
160         if (NULL != up)
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 = rbufp->recv_peer;
188         pp = peer->procptr;
189         up = 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             memset(&local, 0, sizeof(local));
244
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;
252
253             unixtime = mktime (&local);
254             if ((gmtp = gmtime (&unixtime)) == NULL)
255             {
256                 refclock_report (peer, CEVNT_FAULT);
257                 return;
258             }
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
271             
272 #else
273             /*
274              * For more rational sites distributing UTC
275              */
276             pp->day    = ymd2yd(year+1900,month,day);
277             pp->hour   = hours;
278             pp->minute = minutes;
279             pp->second = seconds;
280
281 #endif
282             got_good=1;
283         }
284
285         if (!got_good)
286             return;
287
288
289         /*
290          * Process the new sample in the median filter and determine the
291          * timecode timestamp.
292          */
293         if (!refclock_process(pp)) {
294                 refclock_report(peer, CEVNT_BADTIME);
295                 return;
296         }
297         pp->lastref = pp->lastrec;
298         refclock_receive(peer);
299         record_clock_stats(&peer->srcadr, pp->a_lastcode);
300         up->lasthour = (u_char)pp->hour;
301 }
302
303
304 /*
305  * chronolog_poll - called by the transmit procedure
306  */
307 static void
308 chronolog_poll(
309         int unit,
310         struct peer *peer
311         )
312 {
313         /*
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.
320          */
321 #if 0
322         register struct chronolog_unit *up;
323         struct refclockproc *pp;
324         char pollchar;
325
326         pp = peer->procptr;
327         up = 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
339 }
340
341 #else
342 int refclock_chronolog_bs;
343 #endif /* REFCLOCK */