/src/ntp-dev/ntpd/refclock_hpgps.c
Line | Count | Source (jump to first uncovered line) |
1 | | /* |
2 | | * refclock_hpgps - clock driver for HP 58503A GPS receiver |
3 | | */ |
4 | | |
5 | | #ifdef HAVE_CONFIG_H |
6 | | # include <config.h> |
7 | | #endif |
8 | | |
9 | | #if defined(REFCLOCK) && defined(CLOCK_HPGPS) |
10 | | |
11 | | #include "ntpd.h" |
12 | | #include "ntp_io.h" |
13 | | #include "ntp_refclock.h" |
14 | | #include "ntp_stdlib.h" |
15 | | |
16 | | #include <stdio.h> |
17 | | #include <ctype.h> |
18 | | |
19 | | /* Version 0.1 April 1, 1995 |
20 | | * 0.2 April 25, 1995 |
21 | | * tolerant of missing timecode response prompt and sends |
22 | | * clear status if prompt indicates error; |
23 | | * can use either local time or UTC from receiver; |
24 | | * can get receiver status screen via flag4 |
25 | | * |
26 | | * WARNING!: This driver is UNDER CONSTRUCTION |
27 | | * Everything in here should be treated with suspicion. |
28 | | * If it looks wrong, it probably is. |
29 | | * |
30 | | * Comments and/or questions to: Dave Vitanye |
31 | | * Hewlett Packard Company |
32 | | * dave@scd.hp.com |
33 | | * (408) 553-2856 |
34 | | * |
35 | | * Thanks to the author of the PST driver, which was the starting point for |
36 | | * this one. |
37 | | * |
38 | | * This driver supports the HP 58503A Time and Frequency Reference Receiver. |
39 | | * This receiver uses HP SmartClock (TM) to implement an Enhanced GPS receiver. |
40 | | * The receiver accuracy when locked to GPS in normal operation is better |
41 | | * than 1 usec. The accuracy when operating in holdover is typically better |
42 | | * than 10 usec. per day. |
43 | | * |
44 | | * The same driver also handles the HP Z3801A which is available surplus |
45 | | * from the cell phone industry. It's popular with hams. |
46 | | * It needs a different line setup: 19200 baud, 7 data bits, odd parity |
47 | | * That is selected by adding "mode 1" to the server line in ntp.conf |
48 | | * HP Z3801A code from Jeff Mock added by Hal Murray, Sep 2005 |
49 | | * |
50 | | * |
51 | | * The receiver should be operated with factory default settings. |
52 | | * Initial driver operation: expects the receiver to be already locked |
53 | | * to GPS, configured and able to output timecode format 2 messages. |
54 | | * |
55 | | * The driver uses the poll sequence :PTIME:TCODE? to get a response from |
56 | | * the receiver. The receiver responds with a timecode string of ASCII |
57 | | * printing characters, followed by a <cr><lf>, followed by a prompt string |
58 | | * issued by the receiver, in the following format: |
59 | | * T#yyyymmddhhmmssMFLRVcc<cr><lf>scpi > |
60 | | * |
61 | | * The driver processes the response at the <cr> and <lf>, so what the |
62 | | * driver sees is the prompt from the previous poll, followed by this |
63 | | * timecode. The prompt from the current poll is (usually) left unread until |
64 | | * the next poll. So (except on the very first poll) the driver sees this: |
65 | | * |
66 | | * scpi > T#yyyymmddhhmmssMFLRVcc<cr><lf> |
67 | | * |
68 | | * The T is the on-time character, at 980 msec. before the next 1PPS edge. |
69 | | * The # is the timecode format type. We look for format 2. |
70 | | * Without any of the CLK or PPS stuff, then, the receiver buffer timestamp |
71 | | * at the <cr> is 24 characters later, which is about 25 msec. at 9600 bps, |
72 | | * so the first approximation for fudge time1 is nominally -0.955 seconds. |
73 | | * This number probably needs adjusting for each machine / OS type, so far: |
74 | | * -0.955000 on an HP 9000 Model 712/80 HP-UX 9.05 |
75 | | * -0.953175 on an HP 9000 Model 370 HP-UX 9.10 |
76 | | * |
77 | | * This receiver also provides a 1PPS signal, but I haven't figured out |
78 | | * how to deal with any of the CLK or PPS stuff yet. Stay tuned. |
79 | | * |
80 | | */ |
81 | | |
82 | | /* |
83 | | * Fudge Factors |
84 | | * |
85 | | * Fudge time1 is used to accomodate the timecode serial interface adjustment. |
86 | | * Fudge flag4 can be set to request a receiver status screen summary, which |
87 | | * is recorded in the clockstats file. |
88 | | */ |
89 | | |
90 | | /* |
91 | | * Interface definitions |
92 | | */ |
93 | | #define DEVICE "/dev/hpgps%d" /* device name and unit */ |
94 | 0 | #define SPEED232 B9600 /* uart speed (9600 baud) */ |
95 | 0 | #define SPEED232Z B19200 /* uart speed (19200 baud) */ |
96 | 0 | #define PRECISION (-10) /* precision assumed (about 1 ms) */ |
97 | 0 | #define REFID "GPS\0" /* reference ID */ |
98 | 0 | #define DESCRIPTION "HP 58503A GPS Time and Frequency Reference Receiver" |
99 | | |
100 | 0 | #define SMAX 23*80+1 /* for :SYSTEM:PRINT? status screen response */ |
101 | | |
102 | 0 | #define MTZONE 2 /* number of fields in timezone reply */ |
103 | 0 | #define MTCODET2 12 /* number of fields in timecode format T2 */ |
104 | 0 | #define NTCODET2 21 /* number of chars to checksum in format T2 */ |
105 | | |
106 | | /* |
107 | | * Tables to compute the day of year from yyyymmdd timecode. |
108 | | * Viva la leap. |
109 | | */ |
110 | | static int day1tab[] = {31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31}; |
111 | | static int day2tab[] = {31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31}; |
112 | | |
113 | | /* |
114 | | * Unit control structure |
115 | | */ |
116 | | struct hpgpsunit { |
117 | | int pollcnt; /* poll message counter */ |
118 | | int tzhour; /* timezone offset, hours */ |
119 | | int tzminute; /* timezone offset, minutes */ |
120 | | int linecnt; /* set for expected multiple line responses */ |
121 | | char *lastptr; /* pointer to receiver response data */ |
122 | | char statscrn[SMAX]; /* receiver status screen buffer */ |
123 | | }; |
124 | | |
125 | | /* |
126 | | * Function prototypes |
127 | | */ |
128 | | static int hpgps_start (int, struct peer *); |
129 | | static void hpgps_shutdown (int, struct peer *); |
130 | | static void hpgps_receive (struct recvbuf *); |
131 | | static void hpgps_poll (int, struct peer *); |
132 | | |
133 | | /* |
134 | | * Transfer vector |
135 | | */ |
136 | | struct refclock refclock_hpgps = { |
137 | | hpgps_start, /* start up driver */ |
138 | | hpgps_shutdown, /* shut down driver */ |
139 | | hpgps_poll, /* transmit poll message */ |
140 | | noentry, /* not used (old hpgps_control) */ |
141 | | noentry, /* initialize driver */ |
142 | | noentry, /* not used (old hpgps_buginfo) */ |
143 | | NOFLAGS /* not used */ |
144 | | }; |
145 | | |
146 | | |
147 | | /* |
148 | | * hpgps_start - open the devices and initialize data for processing |
149 | | */ |
150 | | static int |
151 | | hpgps_start( |
152 | | int unit, |
153 | | struct peer *peer |
154 | | ) |
155 | 0 | { |
156 | 0 | register struct hpgpsunit *up; |
157 | 0 | struct refclockproc *pp; |
158 | 0 | int fd; |
159 | 0 | int speed, ldisc; |
160 | 0 | char device[20]; |
161 | | |
162 | | /* |
163 | | * Open serial port. Use CLK line discipline, if available. |
164 | | * Default is HP 58503A, mode arg selects HP Z3801A |
165 | | */ |
166 | 0 | snprintf(device, sizeof(device), DEVICE, unit); |
167 | 0 | ldisc = LDISC_CLK; |
168 | 0 | speed = SPEED232; |
169 | | /* mode parameter to server config line shares ttl slot */ |
170 | 0 | if (1 == peer->ttl) { |
171 | 0 | ldisc |= LDISC_7O1; |
172 | 0 | speed = SPEED232Z; |
173 | 0 | } |
174 | 0 | fd = refclock_open(device, speed, ldisc); |
175 | 0 | if (fd <= 0) |
176 | 0 | return (0); |
177 | | /* |
178 | | * Allocate and initialize unit structure |
179 | | */ |
180 | 0 | up = emalloc_zero(sizeof(*up)); |
181 | 0 | pp = peer->procptr; |
182 | 0 | pp->io.clock_recv = hpgps_receive; |
183 | 0 | pp->io.srcclock = peer; |
184 | 0 | pp->io.datalen = 0; |
185 | 0 | pp->io.fd = fd; |
186 | 0 | if (!io_addclock(&pp->io)) { |
187 | 0 | close(fd); |
188 | 0 | pp->io.fd = -1; |
189 | 0 | free(up); |
190 | 0 | return (0); |
191 | 0 | } |
192 | 0 | pp->unitptr = up; |
193 | | |
194 | | /* |
195 | | * Initialize miscellaneous variables |
196 | | */ |
197 | 0 | peer->precision = PRECISION; |
198 | 0 | pp->clockdesc = DESCRIPTION; |
199 | 0 | memcpy((char *)&pp->refid, REFID, 4); |
200 | 0 | up->tzhour = 0; |
201 | 0 | up->tzminute = 0; |
202 | |
|
203 | 0 | *up->statscrn = '\0'; |
204 | 0 | up->lastptr = up->statscrn; |
205 | 0 | up->pollcnt = 2; |
206 | | |
207 | | /* |
208 | | * Get the identifier string, which is logged but otherwise ignored, |
209 | | * and get the local timezone information |
210 | | */ |
211 | 0 | up->linecnt = 1; |
212 | 0 | if (write(pp->io.fd, "*IDN?\r:PTIME:TZONE?\r", 20) != 20) |
213 | 0 | refclock_report(peer, CEVNT_FAULT); |
214 | |
|
215 | 0 | return (1); |
216 | 0 | } |
217 | | |
218 | | |
219 | | /* |
220 | | * hpgps_shutdown - shut down the clock |
221 | | */ |
222 | | static void |
223 | | hpgps_shutdown( |
224 | | int unit, |
225 | | struct peer *peer |
226 | | ) |
227 | 0 | { |
228 | 0 | register struct hpgpsunit *up; |
229 | 0 | struct refclockproc *pp; |
230 | |
|
231 | 0 | pp = peer->procptr; |
232 | 0 | up = pp->unitptr; |
233 | 0 | if (-1 != pp->io.fd) |
234 | 0 | io_closeclock(&pp->io); |
235 | 0 | if (NULL != up) |
236 | 0 | free(up); |
237 | 0 | } |
238 | | |
239 | | |
240 | | /* |
241 | | * hpgps_receive - receive data from the serial interface |
242 | | */ |
243 | | static void |
244 | | hpgps_receive( |
245 | | struct recvbuf *rbufp |
246 | | ) |
247 | 0 | { |
248 | 0 | register struct hpgpsunit *up; |
249 | 0 | struct refclockproc *pp; |
250 | 0 | struct peer *peer; |
251 | 0 | l_fp trtmp; |
252 | 0 | char tcodechar1; /* identifies timecode format */ |
253 | 0 | char tcodechar2; /* identifies timecode format */ |
254 | 0 | char timequal; /* time figure of merit: 0-9 */ |
255 | 0 | char freqqual; /* frequency figure of merit: 0-3 */ |
256 | 0 | char leapchar; /* leapsecond: + or 0 or - */ |
257 | 0 | char servchar; /* request for service: 0 = no, 1 = yes */ |
258 | 0 | char syncchar; /* time info is invalid: 0 = no, 1 = yes */ |
259 | 0 | short expectedsm; /* expected timecode byte checksum */ |
260 | 0 | short tcodechksm; /* computed timecode byte checksum */ |
261 | 0 | int i,m,n; |
262 | 0 | int month, day, lastday; |
263 | 0 | char *tcp; /* timecode pointer (skips over the prompt) */ |
264 | 0 | char prompt[BMAX]; /* prompt in response from receiver */ |
265 | | |
266 | | /* |
267 | | * Initialize pointers and read the receiver response |
268 | | */ |
269 | 0 | peer = rbufp->recv_peer; |
270 | 0 | pp = peer->procptr; |
271 | 0 | up = pp->unitptr; |
272 | 0 | *pp->a_lastcode = '\0'; |
273 | 0 | pp->lencode = refclock_gtlin(rbufp, pp->a_lastcode, BMAX, &trtmp); |
274 | |
|
275 | 0 | #ifdef DEBUG |
276 | 0 | if (debug) |
277 | 0 | printf("hpgps: lencode: %d timecode:%s\n", |
278 | 0 | pp->lencode, pp->a_lastcode); |
279 | 0 | #endif |
280 | | |
281 | | /* |
282 | | * If there's no characters in the reply, we can quit now |
283 | | */ |
284 | 0 | if (pp->lencode == 0) |
285 | 0 | return; |
286 | | |
287 | | /* |
288 | | * If linecnt is greater than zero, we are getting information only, |
289 | | * such as the receiver identification string or the receiver status |
290 | | * screen, so put the receiver response at the end of the status |
291 | | * screen buffer. When we have the last line, write the buffer to |
292 | | * the clockstats file and return without further processing. |
293 | | * |
294 | | * If linecnt is zero, we are expecting either the timezone |
295 | | * or a timecode. At this point, also write the response |
296 | | * to the clockstats file, and go on to process the prompt (if any), |
297 | | * timezone, or timecode and timestamp. |
298 | | */ |
299 | | |
300 | | |
301 | 0 | if (up->linecnt-- > 0) { |
302 | 0 | if ((int)(pp->lencode + 2) <= (SMAX - (up->lastptr - up->statscrn))) { |
303 | 0 | *up->lastptr++ = '\n'; |
304 | 0 | memcpy(up->lastptr, pp->a_lastcode, pp->lencode); |
305 | 0 | up->lastptr += pp->lencode; |
306 | 0 | } |
307 | 0 | if (up->linecnt == 0) |
308 | 0 | record_clock_stats(&peer->srcadr, up->statscrn); |
309 | | |
310 | 0 | return; |
311 | 0 | } |
312 | | |
313 | 0 | record_clock_stats(&peer->srcadr, pp->a_lastcode); |
314 | 0 | pp->lastrec = trtmp; |
315 | | |
316 | 0 | up->lastptr = up->statscrn; |
317 | 0 | *up->lastptr = '\0'; |
318 | 0 | up->pollcnt = 2; |
319 | | |
320 | | /* |
321 | | * We get down to business: get a prompt if one is there, issue |
322 | | * a clear status command if it contains an error indication. |
323 | | * Next, check for either the timezone reply or the timecode reply |
324 | | * and decode it. If we don't recognize the reply, or don't get the |
325 | | * proper number of decoded fields, or get an out of range timezone, |
326 | | * or if the timecode checksum is bad, then we declare bad format |
327 | | * and exit. |
328 | | * |
329 | | * Timezone format (including nominal prompt): |
330 | | * scpi > -H,-M<cr><lf> |
331 | | * |
332 | | * Timecode format (including nominal prompt): |
333 | | * scpi > T2yyyymmddhhmmssMFLRVcc<cr><lf> |
334 | | * |
335 | | */ |
336 | |
|
337 | 0 | strlcpy(prompt, pp->a_lastcode, sizeof(prompt)); |
338 | 0 | tcp = strrchr(pp->a_lastcode,'>'); |
339 | 0 | if (tcp == NULL) |
340 | 0 | tcp = pp->a_lastcode; |
341 | 0 | else |
342 | 0 | tcp++; |
343 | 0 | prompt[tcp - pp->a_lastcode] = '\0'; |
344 | 0 | while ((*tcp == ' ') || (*tcp == '\t')) tcp++; |
345 | | |
346 | | /* |
347 | | * deal with an error indication in the prompt here |
348 | | */ |
349 | 0 | if (strrchr(prompt,'E') > strrchr(prompt,'s')){ |
350 | 0 | #ifdef DEBUG |
351 | 0 | if (debug) |
352 | 0 | printf("hpgps: error indicated in prompt: %s\n", prompt); |
353 | 0 | #endif |
354 | 0 | if (write(pp->io.fd, "*CLS\r\r", 6) != 6) |
355 | 0 | refclock_report(peer, CEVNT_FAULT); |
356 | 0 | } |
357 | | |
358 | | /* |
359 | | * make sure we got a timezone or timecode format and |
360 | | * then process accordingly |
361 | | */ |
362 | 0 | m = sscanf(tcp,"%c%c", &tcodechar1, &tcodechar2); |
363 | |
|
364 | 0 | if (m != 2){ |
365 | 0 | #ifdef DEBUG |
366 | 0 | if (debug) |
367 | 0 | printf("hpgps: no format indicator\n"); |
368 | 0 | #endif |
369 | 0 | refclock_report(peer, CEVNT_BADREPLY); |
370 | 0 | return; |
371 | 0 | } |
372 | | |
373 | 0 | switch (tcodechar1) { |
374 | | |
375 | 0 | case '+': |
376 | 0 | case '-': |
377 | 0 | m = sscanf(tcp,"%d,%d", &up->tzhour, &up->tzminute); |
378 | 0 | if (m != MTZONE) { |
379 | 0 | #ifdef DEBUG |
380 | 0 | if (debug) |
381 | 0 | printf("hpgps: only %d fields recognized in timezone\n", m); |
382 | 0 | #endif |
383 | 0 | refclock_report(peer, CEVNT_BADREPLY); |
384 | 0 | return; |
385 | 0 | } |
386 | 0 | if ((up->tzhour < -12) || (up->tzhour > 13) || |
387 | 0 | (up->tzminute < -59) || (up->tzminute > 59)){ |
388 | 0 | #ifdef DEBUG |
389 | 0 | if (debug) |
390 | 0 | printf("hpgps: timezone %d, %d out of range\n", |
391 | 0 | up->tzhour, up->tzminute); |
392 | 0 | #endif |
393 | 0 | refclock_report(peer, CEVNT_BADREPLY); |
394 | 0 | return; |
395 | 0 | } |
396 | 0 | return; |
397 | | |
398 | 0 | case 'T': |
399 | 0 | break; |
400 | | |
401 | 0 | default: |
402 | 0 | #ifdef DEBUG |
403 | 0 | if (debug) |
404 | 0 | printf("hpgps: unrecognized reply format %c%c\n", |
405 | 0 | tcodechar1, tcodechar2); |
406 | 0 | #endif |
407 | 0 | refclock_report(peer, CEVNT_BADREPLY); |
408 | 0 | return; |
409 | 0 | } /* end of tcodechar1 switch */ |
410 | | |
411 | | |
412 | 0 | switch (tcodechar2) { |
413 | | |
414 | 0 | case '2': |
415 | 0 | m = sscanf(tcp,"%*c%*c%4d%2d%2d%2d%2d%2d%c%c%c%c%c%2hx", |
416 | 0 | &pp->year, &month, &day, &pp->hour, &pp->minute, &pp->second, |
417 | 0 | &timequal, &freqqual, &leapchar, &servchar, &syncchar, |
418 | 0 | &expectedsm); |
419 | 0 | n = NTCODET2; |
420 | |
|
421 | 0 | if (m != MTCODET2){ |
422 | 0 | #ifdef DEBUG |
423 | 0 | if (debug) |
424 | 0 | printf("hpgps: only %d fields recognized in timecode\n", m); |
425 | 0 | #endif |
426 | 0 | refclock_report(peer, CEVNT_BADREPLY); |
427 | 0 | return; |
428 | 0 | } |
429 | 0 | break; |
430 | | |
431 | 0 | default: |
432 | 0 | #ifdef DEBUG |
433 | 0 | if (debug) |
434 | 0 | printf("hpgps: unrecognized timecode format %c%c\n", |
435 | 0 | tcodechar1, tcodechar2); |
436 | 0 | #endif |
437 | 0 | refclock_report(peer, CEVNT_BADREPLY); |
438 | 0 | return; |
439 | 0 | } /* end of tcodechar2 format switch */ |
440 | | |
441 | | /* |
442 | | * Compute and verify the checksum. |
443 | | * Characters are summed starting at tcodechar1, ending at just |
444 | | * before the expected checksum. Bail out if incorrect. |
445 | | */ |
446 | 0 | tcodechksm = 0; |
447 | 0 | while (n-- > 0) tcodechksm += *tcp++; |
448 | 0 | tcodechksm &= 0x00ff; |
449 | |
|
450 | 0 | if (tcodechksm != expectedsm) { |
451 | 0 | #ifdef DEBUG |
452 | 0 | if (debug) |
453 | 0 | printf("hpgps: checksum %2hX doesn't match %2hX expected\n", |
454 | 0 | tcodechksm, expectedsm); |
455 | 0 | #endif |
456 | 0 | refclock_report(peer, CEVNT_BADREPLY); |
457 | 0 | return; |
458 | 0 | } |
459 | | |
460 | | /* |
461 | | * Compute the day of year from the yyyymmdd format. |
462 | | */ |
463 | 0 | if (month < 1 || month > 12 || day < 1) { |
464 | 0 | refclock_report(peer, CEVNT_BADTIME); |
465 | 0 | return; |
466 | 0 | } |
467 | | |
468 | 0 | if ( ! isleap_4(pp->year) ) { /* Y2KFixes */ |
469 | | /* not a leap year */ |
470 | 0 | if (day > day1tab[month - 1]) { |
471 | 0 | refclock_report(peer, CEVNT_BADTIME); |
472 | 0 | return; |
473 | 0 | } |
474 | 0 | for (i = 0; i < month - 1; i++) day += day1tab[i]; |
475 | 0 | lastday = 365; |
476 | 0 | } else { |
477 | | /* a leap year */ |
478 | 0 | if (day > day2tab[month - 1]) { |
479 | 0 | refclock_report(peer, CEVNT_BADTIME); |
480 | 0 | return; |
481 | 0 | } |
482 | 0 | for (i = 0; i < month - 1; i++) day += day2tab[i]; |
483 | 0 | lastday = 366; |
484 | 0 | } |
485 | | |
486 | | /* |
487 | | * Deal with the timezone offset here. The receiver timecode is in |
488 | | * local time = UTC + :PTIME:TZONE, so SUBTRACT the timezone values. |
489 | | * For example, Pacific Standard Time is -8 hours , 0 minutes. |
490 | | * Deal with the underflows and overflows. |
491 | | */ |
492 | 0 | pp->minute -= up->tzminute; |
493 | 0 | pp->hour -= up->tzhour; |
494 | |
|
495 | 0 | if (pp->minute < 0) { |
496 | 0 | pp->minute += 60; |
497 | 0 | pp->hour--; |
498 | 0 | } |
499 | 0 | if (pp->minute > 59) { |
500 | 0 | pp->minute -= 60; |
501 | 0 | pp->hour++; |
502 | 0 | } |
503 | 0 | if (pp->hour < 0) { |
504 | 0 | pp->hour += 24; |
505 | 0 | day--; |
506 | 0 | if (day < 1) { |
507 | 0 | pp->year--; |
508 | 0 | if ( isleap_4(pp->year) ) /* Y2KFixes */ |
509 | 0 | day = 366; |
510 | 0 | else |
511 | 0 | day = 365; |
512 | 0 | } |
513 | 0 | } |
514 | |
|
515 | 0 | if (pp->hour > 23) { |
516 | 0 | pp->hour -= 24; |
517 | 0 | day++; |
518 | 0 | if (day > lastday) { |
519 | 0 | pp->year++; |
520 | 0 | day = 1; |
521 | 0 | } |
522 | 0 | } |
523 | |
|
524 | 0 | pp->day = day; |
525 | | |
526 | | /* |
527 | | * Decode the MFLRV indicators. |
528 | | * NEED TO FIGURE OUT how to deal with the request for service, |
529 | | * time quality, and frequency quality indicators some day. |
530 | | */ |
531 | 0 | if (syncchar != '0') { |
532 | 0 | pp->leap = LEAP_NOTINSYNC; |
533 | 0 | } |
534 | 0 | else { |
535 | 0 | pp->leap = LEAP_NOWARNING; |
536 | 0 | switch (leapchar) { |
537 | | |
538 | 0 | case '0': |
539 | 0 | break; |
540 | | |
541 | | /* See http://bugs.ntp.org/1090 |
542 | | * Ignore leap announcements unless June or December. |
543 | | * Better would be to use :GPSTime? to find the month, |
544 | | * but that seems too likely to introduce other bugs. |
545 | | */ |
546 | 0 | case '+': |
547 | 0 | if ((month==6) || (month==12)) |
548 | 0 | pp->leap = LEAP_ADDSECOND; |
549 | 0 | break; |
550 | | |
551 | 0 | case '-': |
552 | 0 | if ((month==6) || (month==12)) |
553 | 0 | pp->leap = LEAP_DELSECOND; |
554 | 0 | break; |
555 | | |
556 | 0 | default: |
557 | 0 | #ifdef DEBUG |
558 | 0 | if (debug) |
559 | 0 | printf("hpgps: unrecognized leap indicator: %c\n", |
560 | 0 | leapchar); |
561 | 0 | #endif |
562 | 0 | refclock_report(peer, CEVNT_BADTIME); |
563 | 0 | return; |
564 | 0 | } /* end of leapchar switch */ |
565 | 0 | } |
566 | | |
567 | | /* |
568 | | * Process the new sample in the median filter and determine the |
569 | | * reference clock offset and dispersion. We use lastrec as both |
570 | | * the reference time and receive time in order to avoid being |
571 | | * cute, like setting the reference time later than the receive |
572 | | * time, which may cause a paranoid protocol module to chuck out |
573 | | * the data. |
574 | | */ |
575 | 0 | if (!refclock_process(pp)) { |
576 | 0 | refclock_report(peer, CEVNT_BADTIME); |
577 | 0 | return; |
578 | 0 | } |
579 | 0 | pp->lastref = pp->lastrec; |
580 | 0 | refclock_receive(peer); |
581 | | |
582 | | /* |
583 | | * If CLK_FLAG4 is set, ask for the status screen response. |
584 | | */ |
585 | 0 | if (pp->sloppyclockflag & CLK_FLAG4){ |
586 | 0 | up->linecnt = 22; |
587 | 0 | if (write(pp->io.fd, ":SYSTEM:PRINT?\r", 15) != 15) |
588 | 0 | refclock_report(peer, CEVNT_FAULT); |
589 | 0 | } |
590 | 0 | } |
591 | | |
592 | | |
593 | | /* |
594 | | * hpgps_poll - called by the transmit procedure |
595 | | */ |
596 | | static void |
597 | | hpgps_poll( |
598 | | int unit, |
599 | | struct peer *peer |
600 | | ) |
601 | 0 | { |
602 | 0 | register struct hpgpsunit *up; |
603 | 0 | struct refclockproc *pp; |
604 | | |
605 | | /* |
606 | | * Time to poll the clock. The HP 58503A responds to a |
607 | | * ":PTIME:TCODE?" by returning a timecode in the format specified |
608 | | * above. If nothing is heard from the clock for two polls, |
609 | | * declare a timeout and keep going. |
610 | | */ |
611 | 0 | pp = peer->procptr; |
612 | 0 | up = pp->unitptr; |
613 | 0 | if (up->pollcnt == 0) |
614 | 0 | refclock_report(peer, CEVNT_TIMEOUT); |
615 | 0 | else |
616 | 0 | up->pollcnt--; |
617 | 0 | if (write(pp->io.fd, ":PTIME:TCODE?\r", 14) != 14) { |
618 | 0 | refclock_report(peer, CEVNT_FAULT); |
619 | 0 | } |
620 | 0 | else |
621 | 0 | pp->polls++; |
622 | 0 | } |
623 | | |
624 | | #else |
625 | | int refclock_hpgps_bs; |
626 | | #endif /* REFCLOCK */ |