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