1 | /* |
---|
2 | * refclock_arbiter - clock driver for Arbiter 1088A/B Satellite |
---|
3 | * Controlled Clock |
---|
4 | */ |
---|
5 | |
---|
6 | #ifdef HAVE_CONFIG_H |
---|
7 | #include <config.h> |
---|
8 | #endif |
---|
9 | |
---|
10 | #if defined(REFCLOCK) && defined(ARBITER) |
---|
11 | |
---|
12 | #include <stdio.h> |
---|
13 | #include <ctype.h> |
---|
14 | #include <sys/time.h> |
---|
15 | |
---|
16 | #include "ntpd.h" |
---|
17 | #include "ntp_io.h" |
---|
18 | #include "ntp_refclock.h" |
---|
19 | #include "ntp_stdlib.h" |
---|
20 | |
---|
21 | /* |
---|
22 | * This driver supports the Arbiter 1088A/B Satellite Controlled Clock. |
---|
23 | * The claimed accuracy of this clock is 100 ns relative to the PPS |
---|
24 | * output when receiving four or more satellites. |
---|
25 | * |
---|
26 | * The receiver should be configured before starting the NTP daemon, in |
---|
27 | * order to establish reliable position and operating conditions. It |
---|
28 | * does not initiate surveying or hold mode. For use with NTP, the |
---|
29 | * daylight savings time feature should be disables (D0 command) and the |
---|
30 | * broadcast mode set to operate in UTC (BU command). |
---|
31 | * |
---|
32 | * The timecode format supported by this driver is selected by the poll |
---|
33 | * sequence "B5", which initiates a line in the following format to be |
---|
34 | * repeated once per second until turned off by the "B0" poll sequence. |
---|
35 | * |
---|
36 | * Format B5 (24 ASCII printing characters): |
---|
37 | * |
---|
38 | * <cr><lf>i yy ddd hh:mm:ss.000bbb |
---|
39 | * |
---|
40 | * on-time = <cr> |
---|
41 | * i = synchronization flag (' ' = locked, '?' = unlocked) |
---|
42 | * yy = year of century |
---|
43 | * ddd = day of year |
---|
44 | * hh:mm:ss = hours, minutes, seconds |
---|
45 | * .000 = fraction of second (not used) |
---|
46 | * bbb = tailing spaces for fill |
---|
47 | * |
---|
48 | * The alarm condition is indicated by a '?' at i, which indicates the |
---|
49 | * receiver is not synchronized. In normal operation, a line consisting |
---|
50 | * of the timecode followed by the time quality character (TQ) followed |
---|
51 | * by the receiver status string (SR) is written to the clockstats file. |
---|
52 | * The time quality character is encoded in IEEE P1344 standard: |
---|
53 | * |
---|
54 | * Format TQ (IEEE P1344 estimated worst-case time quality) |
---|
55 | * |
---|
56 | * 0 clock locked, maximum accuracy |
---|
57 | * F clock failure, time not reliable |
---|
58 | * 4 clock unlocked, accuracy < 1 us |
---|
59 | * 5 clock unlocked, accuracy < 10 us |
---|
60 | * 6 clock unlocked, accuracy < 100 us |
---|
61 | * 7 clock unlocked, accuracy < 1 ms |
---|
62 | * 8 clock unlocked, accuracy < 10 ms |
---|
63 | * 9 clock unlocked, accuracy < 100 ms |
---|
64 | * A clock unlocked, accuracy < 1 s |
---|
65 | * B clock unlocked, accuracy < 10 s |
---|
66 | * |
---|
67 | * The status string is encoded as follows: |
---|
68 | * |
---|
69 | * Format SR (25 ASCII printing characters) |
---|
70 | * |
---|
71 | * V=vv S=ss T=t P=pdop E=ee |
---|
72 | * |
---|
73 | * vv = satellites visible |
---|
74 | * ss = relative signal strength |
---|
75 | * t = satellites tracked |
---|
76 | * pdop = position dilution of precision (meters) |
---|
77 | * ee = hardware errors |
---|
78 | * |
---|
79 | * If flag4 is set, an additional line consisting of the receiver |
---|
80 | * latitude (LA), longitude (LO) and elevation (LH) (meters) is written |
---|
81 | * to this file. If channel B is enabled for deviation mode and connected |
---|
82 | * to a 1-PPS signal, the last two numbers on the line are the deviation |
---|
83 | * and standard deviation averaged over the last 15 seconds. |
---|
84 | */ |
---|
85 | |
---|
86 | /* |
---|
87 | * Interface definitions |
---|
88 | */ |
---|
89 | #define DEVICE "/dev/gps%d" /* device name and unit */ |
---|
90 | #define SPEED232 B9600 /* uart speed (9600 baud) */ |
---|
91 | #define PRECISION (-10) /* precision assumed (about 1 ms) */ |
---|
92 | #define REFID "GPS " /* reference ID */ |
---|
93 | #define DESCRIPTION "Arbiter 1088A/B GPS Receiver" /* WRU */ |
---|
94 | |
---|
95 | #define NSAMPLES 3 /* stages of median filter */ |
---|
96 | #define LENARB 24 /* format B5 timecode length */ |
---|
97 | #define MAXSTA 30 /* max length of status string */ |
---|
98 | #define MAXPOS 70 /* max length of position string */ |
---|
99 | |
---|
100 | /* |
---|
101 | * Imported from ntp_timer module |
---|
102 | */ |
---|
103 | extern u_long current_time; /* current time (s) */ |
---|
104 | |
---|
105 | /* |
---|
106 | * Imported from ntpd module |
---|
107 | */ |
---|
108 | extern int debug; /* global debug flag */ |
---|
109 | |
---|
110 | /* |
---|
111 | * ARB unit control structure |
---|
112 | */ |
---|
113 | struct arbunit { |
---|
114 | int pollcnt; /* poll message counter */ |
---|
115 | l_fp laststamp; /* last receive timestamp */ |
---|
116 | u_char tcswitch; /* timecode enable switch */ |
---|
117 | char qualchar; /* IEEE P1344 quality (TQ command) */ |
---|
118 | char status[MAXSTA]; /* receiver status (SR command) */ |
---|
119 | char latlon[MAXPOS]; /* receiver position (lat/lon/alt) */ |
---|
120 | }; |
---|
121 | |
---|
122 | /* |
---|
123 | * Function prototypes |
---|
124 | */ |
---|
125 | static int arb_start P((int, struct peer *)); |
---|
126 | static void arb_shutdown P((int, struct peer *)); |
---|
127 | static void arb_receive P((struct recvbuf *)); |
---|
128 | static void arb_poll P((int, struct peer *)); |
---|
129 | |
---|
130 | /* |
---|
131 | * Transfer vector |
---|
132 | */ |
---|
133 | struct refclock refclock_arbiter = { |
---|
134 | arb_start, /* start up driver */ |
---|
135 | arb_shutdown, /* shut down driver */ |
---|
136 | arb_poll, /* transmit poll message */ |
---|
137 | noentry, /* not used (old arb_control) */ |
---|
138 | noentry, /* initialize driver (not used) */ |
---|
139 | noentry, /* not used (old arb_buginfo) */ |
---|
140 | NOFLAGS /* not used */ |
---|
141 | }; |
---|
142 | |
---|
143 | |
---|
144 | /* |
---|
145 | * arb_start - open the devices and initialize data for processing |
---|
146 | */ |
---|
147 | static int |
---|
148 | arb_start(unit, peer) |
---|
149 | int unit; |
---|
150 | struct peer *peer; |
---|
151 | { |
---|
152 | register struct arbunit *up; |
---|
153 | struct refclockproc *pp; |
---|
154 | int fd; |
---|
155 | char device[20]; |
---|
156 | |
---|
157 | /* |
---|
158 | * Open serial port. Use CLK line discipline, if available. |
---|
159 | */ |
---|
160 | (void)sprintf(device, DEVICE, unit); |
---|
161 | #ifdef TTYCLK |
---|
162 | if (!(fd = refclock_open(device, SPEED232, LDISC_CLK))) |
---|
163 | #else |
---|
164 | if (!(fd = refclock_open(device, SPEED232, 0))) |
---|
165 | #endif /* TTYCLK */ |
---|
166 | return (0); |
---|
167 | |
---|
168 | /* |
---|
169 | * Allocate and initialize unit structure |
---|
170 | */ |
---|
171 | if (!(up = (struct arbunit *) |
---|
172 | emalloc(sizeof(struct arbunit)))) { |
---|
173 | (void) close(fd); |
---|
174 | return (0); |
---|
175 | } |
---|
176 | memset((char *)up, 0, sizeof(struct arbunit)); |
---|
177 | pp = peer->procptr; |
---|
178 | pp->io.clock_recv = arb_receive; |
---|
179 | pp->io.srcclock = (caddr_t)peer; |
---|
180 | pp->io.datalen = 0; |
---|
181 | pp->io.fd = fd; |
---|
182 | if (!io_addclock(&pp->io)) { |
---|
183 | (void) close(fd); |
---|
184 | free(up); |
---|
185 | return (0); |
---|
186 | } |
---|
187 | pp->unitptr = (caddr_t)up; |
---|
188 | |
---|
189 | /* |
---|
190 | * Initialize miscellaneous variables |
---|
191 | */ |
---|
192 | peer->precision = PRECISION; |
---|
193 | pp->clockdesc = DESCRIPTION; |
---|
194 | memcpy((char *)&pp->refid, REFID, 4); |
---|
195 | up->pollcnt = 2; |
---|
196 | return (1); |
---|
197 | } |
---|
198 | |
---|
199 | |
---|
200 | /* |
---|
201 | * arb_shutdown - shut down the clock |
---|
202 | */ |
---|
203 | static void |
---|
204 | arb_shutdown(unit, peer) |
---|
205 | int unit; |
---|
206 | struct peer *peer; |
---|
207 | { |
---|
208 | register struct arbunit *up; |
---|
209 | struct refclockproc *pp; |
---|
210 | |
---|
211 | pp = peer->procptr; |
---|
212 | up = (struct arbunit *)pp->unitptr; |
---|
213 | io_closeclock(&pp->io); |
---|
214 | free(up); |
---|
215 | } |
---|
216 | |
---|
217 | |
---|
218 | /* |
---|
219 | * arb_receive - receive data from the serial interface |
---|
220 | */ |
---|
221 | static void |
---|
222 | arb_receive(rbufp) |
---|
223 | struct recvbuf *rbufp; |
---|
224 | { |
---|
225 | register struct arbunit *up; |
---|
226 | struct refclockproc *pp; |
---|
227 | struct peer *peer; |
---|
228 | l_fp trtmp; |
---|
229 | u_long ltemp; |
---|
230 | int temp; |
---|
231 | u_char syncchar; /* synchronization indicator */ |
---|
232 | u_char leapchar; /* leap indicator */ |
---|
233 | |
---|
234 | /* |
---|
235 | * Initialize pointers and read the timecode and timestamp |
---|
236 | */ |
---|
237 | peer = (struct peer *)rbufp->recv_srcclock; |
---|
238 | pp = peer->procptr; |
---|
239 | up = (struct arbunit *)pp->unitptr; |
---|
240 | temp = refclock_gtlin(rbufp, pp->a_lastcode, BMAX, &trtmp); |
---|
241 | if (up->tcswitch) |
---|
242 | return; |
---|
243 | |
---|
244 | /* |
---|
245 | * Note we get a buffer and timestamp for both a <cr> and <lf>, |
---|
246 | * but only the <cr> timestamp is retained. The program first |
---|
247 | * sends a TQ and expects the echo followed by the time quality |
---|
248 | * character. It then sends a B5 starting the timecode broadcast |
---|
249 | * and expects the echo followed some time later by the on-time |
---|
250 | * character <cr> and then the <lf> beginning the timecode |
---|
251 | * itself. Finally, at the <cr> beginning the next timecode at |
---|
252 | * the next second, the program sends a B0 shutting down the the |
---|
253 | * timecode broadcast. |
---|
254 | * |
---|
255 | * If flag4 is set, the program snatches the latitude, longitude |
---|
256 | * and elevation and writes it to the clockstats file. |
---|
257 | */ |
---|
258 | if (temp == 0) |
---|
259 | return; |
---|
260 | pp->lastrec = up->laststamp; |
---|
261 | up->laststamp = trtmp; |
---|
262 | if (temp < 3) |
---|
263 | return; |
---|
264 | if (!strncmp(pp->a_lastcode, "TQ", 2)) { |
---|
265 | up->qualchar = pp->a_lastcode[2]; |
---|
266 | write(pp->io.fd, "SR", 2); |
---|
267 | return; |
---|
268 | } else if (!strncmp(pp->a_lastcode, "SR", 2)) { |
---|
269 | strcpy(up->status, pp->a_lastcode + 2); |
---|
270 | if (pp->sloppyclockflag & CLK_FLAG4) |
---|
271 | write(pp->io.fd, "LA", 2); |
---|
272 | else |
---|
273 | write(pp->io.fd, "B5", 2); |
---|
274 | return; |
---|
275 | } else if (!strncmp(pp->a_lastcode, "LA", 2)) { |
---|
276 | strcpy(up->latlon, pp->a_lastcode + 2); |
---|
277 | write(pp->io.fd, "LO", 2); |
---|
278 | return; |
---|
279 | } else if (!strncmp(pp->a_lastcode, "LO", 2)) { |
---|
280 | strcat(up->latlon, " "); |
---|
281 | strcat(up->latlon, pp->a_lastcode + 2); |
---|
282 | write(pp->io.fd, "LH", 2); |
---|
283 | return; |
---|
284 | } else if (!strncmp(pp->a_lastcode, "LH", 2)) { |
---|
285 | strcat(up->latlon, " "); |
---|
286 | strcat(up->latlon, pp->a_lastcode + 2); |
---|
287 | write(pp->io.fd, "DB", 2); |
---|
288 | return; |
---|
289 | } else if (!strncmp(pp->a_lastcode, "DB", 2)) { |
---|
290 | strcat(up->latlon, " "); |
---|
291 | strcat(up->latlon, pp->a_lastcode + 2); |
---|
292 | record_clock_stats(&peer->srcadr, up->latlon); |
---|
293 | write(pp->io.fd, "B5", 2); |
---|
294 | return; |
---|
295 | } |
---|
296 | write(pp->io.fd, "B0", 2); |
---|
297 | pp->lencode = temp; |
---|
298 | up->pollcnt = 2; |
---|
299 | up->tcswitch++; |
---|
300 | |
---|
301 | #ifdef DEBUG |
---|
302 | if (debug) |
---|
303 | printf("arbiter: timecode %d %s\n", pp->lencode, |
---|
304 | pp->a_lastcode); |
---|
305 | #endif |
---|
306 | |
---|
307 | /* |
---|
308 | * We get down to business, check the timecode format and decode |
---|
309 | * its contents. If the timecode has valid length, but not in |
---|
310 | * proper format, we declare bad format and exit. If the |
---|
311 | * timecode has invalid length, which sometimes occurs when the |
---|
312 | * B0 amputates the broadcast, we just quietly steal away. Note |
---|
313 | * that the time quality character and receiver status string is |
---|
314 | * tacked on the end for clockstats display. |
---|
315 | */ |
---|
316 | if (pp->lencode == LENARB) { |
---|
317 | /* |
---|
318 | * Timecode format B5: "i yy ddd hh:mm:ss.000 " |
---|
319 | */ |
---|
320 | pp->a_lastcode[LENARB - 2] = up->qualchar; |
---|
321 | strcat(pp->a_lastcode, up->status); |
---|
322 | record_clock_stats(&peer->srcadr, pp->a_lastcode); |
---|
323 | syncchar = leapchar = ' '; |
---|
324 | if (sscanf(pp->a_lastcode, "%c%2d %3d %2d:%2d:%2d", |
---|
325 | &syncchar, &pp->year, &pp->day, &pp->hour, |
---|
326 | &pp->minute, &pp->second) != 6) { |
---|
327 | refclock_report(peer, CEVNT_BADREPLY); |
---|
328 | return; |
---|
329 | } |
---|
330 | } else { |
---|
331 | return; |
---|
332 | } |
---|
333 | |
---|
334 | /* |
---|
335 | * We decode the clock dispersion from the time quality |
---|
336 | * character. This assumes dispersion increases at about 1 ms |
---|
337 | * per minute, or about 16 ppm, which may even be a reasonable |
---|
338 | * figure for the radio clock oscillator. If the clock has not |
---|
339 | * yet tracked any satellites or failed, the leap bits are |
---|
340 | * marked unsynchronized. |
---|
341 | */ |
---|
342 | switch (up->qualchar) { |
---|
343 | |
---|
344 | case '0': /* locked, max accuracy */ |
---|
345 | case '4': /* unlock accuracy < 1 us */ |
---|
346 | case '5': /* unlock accuracy < 10 us */ |
---|
347 | ltemp = 0; |
---|
348 | break; |
---|
349 | |
---|
350 | case '6': /* unlock accuracy < 100 us */ |
---|
351 | ltemp = 6; |
---|
352 | break; |
---|
353 | |
---|
354 | case '7': /* unlock accuracy < 1 ms */ |
---|
355 | ltemp = 63; |
---|
356 | break; |
---|
357 | |
---|
358 | case '8': /* unlock accuracy < 10 ms */ |
---|
359 | ltemp = 625; |
---|
360 | break; |
---|
361 | |
---|
362 | case '9': /* unlock accuracy < 100 ms */ |
---|
363 | ltemp = 6250; |
---|
364 | break; |
---|
365 | |
---|
366 | case 'A': /* unlock accuracy < 1 s */ |
---|
367 | ltemp = 62500; |
---|
368 | break; |
---|
369 | |
---|
370 | case 'B': /* unlock accuracy < 10 s */ |
---|
371 | case 'F': /* clock failure */ |
---|
372 | ltemp = NTP_MAXAGE; |
---|
373 | pp->leap = LEAP_NOTINSYNC; |
---|
374 | return; |
---|
375 | |
---|
376 | default: |
---|
377 | ltemp = NTP_MAXAGE; |
---|
378 | pp->leap = LEAP_NOTINSYNC; |
---|
379 | refclock_report(peer, CEVNT_BADREPLY); |
---|
380 | return; |
---|
381 | } |
---|
382 | pp->leap = 0; |
---|
383 | pp->lasttime = current_time - ltemp; |
---|
384 | |
---|
385 | /* |
---|
386 | * Process the new sample in the median filter and determine the |
---|
387 | * reference clock offset and dispersion. We use lastrec as both |
---|
388 | * the reference time and receive time in order to avoid being |
---|
389 | * cute, like setting the reference time later than the receive |
---|
390 | * time, which may cause a paranoid protocol module to chuck out |
---|
391 | * the data. |
---|
392 | */ |
---|
393 | if (!refclock_process(pp, NSAMPLES, NSAMPLES)) { |
---|
394 | refclock_report(peer, CEVNT_BADTIME); |
---|
395 | return; |
---|
396 | } |
---|
397 | trtmp = pp->lastrec; |
---|
398 | trtmp.l_ui -= ltemp; |
---|
399 | refclock_receive(peer, &pp->offset, 0, pp->dispersion, |
---|
400 | &trtmp, &pp->lastrec, pp->leap); |
---|
401 | } |
---|
402 | |
---|
403 | |
---|
404 | /* |
---|
405 | * arb_poll - called by the transmit procedure |
---|
406 | */ |
---|
407 | static void |
---|
408 | arb_poll(unit, peer) |
---|
409 | int unit; |
---|
410 | struct peer *peer; |
---|
411 | { |
---|
412 | register struct arbunit *up; |
---|
413 | struct refclockproc *pp; |
---|
414 | |
---|
415 | /* |
---|
416 | * Time to poll the clock. The Arbiter clock responds to a "B5" |
---|
417 | * by returning a timecode in the format specified above. |
---|
418 | * Transmission occurs once per second, unless turned off by a |
---|
419 | * "B0". Note there is no checking on state, since this may not |
---|
420 | * be the only customer reading the clock. Only one customer |
---|
421 | * need poll the clock; all others just listen in. If nothing is |
---|
422 | * heard from the clock for two polls, declare a timeout and |
---|
423 | * keep going. |
---|
424 | */ |
---|
425 | pp = peer->procptr; |
---|
426 | up = (struct arbunit *)pp->unitptr; |
---|
427 | if (up->pollcnt == 0) |
---|
428 | refclock_report(peer, CEVNT_TIMEOUT); |
---|
429 | else |
---|
430 | up->pollcnt--; |
---|
431 | if (write(pp->io.fd, "TQ", 2) != 2) { |
---|
432 | refclock_report(peer, CEVNT_FAULT); |
---|
433 | } else |
---|
434 | pp->polls++; |
---|
435 | up->tcswitch = 0; |
---|
436 | |
---|
437 | } |
---|
438 | |
---|
439 | #endif |
---|