1 /* $NetBSD: refclock_chronolog.c,v 1.1.1.1 2009/12/13 16:55:46 kardel Exp $ */ 2 3 /* 4 * refclock_chronolog - clock driver for Chronolog K-series WWVB receiver. 5 */ 6 7 /* 8 * Must interpolate back to local time. Very annoying. 9 */ 10 #define GET_LOCALTIME 11 12 #ifdef HAVE_CONFIG_H 13 #include <config.h> 14 #endif 15 16 #if defined(REFCLOCK) && defined(CLOCK_CHRONOLOG) 17 18 #include "ntpd.h" 19 #include "ntp_io.h" 20 #include "ntp_refclock.h" 21 #include "ntp_calendar.h" 22 #include "ntp_stdlib.h" 23 24 #include <stdio.h> 25 #include <ctype.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 (int, struct peer *); 73 static void chronolog_shutdown (int, struct peer *); 74 static void chronolog_receive (struct recvbuf *); 75 static void chronolog_poll (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 got_good=0; 221 if (sscanf(pp->a_lastcode, "Y %d/%d/%d", &up->year,&up->month,&up->day)) 222 { 223 /* 224 * Y2K convert the 2-digit year 225 */ 226 up->year = up->year >= 69 ? up->year : up->year + 100; 227 return; 228 } 229 if (sscanf(pp->a_lastcode,"Z %02d:%02d:%02d", 230 &hours,&minutes,&seconds) == 3) 231 { 232 #ifdef GET_LOCALTIME 233 struct tm local; 234 struct tm *gmtp; 235 time_t unixtime; 236 int adjyear; 237 int adjmon; 238 239 /* 240 * Convert to GMT for sites that distribute localtime. This 241 * means we have to do Y2K conversion on the 2-digit year; 242 * otherwise, we get the time wrong. 243 */ 244 245 memset(&local, 0, sizeof(local)); 246 247 local.tm_year = up->year; 248 local.tm_mon = up->month-1; 249 local.tm_mday = up->day; 250 local.tm_hour = hours; 251 local.tm_min = minutes; 252 local.tm_sec = seconds; 253 local.tm_isdst = -1; 254 255 unixtime = mktime (&local); 256 if ((gmtp = gmtime (&unixtime)) == NULL) 257 { 258 refclock_report (peer, CEVNT_FAULT); 259 return; 260 } 261 adjyear = gmtp->tm_year+1900; 262 adjmon = gmtp->tm_mon+1; 263 pp->day = ymd2yd (adjyear, adjmon, gmtp->tm_mday); 264 pp->hour = gmtp->tm_hour; 265 pp->minute = gmtp->tm_min; 266 pp->second = gmtp->tm_sec; 267 #ifdef DEBUG 268 if (debug) 269 printf ("time is %04d/%02d/%02d %02d:%02d:%02d UTC\n", 270 adjyear,adjmon,gmtp->tm_mday,pp->hour,pp->minute, 271 pp->second); 272 #endif 273 274 #else 275 /* 276 * For more rational sites distributing UTC 277 */ 278 pp->day = ymd2yd(year+1900,month,day); 279 pp->hour = hours; 280 pp->minute = minutes; 281 pp->second = seconds; 282 283 #endif 284 got_good=1; 285 } 286 287 if (!got_good) 288 return; 289 290 291 /* 292 * Process the new sample in the median filter and determine the 293 * timecode timestamp. 294 */ 295 if (!refclock_process(pp)) { 296 refclock_report(peer, CEVNT_BADTIME); 297 return; 298 } 299 pp->lastref = pp->lastrec; 300 refclock_receive(peer); 301 record_clock_stats(&peer->srcadr, pp->a_lastcode); 302 up->lasthour = pp->hour; 303 } 304 305 306 /* 307 * chronolog_poll - called by the transmit procedure 308 */ 309 static void 310 chronolog_poll( 311 int unit, 312 struct peer *peer 313 ) 314 { 315 /* 316 * Time to poll the clock. The Chrono-log clock is supposed to 317 * respond to a 'T' by returning a timecode in the format(s) 318 * specified above. Ours does (can?) not, but this seems to be 319 * an installation-specific problem. This code is dyked out, 320 * but may be re-enabled if anyone ever finds a Chrono-log that 321 * actually listens to this command. 322 */ 323 #if 0 324 register struct chronolog_unit *up; 325 struct refclockproc *pp; 326 char pollchar; 327 328 pp = peer->procptr; 329 up = (struct chronolog_unit *)pp->unitptr; 330 if (peer->burst == 0 && peer->reach == 0) 331 refclock_report(peer, CEVNT_TIMEOUT); 332 if (up->linect > 0) 333 pollchar = 'R'; 334 else 335 pollchar = 'T'; 336 if (write(pp->io.fd, &pollchar, 1) != 1) 337 refclock_report(peer, CEVNT_FAULT); 338 else 339 pp->polls++; 340 #endif 341 } 342 343 #else 344 int refclock_chronolog_bs; 345 #endif /* REFCLOCK */ 346