1: #if defined(REFCLOCK) && defined(PSTI) 2: #ifndef lint 3: static char *rcsid = "$Header: /usr/users/louie/ntp/RCS/read_psti.c,v 3.4.1.1 89/05/18 18:36:45 louie Exp Locker: louie $"; 4: static char *sccsid = "@(#)read_psti.c 1.1 MS/ACF 89/02/17"; 5: #endif lint 6: 7: #define ERR_RATE 60 /* Repeat errors once an hour */ 8: 9: /* 10: * read_psti.c 11: * January 1988 -- orignal by Jeffrey I. Schiller <JIS@BITSY.MIT.EDU> 12: * January 1989 -- QU version by Doug Kingston <DPK@Morgan.COM> 13: * 14: * This module facilitates reading a Precision Time Standard, Inc. 15: * WWV radio clock. We assume that clock is configured for 9600 baud, 16: * no parity. Output is accepted in either 24 or 12 hour format. 17: * Time is requested and processed in GMT. 18: * 19: * This version is designed to make use of the QU command due to 20: * additional information it provides (like date and flags). 21: * Select is used to prevent hanging in a read when there are 22: * no characters to read. The line is run in cooked mode to 23: * reduce overhead. 24: * 25: * This requires a PSTI ROM revision later 4.01.000 or later. 26: * 27: * Routines defined: 28: * init_clock_psti(): Open the tty associated with the clock and 29: * set its tty mode bits. Returns fd on success 30: * and -1 on failure. 31: * read_clock_psti(): Reads the clock and returns either 0 on success 32: * or non-zero on error. On success, pointers are 33: * provided to the reference and local time. 34: */ 35: #include <stdio.h> 36: #include <syslog.h> 37: #include <sys/time.h> 38: #include <sys/types.h> 39: #include <sys/ioctl.h> 40: #if defined(sun) 41: #include <termio.h> 42: #endif 43: 44: #ifdef DEBUG 45: extern int debug; 46: #endif DEBUG 47: 48: static int nerrors = 0; 49: static char clockdata[32]; 50: #define MIN_READ 13 /* for Rev 4.01.001 */ 51: 52: static double reltime(); 53: 54: #ifdef STANDALONE 55: 56: #ifndef CLOCKDEV 57: #define CLOCKDEV "/dev/radioclock" 58: #endif 59: 60: #define DEBUG 1 61: int debug = 1; 62: 63: main(argc, argv) 64: int argc; 65: char **argv; 66: { 67: struct timeval *tvp, *otvp; 68: 69: debug = argc; 70: if (openclock(CLOCKDEV)) 71: do { 72: (void)readclock(&tvp, &otvp); 73: sleep(1); 74: } while (debug>1); 75: exit(0); 76: } 77: #endif STANDALONE 78: 79: 80: init_clock_psti(timesource) 81: char *timesource; 82: { 83: int cfd; 84: #ifdef TCSETA 85: struct termio tty; 86: #else 87: struct sgttyb tty; 88: #endif 89: 90: if ((cfd = open(timesource, 2)) < 0) { 91: #ifdef DEBUG 92: if (debug) perror(timesource); else 93: #endif DEBUG 94: syslog(LOG_ERR, "can't open %s: %m", timesource); 95: return(-1); 96: } 97: 98: if (ioctl(cfd, TIOCEXCL, 0) < 0) { 99: #ifdef DEBUG 100: if (debug) perror("TIOCEXCL on radioclock failed"); else 101: #endif DEBUG 102: syslog(LOG_ERR, "TIOCEXCL on %s failed: %m", timesource); 103: return(-1); 104: } 105: 106: #ifdef TCSETA 107: if (ioctl(cfd, TCGETA, &tty) < 0) { 108: #ifdef DEBUG 109: if (debug) perror("ioctl on radioclock failed"); else 110: #endif DEBUG 111: syslog(LOG_ERR, "ioctl on %s failed: %m", timesource); 112: return(-1); 113: } 114: tty.c_cflag = (B9600<<16)|B9600|CS8|CLOCAL|CREAD; 115: tty.c_iflag = ICRNL; 116: tty.c_oflag = 0; 117: tty.c_lflag = 0; 118: bzero((char *)tty.c_cc, sizeof tty.c_cc); 119: tty.c_cc[VMIN] = MIN_READ; 120: tty.c_cc[VTIME] = 0; 121: if (ioctl(cfd, TCSETA, &tty) < 0) { 122: #else TCSETA /* Use older Berkeley style IOCTL's */ 123: bzero((char *)&tty, sizeof tty); 124: tty.sg_ispeed = tty.sg_ospeed = B9600; 125: tty.sg_flags = ANYP|CRMOD; 126: tty.sg_erase = tty.sg_kill = '\0'; 127: if (ioctl(cfd, TIOCSETP, &tty) < 0) { 128: #endif TCSETA 129: #ifdef DEBUG 130: if (debug) perror("ioctl on radioclock failed"); else 131: #endif DEBUG 132: syslog(LOG_ERR, "ioctl on %s failed: %m", timesource); 133: return(-1); 134: } 135: if (write(cfd, "xxxxxxsn\r", 9) != 9) { 136: #ifdef DEBUG 137: if (debug) perror("init write to radioclock failed"); else 138: #endif DEBUG 139: syslog(LOG_ERR, "init write to %s failed: %m", timesource); 140: return(-1); 141: } 142: return(cfd); /* Succeeded in opening the clock */ 143: } 144: 145: /* 146: * read_clock_psti() -- Read the PSTI Radio Clock. 147: */ 148: read_clock_psti(cfd, tvpp, otvpp) 149: int cfd; 150: struct timeval **tvpp, **otvpp; 151: { 152: static struct timeval radiotime; 153: static struct timeval mytime; 154: struct timeval timeout; 155: struct tm *mtm; 156: struct tm radio_tm, *rtm = &radio_tm; 157: register int i; 158: register int millis; 159: register double diff; 160: int stat1, stat2; 161: fd_set readfds; 162: char message[256]; 163: #ifndef TCSETA 164: register char *cp; 165: int need; 166: #endif TCSETA 167: 168: FD_ZERO(&readfds); 169: FD_SET(cfd, &readfds); 170: timeout.tv_sec = 2; 171: timeout.tv_usec = 0; 172: 173: (void) ioctl(cfd, TIOCFLUSH, 0); /* scrap the I/O queues */ 174: 175: /* BEGIN TIME CRITICAL CODE SECTION!!!!!! */ 176: /* EVERY CYCLE FROM THIS POINT OUT ADDS TO THE INACCURACY OF 177: THE READ CLOCK VALUE!!!!! */ 178: if (write(cfd, "\003qu0000", 7) != 7) { 179: #ifdef DEBUG 180: if (debug) printf("radioclock write failed\n"); else 181: #endif DEBUG 182: if ((nerrors++%ERR_RATE) == 0) 183: syslog(LOG_ERR, "write to radioclock failed: %m"); 184: return(1); 185: } 186: if(select(cfd+1, &readfds, 0, 0, &timeout) != 1) { 187: #ifdef DEBUG 188: if (debug) printf("radioclock poll timed out\n"); else 189: #endif DEBUG 190: if ((nerrors++%ERR_RATE) == 0) 191: syslog(LOG_ERR, "poll of radioclock failed: %m"); 192: return(1); 193: } 194: if ((i = read(cfd, clockdata, sizeof clockdata)) < MIN_READ) { 195: #ifdef DEBUG 196: if (debug) printf("radioclock read error (%d)\n", i); else 197: #endif DEBUG 198: if ((nerrors++%ERR_RATE) == 0) 199: syslog(LOG_ERR, "radioclock read error (%d!=13): %m", i); 200: return(1); 201: } 202: 203: (void) gettimeofday(&mytime, (struct timezone *)0); 204: /* END OF TIME CRITICAL CODE SECTION!!!! */ 205: 206: if (clockdata[i-1] != '\n') { 207: #ifdef DEBUG 208: if (debug) printf("radioclock format error1 (%.12s)(0x%x)\n", 209: clockdata, clockdata[12]); else 210: #endif DEBUG 211: if ((nerrors++%ERR_RATE) == 0) 212: syslog(LOG_ERR, "radioclock format error1 (%.12s)(0x%x)", 213: clockdata, clockdata[12]); 214: return(1); 215: } 216: for (i = 0; i < 12; i++) { 217: if (clockdata[i] < '0' || clockdata[i] > 'o') { 218: #ifdef DEBUG 219: if (debug) printf("radioclock format error2\n"); else 220: #endif DEBUG 221: if ((nerrors++%ERR_RATE) == 0) 222: syslog(LOG_ERR, "radioclock format error2\n"); 223: return(1); 224: } 225: } 226: stat1 = clockdata[0]-'0'; 227: stat2 = clockdata[1]-'0'; 228: millis = ((clockdata[2]-'0')*64)+(clockdata[3]-'0'); 229: rtm->tm_sec = (clockdata[4]-'0'); 230: rtm->tm_min = (clockdata[5]-'0'); 231: rtm->tm_hour = (clockdata[6]-'0'); 232: rtm->tm_yday = ((clockdata[7]-'0')*64)+(clockdata[8]-'0')-1; 233: rtm->tm_year = 86+(clockdata[9]-'0'); 234: /* byte 10 and 11 reserved */ 235: 236: /* 237: * Correct "hours" based on whether or not AM/PM mode is enabled. 238: * If clock is in 24 hour (military) mode then no correction is 239: * needed. 240: */ 241: if(stat2&0x10) { /* Map AM/PM time to Military */ 242: if (stat2&0x8) { 243: if (rtm->tm_hour != 12) rtm->tm_hour += 12; 244: } else { 245: if (rtm->tm_hour == 12) rtm->tm_hour = 0; 246: } 247: } 248: 249: if (stat1 != 0x4 && (nerrors++%ERR_RATE)==0) { 250: #ifdef DEBUG 251: if (debug) printf("radioclock fault #%d 0x%x:%s%s%s%s%s%s\n", 252: nerrors, stat1, 253: stat1&0x20?" Out of Spec,":"", 254: stat1&0x10?" Hardware Fault,":"", 255: stat1&0x8?" Signal Fault,":"", 256: stat1&0x4?" Time Avail,":"", 257: stat1&0x2?" Year Mismatch,":"", 258: stat1&0x1?" Clock Reset,":""); 259: else { 260: #endif DEBUG 261: sprintf(message, "radioclock fault #%d 0x%x:%s%s%s%s%s%s\n", 262: nerrors, stat1, 263: stat1&0x20?" Out of Spec,":"", 264: stat1&0x10?" Hardware Fault,":"", 265: stat1&0x8?" Signal Fault,":"", 266: stat1&0x4?" Time Avail,":"", 267: stat1&0x2?" Year Mismatch,":"", 268: stat1&0x1?" Clock Reset,":""); 269: syslog(LOG_ERR, message); 270: } 271: } 272: if (stat1&0x38) /* Out of Spec, Hardware Fault, Signal Fault */ 273: return(1); 274: if ((millis > 999 || rtm->tm_sec > 60 || rtm->tm_min > 60 || 275: rtm->tm_hour > 23 || rtm->tm_yday > 365) && (nerrors++%ERR_RATE)==0) { 276: #ifdef DEBUG 277: if (debug) printf("radioclock bogon #%d: %dd %dh %dm %ds %dms\n", 278: nerrors, rtm->tm_yday, rtm->tm_hour, 279: rtm->tm_min, rtm->tm_sec, millis); 280: else 281: #endif DEBUG 282: sprintf(message, "radioclock bogon #%d: %dd %dh %dm %ds %dms\n", 283: nerrors, rtm->tm_yday, rtm->tm_hour, 284: rtm->tm_min, rtm->tm_sec, millis); 285: syslog(LOG_ERR, message); 286: return(1); 287: } 288: 289: mtm = gmtime(&mytime.tv_sec); 290: diff = reltime(rtm, millis*1000) - reltime(mtm, mytime.tv_usec); 291: #ifdef DEBUG 292: if (debug > 1) 293: printf("Clock time: 19%d day %03d %02d:%02d:%02d.%03d diff %.3f\n", 294: rtm->tm_year, rtm->tm_yday, rtm->tm_hour, 295: rtm->tm_min, rtm->tm_sec, millis, diff); 296: #endif DEBUG 297: 298: if (diff > (90*24*60*60.0) && (nerrors++%ERR_RATE)==0) { 299: #ifdef DEBUG 300: if (debug) 301: printf("offset excessive (system 19%d/%d, clock 19%d/%d)\n", 302: mtm->tm_year, mtm->tm_yday, 303: rtm->tm_year, mtm->tm_yday); 304: else 305: #endif DEBUG 306: syslog(LOG_ERR, "offset excessive (system 19%d/%d, clock 19%d/%d)\n", 307: mtm->tm_year, mtm->tm_yday, 308: rtm->tm_year, mtm->tm_yday); 309: return(1); 310: } 311: 312: diff += (double)mytime.tv_sec + ((double)mytime.tv_usec/1000000.0); 313: radiotime.tv_sec = diff; 314: radiotime.tv_usec = (diff - (double)radiotime.tv_sec) * 1000000; 315: #ifdef DEBUG 316: if (debug > 1) { 317: printf("System time: 19%d day %03d %02d:%02d:%02d.%03d\n", 318: mtm->tm_year, mtm->tm_yday, mtm->tm_hour, 319: mtm->tm_min, mtm->tm_sec, mytime.tv_usec/1000); 320: printf("stat1 0%o, stat2 0%o: ", stat1, stat2); 321: if (stat1 || stat2) 322: printf("%s%s%s%s%s%s%s%s%s%s%s%s", 323: stat1&0x20?" Out of Spec,":"", 324: stat1&0x10?" Hardware Fault,":"", 325: stat1&0x8?" Signal Fault,":"", 326: stat1&0x4?" Time Avail,":"", 327: stat1&0x2?" Year Mismatch,":"", 328: stat1&0x1?" Clock Reset,":"", 329: stat2&0x20?" DST on,":"", 330: stat2&0x10?" 12hr mode,":"", 331: stat2&0x8?" PM,":"", 332: stat2&0x4?" Spare?,":"", 333: stat2&0x2?" DST??? +1,":"", 334: stat2&0x1?" DST??? -1,":""); 335: printf("\n"); 336: } 337: #endif DEBUG 338: /* If necessary, acknowledge "Clock Reset" flag bit */ 339: if (stat1 & 0x1) { 340: if (write(cfd, "si0", 3) != 3) { 341: #ifdef DEBUG 342: if (debug) printf("radioclock reset write failed\n"); else 343: #endif DEBUG 344: syslog(LOG_ERR, "reset write to radioclock failed: %m"); 345: return(1); 346: } 347: } 348: if (nerrors && stat1==0x4) { 349: syslog(LOG_ERR, "radioclock OK (after %d errors)", nerrors); 350: nerrors = 0; 351: } 352: *tvpp = &radiotime; 353: *otvpp = &mytime; 354: return(0); 355: } 356: 357: static double 358: reltime(tm, usec) 359: register struct tm *tm; 360: register int usec; 361: { 362: return(tm->tm_year*(366.0*24.0*60.0*60.0) + 363: tm->tm_yday*(24.0*60.0*60.0) + 364: tm->tm_hour*(60.0*60.0) + 365: tm->tm_min*(60.0) + 366: tm->tm_sec + 367: usec/1000000.0); 368: } 369: #endif