refclock_zyfer.c revision 106163
1229997Sken/* 2229997Sken * refclock_zyfer - clock driver for the Zyfer GPSTarplus Clock 3288348Smav * 4229997Sken * Harlan Stenn, Jan 2002 5229997Sken */ 6229997Sken 7229997Sken#ifdef HAVE_CONFIG_H 8229997Sken#include <config.h> 9229997Sken#endif 10229997Sken 11229997Sken#if defined(REFCLOCK) && defined(CLOCK_ZYFER) 12229997Sken 13229997Sken#include "ntpd.h" 14229997Sken#include "ntp_io.h" 15229997Sken#include "ntp_refclock.h" 16229997Sken#include "ntp_stdlib.h" 17229997Sken#include "ntp_unixtime.h" 18229997Sken 19229997Sken#include <stdio.h> 20229997Sken#include <ctype.h> 21229997Sken 22229997Sken#ifdef HAVE_SYS_TERMIOS_H 23229997Sken# include <sys/termios.h> 24229997Sken#endif 25229997Sken#ifdef HAVE_SYS_PPSCLOCK_H 26229997Sken# include <sys/ppsclock.h> 27229997Sken#endif 28229997Sken 29229997Sken/* 30229997Sken * This driver provides support for the TOD serial port of a Zyfer GPStarplus. 31229997Sken * This clock also provides PPS as well as IRIG outputs. 32229997Sken * Precision is limited by the serial driver, etc. 33229997Sken * 34229997Sken * If I was really brave I'd hack/generalize the serial driver to deal 35229997Sken * with arbitrary on-time characters. This clock *begins* the stream with 36229997Sken * `!`, the on-time character, and the string is *not* EOL-terminated. 37229997Sken * 38229997Sken * Configure the beast for 9600, 8N1. While I see leap-second stuff 39229997Sken * in the documentation, the published specs on the TOD format only show 40229997Sken * the seconds going to '59'. I see no leap warning in the TOD format. 41229997Sken * 42229997Sken * The clock sends the following message once per second: 43229997Sken * 44229997Sken * !TIME,2002,017,07,59,32,2,4,1 45229997Sken * YYYY DDD HH MM SS m T O 46229997Sken * 47229997Sken * ! On-time character 48233963Sken * YYYY Year 49229997Sken * DDD 001-366 Day of Year 50229997Sken * HH 00-23 Hour 51229997Sken * MM 00-59 Minute 52229997Sken * SS 00-59 Second (probably 00-60) 53229997Sken * m 1-5 Time Mode: 54229997Sken * 1 = GPS time 55229997Sken * 2 = UTC time 56229997Sken * 3 = LGPS time (Local GPS) 57229997Sken * 4 = LUTC time (Local UTC) 58229997Sken * 5 = Manual time 59229997Sken * T 4-9 Time Figure Of Merit: 60229997Sken * 4 x <= 1us 61268096Smav * 5 1us < x <= 10 us 62268096Smav * 6 10us < x <= 100us 63229997Sken * 7 100us < x <= 1ms 64268096Smav * 8 1ms < x <= 10ms 65268096Smav * 9 10ms < x 66268096Smav * O 0-4 Operation Mode: 67229997Sken * 0 Warm-up 68268096Smav * 1 Time Locked 69268581Smav * 2 Coasting 70268096Smav * 3 Recovering 71288348Smav * 4 Manual 72287621Smav * 73268096Smav */ 74268096Smav 75268096Smav/* 76268096Smav * Interface definitions 77268096Smav */ 78268096Smav#define DEVICE "/dev/zyfer%d" /* device name and unit */ 79268581Smav#define SPEED232 B9600 /* uart speed (9600 baud) */ 80268096Smav#define PRECISION (-20) /* precision assumed (about 1 us) */ 81288348Smav#define REFID "GPS\0" /* reference ID */ 82287621Smav#define DESCRIPTION "Zyfer GPStarplus" /* WRU */ 83268096Smav 84268096Smav#define LENZYFER 29 /* timecode length */ 85268096Smav 86268096Smav/* 87268096Smav * Unit control structure 88268096Smav */ 89268096Smavstruct zyferunit { 90268096Smav u_char Rcvbuf[LENZYFER + 1]; 91288348Smav u_char polled; /* poll message flag */ 92287621Smav int pollcnt; 93268096Smav l_fp tstamp; /* timestamp of last poll */ 94268096Smav int Rcvptr; 95268096Smav}; 96268096Smav 97268096Smav/* 98268096Smav * Function prototypes 99268363Smav */ 100268363Smavstatic int zyfer_start P((int, struct peer *)); 101288348Smavstatic void zyfer_shutdown P((int, struct peer *)); 102287621Smavstatic void zyfer_receive P((struct recvbuf *)); 103268363Smavstatic void zyfer_poll P((int, struct peer *)); 104268363Smav 105268363Smav/* 106268363Smav * Transfer vector 107268096Smav */ 108268096Smavstruct refclock refclock_zyfer = { 109268096Smav zyfer_start, /* start up driver */ 110268096Smav zyfer_shutdown, /* shut down driver */ 111268096Smav zyfer_poll, /* transmit poll message */ 112268096Smav noentry, /* not used (old zyfer_control) */ 113268096Smav noentry, /* initialize driver (not used) */ 114268096Smav noentry, /* not used (old zyfer_buginfo) */ 115268581Smav NOFLAGS /* not used */ 116268581Smav}; 117288348Smav 118287621Smav 119268581Smav/* 120268581Smav * zyfer_start - open the devices and initialize data for processing 121268096Smav */ 122268096Smavstatic int 123268096Smavzyfer_start( 124268096Smav int unit, 125268581Smav struct peer *peer 126268581Smav ) 127288348Smav{ 128287621Smav register struct zyferunit *up; 129268581Smav struct refclockproc *pp; 130268581Smav int fd; 131268096Smav char device[20]; 132288229Smav 133268096Smav /* 134268096Smav * Open serial port. 135268581Smav * Something like LDISC_ACTS that looked for ! would be nice... 136268581Smav */ 137288348Smav (void)sprintf(device, DEVICE, unit); 138287621Smav if ( !(fd = refclock_open(device, SPEED232, LDISC_RAW)) ) 139268581Smav return (0); 140268581Smav 141268096Smav msyslog(LOG_NOTICE, "zyfer(%d) fd: %d dev <%s>", unit, fd, device); 142288229Smav 143268096Smav /* 144268096Smav * Allocate and initialize unit structure 145268581Smav */ 146268581Smav if (!(up = (struct zyferunit *) 147288348Smav emalloc(sizeof(struct zyferunit)))) { 148287621Smav (void) close(fd); 149268581Smav return (0); 150268581Smav } 151268096Smav memset((char *)up, 0, sizeof(struct zyferunit)); 152268361Smav pp = peer->procptr; 153268096Smav pp->io.clock_recv = zyfer_receive; 154268096Smav pp->io.srcclock = (caddr_t)peer; 155268581Smav pp->io.datalen = 0; 156268581Smav pp->io.fd = fd; 157288348Smav if (!io_addclock(&pp->io)) { 158287621Smav (void) close(fd); 159268581Smav free(up); 160268581Smav return (0); 161268096Smav } 162268361Smav pp->unitptr = (caddr_t)up; 163268096Smav 164268096Smav /* 165274333Smav * Initialize miscellaneous variables 166274333Smav */ 167288348Smav peer->precision = PRECISION; 168287621Smav pp->clockdesc = DESCRIPTION; 169274333Smav memcpy((char *)&pp->refid, REFID, 4); 170274333Smav up->pollcnt = 2; 171274333Smav up->polled = 0; /* May not be needed... */ 172274333Smav 173268096Smav return (1); 174268096Smav} 175268581Smav 176268581Smav 177288348Smav/* 178287621Smav * zyfer_shutdown - shut down the clock 179268581Smav */ 180268581Smavstatic void 181268096Smavzyfer_shutdown( 182268361Smav int unit, 183268096Smav struct peer *peer 184268096Smav ) 185268096Smav{ 186268096Smav register struct zyferunit *up; 187268096Smav struct refclockproc *pp; 188268096Smav 189268096Smav pp = peer->procptr; 190268767Smav up = (struct zyferunit *)pp->unitptr; 191268767Smav io_closeclock(&pp->io); 192268767Smav free(up); 193268767Smav} 194268767Smav 195288348Smav 196268767Smav/* 197268767Smav * zyfer_receive - receive data from the serial interface 198268767Smav */ 199268767Smavstatic void 200268767Smavzyfer_receive( 201268767Smav struct recvbuf *rbufp 202288348Smav ) 203268767Smav{ 204268767Smav register struct zyferunit *up; 205268767Smav struct refclockproc *pp; 206268767Smav struct peer *peer; 207268767Smav int tmode; /* Time mode */ 208268767Smav int tfom; /* Time Figure Of Merit */ 209268767Smav int omode; /* Operation mode */ 210268767Smav u_char *p; 211268767Smav#ifdef PPS 212268767Smav struct ppsclockev ppsev; 213268767Smav int request; 214268767Smav#ifdef HAVE_CIOGETEV 215268767Smav request = CIOGETEV; 216268767Smav#endif 217268767Smav#ifdef HAVE_TIOCGPPSEV 218268767Smav request = TIOCGPPSEV; 219268767Smav#endif 220268767Smav#endif /* PPS */ 221268767Smav 222268767Smav peer = (struct peer *)rbufp->recv_srcclock; 223268767Smav pp = peer->procptr; 224268767Smav up = (struct zyferunit *)pp->unitptr; 225268767Smav p = (u_char *) &rbufp->recv_space; 226268767Smav /* 227268767Smav * If lencode is 0: 228268767Smav * - if *rbufp->recv_space is ! 229268767Smav * - - call refclock_gtlin to get things going 230268767Smav * - else flush 231268767Smav * else stuff it on the end of lastcode 232268767Smav * If we don't have LENZYFER bytes 233268767Smav * - wait for more data 234268767Smav * Crack the beast, and if it's OK, process it. 235268767Smav * 236268767Smav * We use refclock_getlin() because we might use LDISC_CLK. 237268767Smav * 238268767Smav * Under FreeBSD, we get the ! followed by two 14-byte packets. 239268767Smav */ 240268767Smav 241268767Smav if (pp->lencode >= LENZYFER) 242268767Smav pp->lencode = 0; 243268767Smav 244268767Smav if (!pp->lencode) { 245268767Smav if (*p == '!') 246268767Smav pp->lencode = refclock_gtlin(rbufp, pp->a_lastcode, 247268767Smav BMAX, &pp->lastrec); 248268767Smav else 249268767Smav return; 250288310Smav } else { 251273730Smav memcpy(pp->a_lastcode + pp->lencode, p, rbufp->recv_length); 252273730Smav pp->lencode += rbufp->recv_length; 253269497Smav pp->a_lastcode[pp->lencode] = '\0'; 254269497Smav } 255269497Smav 256268767Smav if (pp->lencode < LENZYFER) 257268767Smav return; 258288310Smav 259269497Smav record_clock_stats(&peer->srcadr, pp->a_lastcode); 260269497Smav 261269497Smav /* 262269497Smav * We get down to business, check the timecode format and decode 263268767Smav * its contents. If the timecode has invalid length or is not in 264268767Smav * proper format, we declare bad format and exit. 265268767Smav */ 266268767Smav 267268767Smav if (pp->lencode != LENZYFER) { 268268767Smav refclock_report(peer, CEVNT_BADTIME); 269268767Smav return; 270268767Smav } 271268767Smav 272268767Smav /* 273268767Smav * Timecode sample: "!TIME,2002,017,07,59,32,2,4,1" 274268767Smav */ 275268767Smav if (sscanf(pp->a_lastcode, "!TIME,%4d,%3d,%2d,%2d,%2d,%d,%d,%d", 276268767Smav &pp->year, &pp->day, &pp->hour, &pp->minute, &pp->second, 277268767Smav &tmode, &tfom, &omode) != 8) { 278268767Smav refclock_report(peer, CEVNT_BADREPLY); 279268767Smav return; 280268767Smav } 281268767Smav 282268767Smav if (tmode != 2) { 283268767Smav refclock_report(peer, CEVNT_BADTIME); 284268767Smav return; 285268767Smav } 286268767Smav 287268767Smav /* Should we make sure tfom is 4? */ 288268767Smav 289268767Smav if (omode != 1) { 290268767Smav pp->leap = LEAP_NOTINSYNC; 291268767Smav return; 292268767Smav } 293268767Smav#ifdef PPS 294268767Smav if(ioctl(fdpps,request,(caddr_t) &ppsev) >=0) { 295268767Smav ppsev.tv.tv_sec += (u_int32) JAN_1970; 296288348Smav TVTOTS(&ppsev.tv,&up->tstamp); 297268767Smav } 298268767Smav /* record the last ppsclock event time stamp */ 299268767Smav pp->lastrec = up->tstamp; 300268767Smav#endif /* PPS */ 301268767Smav if (!refclock_process(pp)) { 302268767Smav refclock_report(peer, CEVNT_BADTIME); 303268767Smav return; 304268767Smav } 305268767Smav 306268767Smav /* 307268767Smav * Good place for record_clock_stats() 308288348Smav */ 309271845Smav up->pollcnt = 2; 310271845Smav 311268767Smav if (up->polled) { 312268767Smav up->polled = 0; 313268767Smav refclock_receive(peer); 314268767Smav } 315268767Smav} 316268767Smav 317268767Smav 318268767Smav/* 319268767Smav * zyfer_poll - called by the transmit procedure 320268767Smav */ 321268767Smavstatic void 322268767Smavzyfer_poll( 323288348Smav int unit, 324287621Smav struct peer *peer 325271845Smav ) 326271845Smav{ 327268767Smav register struct zyferunit *up; 328268767Smav struct refclockproc *pp; 329268767Smav 330268767Smav /* 331268767Smav * We don't really do anything here, except arm the receiving 332268767Smav * side to capture a sample and check for timeouts. 333288348Smav */ 334271845Smav pp = peer->procptr; 335271845Smav up = (struct zyferunit *)pp->unitptr; 336268767Smav if (!up->pollcnt) 337268767Smav refclock_report(peer, CEVNT_TIMEOUT); 338268767Smav else 339268767Smav up->pollcnt--; 340268767Smav pp->polls++; 341268767Smav up->polled = 1; 342288348Smav} 343273730Smav 344273730Smav#else 345268767Smavint refclock_zyfer_bs; 346268767Smav#endif /* REFCLOCK */ 347268767Smav