1 /* 2 * refclock_ulink - clock driver for Ultralink Model 320 WWVB receivers 3 * By Dave Strout <dstrout@linuxfoundary.com> 4 * 5 * Latest version is always on www.linuxfoundary.com 6 * 7 * Based on the Spectracom driver 8 */ 9 10 #ifdef HAVE_CONFIG_H 11 #include <config.h> 12 #endif 13 14 #if defined(REFCLOCK) && defined(CLOCK_ULINK) 15 16 #include <stdio.h> 17 #include <ctype.h> 18 #include <sys/time.h> 19 #include <time.h> 20 21 #include "ntpd.h" 22 #include "ntp_io.h" 23 #include "ntp_refclock.h" 24 #include "ntp_calendar.h" 25 #include "ntp_stdlib.h" 26 27 /* 28 * This driver supports the Ultralink Model 320 WWVB receiver. The Model 320 is 29 * an RS-232 powered unit which consists of two parts: a DB-25 shell that contains 30 * a microprocessor, and an approx 2"x4" plastic box that contains the antenna. 31 * The two are connected by a 6-wire RJ-25 cable of length up to 1000'. The 32 * microprocessor steals power from the RS-232 port, which means that the port must 33 * be kept open all of the time. The unit also has an internal clock for loss of signal 34 * periods. Claimed accuracy is 0.1 sec. 35 * 36 * The timecode format is: 37 * 38 * <cr><lf>SQRYYYYDDD+HH:MM:SS.mmLT<cr> 39 * 40 * where: 41 * 42 * S = 'S' -- sync'd in last hour, '0'-'9' - hours x 10 since last update, else '?' 43 * Q = Number of correlating time-frames, from 0 to 5 44 * R = 'R' -- reception in progress, 'N' -- Noisy reception, ' ' -- standby mode 45 * YYYY = year from 1990 to 2089 46 * DDD = current day from 1 to 366 47 * + = '+' if current year is a leap year, else ' ' 48 * HH = UTC hour 0 to 23 49 * MM = Minutes of current hour from 0 to 59 50 * SS = Seconds of current minute from 0 to 59 51 * mm = 10's milliseconds of the current second from 00 to 99 52 * L = Leap second pending at end of month -- 'I' = inset, 'D'=delete 53 * T = DST <-> STD transition indicators 54 * 55 * Note that this driver does not do anything with the L or T flags. 56 * 57 * The M320 also has a 'U' command which returns UT1 correction information. It 58 * is not used in this driver. 59 * 60 */ 61 62 /* 63 * Interface definitions 64 */ 65 #define DEVICE "/dev/ulink%d" /* device name and unit */ 66 #define SPEED232 B9600 /* uart speed (9600 baud) */ 67 #define PRECISION (-13) /* precision assumed (about 100 us) */ 68 #define REFID "M320" /* reference ID */ 69 #define DESCRIPTION "Ultralink WWVB Receiver" /* WRU */ 70 71 #define LENWWVB0 28 /* format 0 timecode length */ 72 #define LENWWVB2 24 /* format 2 timecode length */ 73 #define LENWWVB3 29 /* format 3 timecode length */ 74 75 #define MONLIN 15 /* number of monitoring lines */ 76 77 /* 78 * ULINK unit control structure 79 */ 80 struct ulinkunit { 81 u_char tcswitch; /* timecode switch */ 82 l_fp laststamp; /* last receive timestamp */ 83 u_char lasthour; /* last hour (for monitor) */ 84 u_char linect; /* count ignored lines (for monitor */ 85 }; 86 87 /* 88 * Function prototypes 89 */ 90 static int ulink_start P((int, struct peer *)); 91 static void ulink_shutdown P((int, struct peer *)); 92 static void ulink_receive P((struct recvbuf *)); 93 static void ulink_poll P((int, struct peer *)); 94 static int fd; /* We need to keep the serial port open to power the ULM320 */ 95 96 /* 97 * Transfer vector 98 */ 99 struct refclock refclock_ulink = { 100 ulink_start, /* start up driver */ 101 ulink_shutdown, /* shut down driver */ 102 ulink_poll, /* transmit poll message */ 103 noentry, /* not used (old wwvb_control) */ 104 noentry, /* initialize driver (not used) */ 105 noentry, /* not used (old wwvb_buginfo) */ 106 NOFLAGS /* not used */ 107 }; 108 109 110 /* 111 * ulink_start - open the devices and initialize data for processing 112 */ 113 static int 114 ulink_start( 115 int unit, 116 struct peer *peer 117 ) 118 { 119 register struct ulinkunit *up; 120 struct refclockproc *pp; 121 char device[20]; 122 fprintf(stderr, "Starting Ulink driver\n"); 123 /* 124 * Open serial port. Use CLK line discipline, if available. 125 */ 126 (void)sprintf(device, DEVICE, unit); 127 if (!(fd = refclock_open(device, SPEED232, LDISC_CLK))) 128 return (0); 129 130 /* 131 * Allocate and initialize unit structure 132 */ 133 if (!(up = (struct ulinkunit *) 134 emalloc(sizeof(struct ulinkunit)))) { 135 (void) close(fd); 136 return (0); 137 } 138 memset((char *)up, 0, sizeof(struct ulinkunit)); 139 pp = peer->procptr; 140 pp->unitptr = (caddr_t)up; 141 pp->io.clock_recv = ulink_receive; 142 pp->io.srcclock = (caddr_t)peer; 143 pp->io.datalen = 0; 144 pp->io.fd = fd; 145 if (!io_addclock(&pp->io)) { 146 (void) close(fd); 147 free(up); 148 return (0); 149 } 150 151 /* 152 * Initialize miscellaneous variables 153 */ 154 peer->precision = PRECISION; 155 peer->flags |= FLAG_BURST; 156 peer->burst = NSTAGE; 157 pp->clockdesc = DESCRIPTION; 158 memcpy((char *)&pp->refid, REFID, 4); 159 return (1); 160 } 161 162 163 /* 164 * ulink_shutdown - shut down the clock 165 */ 166 static void 167 ulink_shutdown( 168 int unit, 169 struct peer *peer 170 ) 171 { 172 register struct ulinkunit *up; 173 struct refclockproc *pp; 174 175 pp = peer->procptr; 176 up = (struct ulinkunit *)pp->unitptr; 177 io_closeclock(&pp->io); 178 free(up); 179 close(fd); 180 } 181 182 183 /* 184 * ulink_receive - receive data from the serial interface 185 */ 186 static void 187 ulink_receive( 188 struct recvbuf *rbufp 189 ) 190 { 191 struct ulinkunit *up; 192 struct refclockproc *pp; 193 struct peer *peer; 194 195 l_fp trtmp; /* arrival timestamp */ 196 char syncchar; /* synchronization indicator */ 197 char qualchar; /* quality indicator */ 198 char modechar; /* Modes: 'R'=rx, 'N'=noise, ' '=standby */ 199 char leapchar; /* leap indicator */ 200 int temp; /* int temp */ 201 202 /* 203 * Initialize pointers and read the timecode and timestamp 204 */ 205 peer = (struct peer *)rbufp->recv_srcclock; 206 pp = peer->procptr; 207 up = (struct ulinkunit *)pp->unitptr; 208 temp = refclock_gtlin(rbufp, pp->a_lastcode, BMAX, &trtmp); 209 210 /* 211 * Note we get a buffer and timestamp for both a <cr> and <lf>, 212 * but only the <cr> timestamp is retained. Note: in format 0 on 213 * a Netclock/2 or upgraded 8170 the start bit is delayed 100 214 * +-50 us relative to the pps; however, on an unmodified 8170 215 * the start bit can be delayed up to 10 ms. In format 2 the 216 * reading precision is only to the millisecond. Thus, unless 217 * you have a pps gadget and don't have to have the year, format 218 * 0 provides the lowest jitter. 219 */ 220 if (temp == 0) { 221 if (up->tcswitch == 0) { 222 up->tcswitch = 1; 223 up->laststamp = trtmp; 224 } else 225 up->tcswitch = 0; 226 return; 227 } 228 pp->lencode = temp; 229 pp->lastrec = up->laststamp; 230 up->laststamp = trtmp; 231 up->tcswitch = 1; 232 #ifdef DEBUG 233 if (debug) 234 printf("ulink: timecode %d %s\n", pp->lencode, 235 pp->a_lastcode); 236 #endif 237 238 /* 239 * We get down to business, check the timecode format and decode 240 * its contents. This code uses the timecode length to determine 241 * whether format 0 or format 2. If the timecode has invalid 242 * length or is not in proper format, we declare bad format and 243 * exit. 244 */ 245 syncchar = qualchar = leapchar = ' '; 246 pp->msec = 0; 247 248 /* 249 * Timecode format SQRYYYYDDD+HH:MM:SS.mmLT 250 */ 251 sscanf(pp->a_lastcode, "%c%c%c%4d%3d%c%2d:%2d:%2d.%2d", 252 &syncchar, &qualchar, &modechar, &pp->year, &pp->day, 253 &leapchar,&pp->hour, &pp->minute, &pp->second,&pp->msec); 254 255 pp->msec *= 10; /* M320 returns 10's of msecs */ 256 qualchar = ' '; 257 258 /* 259 * Decode synchronization, quality and leap characters. If 260 * unsynchronized, set the leap bits accordingly and exit. 261 * Otherwise, set the leap bits according to the leap character. 262 * Once synchronized, the dispersion depends only on the 263 * quality character. 264 */ 265 pp->disp = .001; 266 pp->leap = LEAP_NOWARNING; 267 268 /* 269 * Process the new sample in the median filter and determine the 270 * timecode timestamp. 271 */ 272 if (!refclock_process(pp)) 273 refclock_report(peer, CEVNT_BADTIME); 274 } 275 276 277 /* 278 * ulink_poll - called by the transmit procedure 279 */ 280 static void 281 ulink_poll( 282 int unit, 283 struct peer *peer 284 ) 285 { 286 register struct ulinkunit *up; 287 struct refclockproc *pp; 288 char pollchar; 289 290 pp = peer->procptr; 291 up = (struct ulinkunit *)pp->unitptr; 292 pollchar = 'T'; 293 if (write(pp->io.fd, &pollchar, 1) != 1) 294 refclock_report(peer, CEVNT_FAULT); 295 else 296 pp->polls++; 297 if (peer->burst > 0) 298 return; 299 if (pp->coderecv == pp->codeproc) { 300 refclock_report(peer, CEVNT_TIMEOUT); 301 return; 302 } 303 record_clock_stats(&peer->srcadr, pp->a_lastcode); 304 refclock_receive(peer); 305 peer->burst = NSTAGE; 306 307 /* 308 * If the monitor flag is set (flag4), we dump the internal 309 * quality table at the first timecode beginning the day. 310 */ 311 if (pp->sloppyclockflag & CLK_FLAG4 && pp->hour < 312 (int)up->lasthour) 313 up->linect = MONLIN; 314 up->lasthour = pp->hour; 315 } 316 317 #else 318 int refclock_ulink_bs; 319 #endif /* REFCLOCK */ 320