1 /* 2 * refclock_chronolog - clock driver for Chronolog K-series WWVB receiver. 3 */ 4 5 /* 6 * Must interpolate back to local time. Very annoying. 7 */ 8 #define GET_LOCALTIME 9 10 #ifdef HAVE_CONFIG_H 11 #include <config.h> 12 #endif 13 14 #if defined(REFCLOCK) && defined(CLOCK_CHRONOLOG) 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 Chronolog K-series WWVB receiver. 29 * 30 * Input format: 31 * 32 * Y YY/MM/DD<cr><lf> 33 * Z hh:mm:ss<cr><lf> 34 * 35 * YY/MM/DD -- what you'd expect. This arrives a few seconds before the 36 * timestamp. 37 * hh:mm:ss -- what you'd expect. We take time on the <cr>. 38 * 39 * Our Chronolog writes time out at 2400 bps 8/N/1, but it can be configured 40 * otherwise. The clock seems to appear every 60 seconds, which doesn't make 41 * for good statistics collection. 42 * 43 * The original source of this module was the WWVB module. 44 */ 45 46 /* 47 * Interface definitions 48 */ 49 #define DEVICE "/dev/chronolog%d" /* device name and unit */ 50 #define SPEED232 B2400 /* uart speed (2400 baud) */ 51 #define PRECISION (-13) /* precision assumed (about 100 us) */ 52 #define REFID "chronolog" /* reference ID */ 53 #define DESCRIPTION "Chrono-log K" /* WRU */ 54 55 #define MONLIN 15 /* number of monitoring lines */ 56 57 /* 58 * Chrono-log unit control structure 59 */ 60 struct chronolog_unit { 61 u_char tcswitch; /* timecode switch */ 62 l_fp laststamp; /* last receive timestamp */ 63 u_char lasthour; /* last hour (for monitor) */ 64 int year; /* Y2K-adjusted year */ 65 int day; /* day-of-month */ 66 int month; /* month-of-year */ 67 }; 68 69 /* 70 * Function prototypes 71 */ 72 static int chronolog_start P((int, struct peer *)); 73 static void chronolog_shutdown P((int, struct peer *)); 74 static void chronolog_receive P((struct recvbuf *)); 75 static void chronolog_poll P((int, struct peer *)); 76 77 /* 78 * Transfer vector 79 */ 80 struct refclock refclock_chronolog = { 81 chronolog_start, /* start up driver */ 82 chronolog_shutdown, /* shut down driver */ 83 chronolog_poll, /* poll the driver -- a nice fabrication */ 84 noentry, /* not used */ 85 noentry, /* not used */ 86 noentry, /* not used */ 87 NOFLAGS /* not used */ 88 }; 89 90 91 /* 92 * chronolog_start - open the devices and initialize data for processing 93 */ 94 static int 95 chronolog_start( 96 int unit, 97 struct peer *peer 98 ) 99 { 100 register struct chronolog_unit *up; 101 struct refclockproc *pp; 102 int fd; 103 char device[20]; 104 105 /* 106 * Open serial port. Don't bother with CLK line discipline, since 107 * it's not available. 108 */ 109 (void)sprintf(device, DEVICE, unit); 110 #ifdef DEBUG 111 if (debug) 112 printf ("starting Chronolog with device %s\n",device); 113 #endif 114 if (!(fd = refclock_open(device, SPEED232, 0))) 115 return (0); 116 117 /* 118 * Allocate and initialize unit structure 119 */ 120 if (!(up = (struct chronolog_unit *) 121 emalloc(sizeof(struct chronolog_unit)))) { 122 (void) close(fd); 123 return (0); 124 } 125 memset((char *)up, 0, sizeof(struct chronolog_unit)); 126 pp = peer->procptr; 127 pp->unitptr = (caddr_t)up; 128 pp->io.clock_recv = chronolog_receive; 129 pp->io.srcclock = (caddr_t)peer; 130 pp->io.datalen = 0; 131 pp->io.fd = fd; 132 if (!io_addclock(&pp->io)) { 133 (void) close(fd); 134 free(up); 135 return (0); 136 } 137 138 /* 139 * Initialize miscellaneous variables 140 */ 141 peer->precision = PRECISION; 142 pp->clockdesc = DESCRIPTION; 143 memcpy((char *)&pp->refid, REFID, 4); 144 return (1); 145 } 146 147 148 /* 149 * chronolog_shutdown - shut down the clock 150 */ 151 static void 152 chronolog_shutdown( 153 int unit, 154 struct peer *peer 155 ) 156 { 157 register struct chronolog_unit *up; 158 struct refclockproc *pp; 159 160 pp = peer->procptr; 161 up = (struct chronolog_unit *)pp->unitptr; 162 io_closeclock(&pp->io); 163 free(up); 164 } 165 166 167 /* 168 * chronolog_receive - receive data from the serial interface 169 */ 170 static void 171 chronolog_receive( 172 struct recvbuf *rbufp 173 ) 174 { 175 struct chronolog_unit *up; 176 struct refclockproc *pp; 177 struct peer *peer; 178 179 l_fp trtmp; /* arrival timestamp */ 180 int hours; /* hour-of-day */ 181 int minutes; /* minutes-past-the-hour */ 182 int seconds; /* seconds */ 183 int temp; /* int temp */ 184 int got_good; /* got a good time flag */ 185 186 /* 187 * Initialize pointers and read the timecode and timestamp 188 */ 189 peer = (struct peer *)rbufp->recv_srcclock; 190 pp = peer->procptr; 191 up = (struct chronolog_unit *)pp->unitptr; 192 temp = refclock_gtlin(rbufp, pp->a_lastcode, BMAX, &trtmp); 193 194 if (temp == 0) { 195 if (up->tcswitch == 0) { 196 up->tcswitch = 1; 197 up->laststamp = trtmp; 198 } else 199 up->tcswitch = 0; 200 return; 201 } 202 pp->lencode = temp; 203 pp->lastrec = up->laststamp; 204 up->laststamp = trtmp; 205 up->tcswitch = 1; 206 207 #ifdef DEBUG 208 if (debug) 209 printf("chronolog: timecode %d %s\n", pp->lencode, 210 pp->a_lastcode); 211 #endif 212 213 /* 214 * We get down to business. Check the timecode format and decode 215 * its contents. This code uses the first character to see whether 216 * we're looking at a date or a time. We store data data across 217 * calls since it is transmitted a few seconds ahead of the 218 * timestamp. 219 */ 220 pp->msec = 0; 221 got_good=0; 222 if (sscanf(pp->a_lastcode, "Y %d/%d/%d", &up->year,&up->month,&up->day)) 223 { 224 /* 225 * Y2K convert the 2-digit year 226 */ 227 up->year = up->year >= 69 ? up->year : up->year + 100; 228 return; 229 } 230 if (sscanf(pp->a_lastcode,"Z %02d:%02d:%02d", 231 &hours,&minutes,&seconds) == 3) 232 { 233 #ifdef GET_LOCALTIME 234 struct tm local; 235 struct tm *gmtp; 236 time_t unixtime; 237 int adjyear; 238 int adjmon; 239 240 /* 241 * Convert to GMT for sites that distribute localtime. This 242 * means we have to do Y2K conversion on the 2-digit year; 243 * otherwise, we get the time wrong. 244 */ 245 246 local.tm_year = up->year; 247 local.tm_mon = up->month-1; 248 local.tm_mday = up->day; 249 local.tm_hour = hours; 250 local.tm_min = minutes; 251 local.tm_sec = seconds; 252 local.tm_isdst = -1; 253 254 unixtime = mktime (&local); 255 if ((gmtp = gmtime (&unixtime)) == NULL) 256 { 257 refclock_report (peer, CEVNT_FAULT); 258 return; 259 } 260 adjyear = gmtp->tm_year+1900; 261 adjmon = gmtp->tm_mon+1; 262 pp->day = ymd2yd (adjyear, adjmon, gmtp->tm_mday); 263 pp->hour = gmtp->tm_hour; 264 pp->minute = gmtp->tm_min; 265 pp->second = gmtp->tm_sec; 266 #ifdef DEBUG 267 if (debug) 268 printf ("time is %04d/%02d/%02d %02d:%02d:%02d UTC\n", 269 adjyear,adjmon,gmtp->tm_mday,pp->hour,pp->minute, 270 pp->second); 271 #endif 272 273 #else 274 /* 275 * For more rational sites distributing UTC 276 */ 277 pp->day = ymd2yd(year+1900,month,day); 278 pp->hour = hours; 279 pp->minute = minutes; 280 pp->second = seconds; 281 282 #endif 283 got_good=1; 284 } 285 286 if (!got_good) 287 return; 288 289 290 /* 291 * Process the new sample in the median filter and determine the 292 * timecode timestamp. 293 */ 294 if (!refclock_process(pp)) { 295 refclock_report(peer, CEVNT_BADTIME); 296 return; 297 } 298 record_clock_stats(&peer->srcadr, pp->a_lastcode); 299 refclock_receive(peer); 300 up->lasthour = pp->hour; 301 } 302 303 304 /* 305 * chronolog_poll - called by the transmit procedure 306 */ 307 static void 308 chronolog_poll( 309 int unit, 310 struct peer *peer 311 ) 312 { 313 /* 314 * Time to poll the clock. The Chrono-log clock is supposed to 315 * respond to a 'T' by returning a timecode in the format(s) 316 * specified above. Ours does (can?) not, but this seems to be 317 * an installation-specific problem. This code is dyked out, 318 * but may be re-enabled if anyone ever finds a Chrono-log that 319 * actually listens to this command. 320 */ 321 #if 0 322 register struct chronolog_unit *up; 323 struct refclockproc *pp; 324 char pollchar; 325 326 pp = peer->procptr; 327 up = (struct chronolog_unit *)pp->unitptr; 328 if (peer->burst == 0 && peer->reach == 0) 329 refclock_report(peer, CEVNT_TIMEOUT); 330 if (up->linect > 0) 331 pollchar = 'R'; 332 else 333 pollchar = 'T'; 334 if (write(pp->io.fd, &pollchar, 1) != 1) 335 refclock_report(peer, CEVNT_FAULT); 336 else 337 pp->polls++; 338 #endif 339 } 340 341 #else 342 int refclock_chronolog_bs; 343 #endif /* REFCLOCK */ 344