/src/ntp-dev/ntpd/refclock_true.c
Line | Count | Source (jump to first uncovered line) |
1 | | /* |
2 | | * refclock_true - clock driver for the Kinemetrics/TrueTime receivers |
3 | | * Receiver Version 3.0C - tested plain, with CLKLDISC |
4 | | * Development work being done: |
5 | | * - Support TL-3 WWV TOD receiver |
6 | | */ |
7 | | |
8 | | #ifdef HAVE_CONFIG_H |
9 | | #include <config.h> |
10 | | #endif |
11 | | |
12 | | #if defined(REFCLOCK) && defined(CLOCK_TRUETIME) |
13 | | |
14 | | #include <stdio.h> |
15 | | #include <ctype.h> |
16 | | |
17 | | #include "ntpd.h" |
18 | | #include "ntp_io.h" |
19 | | #include "ntp_refclock.h" |
20 | | #include "ntp_unixtime.h" |
21 | | #include "ntp_stdlib.h" |
22 | | |
23 | | #ifdef SYS_WINNT |
24 | | extern int async_write(int, const void *, unsigned int); |
25 | | #undef write |
26 | | #define write(fd, data, octets) async_write(fd, data, octets) |
27 | | #endif |
28 | | |
29 | | /* This should be an atom clock but those are very hard to build. |
30 | | * |
31 | | * The PCL720 from P C Labs has an Intel 8253 lookalike, as well as a bunch |
32 | | * of TTL input and output pins, all brought out to the back panel. If you |
33 | | * wire a PPS signal (such as the TTL PPS coming out of a GOES or other |
34 | | * Kinemetrics/Truetime clock) to the 8253's GATE0, and then also wire the |
35 | | * 8253's OUT0 to the PCL720's INPUT3.BIT0, then we can read CTR0 to get the |
36 | | * number of uSecs since the last PPS upward swing, mediated by reading OUT0 |
37 | | * to find out if the counter has wrapped around (this happens if more than |
38 | | * 65535us (65ms) elapses between the PPS event and our being called.) |
39 | | */ |
40 | | #ifdef CLOCK_PPS720 |
41 | | # undef min /* XXX */ |
42 | | # undef max /* XXX */ |
43 | | # include <machine/inline.h> |
44 | | # include <sys/pcl720.h> |
45 | | # include <sys/i8253.h> |
46 | | # define PCL720_IOB 0x2a0 /* XXX */ |
47 | | # define PCL720_CTR 0 /* XXX */ |
48 | | #endif |
49 | | |
50 | | /* |
51 | | * Support for Kinemetrics Truetime Receivers |
52 | | * GOES: (468-DC, usable with GPS->GOES converting antenna) |
53 | | * GPS/TM-TMD: |
54 | | * XL-DC: (a 151-602-210, reported by the driver as a GPS/TM-TMD) |
55 | | * GPS-800 TCU: (an 805-957 with the RS232 Talker/Listener module) |
56 | | * TL-3: 3 channel WWV/H receiver w/ IRIG and RS-232 outputs |
57 | | * OM-DC: getting stale ("OMEGA") |
58 | | * |
59 | | * Most of this code is originally from refclock_wwvb.c with thanks. |
60 | | * It has been so mangled that wwvb is not a recognizable ancestor. |
61 | | * |
62 | | * Timcode format: ADDD:HH:MM:SSQCL |
63 | | * A - control A (this is stripped before we see it) |
64 | | * Q - Quality indication (see below) |
65 | | * C - Carriage return |
66 | | * L - Line feed |
67 | | * |
68 | | * Quality codes indicate possible error of |
69 | | * 468-DC GOES Receiver: |
70 | | * GPS-TM/TMD Receiver: (default quality codes for XL-DC) |
71 | | * ? +/- 1 milliseconds # +/- 100 microseconds |
72 | | * * +/- 10 microseconds . +/- 1 microsecond |
73 | | * space less than 1 microsecond |
74 | | * TL-3 Receiver: (default quality codes for TL-3) |
75 | | * ? unknown quality (receiver is unlocked) |
76 | | * space +/- 5 milliseconds |
77 | | * OM-DC OMEGA Receiver: (default quality codes for OMEGA) |
78 | | * WARNING OMEGA navigation system is no longer existent |
79 | | * > >+- 5 seconds |
80 | | * ? >+/- 500 milliseconds # >+/- 50 milliseconds |
81 | | * * >+/- 5 milliseconds . >+/- 1 millisecond |
82 | | * A-H less than 1 millisecond. Character indicates which station |
83 | | * is being received as follows: |
84 | | * A = Norway, B = Liberia, C = Hawaii, D = North Dakota, |
85 | | * E = La Reunion, F = Argentina, G = Australia, H = Japan. |
86 | | * |
87 | | * The carriage return start bit begins on 0 seconds and extends to 1 bit time. |
88 | | * |
89 | | * Notes on 468-DC and OMEGA receiver: |
90 | | * |
91 | | * Send the clock a 'R' or 'C' and once per second a timestamp will |
92 | | * appear. Send a 'P' to get the satellite position once (GOES only.) |
93 | | * |
94 | | * Notes on the 468-DC receiver: |
95 | | * |
96 | | * Since the old east/west satellite locations are only historical, you can't |
97 | | * set your clock propagation delay settings correctly and still use |
98 | | * automatic mode. The manual says to use a compromise when setting the |
99 | | * switches. This results in significant errors. The solution; use fudge |
100 | | * time1 and time2 to incorporate corrections. If your clock is set for |
101 | | * 50 and it should be 58 for using the west and 46 for using the east, |
102 | | * use the line |
103 | | * |
104 | | * fudge 127.127.5.0 time1 +0.008 time2 -0.004 |
105 | | * |
106 | | * This corrects the 4 milliseconds advance and 8 milliseconds retard |
107 | | * needed. The software will ask the clock which satellite it sees. |
108 | | * |
109 | | * Notes on the TrueTime TimeLink TL-3 WWV TOD receiver: |
110 | | * |
111 | | * This clock may be polled, or send one timecode per second. |
112 | | * That mode may be toggled via the front panel ("C" mode), or controlled |
113 | | * from the RS-232 port. Send the receiver "ST1" to turn it on, and |
114 | | * "ST0" to turn it off. Send "QV" to get the firmware revision (useful |
115 | | * for identifying this model.) |
116 | | * |
117 | | * Note that it can take several polling cycles, especially if the receiver |
118 | | * was in the continuous timecode mode. (It can be slow to leave that mode.) |
119 | | * |
120 | | * ntp.conf parameters: |
121 | | * time1 - offset applied to samples when reading WEST satellite (default = 0) |
122 | | * time2 - offset applied to samples when reading EAST satellite (default = 0) |
123 | | * stratum - stratum to assign to this clock (default = 0) |
124 | | * refid - refid assigned to this clock (default = "TRUE", see below) |
125 | | * flag1 - will silence the clock side of ntpd, just reading the clock |
126 | | * without trying to write to it. (default = 0) |
127 | | * flag2 - generate a debug file /tmp/true%d. |
128 | | * flag3 - enable ppsclock streams module |
129 | | * flag4 - use the PCL-720 (BSD/OS only) |
130 | | */ |
131 | | |
132 | | |
133 | | /* |
134 | | * Definitions |
135 | | */ |
136 | | #define DEVICE "/dev/true%d" |
137 | 0 | #define SPEED232 B9600 /* 9600 baud */ |
138 | | |
139 | | /* |
140 | | * Radio interface parameters |
141 | | */ |
142 | 0 | #define PRECISION (-10) /* precision assumed (about 1 ms) */ |
143 | 0 | #define REFID "TRUE" /* reference id */ |
144 | 0 | #define DESCRIPTION "Kinemetrics/TrueTime Receiver" |
145 | | |
146 | | /* |
147 | | * Tags which station (satellite) we see |
148 | | */ |
149 | 0 | #define GOES_WEST 0 /* Default to WEST satellite and apply time1 */ |
150 | 0 | #define GOES_EAST 1 /* until you discover otherwise */ |
151 | | |
152 | | /* |
153 | | * used by the state machine |
154 | | */ |
155 | | enum true_event {e_Init, e_Huh, e_F18, e_F50, e_F51, e_Satellite, |
156 | | e_TL3, e_Poll, e_Location, e_TS, e_Max}; |
157 | | const char *events[] = {"Init", "Huh", "F18", "F50", "F51", "Satellite", |
158 | | "TL3", "Poll", "Location", "TS"}; |
159 | 0 | #define eventStr(x) (((int)x<(int)e_Max) ? events[(int)x] : "?") |
160 | | |
161 | | enum true_state {s_Base, s_InqTM, s_InqTCU, s_InqOmega, s_InqGOES, |
162 | | s_InqTL3, s_Init, s_F18, s_F50, s_Start, s_Auto, s_Max}; |
163 | | const char *states[] = {"Base", "InqTM", "InqTCU", "InqOmega", "InqGOES", |
164 | | "InqTL3", "Init", "F18", "F50", "Start", "Auto"}; |
165 | 0 | #define stateStr(x) (((int)x<(int)s_Max) ? states[(int)x] : "?") |
166 | | |
167 | | enum true_type {t_unknown, t_goes, t_tm, t_tcu, t_omega, t_tl3, t_Max}; |
168 | | const char *types[] = {"unknown", "goes", "tm", "tcu", "omega", "tl3"}; |
169 | 0 | #define typeStr(x) (((int)x<(int)t_Max) ? types[(int)x] : "?") |
170 | | |
171 | | /* |
172 | | * unit control structure |
173 | | */ |
174 | | struct true_unit { |
175 | | unsigned int pollcnt; /* poll message counter */ |
176 | | unsigned int station; /* which station we are on */ |
177 | | unsigned int polled; /* Hand in a time sample? */ |
178 | | enum true_state state; /* state machine */ |
179 | | enum true_type type; /* what kind of clock is it? */ |
180 | | int unit; /* save an extra copy of this */ |
181 | | FILE *debug; /* debug logging file */ |
182 | | #ifdef CLOCK_PPS720 |
183 | | int pcl720init; /* init flag for PCL 720 */ |
184 | | #endif |
185 | | }; |
186 | | |
187 | | /* |
188 | | * Function prototypes |
189 | | */ |
190 | | static int true_start (int, struct peer *); |
191 | | static void true_shutdown (int, struct peer *); |
192 | | static void true_receive (struct recvbuf *); |
193 | | static void true_poll (int, struct peer *); |
194 | | static void true_send (struct peer *, const char *); |
195 | | static void true_doevent (struct peer *, enum true_event); |
196 | | |
197 | | #ifdef CLOCK_PPS720 |
198 | | static u_long true_sample720 (void); |
199 | | #endif |
200 | | |
201 | | /* |
202 | | * Transfer vector |
203 | | */ |
204 | | struct refclock refclock_true = { |
205 | | true_start, /* start up driver */ |
206 | | true_shutdown, /* shut down driver */ |
207 | | true_poll, /* transmit poll message */ |
208 | | noentry, /* not used (old true_control) */ |
209 | | noentry, /* initialize driver (not used) */ |
210 | | noentry, /* not used (old true_buginfo) */ |
211 | | NOFLAGS /* not used */ |
212 | | }; |
213 | | |
214 | | |
215 | | #if !defined(__STDC__) |
216 | | # define true_debug (void) |
217 | | #else |
218 | | NTP_PRINTF(2, 3) |
219 | | static void |
220 | | true_debug(struct peer *peer, const char *fmt, ...) |
221 | 0 | { |
222 | 0 | va_list ap; |
223 | 0 | int want_debugging, now_debugging; |
224 | 0 | struct refclockproc *pp; |
225 | 0 | struct true_unit *up; |
226 | |
|
227 | 0 | va_start(ap, fmt); |
228 | 0 | pp = peer->procptr; |
229 | 0 | up = pp->unitptr; |
230 | |
|
231 | 0 | want_debugging = (pp->sloppyclockflag & CLK_FLAG2) != 0; |
232 | 0 | now_debugging = (up->debug != NULL); |
233 | 0 | if (want_debugging != now_debugging) |
234 | 0 | { |
235 | 0 | if (want_debugging) { |
236 | 0 | char filename[40]; |
237 | 0 | int fd; |
238 | |
|
239 | 0 | snprintf(filename, sizeof(filename), |
240 | 0 | "/tmp/true%d.debug", up->unit); |
241 | 0 | fd = open(filename, O_CREAT | O_WRONLY | O_EXCL, |
242 | 0 | 0600); |
243 | 0 | if (fd >= 0 && (up->debug = fdopen(fd, "w"))) { |
244 | 0 | #ifdef HAVE_SETVBUF |
245 | 0 | static char buf[BUFSIZ]; |
246 | |
|
247 | 0 | setvbuf(up->debug, buf, _IOLBF, BUFSIZ); |
248 | | #else |
249 | | setlinebuf(up->debug); |
250 | | #endif |
251 | 0 | } |
252 | 0 | } else { |
253 | 0 | fclose(up->debug); |
254 | 0 | up->debug = NULL; |
255 | 0 | } |
256 | 0 | } |
257 | |
|
258 | 0 | if (up->debug) { |
259 | 0 | fprintf(up->debug, "true%d: ", up->unit); |
260 | 0 | vfprintf(up->debug, fmt, ap); |
261 | 0 | } |
262 | 0 | va_end(ap); |
263 | 0 | } |
264 | | #endif /*STDC*/ |
265 | | |
266 | | /* |
267 | | * true_start - open the devices and initialize data for processing |
268 | | */ |
269 | | static int |
270 | | true_start( |
271 | | int unit, |
272 | | struct peer *peer |
273 | | ) |
274 | 0 | { |
275 | 0 | register struct true_unit *up; |
276 | 0 | struct refclockproc *pp; |
277 | 0 | char device[40]; |
278 | 0 | int fd; |
279 | | |
280 | | /* |
281 | | * Open serial port |
282 | | */ |
283 | 0 | snprintf(device, sizeof(device), DEVICE, unit); |
284 | 0 | fd = refclock_open(device, SPEED232, LDISC_CLK); |
285 | 0 | if (fd <= 0) |
286 | 0 | return 0; |
287 | | |
288 | | /* |
289 | | * Allocate and initialize unit structure |
290 | | */ |
291 | 0 | up = emalloc_zero(sizeof(*up)); |
292 | 0 | pp = peer->procptr; |
293 | 0 | pp->io.clock_recv = true_receive; |
294 | 0 | pp->io.srcclock = peer; |
295 | 0 | pp->io.datalen = 0; |
296 | 0 | pp->io.fd = fd; |
297 | 0 | if (!io_addclock(&pp->io)) { |
298 | 0 | close(fd); |
299 | 0 | pp->io.fd = -1; |
300 | 0 | free(up); |
301 | 0 | return (0); |
302 | 0 | } |
303 | 0 | pp->unitptr = up; |
304 | | |
305 | | /* |
306 | | * Initialize miscellaneous variables |
307 | | */ |
308 | 0 | peer->precision = PRECISION; |
309 | 0 | pp->clockdesc = DESCRIPTION; |
310 | 0 | memcpy(&pp->refid, REFID, 4); |
311 | 0 | up->pollcnt = 2; |
312 | 0 | up->type = t_unknown; |
313 | 0 | up->state = s_Base; |
314 | | |
315 | | /* |
316 | | * Send a CTRL-C character at the start, |
317 | | * just in case the clock is already |
318 | | * sending timecodes |
319 | | */ |
320 | 0 | true_send(peer, "\03\r"); |
321 | | |
322 | 0 | true_doevent(peer, e_Init); |
323 | |
|
324 | 0 | return (1); |
325 | 0 | } |
326 | | |
327 | | |
328 | | /* |
329 | | * true_shutdown - shut down the clock |
330 | | */ |
331 | | static void |
332 | | true_shutdown( |
333 | | int unit, |
334 | | struct peer *peer |
335 | | ) |
336 | 0 | { |
337 | 0 | register struct true_unit *up; |
338 | 0 | struct refclockproc *pp; |
339 | |
|
340 | 0 | pp = peer->procptr; |
341 | 0 | up = pp->unitptr; |
342 | 0 | if (pp->io.fd != -1) |
343 | 0 | io_closeclock(&pp->io); |
344 | 0 | if (up != NULL) |
345 | 0 | free(up); |
346 | 0 | } |
347 | | |
348 | | |
349 | | /* |
350 | | * true_receive - receive data from the serial interface on a clock |
351 | | */ |
352 | | static void |
353 | | true_receive( |
354 | | struct recvbuf *rbufp |
355 | | ) |
356 | 0 | { |
357 | 0 | register struct true_unit *up; |
358 | 0 | struct refclockproc *pp; |
359 | 0 | struct peer *peer; |
360 | 0 | u_short new_station; |
361 | 0 | char synced; |
362 | 0 | int i; |
363 | 0 | int lat, lon, off; /* GOES Satellite position */ |
364 | | /* These variables hold data until we decide to keep it */ |
365 | 0 | char rd_lastcode[BMAX]; |
366 | 0 | l_fp rd_tmp; |
367 | 0 | u_short rd_lencode; |
368 | | |
369 | | /* |
370 | | * Get the clock this applies to and pointers to the data. |
371 | | */ |
372 | 0 | peer = rbufp->recv_peer; |
373 | 0 | pp = peer->procptr; |
374 | 0 | up = pp->unitptr; |
375 | | |
376 | | /* |
377 | | * Read clock output. Automatically handles STREAMS, CLKLDISC. |
378 | | */ |
379 | 0 | rd_lencode = refclock_gtlin(rbufp, rd_lastcode, BMAX, &rd_tmp); |
380 | 0 | rd_lastcode[rd_lencode] = '\0'; |
381 | | |
382 | | /* |
383 | | * There is a case where <cr><lf> generates 2 timestamps. |
384 | | */ |
385 | 0 | if (rd_lencode == 0) |
386 | 0 | return; |
387 | 0 | pp->lencode = rd_lencode; |
388 | 0 | strlcpy(pp->a_lastcode, rd_lastcode, sizeof(pp->a_lastcode)); |
389 | 0 | pp->lastrec = rd_tmp; |
390 | 0 | true_debug(peer, "receive(%s) [%d]\n", pp->a_lastcode, |
391 | 0 | pp->lencode); |
392 | |
|
393 | 0 | up->pollcnt = 2; |
394 | 0 | record_clock_stats(&peer->srcadr, pp->a_lastcode); |
395 | | |
396 | | /* |
397 | | * We get down to business, check the timecode format and decode |
398 | | * its contents. This code decodes a multitude of different |
399 | | * clock messages. Timecodes are processed if needed. All replies |
400 | | * will be run through the state machine to tweak driver options |
401 | | * and program the clock. |
402 | | */ |
403 | | |
404 | | /* |
405 | | * Clock misunderstood our last command? |
406 | | */ |
407 | 0 | if (pp->a_lastcode[0] == '?' || |
408 | 0 | strcmp(pp->a_lastcode, "ERROR 05 NO SUCH FUNCTION") == 0) { |
409 | 0 | true_doevent(peer, e_Huh); |
410 | 0 | return; |
411 | 0 | } |
412 | | |
413 | | /* |
414 | | * Timecode: "nnnnn+nnn-nnn" |
415 | | * (from GOES clock when asked about satellite position) |
416 | | */ |
417 | 0 | if ((pp->a_lastcode[5] == '+' || pp->a_lastcode[5] == '-') && |
418 | 0 | (pp->a_lastcode[9] == '+' || pp->a_lastcode[9] == '-') && |
419 | 0 | sscanf(pp->a_lastcode, "%5d%*c%3d%*c%3d", &lon, &lat, &off) == 3 |
420 | 0 | ) { |
421 | 0 | const char *label = "Botch!"; |
422 | | |
423 | | /* |
424 | | * This is less than perfect. Call the (satellite) |
425 | | * either EAST or WEST and adjust slop accodingly |
426 | | * Perfectionists would recalculate the exact delay |
427 | | * and adjust accordingly... |
428 | | */ |
429 | 0 | if (lon > 7000 && lon < 14000) { |
430 | 0 | if (lon < 10000) { |
431 | 0 | new_station = GOES_EAST; |
432 | 0 | label = "EAST"; |
433 | 0 | } else { |
434 | 0 | new_station = GOES_WEST; |
435 | 0 | label = "WEST"; |
436 | 0 | } |
437 | | |
438 | 0 | if (new_station != up->station) { |
439 | 0 | double dtemp; |
440 | |
|
441 | 0 | dtemp = pp->fudgetime1; |
442 | 0 | pp->fudgetime1 = pp->fudgetime2; |
443 | 0 | pp->fudgetime2 = dtemp; |
444 | 0 | up->station = new_station; |
445 | 0 | } |
446 | 0 | } |
447 | 0 | else { |
448 | | /*refclock_report(peer, CEVNT_BADREPLY);*/ |
449 | 0 | label = "UNKNOWN"; |
450 | 0 | } |
451 | 0 | true_debug(peer, "GOES: station %s\n", label); |
452 | 0 | true_doevent(peer, e_Satellite); |
453 | 0 | return; |
454 | 0 | } |
455 | | |
456 | | /* |
457 | | * Timecode: "Fnn" |
458 | | * (from TM/TMD clock when it wants to tell us what it's up to.) |
459 | | */ |
460 | 0 | if (sscanf(pp->a_lastcode, "F%2d", &i) == 1 && i > 0 && i < 80) { |
461 | 0 | switch (i) { |
462 | 0 | case 50: |
463 | 0 | true_doevent(peer, e_F50); |
464 | 0 | break; |
465 | 0 | case 51: |
466 | 0 | true_doevent(peer, e_F51); |
467 | 0 | break; |
468 | 0 | default: |
469 | 0 | true_debug(peer, "got F%02d - ignoring\n", i); |
470 | 0 | break; |
471 | 0 | } |
472 | 0 | return; |
473 | 0 | } |
474 | | |
475 | | /* |
476 | | * Timecode: "VER xx.xx" |
477 | | * (from a TL3 when sent "QV", so id's it during initialization.) |
478 | | */ |
479 | 0 | if (pp->a_lastcode[0] == 'V' && pp->a_lastcode[1] == 'E' && |
480 | 0 | pp->a_lastcode[2] == 'R' && pp->a_lastcode[6] == '.') { |
481 | 0 | true_doevent(peer, e_TL3); |
482 | 0 | NLOG(NLOG_CLOCKSTATUS) { |
483 | 0 | msyslog(LOG_INFO, "TL3: %s", pp->a_lastcode); |
484 | 0 | } |
485 | 0 | return; |
486 | 0 | } |
487 | | |
488 | | /* |
489 | | * Timecode: " TRUETIME Mk III" or " TRUETIME XL" |
490 | | * (from a TM/TMD/XL clock during initialization.) |
491 | | */ |
492 | 0 | if (strncmp(pp->a_lastcode, " TRUETIME Mk III ", 17) == 0 || |
493 | 0 | strncmp(pp->a_lastcode, " TRUETIME XL", 12) == 0) { |
494 | 0 | true_doevent(peer, e_F18); |
495 | 0 | NLOG(NLOG_CLOCKSTATUS) { |
496 | 0 | msyslog(LOG_INFO, "TM/TMD/XL: %s", pp->a_lastcode); |
497 | 0 | } |
498 | 0 | return; |
499 | 0 | } |
500 | | |
501 | | /* |
502 | | * Timecode: "N03726428W12209421+000033" |
503 | | * 1 2 |
504 | | * index 0123456789012345678901234 |
505 | | * (from a TCU during initialization) |
506 | | */ |
507 | 0 | if ((pp->a_lastcode[0] == 'N' || pp->a_lastcode[0] == 'S') && |
508 | 0 | (pp->a_lastcode[9] == 'W' || pp->a_lastcode[9] == 'E') && |
509 | 0 | pp->a_lastcode[18] == '+') { |
510 | 0 | true_doevent(peer, e_Location); |
511 | 0 | NLOG(NLOG_CLOCKSTATUS) { |
512 | 0 | msyslog(LOG_INFO, "TCU-800: %s", pp->a_lastcode); |
513 | 0 | } |
514 | 0 | return; |
515 | 0 | } |
516 | | /* |
517 | | * Timecode: "ddd:hh:mm:ssQ" |
518 | | * 1 2 |
519 | | * index 0123456789012345678901234 |
520 | | * (from all clocks supported by this driver.) |
521 | | */ |
522 | 0 | if (pp->a_lastcode[3] == ':' && |
523 | 0 | pp->a_lastcode[6] == ':' && |
524 | 0 | pp->a_lastcode[9] == ':' && |
525 | 0 | sscanf(pp->a_lastcode, "%3d:%2d:%2d:%2d%c", |
526 | 0 | &pp->day, &pp->hour, &pp->minute, |
527 | 0 | &pp->second, &synced) == 5) { |
528 | | |
529 | | /* |
530 | | * Adjust the synchronize indicator according to timecode |
531 | | * say were OK, and then say not if we really are not OK |
532 | | */ |
533 | 0 | if (synced == '>' || synced == '#' || synced == '?' |
534 | 0 | || synced == 'X') |
535 | 0 | pp->leap = LEAP_NOTINSYNC; |
536 | 0 | else |
537 | 0 | pp->leap = LEAP_NOWARNING; |
538 | |
|
539 | 0 | true_doevent(peer, e_TS); |
540 | |
|
541 | | #ifdef CLOCK_PPS720 |
542 | | /* If it's taken more than 65ms to get here, we'll lose. */ |
543 | | if ((pp->sloppyclockflag & CLK_FLAG4) && up->pcl720init) { |
544 | | l_fp off; |
545 | | |
546 | | #ifdef CLOCK_ATOM |
547 | | /* |
548 | | * find out what time it really is. Include |
549 | | * the count from the PCL720 |
550 | | */ |
551 | | if (!clocktime(pp->day, pp->hour, pp->minute, |
552 | | pp->second, GMT, pp->lastrec.l_ui, |
553 | | &pp->yearstart, &off.l_ui)) { |
554 | | refclock_report(peer, CEVNT_BADTIME); |
555 | | return; |
556 | | } |
557 | | off.l_uf = 0; |
558 | | #endif |
559 | | |
560 | | pp->usec = true_sample720(); |
561 | | #ifdef CLOCK_ATOM |
562 | | TVUTOTSF(pp->usec, off.l_uf); |
563 | | #endif |
564 | | |
565 | | /* |
566 | | * Stomp all over the timestamp that was pulled out |
567 | | * of the input stream. It's irrelevant since we've |
568 | | * adjusted the input time to reflect now (via pp->usec) |
569 | | * rather than when the data was collected. |
570 | | */ |
571 | | get_systime(&pp->lastrec); |
572 | | #ifdef CLOCK_ATOM |
573 | | /* |
574 | | * Create a true offset for feeding to pps_sample() |
575 | | */ |
576 | | L_SUB(&off, &pp->lastrec); |
577 | | |
578 | | pps_sample(peer, &off); |
579 | | #endif |
580 | | true_debug(peer, "true_sample720: %luus\n", pp->usec); |
581 | | } |
582 | | #endif |
583 | | |
584 | | /* |
585 | | * The clock will blurt a timecode every second but we only |
586 | | * want one when polled. If we havn't been polled, bail out. |
587 | | */ |
588 | 0 | if (!up->polled) |
589 | 0 | return; |
590 | | |
591 | | /* We only call doevent if additional things need be done |
592 | | * at poll interval. Currently, its only for GOES. We also |
593 | | * call it for clock unknown so that it gets logged. |
594 | | */ |
595 | 0 | if (up->type == t_goes || up->type == t_unknown) |
596 | 0 | true_doevent(peer, e_Poll); |
597 | |
|
598 | 0 | if (!refclock_process(pp)) { |
599 | 0 | refclock_report(peer, CEVNT_BADTIME); |
600 | 0 | return; |
601 | 0 | } |
602 | | /* |
603 | | * If clock is good we send a NOMINAL message so that |
604 | | * any previous BAD messages are nullified |
605 | | */ |
606 | 0 | pp->lastref = pp->lastrec; |
607 | 0 | refclock_receive(peer); |
608 | 0 | refclock_report(peer, CEVNT_NOMINAL); |
609 | | |
610 | | /* |
611 | | * We have succedded in answering the poll. |
612 | | * Turn off the flag and return |
613 | | */ |
614 | 0 | up->polled = 0; |
615 | |
|
616 | 0 | return; |
617 | 0 | } |
618 | | |
619 | | /* |
620 | | * No match to known timecodes, report failure and return |
621 | | */ |
622 | 0 | refclock_report(peer, CEVNT_BADREPLY); |
623 | 0 | return; |
624 | 0 | } |
625 | | |
626 | | |
627 | | /* |
628 | | * true_send - time to send the clock a signal to cough up a time sample |
629 | | */ |
630 | | static void |
631 | | true_send( |
632 | | struct peer *peer, |
633 | | const char *cmd |
634 | | ) |
635 | 0 | { |
636 | 0 | struct refclockproc *pp; |
637 | |
|
638 | 0 | pp = peer->procptr; |
639 | 0 | if (!(pp->sloppyclockflag & CLK_FLAG1)) { |
640 | 0 | size_t len = strlen(cmd); |
641 | |
|
642 | 0 | true_debug(peer, "Send '%s'\n", cmd); |
643 | 0 | if (write(pp->io.fd, cmd, len) != (ssize_t)len) |
644 | 0 | refclock_report(peer, CEVNT_FAULT); |
645 | 0 | else |
646 | 0 | pp->polls++; |
647 | 0 | } |
648 | 0 | } |
649 | | |
650 | | |
651 | | /* |
652 | | * state machine for initializing and controlling a clock |
653 | | */ |
654 | | static void |
655 | | true_doevent( |
656 | | struct peer *peer, |
657 | | enum true_event event |
658 | | ) |
659 | 0 | { |
660 | 0 | struct true_unit *up; |
661 | 0 | struct refclockproc *pp; |
662 | |
|
663 | 0 | pp = peer->procptr; |
664 | 0 | up = pp->unitptr; |
665 | 0 | if (event != e_TS) { |
666 | 0 | NLOG(NLOG_CLOCKSTATUS) { |
667 | 0 | msyslog(LOG_INFO, "TRUE: clock %s, state %s, event %s", |
668 | 0 | typeStr(up->type), |
669 | 0 | stateStr(up->state), |
670 | 0 | eventStr(event)); |
671 | 0 | } |
672 | 0 | } |
673 | 0 | true_debug(peer, "clock %s, state %s, event %s\n", |
674 | 0 | typeStr(up->type), stateStr(up->state), eventStr(event)); |
675 | 0 | switch (up->type) { |
676 | 0 | case t_goes: |
677 | 0 | switch (event) { |
678 | 0 | case e_Init: /* FALLTHROUGH */ |
679 | 0 | case e_Satellite: |
680 | | /* |
681 | | * Switch back to on-second time codes and return. |
682 | | */ |
683 | 0 | true_send(peer, "C"); |
684 | 0 | up->state = s_Start; |
685 | 0 | break; |
686 | 0 | case e_Poll: |
687 | | /* |
688 | | * After each poll, check the station (satellite). |
689 | | */ |
690 | 0 | true_send(peer, "P"); |
691 | | /* No state change needed. */ |
692 | 0 | break; |
693 | 0 | default: |
694 | 0 | break; |
695 | 0 | } |
696 | | /* FALLTHROUGH */ |
697 | 0 | case t_omega: |
698 | 0 | switch (event) { |
699 | 0 | case e_Init: |
700 | 0 | true_send(peer, "C"); |
701 | 0 | up->state = s_Start; |
702 | 0 | break; |
703 | 0 | case e_TS: |
704 | 0 | if (up->state != s_Start && up->state != s_Auto) { |
705 | 0 | true_send(peer, "\03\r"); |
706 | 0 | break; |
707 | 0 | } |
708 | 0 | up->state = s_Auto; |
709 | 0 | break; |
710 | 0 | default: |
711 | 0 | break; |
712 | 0 | } |
713 | 0 | break; |
714 | 0 | case t_tm: |
715 | 0 | switch (event) { |
716 | 0 | case e_Init: |
717 | 0 | true_send(peer, "F18\r"); |
718 | 0 | up->state = s_Init; |
719 | 0 | break; |
720 | 0 | case e_F18: |
721 | 0 | true_send(peer, "F50\r"); |
722 | | /* |
723 | | * Timecode: " TRUETIME Mk III" or " TRUETIME XL" |
724 | | * (from a TM/TMD/XL clock during initialization.) |
725 | | */ |
726 | 0 | if ( strcmp(pp->a_lastcode, " TRUETIME Mk III") == 0 || |
727 | 0 | strncmp(pp->a_lastcode, " TRUETIME XL", 12) == 0) { |
728 | 0 | true_doevent(peer, e_F18); |
729 | 0 | NLOG(NLOG_CLOCKSTATUS) { |
730 | 0 | msyslog(LOG_INFO, "TM/TMD/XL: %s", |
731 | 0 | pp->a_lastcode); |
732 | 0 | } |
733 | 0 | return; |
734 | 0 | } |
735 | 0 | up->state = s_F18; |
736 | 0 | break; |
737 | 0 | case e_F50: |
738 | 0 | true_send(peer, "F51\r"); |
739 | 0 | up->state = s_F50; |
740 | 0 | break; |
741 | 0 | case e_F51: |
742 | 0 | true_send(peer, "F08\r"); |
743 | 0 | up->state = s_Start; |
744 | 0 | break; |
745 | 0 | case e_TS: |
746 | 0 | if (up->state != s_Start && up->state != s_Auto) { |
747 | 0 | true_send(peer, "\03\r"); |
748 | 0 | break; |
749 | 0 | } |
750 | 0 | up->state = s_Auto; |
751 | 0 | break; |
752 | 0 | default: |
753 | 0 | break; |
754 | 0 | } |
755 | 0 | break; |
756 | 0 | case t_tcu: |
757 | 0 | switch (event) { |
758 | 0 | case e_Init: |
759 | 0 | true_send(peer, "MD3\r"); /* GPS Synch'd Gen. */ |
760 | 0 | true_send(peer, "TSU\r"); /* UTC, not GPS. */ |
761 | 0 | true_send(peer, "AU\r"); /* Auto Timestamps. */ |
762 | 0 | up->state = s_Start; |
763 | 0 | break; |
764 | 0 | case e_TS: |
765 | 0 | if (up->state != s_Start && up->state != s_Auto) { |
766 | 0 | true_send(peer, "\03\r"); |
767 | 0 | break; |
768 | 0 | } |
769 | 0 | up->state = s_Auto; |
770 | 0 | break; |
771 | 0 | default: |
772 | 0 | break; |
773 | 0 | } |
774 | 0 | break; |
775 | 0 | case t_tl3: |
776 | 0 | switch (event) { |
777 | 0 | case e_Init: |
778 | 0 | true_send(peer, "ST1"); /* Turn on continuous stream */ |
779 | 0 | break; |
780 | 0 | case e_TS: |
781 | 0 | up->state = s_Auto; |
782 | 0 | break; |
783 | 0 | default: |
784 | 0 | break; |
785 | 0 | } |
786 | 0 | break; |
787 | 0 | case t_unknown: |
788 | 0 | if (event == e_Poll) |
789 | 0 | break; |
790 | 0 | switch (up->state) { |
791 | 0 | case s_Base: |
792 | 0 | if (event != e_Init) |
793 | 0 | abort(); |
794 | 0 | true_send(peer, "P\r"); |
795 | 0 | up->state = s_InqGOES; |
796 | 0 | break; |
797 | 0 | case s_InqGOES: |
798 | 0 | switch (event) { |
799 | 0 | case e_Satellite: |
800 | 0 | up->type = t_goes; |
801 | 0 | true_doevent(peer, e_Init); |
802 | 0 | break; |
803 | 0 | case e_Init: /*FALLTHROUGH*/ |
804 | 0 | case e_Huh: |
805 | 0 | case e_TS: |
806 | 0 | true_send(peer, "ST0"); /* turn off TL3 auto */ |
807 | 0 | sleep(1); /* wait for it */ |
808 | 0 | up->state = s_InqTL3; |
809 | 0 | true_send(peer, "QV"); /* see if its a TL3 */ |
810 | 0 | break; |
811 | 0 | default: |
812 | 0 | abort(); |
813 | 0 | } |
814 | 0 | break; |
815 | 0 | case s_InqTL3: |
816 | 0 | switch (event) { |
817 | 0 | case e_TL3: |
818 | 0 | up->type = t_tl3; |
819 | 0 | up->state = s_Auto; /* Inq side-effect. */ |
820 | 0 | true_send(peer, "ST1"); /* Turn on 1/sec data */ |
821 | 0 | break; |
822 | 0 | case e_Init: /*FALLTHROUGH*/ |
823 | 0 | case e_Huh: |
824 | 0 | up->state = s_InqOmega; |
825 | 0 | true_send(peer, "C\r"); |
826 | 0 | break; |
827 | 0 | case e_TS: |
828 | 0 | up->type = t_tl3; /* Already sending data */ |
829 | 0 | up->state = s_Auto; |
830 | 0 | break; |
831 | 0 | default: |
832 | 0 | msyslog(LOG_INFO, |
833 | 0 | "TRUE: TL3 init fellthrough! (%d)", event); |
834 | 0 | break; |
835 | 0 | } |
836 | 0 | break; |
837 | 0 | case s_InqOmega: |
838 | 0 | switch (event) { |
839 | 0 | case e_TS: |
840 | 0 | up->type = t_omega; |
841 | 0 | up->state = s_Auto; /* Inq side-effect. */ |
842 | 0 | break; |
843 | 0 | case e_Init: /*FALLTHROUGH*/ |
844 | 0 | case e_Huh: |
845 | 0 | up->state = s_InqTM; |
846 | 0 | true_send(peer, "F18\r"); |
847 | 0 | break; |
848 | 0 | default: |
849 | 0 | abort(); |
850 | 0 | } |
851 | 0 | break; |
852 | 0 | case s_InqTM: |
853 | 0 | switch (event) { |
854 | 0 | case e_F18: |
855 | 0 | up->type = t_tm; |
856 | 0 | true_doevent(peer, e_Init); |
857 | 0 | break; |
858 | 0 | case e_Init: /*FALLTHROUGH*/ |
859 | 0 | case e_Huh: |
860 | 0 | true_send(peer, "PO\r"); |
861 | 0 | up->state = s_InqTCU; |
862 | 0 | break; |
863 | 0 | default: |
864 | 0 | msyslog(LOG_INFO, |
865 | 0 | "TRUE: TM/TMD init fellthrough!"); |
866 | 0 | break; |
867 | 0 | } |
868 | 0 | break; |
869 | 0 | case s_InqTCU: |
870 | 0 | switch (event) { |
871 | 0 | case e_Location: |
872 | 0 | up->type = t_tcu; |
873 | 0 | true_doevent(peer, e_Init); |
874 | 0 | break; |
875 | 0 | case e_Init: /*FALLTHROUGH*/ |
876 | 0 | case e_Huh: |
877 | 0 | up->state = s_Base; |
878 | 0 | sleep(1); /* XXX */ |
879 | 0 | break; |
880 | 0 | default: |
881 | 0 | msyslog(LOG_INFO, |
882 | 0 | "TRUE: TCU init fellthrough!"); |
883 | 0 | break; |
884 | 0 | } |
885 | 0 | break; |
886 | | /* |
887 | | * An expedient hack to prevent lint complaints, |
888 | | * these don't actually need to be used here... |
889 | | */ |
890 | 0 | case s_Init: |
891 | 0 | case s_F18: |
892 | 0 | case s_F50: |
893 | 0 | case s_Start: |
894 | 0 | case s_Auto: |
895 | 0 | case s_Max: |
896 | 0 | msyslog(LOG_INFO, "TRUE: state %s is unexpected!", |
897 | 0 | stateStr(up->state)); |
898 | 0 | } |
899 | 0 | break; |
900 | 0 | default: |
901 | 0 | msyslog(LOG_INFO, "TRUE: cannot identify refclock!"); |
902 | 0 | abort(); |
903 | | /* NOTREACHED */ |
904 | 0 | } |
905 | |
|
906 | | #ifdef CLOCK_PPS720 |
907 | | if ((pp->sloppyclockflag & CLK_FLAG4) && !up->pcl720init) { |
908 | | /* Make counter trigger on gate0, count down from 65535. */ |
909 | | pcl720_load(PCL720_IOB, PCL720_CTR, i8253_oneshot, 65535); |
910 | | /* |
911 | | * (These constants are OK since |
912 | | * they represent hardware maximums.) |
913 | | */ |
914 | | NLOG(NLOG_CLOCKINFO) { |
915 | | msyslog(LOG_NOTICE, "PCL-720 initialized"); |
916 | | } |
917 | | up->pcl720init++; |
918 | | } |
919 | | #endif |
920 | | |
921 | |
|
922 | 0 | } |
923 | | |
924 | | /* |
925 | | * true_poll - called by the transmit procedure |
926 | | */ |
927 | | static void |
928 | | true_poll( |
929 | | int unit, |
930 | | struct peer *peer |
931 | | ) |
932 | 0 | { |
933 | 0 | struct true_unit *up; |
934 | 0 | struct refclockproc *pp; |
935 | | |
936 | | /* |
937 | | * You don't need to poll this clock. It puts out timecodes |
938 | | * once per second. If asked for a timestamp, take note. |
939 | | * The next time a timecode comes in, it will be fed back. |
940 | | */ |
941 | 0 | pp = peer->procptr; |
942 | 0 | up = pp->unitptr; |
943 | 0 | if (up->pollcnt > 0) { |
944 | 0 | up->pollcnt--; |
945 | 0 | } else { |
946 | 0 | true_doevent(peer, e_Init); |
947 | 0 | refclock_report(peer, CEVNT_TIMEOUT); |
948 | 0 | } |
949 | | |
950 | | /* |
951 | | * polled every 64 seconds. Ask true_receive to hand in a |
952 | | * timestamp. |
953 | | */ |
954 | 0 | up->polled = 1; |
955 | 0 | pp->polls++; |
956 | 0 | } |
957 | | |
958 | | #ifdef CLOCK_PPS720 |
959 | | /* |
960 | | * true_sample720 - sample the PCL-720 |
961 | | */ |
962 | | static u_long |
963 | | true_sample720(void) |
964 | | { |
965 | | unsigned long f; |
966 | | |
967 | | /* We wire the PCL-720's 8253.OUT0 to bit 0 of connector 3. |
968 | | * If it is not being held low now, we did not get called |
969 | | * within 65535us. |
970 | | */ |
971 | | if (inb(pcl720_data_16_23(PCL720_IOB)) & 0x01) { |
972 | | NLOG(NLOG_CLOCKINFO) { |
973 | | msyslog(LOG_NOTICE, "PCL-720 out of synch"); |
974 | | } |
975 | | return (0); |
976 | | } |
977 | | f = (65536 - pcl720_read(PCL720_IOB, PCL720_CTR)); |
978 | | #ifdef PPS720_DEBUG |
979 | | msyslog(LOG_DEBUG, "PCL-720: %luus", f); |
980 | | #endif |
981 | | return (f); |
982 | | } |
983 | | #endif |
984 | | |
985 | | #else |
986 | | int refclock_true_bs; |
987 | | #endif /* REFCLOCK */ |