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 (int, struct peer *); 71 static void chronolog_shutdown (int, struct peer *); 72 static void chronolog_receive (struct recvbuf *); 73 static void chronolog_poll (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 snprintf(device, sizeof(device), DEVICE, unit); 108 #ifdef DEBUG 109 if (debug) 110 printf ("starting Chronolog with device %s\n",device); 111 #endif 112 fd = refclock_open(&peer->srcadr, device, SPEED232, 0); 113 if (fd <= 0) 114 return (0); 115 116 /* 117 * Allocate and initialize unit structure 118 */ 119 up = emalloc_zero(sizeof(*up)); 120 pp = peer->procptr; 121 pp->unitptr = up; 122 pp->io.clock_recv = chronolog_receive; 123 pp->io.srcclock = peer; 124 pp->io.datalen = 0; 125 pp->io.fd = fd; 126 if (!io_addclock(&pp->io)) { 127 close(fd); 128 pp->io.fd = -1; 129 free(up); 130 pp->unitptr = NULL; 131 return (0); 132 } 133 134 /* 135 * Initialize miscellaneous variables 136 */ 137 peer->precision = PRECISION; 138 pp->clockdesc = DESCRIPTION; 139 memcpy((char *)&pp->refid, REFID, 4); 140 return (1); 141 } 142 143 144 /* 145 * chronolog_shutdown - shut down the clock 146 */ 147 static void 148 chronolog_shutdown( 149 int unit, 150 struct peer *peer 151 ) 152 { 153 register struct chronolog_unit *up; 154 struct refclockproc *pp; 155 156 pp = peer->procptr; 157 up = pp->unitptr; 158 if (-1 != pp->io.fd) 159 io_closeclock(&pp->io); 160 if (NULL != up) 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 = rbufp->recv_peer; 188 pp = peer->procptr; 189 up = 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 memset(&local, 0, sizeof(local)); 244 245 local.tm_year = up->year; 246 local.tm_mon = up->month-1; 247 local.tm_mday = up->day; 248 local.tm_hour = hours; 249 local.tm_min = minutes; 250 local.tm_sec = seconds; 251 local.tm_isdst = -1; 252 253 unixtime = mktime (&local); 254 if ((gmtp = gmtime (&unixtime)) == NULL) 255 { 256 refclock_report (peer, CEVNT_FAULT); 257 return; 258 } 259 adjyear = gmtp->tm_year+1900; 260 adjmon = gmtp->tm_mon+1; 261 pp->day = ymd2yd (adjyear, adjmon, gmtp->tm_mday); 262 pp->hour = gmtp->tm_hour; 263 pp->minute = gmtp->tm_min; 264 pp->second = gmtp->tm_sec; 265 #ifdef DEBUG 266 if (debug) 267 printf ("time is %04d/%02d/%02d %02d:%02d:%02d UTC\n", 268 adjyear,adjmon,gmtp->tm_mday,pp->hour,pp->minute, 269 pp->second); 270 #endif 271 272 #else 273 /* 274 * For more rational sites distributing UTC 275 */ 276 pp->day = ymd2yd(year+1900,month,day); 277 pp->hour = hours; 278 pp->minute = minutes; 279 pp->second = seconds; 280 281 #endif 282 got_good=1; 283 } 284 285 if (!got_good) 286 return; 287 288 289 /* 290 * Process the new sample in the median filter and determine the 291 * timecode timestamp. 292 */ 293 if (!refclock_process(pp)) { 294 refclock_report(peer, CEVNT_BADTIME); 295 return; 296 } 297 pp->lastref = pp->lastrec; 298 refclock_receive(peer); 299 record_clock_stats(&peer->srcadr, pp->a_lastcode); 300 up->lasthour = (u_char)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 = 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 NONEMPTY_TRANSLATION_UNIT 343 #endif /* REFCLOCK */ 344