1/* 2 * refclock_trak - clock driver for the TRAK 8810 GPS Station Clock 3 * 4 * Tomoaki TSURUOKA <tsuruoka@nc.fukuoka-u.ac.jp> 5 * original version Dec, 1993 6 * revised version Sep, 1994 for ntp3.4e or later 7 */ 8 9#ifdef HAVE_CONFIG_H 10#include <config.h> 11#endif 12 13#if defined(REFCLOCK) && defined(CLOCK_TRAK) && defined(PPS) 14 15#include "ntpd.h" 16#include "ntp_io.h" 17#include "ntp_refclock.h" 18#include "ntp_stdlib.h" 19#include "ntp_unixtime.h" 20 21#include <stdio.h> 22#include <ctype.h> 23 24#ifdef HAVE_SYS_TERMIOS_H 25# include <sys/termios.h> 26#endif 27#ifdef HAVE_SYS_PPSCLOCK_H 28# include <sys/ppsclock.h> 29#endif 30 31/* 32 * This driver supports the TRAK 8810/8820 GPS Station Clock. The claimed 33 * accuracy at the 1-pps output is 200-300 ns relative to the broadcast 34 * signal; however, in most cases the actual accuracy is limited by the 35 * precision of the timecode and the latencies of the serial interface 36 * and operating system. 37 * 38 * For best accuracy, this radio requires the LDISC_ACTS line 39 * discipline, which captures a timestamp at the '*' on-time character 40 * of the timecode. Using this discipline the jitter is in the order of 41 * 1 ms and systematic error about 0.5 ms. If unavailable, the buffer 42 * timestamp is used, which is captured at the \r ending the timecode 43 * message. This introduces a systematic error of 23 character times, or 44 * about 24 ms at 9600 bps, together with a jitter well over 8 ms on Sun 45 * IPC-class machines. 46 * 47 * Using the memus, the radio should be set for 9600 bps, one stop bit 48 * and no parity. It should be set to operate in computer (no echo) 49 * mode. The timecode format includes neither the year nor leap-second 50 * warning. No provisions are included in this preliminary version of 51 * the driver to read and record detailed internal radio status. 52 * 53 * In operation, this driver sends a RQTS\r request to the radio at 54 * initialization in order to put it in continuous time output mode. The 55 * radio then sends the following message once each second: 56 * 57 * *RQTS U,ddd:hh:mm:ss.0,q<cr><lf> 58 * 59 * on-time = '*' * ddd = day of year 60 * hh:mm:ss = hours, minutes, seconds 61 * q = quality indicator (phase error), 0-6: 62 * 0 > 20 us 63 * 6 > 10 us 64 * 5 > 1 us 65 * 4 > 100 ns 66 * 3 > 10 ns 67 * 2 < 10 ns 68 * 69 * The alarm condition is indicated by '0' at Q, which means the radio 70 * has a phase error than 20 usec relative to the broadcast time. The 71 * absence of year, DST and leap-second warning in this format is also 72 * alarming. 73 * 74 * The continuous time mode is disabled using the RQTX<cr> request, 75 * following which the radio sends a RQTX DONE<cr><lf> response. In the 76 * normal mode, other control and status requests are effective, 77 * including the leap-second status request RQLS<cr>. The radio responds 78 * wtih RQLS yy,mm,dd<cr><lf>, where yy,mm,dd are the year, month and 79 * day. Presumably, this gives the epoch of the next leap second, 80 * RQLS 00,00,00 if none is specified in the GPS message. Specified in 81 * this form, the information is generally useless and is ignored by 82 * the driver. 83 * 84 * Fudge Factors 85 * 86 * There are no special fudge factors other than the generic. 87 */ 88 89/* 90 * Interface definitions 91 */ 92#define DEVICE "/dev/trak%d" /* device name and unit */ 93#define SPEED232 B9600 /* uart speed (9600 baud) */ 94#define PRECISION (-20) /* precision assumed (about 1 us) */ 95#define REFID "GPS\0" /* reference ID */ 96#define DESCRIPTION "TRACK 8810/8820 Station Clock" /* WRU */ 97 98#define LENTRAK 24 /* timecode length */ 99#define C_CTO "RQTS\r" /* start continuous time output */ 100 101/* 102 * Unit control structure 103 */ 104struct trakunit { 105 int polled; /* poll message flag */ 106 l_fp tstamp; /* timestamp of last poll */ 107}; 108 109/* 110 * Function prototypes 111 */ 112static int trak_start P((int, struct peer *)); 113static void trak_shutdown P((int, struct peer *)); 114static void trak_receive P((struct recvbuf *)); 115static void trak_poll P((int, struct peer *)); 116 117/* 118 * Transfer vector 119 */ 120struct refclock refclock_trak = { 121 trak_start, /* start up driver */ 122 trak_shutdown, /* shut down driver */ 123 trak_poll, /* transmit poll message */ 124 noentry, /* not used (old trak_control) */ 125 noentry, /* initialize driver (not used) */ 126 noentry, /* not used (old trak_buginfo) */ 127 NOFLAGS /* not used */ 128}; 129 130 131/* 132 * trak_start - open the devices and initialize data for processing 133 */ 134static int 135trak_start( 136 int unit, 137 struct peer *peer 138 ) 139{ 140 register struct trakunit *up; 141 struct refclockproc *pp; 142 int fd; 143 char device[20]; 144 145 /* 146 * Open serial port. The LDISC_ACTS line discipline inserts a 147 * timestamp following the "*" on-time character of the 148 * timecode. 149 */ 150 (void)sprintf(device, DEVICE, unit); 151 if ( 152#ifdef PPS 153 !(fd = refclock_open(device, SPEED232, LDISC_CLK)) 154#else 155 !(fd = refclock_open(device, SPEED232, 0)) 156#endif /* PPS */ 157 ) 158 return (0); 159 160 /* 161 * Allocate and initialize unit structure 162 */ 163 if (!(up = (struct trakunit *) 164 emalloc(sizeof(struct trakunit)))) { 165 (void) close(fd); 166 return (0); 167 } 168 memset((char *)up, 0, sizeof(struct trakunit)); 169 pp = peer->procptr; 170 pp->io.clock_recv = trak_receive; 171 pp->io.srcclock = (caddr_t)peer; 172 pp->io.datalen = 0; 173 pp->io.fd = fd; 174 if (!io_addclock(&pp->io)) { 175 (void) close(fd); 176 free(up); 177 return (0); 178 } 179 pp->unitptr = (caddr_t)up; 180 181 /* 182 * Initialize miscellaneous variables 183 */ 184 peer->precision = PRECISION; 185 pp->clockdesc = DESCRIPTION; 186 memcpy((char *)&pp->refid, REFID, 4); 187 up->polled = 0; 188 189 /* 190 * Start continuous time output. If something breaks, fold the 191 * tent and go home. 192 */ 193 if (write(pp->io.fd, C_CTO, sizeof(C_CTO)) != sizeof(C_CTO)) { 194 refclock_report(peer, CEVNT_FAULT); 195 (void) close(fd); 196 free(up); 197 return (0); 198 } 199 return (1); 200} 201 202 203/* 204 * trak_shutdown - shut down the clock 205 */ 206static void 207trak_shutdown( 208 int unit, 209 struct peer *peer 210 ) 211{ 212 register struct trakunit *up; 213 struct refclockproc *pp; 214 215 pp = peer->procptr; 216 up = (struct trakunit *)pp->unitptr; 217 io_closeclock(&pp->io); 218 free(up); 219} 220 221 222/* 223 * trak_receive - receive data from the serial interface 224 */ 225static void 226trak_receive( 227 struct recvbuf *rbufp 228 ) 229{ 230 register struct trakunit *up; 231 struct refclockproc *pp; 232 struct peer *peer; 233 l_fp trtmp; 234 char *dpt, *dpend; 235 char qchar; 236#ifdef PPS 237 struct ppsclockev ppsev; 238 int request; 239#ifdef HAVE_CIOGETEV 240 request = CIOGETEV; 241#endif 242#ifdef HAVE_TIOCGPPSEV 243 request = TIOCGPPSEV; 244#endif 245#endif /* PPS */ 246 247 /* 248 * Initialize pointers and read the timecode and timestamp. We 249 * then chuck out everything, including runts, except one 250 * message each poll interval. 251 */ 252 peer = (struct peer *)rbufp->recv_srcclock; 253 pp = peer->procptr; 254 up = (struct trakunit *)pp->unitptr; 255 pp->lencode = refclock_gtlin(rbufp, pp->a_lastcode, BMAX, 256 &pp->lastrec); 257 258 /* 259 * We get a buffer and timestamp following the '*' on-time 260 * character. If a valid timestamp, we use that in place of the 261 * buffer timestamp and edit out the timestamp for prettyprint 262 * billboards. 263 */ 264 dpt = pp->a_lastcode; 265 dpend = dpt + pp->lencode; 266 if (*dpt == '*' && buftvtots(dpt + 1, &trtmp)) { 267 if (trtmp.l_i == pp->lastrec.l_i || trtmp.l_i == 268 pp->lastrec.l_i + 1) { 269 pp->lastrec = trtmp; 270 dpt += 9; 271 while (dpt < dpend) { 272 *(dpt - 8) = *dpt; 273 ++dpt; 274 } 275 } 276 } 277 if (up->polled == 0) return; 278 up->polled = 0; 279#ifndef PPS 280 get_systime(&up->tstamp); 281#endif 282 record_clock_stats(&peer->srcadr, pp->a_lastcode); 283#ifdef DEBUG 284 if (debug) 285 printf("trak: timecode %d %s\n", pp->lencode, 286 pp->a_lastcode); 287#endif 288 289 /* 290 * We get down to business, check the timecode format and decode 291 * its contents. If the timecode has invalid length or is not in 292 * proper format, we declare bad format and exit. 293 */ 294 if (pp->lencode < LENTRAK) { 295 refclock_report(peer, CEVNT_BADREPLY); 296 return; 297 } 298 299 /* 300 * Timecode format: "*RQTS U,ddd:hh:mm:ss.0,q" 301 */ 302 if (sscanf(pp->a_lastcode, "*RQTS U,%3d:%2d:%2d:%2d.0,%c", 303 &pp->day, &pp->hour, &pp->minute, &pp->second, &qchar) != 5) { 304 refclock_report(peer, CEVNT_BADREPLY); 305 return; 306 } 307 308 /* 309 * Decode quality and leap characters. If unsynchronized, set 310 * the leap bits accordingly and exit. 311 */ 312 if (qchar == '0') { 313 pp->leap = LEAP_NOTINSYNC; 314 return; 315 } 316#ifdef PPS 317 if(ioctl(fdpps,request,(caddr_t) &ppsev) >=0) { 318 ppsev.tv.tv_sec += (u_int32) JAN_1970; 319 TVTOTS(&ppsev.tv,&up->tstamp); 320 } 321#endif /* PPS */ 322 /* record the last ppsclock event time stamp */ 323 pp->lastrec = up->tstamp; 324 if (!refclock_process(pp)) { 325 refclock_report(peer, CEVNT_BADTIME); 326 return; 327 } 328 pp->lastref = pp->lastrec; 329 refclock_receive(peer); 330} 331 332 333/* 334 * trak_poll - called by the transmit procedure 335 */ 336static void 337trak_poll( 338 int unit, 339 struct peer *peer 340 ) 341{ 342 register struct trakunit *up; 343 struct refclockproc *pp; 344 345 /* 346 * We don't really do anything here, except arm the receiving 347 * side to capture a sample and check for timeouts. 348 */ 349 pp = peer->procptr; 350 up = (struct trakunit *)pp->unitptr; 351 if (up->polled) 352 refclock_report(peer, CEVNT_TIMEOUT); 353 pp->polls++; 354 up->polled = 1; 355} 356 357#else 358int refclock_trak_bs; 359#endif /* REFCLOCK */ 360