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