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