1 /* 2 * refclock_zyfer - clock driver for the Zyfer GPSTarplus Clock 3 * 4 * Harlan Stenn, Jan 2002 5 */ 6 7 #ifdef HAVE_CONFIG_H 8 #include <config.h> 9 #endif 10 11 #if defined(REFCLOCK) && defined(CLOCK_ZYFER) 12 13 #include "ntpd.h" 14 #include "ntp_io.h" 15 #include "ntp_refclock.h" 16 #include "ntp_stdlib.h" 17 #include "ntp_unixtime.h" 18 19 #include <stdio.h> 20 #include <ctype.h> 21 22 #ifdef HAVE_SYS_TERMIOS_H 23 # include <sys/termios.h> 24 #endif 25 #ifdef HAVE_SYS_PPSCLOCK_H 26 # include <sys/ppsclock.h> 27 #endif 28 29 /* 30 * This driver provides support for the TOD serial port of a Zyfer GPStarplus. 31 * This clock also provides PPS as well as IRIG outputs. 32 * Precision is limited by the serial driver, etc. 33 * 34 * If I was really brave I'd hack/generalize the serial driver to deal 35 * with arbitrary on-time characters. This clock *begins* the stream with 36 * `!`, the on-time character, and the string is *not* EOL-terminated. 37 * 38 * Configure the beast for 9600, 8N1. While I see leap-second stuff 39 * in the documentation, the published specs on the TOD format only show 40 * the seconds going to '59'. I see no leap warning in the TOD format. 41 * 42 * The clock sends the following message once per second: 43 * 44 * !TIME,2002,017,07,59,32,2,4,1 45 * YYYY DDD HH MM SS m T O 46 * 47 * ! On-time character 48 * YYYY Year 49 * DDD 001-366 Day of Year 50 * HH 00-23 Hour 51 * MM 00-59 Minute 52 * SS 00-59 Second (probably 00-60) 53 * m 1-5 Time Mode: 54 * 1 = GPS time 55 * 2 = UTC time 56 * 3 = LGPS time (Local GPS) 57 * 4 = LUTC time (Local UTC) 58 * 5 = Manual time 59 * T 4-9 Time Figure Of Merit: 60 * 4 x <= 1us 61 * 5 1us < x <= 10 us 62 * 6 10us < x <= 100us 63 * 7 100us < x <= 1ms 64 * 8 1ms < x <= 10ms 65 * 9 10ms < x 66 * O 0-4 Operation Mode: 67 * 0 Warm-up 68 * 1 Time Locked 69 * 2 Coasting 70 * 3 Recovering 71 * 4 Manual 72 * 73 */ 74 75 /* 76 * Interface definitions 77 */ 78 #define DEVICE "/dev/zyfer%d" /* device name and unit */ 79 #define SPEED232 B9600 /* uart speed (9600 baud) */ 80 #define PRECISION (-20) /* precision assumed (about 1 us) */ 81 #define REFID "GPS\0" /* reference ID */ 82 #define DESCRIPTION "Zyfer GPStarplus" /* WRU */ 83 84 #define LENZYFER 29 /* timecode length */ 85 86 /* 87 * Unit control structure 88 */ 89 struct zyferunit { 90 u_char Rcvbuf[LENZYFER + 1]; 91 u_char polled; /* poll message flag */ 92 int pollcnt; 93 l_fp tstamp; /* timestamp of last poll */ 94 int Rcvptr; 95 }; 96 97 /* 98 * Function prototypes 99 */ 100 static int zyfer_start P((int, struct peer *)); 101 static void zyfer_shutdown P((int, struct peer *)); 102 static void zyfer_receive P((struct recvbuf *)); 103 static void zyfer_poll P((int, struct peer *)); 104 105 /* 106 * Transfer vector 107 */ 108 struct refclock refclock_zyfer = { 109 zyfer_start, /* start up driver */ 110 zyfer_shutdown, /* shut down driver */ 111 zyfer_poll, /* transmit poll message */ 112 noentry, /* not used (old zyfer_control) */ 113 noentry, /* initialize driver (not used) */ 114 noentry, /* not used (old zyfer_buginfo) */ 115 NOFLAGS /* not used */ 116 }; 117 118 119 /* 120 * zyfer_start - open the devices and initialize data for processing 121 */ 122 static int 123 zyfer_start( 124 int unit, 125 struct peer *peer 126 ) 127 { 128 register struct zyferunit *up; 129 struct refclockproc *pp; 130 int fd; 131 char device[20]; 132 133 /* 134 * Open serial port. 135 * Something like LDISC_ACTS that looked for ! would be nice... 136 */ 137 (void)sprintf(device, DEVICE, unit); 138 if ( !(fd = refclock_open(device, SPEED232, LDISC_RAW)) ) 139 return (0); 140 141 msyslog(LOG_NOTICE, "zyfer(%d) fd: %d dev <%s>", unit, fd, device); 142 143 /* 144 * Allocate and initialize unit structure 145 */ 146 if (!(up = (struct zyferunit *) 147 emalloc(sizeof(struct zyferunit)))) { 148 (void) close(fd); 149 return (0); 150 } 151 memset((char *)up, 0, sizeof(struct zyferunit)); 152 pp = peer->procptr; 153 pp->io.clock_recv = zyfer_receive; 154 pp->io.srcclock = (caddr_t)peer; 155 pp->io.datalen = 0; 156 pp->io.fd = fd; 157 if (!io_addclock(&pp->io)) { 158 (void) close(fd); 159 free(up); 160 return (0); 161 } 162 pp->unitptr = (caddr_t)up; 163 164 /* 165 * Initialize miscellaneous variables 166 */ 167 peer->precision = PRECISION; 168 pp->clockdesc = DESCRIPTION; 169 memcpy((char *)&pp->refid, REFID, 4); 170 up->pollcnt = 2; 171 up->polled = 0; /* May not be needed... */ 172 173 return (1); 174 } 175 176 177 /* 178 * zyfer_shutdown - shut down the clock 179 */ 180 static void 181 zyfer_shutdown( 182 int unit, 183 struct peer *peer 184 ) 185 { 186 register struct zyferunit *up; 187 struct refclockproc *pp; 188 189 pp = peer->procptr; 190 up = (struct zyferunit *)pp->unitptr; 191 io_closeclock(&pp->io); 192 free(up); 193 } 194 195 196 /* 197 * zyfer_receive - receive data from the serial interface 198 */ 199 static void 200 zyfer_receive( 201 struct recvbuf *rbufp 202 ) 203 { 204 register struct zyferunit *up; 205 struct refclockproc *pp; 206 struct peer *peer; 207 int tmode; /* Time mode */ 208 int tfom; /* Time Figure Of Merit */ 209 int omode; /* Operation mode */ 210 u_char *p; 211 #ifdef PPS 212 struct ppsclockev ppsev; 213 int request; 214 #ifdef HAVE_CIOGETEV 215 request = CIOGETEV; 216 #endif 217 #ifdef HAVE_TIOCGPPSEV 218 request = TIOCGPPSEV; 219 #endif 220 #endif /* PPS */ 221 222 peer = (struct peer *)rbufp->recv_srcclock; 223 pp = peer->procptr; 224 up = (struct zyferunit *)pp->unitptr; 225 p = (u_char *) &rbufp->recv_space; 226 /* 227 * If lencode is 0: 228 * - if *rbufp->recv_space is ! 229 * - - call refclock_gtlin to get things going 230 * - else flush 231 * else stuff it on the end of lastcode 232 * If we don't have LENZYFER bytes 233 * - wait for more data 234 * Crack the beast, and if it's OK, process it. 235 * 236 * We use refclock_gtlin() because we might use LDISC_CLK. 237 * 238 * Under FreeBSD, we get the ! followed by two 14-byte packets. 239 */ 240 241 if (pp->lencode >= LENZYFER) 242 pp->lencode = 0; 243 244 if (!pp->lencode) { 245 if (*p == '!') 246 pp->lencode = refclock_gtlin(rbufp, pp->a_lastcode, 247 BMAX, &pp->lastrec); 248 else 249 return; 250 } else { 251 memcpy(pp->a_lastcode + pp->lencode, p, rbufp->recv_length); 252 pp->lencode += rbufp->recv_length; 253 pp->a_lastcode[pp->lencode] = '\0'; 254 } 255 256 if (pp->lencode < LENZYFER) 257 return; 258 259 record_clock_stats(&peer->srcadr, pp->a_lastcode); 260 261 /* 262 * We get down to business, check the timecode format and decode 263 * its contents. If the timecode has invalid length or is not in 264 * proper format, we declare bad format and exit. 265 */ 266 267 if (pp->lencode != LENZYFER) { 268 refclock_report(peer, CEVNT_BADTIME); 269 return; 270 } 271 272 /* 273 * Timecode sample: "!TIME,2002,017,07,59,32,2,4,1" 274 */ 275 if (sscanf(pp->a_lastcode, "!TIME,%4d,%3d,%2d,%2d,%2d,%d,%d,%d", 276 &pp->year, &pp->day, &pp->hour, &pp->minute, &pp->second, 277 &tmode, &tfom, &omode) != 8) { 278 refclock_report(peer, CEVNT_BADREPLY); 279 return; 280 } 281 282 if (tmode != 2) { 283 refclock_report(peer, CEVNT_BADTIME); 284 return; 285 } 286 287 /* Should we make sure tfom is 4? */ 288 289 if (omode != 1) { 290 pp->leap = LEAP_NOTINSYNC; 291 return; 292 } 293 #ifdef PPS 294 if(ioctl(fdpps,request,(caddr_t) &ppsev) >=0) { 295 ppsev.tv.tv_sec += (u_int32) JAN_1970; 296 TVTOTS(&ppsev.tv,&up->tstamp); 297 } 298 /* record the last ppsclock event time stamp */ 299 pp->lastrec = up->tstamp; 300 #endif /* PPS */ 301 if (!refclock_process(pp)) { 302 refclock_report(peer, CEVNT_BADTIME); 303 return; 304 } 305 306 /* 307 * Good place for record_clock_stats() 308 */ 309 up->pollcnt = 2; 310 311 if (up->polled) { 312 up->polled = 0; 313 refclock_receive(peer); 314 } 315 } 316 317 318 /* 319 * zyfer_poll - called by the transmit procedure 320 */ 321 static void 322 zyfer_poll( 323 int unit, 324 struct peer *peer 325 ) 326 { 327 register struct zyferunit *up; 328 struct refclockproc *pp; 329 330 /* 331 * We don't really do anything here, except arm the receiving 332 * side to capture a sample and check for timeouts. 333 */ 334 pp = peer->procptr; 335 up = (struct zyferunit *)pp->unitptr; 336 if (!up->pollcnt) 337 refclock_report(peer, CEVNT_TIMEOUT); 338 else 339 up->pollcnt--; 340 pp->polls++; 341 up->polled = 1; 342 } 343 344 #else 345 int refclock_zyfer_bs; 346 #endif /* REFCLOCK */ 347