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
chronolog_start(int unit,struct peer * peer)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(&peer->srcadr, 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
chronolog_shutdown(int unit,struct peer * peer)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
chronolog_receive(struct recvbuf * rbufp)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
chronolog_poll(int unit,struct peer * peer)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 */
344