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 pp->msec = 0; 219 got_good=0; 220 if (sscanf(pp->a_lastcode, "Y %d/%d/%d", &up->year,&up->month,&up->day)) 221 { 222 /* 223 * Y2K convert the 2-digit year 224 */ 225 up->year = up->year >= 69 ? up->year : up->year + 100; 226 return; 227 } 228 if (sscanf(pp->a_lastcode,"Z %02d:%02d:%02d", 229 &hours,&minutes,&seconds) == 3) 230 { 231 #ifdef GET_LOCALTIME 232 struct tm local; 233 struct tm *gmtp; 234 time_t unixtime; 235 int adjyear; 236 int adjmon; 237 238 /* 239 * Convert to GMT for sites that distribute localtime. This 240 * means we have to do Y2K conversion on the 2-digit year; 241 * otherwise, we get the time wrong. 242 */ 243 244 local.tm_year = up->year; 245 local.tm_mon = up->month-1; 246 local.tm_mday = up->day; 247 local.tm_hour = hours; 248 local.tm_min = minutes; 249 local.tm_sec = seconds; 250 local.tm_isdst = -1; 251 252 unixtime = mktime (&local); 253 if ((gmtp = gmtime (&unixtime)) == NULL) 254 { 255 refclock_report (peer, CEVNT_FAULT); 256 return; 257 } 258 adjyear = gmtp->tm_year+1900; 259 adjmon = gmtp->tm_mon+1; 260 pp->day = ymd2yd (adjyear, adjmon, gmtp->tm_mday); 261 pp->hour = gmtp->tm_hour; 262 pp->minute = gmtp->tm_min; 263 pp->second = gmtp->tm_sec; 264 #ifdef DEBUG 265 if (debug) 266 printf ("time is %04d/%02d/%02d %02d:%02d:%02d UTC\n", 267 adjyear,adjmon,gmtp->tm_mday,pp->hour,pp->minute, 268 pp->second); 269 #endif 270 271 #else 272 /* 273 * For more rational sites distributing UTC 274 */ 275 pp->day = ymd2yd(year+1900,month,day); 276 pp->hour = hours; 277 pp->minute = minutes; 278 pp->second = seconds; 279 280 #endif 281 got_good=1; 282 } 283 284 if (!got_good) 285 return; 286 287 288 /* 289 * Process the new sample in the median filter and determine the 290 * timecode timestamp. 291 */ 292 if (!refclock_process(pp)) { 293 refclock_report(peer, CEVNT_BADTIME); 294 return; 295 } 296 record_clock_stats(&peer->srcadr, pp->a_lastcode); 297 refclock_receive(peer); 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