/* * chronolog_receive - receive data from the serial interface */ static void chronolog_receive( struct recvbuf *rbufp ) { struct chronolog_unit *up; struct refclockproc *pp; struct peer *peer; l_fp trtmp; /* arrival timestamp */ int hours; /* hour-of-day */ int minutes; /* minutes-past-the-hour */ int seconds; /* seconds */ int temp; /* int temp */ int got_good; /* got a good time flag */ /* * Initialize pointers and read the timecode and timestamp */ peer = rbufp->recv_peer; pp = peer->procptr; up = pp->unitptr; temp = refclock_gtlin(rbufp, pp->a_lastcode, BMAX, &trtmp); if (temp == 0) { if (up->tcswitch == 0) { up->tcswitch = 1; up->laststamp = trtmp; } else up->tcswitch = 0; return; } pp->lencode = temp; pp->lastrec = up->laststamp; up->laststamp = trtmp; up->tcswitch = 1; #ifdef DEBUG if (debug) printf("chronolog: timecode %d %s\n", pp->lencode, pp->a_lastcode); #endif /* * We get down to business. Check the timecode format and decode * its contents. This code uses the first character to see whether * we're looking at a date or a time. We store data data across * calls since it is transmitted a few seconds ahead of the * timestamp. */ got_good=0; if (sscanf(pp->a_lastcode, "Y %d/%d/%d", &up->year,&up->month,&up->day)) { /* * Y2K convert the 2-digit year */ up->year = up->year >= 69 ? up->year : up->year + 100; return; } if (sscanf(pp->a_lastcode,"Z %02d:%02d:%02d", &hours,&minutes,&seconds) == 3) { #ifdef GET_LOCALTIME struct tm local; struct tm *gmtp; time_t unixtime; int adjyear; int adjmon; /* * Convert to GMT for sites that distribute localtime. This * means we have to do Y2K conversion on the 2-digit year; * otherwise, we get the time wrong. */ memset(&local, 0, sizeof(local)); local.tm_year = up->year; local.tm_mon = up->month-1; local.tm_mday = up->day; local.tm_hour = hours; local.tm_min = minutes; local.tm_sec = seconds; local.tm_isdst = -1; unixtime = mktime (&local); if ((gmtp = gmtime (&unixtime)) == NULL) { refclock_report (peer, CEVNT_FAULT); return; } adjyear = gmtp->tm_year+1900; adjmon = gmtp->tm_mon+1; pp->day = ymd2yd (adjyear, adjmon, gmtp->tm_mday); pp->hour = gmtp->tm_hour; pp->minute = gmtp->tm_min; pp->second = gmtp->tm_sec; #ifdef DEBUG if (debug) printf ("time is %04d/%02d/%02d %02d:%02d:%02d UTC\n", adjyear,adjmon,gmtp->tm_mday,pp->hour,pp->minute, pp->second); #endif #else /* * For more rational sites distributing UTC */ pp->day = ymd2yd(year+1900,month,day); pp->hour = hours; pp->minute = minutes; pp->second = seconds; #endif got_good=1; } if (!got_good) return; /* * Process the new sample in the median filter and determine the * timecode timestamp. */ if (!refclock_process(pp)) { refclock_report(peer, CEVNT_BADTIME); return; } pp->lastref = pp->lastrec; refclock_receive(peer); record_clock_stats(&peer->srcadr, pp->a_lastcode); up->lasthour = (u_char)pp->hour; }
/* * dumbclock_receive - receive data from the serial interface */ static void dumbclock_receive( struct recvbuf *rbufp ) { struct dumbclock_unit *up; struct refclockproc *pp; struct peer *peer; l_fp trtmp; /* arrival timestamp */ int hours; /* hour-of-day */ int minutes; /* minutes-past-the-hour */ int seconds; /* seconds */ int temp; /* int temp */ int got_good; /* got a good time flag */ /* * Initialize pointers and read the timecode and timestamp */ peer = rbufp->recv_peer; pp = peer->procptr; up = pp->unitptr; temp = refclock_gtlin(rbufp, pp->a_lastcode, BMAX, &trtmp); if (temp == 0) { if (up->tcswitch == 0) { up->tcswitch = 1; up->laststamp = trtmp; } else up->tcswitch = 0; return; } pp->lencode = (u_short)temp; pp->lastrec = up->laststamp; up->laststamp = trtmp; up->tcswitch = 1; #ifdef DEBUG if (debug) printf("dumbclock: timecode %d %s\n", pp->lencode, pp->a_lastcode); #endif /* * We get down to business. Check the timecode format... */ got_good=0; if (sscanf(pp->a_lastcode,"%02d:%02d:%02d", &hours,&minutes,&seconds) == 3) { struct tm *gmtp; struct tm *lt_p; time_t asserted_time; /* the SPM time based on the composite time+date */ struct tm asserted_tm; /* the struct tm of the same */ int adjyear; int adjmon; time_t reality_delta; time_t now; /* * Convert to GMT for sites that distribute localtime. This * means we have to figure out what day it is. Easier said * than done... */ memset(&asserted_tm, 0, sizeof(asserted_tm)); asserted_tm.tm_year = up->ymd.tm_year; asserted_tm.tm_mon = up->ymd.tm_mon; asserted_tm.tm_mday = up->ymd.tm_mday; asserted_tm.tm_hour = hours; asserted_tm.tm_min = minutes; asserted_tm.tm_sec = seconds; asserted_tm.tm_isdst = -1; #ifdef GET_LOCALTIME asserted_time = mktime (&asserted_tm); time(&now); #else #include "GMT unsupported for dumbclock!" #endif reality_delta = asserted_time - now; /* * We assume that if the time is grossly wrong, it's because we got the * year/month/day wrong. */ if (reality_delta > INSANE_SECONDS) { asserted_time -= SECSPERDAY; /* local clock behind real time */ } else if (-reality_delta > INSANE_SECONDS) { asserted_time += SECSPERDAY; /* local clock ahead of real time */ } lt_p = localtime(&asserted_time); if (lt_p) { up->ymd = *lt_p; } else { refclock_report (peer, CEVNT_FAULT); return; } if ((gmtp = gmtime (&asserted_time)) == NULL) { refclock_report (peer, CEVNT_FAULT); return; } adjyear = gmtp->tm_year+1900; adjmon = gmtp->tm_mon+1; pp->day = ymd2yd (adjyear, adjmon, gmtp->tm_mday); pp->hour = gmtp->tm_hour; pp->minute = gmtp->tm_min; pp->second = gmtp->tm_sec; #ifdef DEBUG if (debug) printf ("time is %04d/%02d/%02d %02d:%02d:%02d UTC\n", adjyear,adjmon,gmtp->tm_mday,pp->hour,pp->minute, pp->second); #endif got_good=1; } if (!got_good) { if (up->linect > 0) up->linect--; else refclock_report(peer, CEVNT_BADREPLY); return; } /* * Process the new sample in the median filter and determine the * timecode timestamp. */ if (!refclock_process(pp)) { refclock_report(peer, CEVNT_BADTIME); return; } pp->lastref = pp->lastrec; refclock_receive(peer); record_clock_stats(&peer->srcadr, pp->a_lastcode); up->lasthour = (u_char)pp->hour; }
/* * wwvb_receive - receive data from the serial interface */ static void wwvb_receive( struct recvbuf *rbufp ) { struct wwvbunit *up; struct refclockproc *pp; struct peer *peer; l_fp trtmp; /* arrival timestamp */ int tz; /* time zone */ int day, month; /* ddd conversion */ int temp; /* int temp */ char syncchar; /* synchronization indicator */ char qualchar; /* quality indicator */ char leapchar; /* leap indicator */ char dstchar; /* daylight/standard indicator */ char tmpchar; /* trashbin */ /* * Initialize pointers and read the timecode and timestamp */ peer = rbufp->recv_peer; pp = peer->procptr; up = pp->unitptr; temp = refclock_gtlin(rbufp, pp->a_lastcode, BMAX, &trtmp); /* * Note we get a buffer and timestamp for both a <cr> and <lf>, * but only the <cr> timestamp is retained. Note: in format 0 on * a Netclock/2 or upgraded 8170 the start bit is delayed 100 * +-50 us relative to the pps; however, on an unmodified 8170 * the start bit can be delayed up to 10 ms. In format 2 the * reading precision is only to the millisecond. Thus, unless * you have a PPS gadget and don't have to have the year, format * 0 provides the lowest jitter. * Save the timestamp of each <CR> in up->laststamp. Lines with * no characters occur for every <LF>, and for some <CR>s when * format 0 is used. Format 0 starts and ends each cycle with a * <CR><LF> pair, format 2 starts each cycle with its only pair. * The preceding <CR> is the on-time character for both formats. * The timestamp provided with non-empty lines corresponds to * the <CR> following the timecode, which is ultimately not used * with format 0 and is used for the following timecode for * format 2. */ if (temp == 0) { if (up->prev_eol_cr) { DPRINTF(2, ("wwvb: <LF> @ %s\n", prettydate(&trtmp))); } else { up->laststamp = trtmp; DPRINTF(2, ("wwvb: <CR> @ %s\n", prettydate(&trtmp))); } up->prev_eol_cr = !up->prev_eol_cr; return; } pp->lencode = temp; pp->lastrec = up->laststamp; up->laststamp = trtmp; up->prev_eol_cr = TRUE; DPRINTF(2, ("wwvb: code @ %s\n" " using %s minus one char\n", prettydate(&trtmp), prettydate(&pp->lastrec))); if (L_ISZERO(&pp->lastrec)) return; /* * We get down to business, check the timecode format and decode * its contents. This code uses the timecode length to determine * format 0, 2 or 3. If the timecode has invalid length or is * not in proper format, we declare bad format and exit. */ syncchar = qualchar = leapchar = dstchar = ' '; tz = 0; switch (pp->lencode) { case LENWWVB0: /* * Timecode format 0: "I ddd hh:mm:ss DTZ=nn" */ if (sscanf(pp->a_lastcode, "%c %3d %2d:%2d:%2d%c%cTZ=%2d", &syncchar, &pp->day, &pp->hour, &pp->minute, &pp->second, &tmpchar, &dstchar, &tz) == 8) { pp->nsec = 0; break; } goto bad_format; case LENWWVB2: /* * Timecode format 2: "IQyy ddd hh:mm:ss.mmm LD" */ if (sscanf(pp->a_lastcode, "%c%c %2d %3d %2d:%2d:%2d.%3ld %c", &syncchar, &qualchar, &pp->year, &pp->day, &pp->hour, &pp->minute, &pp->second, &pp->nsec, &leapchar) == 9) { pp->nsec *= 1000000; break; } goto bad_format; case LENWWVB3: /* * Timecode format 3: "0003I yyyymmdd hhmmss+0000SL#" * WARNING: Undocumented, and the on-time character # is * not yet handled correctly by this driver. It may be * as simple as compensating for an additional 1/960 s. */ if (sscanf(pp->a_lastcode, "0003%c %4d%2d%2d %2d%2d%2d+0000%c%c", &syncchar, &pp->year, &month, &day, &pp->hour, &pp->minute, &pp->second, &dstchar, &leapchar) == 8) { pp->day = ymd2yd(pp->year, month, day); pp->nsec = 0; break; } goto bad_format; default: bad_format: /* * Unknown format: If dumping internal table, record * stats; otherwise, declare bad format. */ if (up->linect > 0) { up->linect--; record_clock_stats(&peer->srcadr, pp->a_lastcode); } else { refclock_report(peer, CEVNT_BADREPLY); } return; } /* * Decode synchronization, quality and leap characters. If * unsynchronized, set the leap bits accordingly and exit. * Otherwise, set the leap bits according to the leap character. * Once synchronized, the dispersion depends only on the * quality character. */ switch (qualchar) { case ' ': pp->disp = .001; pp->lastref = pp->lastrec; break; case 'A': pp->disp = .01; break; case 'B': pp->disp = .1; break; case 'C': pp->disp = .5; break; case 'D': pp->disp = MAXDISPERSE; break; default: pp->disp = MAXDISPERSE; refclock_report(peer, CEVNT_BADREPLY); break; } if (syncchar != ' ') pp->leap = LEAP_NOTINSYNC; else if (leapchar == 'L') pp->leap = LEAP_ADDSECOND; else pp->leap = LEAP_NOWARNING; /* * Process the new sample in the median filter and determine the * timecode timestamp, but only if the PPS is not in control. */ #ifdef HAVE_PPSAPI up->tcount++; if (peer->flags & FLAG_PPS) return; #endif /* HAVE_PPSAPI */ if (!refclock_process_f(pp, pp->fudgetime2)) refclock_report(peer, CEVNT_BADTIME); }
/* * as2201__receive - receive data from the serial interface */ static void as2201_receive( struct recvbuf *rbufp ) { register struct as2201unit *up; struct refclockproc *pp; struct peer *peer; l_fp trtmp; size_t octets; /* * Initialize pointers and read the timecode and timestamp. */ peer = rbufp->recv_peer; pp = peer->procptr; up = pp->unitptr; pp->lencode = refclock_gtlin(rbufp, pp->a_lastcode, BMAX, &trtmp); #ifdef DEBUG if (debug) printf("gps: timecode %d %d %s\n", up->linect, pp->lencode, pp->a_lastcode); #endif if (pp->lencode == 0) return; /* * If linect is greater than zero, we must be in the middle of a * statistics operation, so simply tack the received data at the * end of the statistics string. If not, we could either have * just received the timecode itself or a decimal number * indicating the number of following lines of the statistics * reply. In the former case, write the accumulated statistics * data to the clockstats file and continue onward to process * the timecode; in the later case, save the number of lines and * quietly return. */ if (pp->sloppyclockflag & CLK_FLAG2) pp->lastrec = trtmp; if (up->linect > 0) { up->linect--; if ((int)(up->lastptr - up->stats + pp->lencode) > SMAX - 2) return; *up->lastptr++ = ' '; memcpy(up->lastptr, pp->a_lastcode, 1 + pp->lencode); up->lastptr += pp->lencode; return; } else { if (pp->lencode == 1) { up->linect = atoi(pp->a_lastcode); return; } else { record_clock_stats(&peer->srcadr, up->stats); #ifdef DEBUG if (debug) printf("gps: stat %s\n", up->stats); #endif } } up->lastptr = up->stats; *up->lastptr = '\0'; /* * We get down to business, check the timecode format and decode * its contents. If the timecode has invalid length or is not in * proper format, we declare bad format and exit. */ if (pp->lencode < LENTOC) { refclock_report(peer, CEVNT_BADREPLY); return; } /* * Timecode format: "yy:ddd:hh:mm:ss.mmm" */ if (sscanf(pp->a_lastcode, "%2d:%3d:%2d:%2d:%2d.%3ld", &pp->year, &pp->day, &pp->hour, &pp->minute, &pp->second, &pp->nsec) != 6) { refclock_report(peer, CEVNT_BADREPLY); return; } pp->nsec *= 1000000; /* * Test for synchronization (this is a temporary crock). */ if (pp->a_lastcode[2] != ':') pp->leap = LEAP_NOTINSYNC; else pp->leap = LEAP_NOWARNING; /* * Process the new sample in the median filter and determine the * timecode timestamp. */ if (!refclock_process(pp)) { refclock_report(peer, CEVNT_BADTIME); return; } /* * If CLK_FLAG4 is set, initialize the statistics buffer and * send the next command. If not, simply write the timecode to * the clockstats file. */ if ((int)(up->lastptr - up->stats + pp->lencode) > SMAX - 2) return; memcpy(up->lastptr, pp->a_lastcode, pp->lencode); up->lastptr += pp->lencode; if (pp->sloppyclockflag & CLK_FLAG4) { octets = strlen(stat_command[up->index]); if ((int)(up->lastptr - up->stats + 1 + octets) > SMAX - 2) return; *up->lastptr++ = ' '; memcpy(up->lastptr, stat_command[up->index], octets); up->lastptr += octets - 1; *up->lastptr = '\0'; (void)write(pp->io.fd, stat_command[up->index], strlen(stat_command[up->index])); up->index++; if (*stat_command[up->index] == '\0') up->index = 0; } }
static void neoclock4x_receive(struct recvbuf *rbufp) { struct neoclock4x_unit *up; struct refclockproc *pp; struct peer *peer; unsigned long calc_utc; int day; int month; /* ddd conversion */ int c; int dsec; unsigned char calc_chksum; int recv_chksum; peer = rbufp->recv_peer; pp = peer->procptr; up = pp->unitptr; /* wait till poll interval is reached */ if(0 == up->recvnow) return; /* reset poll interval flag */ up->recvnow = 0; /* read last received timecode */ pp->lencode = refclock_gtlin(rbufp, pp->a_lastcode, BMAX, &pp->lastrec); pp->leap = LEAP_NOWARNING; if(NEOCLOCK4X_TIMECODELEN != pp->lencode) { NLOG(NLOG_CLOCKEVENT) msyslog(LOG_WARNING, "NeoClock4X(%d): received data has invalid length, expected %d bytes, received %d bytes: %s", up->unit, NEOCLOCK4X_TIMECODELEN, pp->lencode, pp->a_lastcode); refclock_report(peer, CEVNT_BADREPLY); return; } neol_hexatoi_len(&pp->a_lastcode[NEOCLOCK4X_OFFSET_CRC], &recv_chksum, 2); /* calculate checksum */ calc_chksum = 0; for(c=0; c < NEOCLOCK4X_OFFSET_CRC; c++) { calc_chksum += pp->a_lastcode[c]; } if(recv_chksum != calc_chksum) { NLOG(NLOG_CLOCKEVENT) msyslog(LOG_WARNING, "NeoClock4X(%d): received data has invalid chksum: %s", up->unit, pp->a_lastcode); refclock_report(peer, CEVNT_BADREPLY); return; } /* Allow synchronization even is quartz clock is * never initialized. * WARNING: This is dangerous! */ up->quarzstatus = pp->a_lastcode[NEOCLOCK4X_OFFSET_QUARZSTATUS]; if(0==(pp->sloppyclockflag & CLK_FLAG2)) { if('I' != up->quarzstatus) { NLOG(NLOG_CLOCKEVENT) msyslog(LOG_NOTICE, "NeoClock4X(%d): quartz clock is not initialized: %s", up->unit, pp->a_lastcode); pp->leap = LEAP_NOTINSYNC; refclock_report(peer, CEVNT_BADDATE); return; } } if('I' != up->quarzstatus) { NLOG(NLOG_CLOCKEVENT) msyslog(LOG_NOTICE, "NeoClock4X(%d): using uninitialized quartz clock for time synchronization: %s", up->unit, pp->a_lastcode); } /* * If NeoClock4X is not synchronized to a radio clock * check if we're allowed to synchronize with the quartz * clock. */ up->timesource = pp->a_lastcode[NEOCLOCK4X_OFFSET_TIMESOURCE]; if(0==(pp->sloppyclockflag & CLK_FLAG2)) { if('A' != up->timesource) { /* not allowed to sync with quartz clock */ if(0==(pp->sloppyclockflag & CLK_FLAG1)) { refclock_report(peer, CEVNT_BADTIME); pp->leap = LEAP_NOTINSYNC; return; } } } /* this should only used when first install is done */ if(pp->sloppyclockflag & CLK_FLAG4) { msyslog(LOG_DEBUG, "NeoClock4X(%d): received data: %s", up->unit, pp->a_lastcode); } /* 123456789012345678901234567890123456789012345 */ /* S/N123456DCF1004021010001202ASX1213CR\r\n */ neol_atoi_len(&pp->a_lastcode[NEOCLOCK4X_OFFSET_YEAR], &pp->year, 2); neol_atoi_len(&pp->a_lastcode[NEOCLOCK4X_OFFSET_MONTH], &month, 2); neol_atoi_len(&pp->a_lastcode[NEOCLOCK4X_OFFSET_DAY], &day, 2); neol_atoi_len(&pp->a_lastcode[NEOCLOCK4X_OFFSET_HOUR], &pp->hour, 2); neol_atoi_len(&pp->a_lastcode[NEOCLOCK4X_OFFSET_MINUTE], &pp->minute, 2); neol_atoi_len(&pp->a_lastcode[NEOCLOCK4X_OFFSET_SECOND], &pp->second, 2); neol_atoi_len(&pp->a_lastcode[NEOCLOCK4X_OFFSET_HSEC], &dsec, 2); #if defined(NTP_PRE_420) pp->msec = dsec * 10; /* convert 1/100s from neoclock to real miliseconds */ #else pp->nsec = dsec * 10 * NSEC_TO_MILLI; /* convert 1/100s from neoclock to nanoseconds */ #endif memcpy(up->radiosignal, &pp->a_lastcode[NEOCLOCK4X_OFFSET_RADIOSIGNAL], 3); up->radiosignal[3] = 0; memcpy(up->serial, &pp->a_lastcode[NEOCLOCK4X_OFFSET_SERIAL], 6); up->serial[6] = 0; up->dststatus = pp->a_lastcode[NEOCLOCK4X_OFFSET_DSTSTATUS]; neol_hexatoi_len(&pp->a_lastcode[NEOCLOCK4X_OFFSET_ANTENNA1], &up->antenna1, 2); neol_hexatoi_len(&pp->a_lastcode[NEOCLOCK4X_OFFSET_ANTENNA2], &up->antenna2, 2); /* Validate received values at least enough to prevent internal array-bounds problems, etc. */ if((pp->hour < 0) || (pp->hour > 23) || (pp->minute < 0) || (pp->minute > 59) || (pp->second < 0) || (pp->second > 60) /*Allow for leap seconds.*/ || (day < 1) || (day > 31) || (month < 1) || (month > 12) || (pp->year < 0) || (pp->year > 99)) { /* Data out of range. */ NLOG(NLOG_CLOCKEVENT) msyslog(LOG_WARNING, "NeoClock4X(%d): date/time out of range: %s", up->unit, pp->a_lastcode); refclock_report(peer, CEVNT_BADDATE); return; } /* Year-2000 check not needed anymore. Same problem * will arise at 2099 but what should we do...? * * wrap 2-digit date into 4-digit * * if(pp->year < YEAR_PIVOT) * { * pp->year += 100; * } */ pp->year += 2000; /* adjust NeoClock4X local time to UTC */ calc_utc = neol_mktime(pp->year, month, day, pp->hour, pp->minute, pp->second); calc_utc -= 3600; /* adjust NeoClock4X daylight saving time if needed */ if('S' == up->dststatus) calc_utc -= 3600; neol_localtime(calc_utc, &pp->year, &month, &day, &pp->hour, &pp->minute, &pp->second); /* some preparations */ pp->day = ymd2yd(pp->year, month, day); pp->leap = 0; if(pp->sloppyclockflag & CLK_FLAG4) { msyslog(LOG_DEBUG, "NeoClock4X(%d): calculated UTC date/time: %04d-%02d-%02d %02d:%02d:%02d.%03ld", up->unit, pp->year, month, day, pp->hour, pp->minute, pp->second, #if defined(NTP_PRE_420) pp->msec #else pp->nsec/NSEC_TO_MILLI #endif ); } up->utc_year = pp->year; up->utc_month = month; up->utc_day = day; up->utc_hour = pp->hour; up->utc_minute = pp->minute; up->utc_second = pp->second; #if defined(NTP_PRE_420) up->utc_msec = pp->msec; #else up->utc_msec = pp->nsec/NSEC_TO_MILLI; #endif if(!refclock_process(pp)) { NLOG(NLOG_CLOCKEVENT) msyslog(LOG_WARNING, "NeoClock4X(%d): refclock_process failed!", up->unit); refclock_report(peer, CEVNT_FAULT); return; } refclock_receive(peer); /* report good status */ refclock_report(peer, CEVNT_NOMINAL); record_clock_stats(&peer->srcadr, pp->a_lastcode); }
/* * ulink_receive - receive data from the serial interface */ static void ulink_receive( struct recvbuf *rbufp ) { struct ulinkunit *up; struct refclockproc *pp; struct peer *peer; l_fp trtmp; /* arrival timestamp */ int quality; /* quality indicator */ int temp; /* int temp */ char syncchar; /* synchronization indicator */ char leapchar; /* leap indicator */ char modechar; /* model 320 mode flag */ char siglchar; /* model difference between 33x/325 */ char char_quality[2]; /* temp quality flag */ /* * Initialize pointers and read the timecode and timestamp */ peer = (struct peer *)rbufp->recv_srcclock; pp = peer->procptr; up = (struct ulinkunit *)pp->unitptr; temp = refclock_gtlin(rbufp, pp->a_lastcode, BMAX, &trtmp); /* * Note we get a buffer and timestamp for both a <cr> and <lf>, * but only the <cr> timestamp is retained. */ if (temp == 0) { if (up->tcswitch == 0) { up->tcswitch = 1; up->laststamp = trtmp; } else up->tcswitch = 0; return; } pp->lencode = temp; pp->lastrec = up->laststamp; up->laststamp = trtmp; up->tcswitch = 1; #ifdef DEBUG if (debug) printf("ulink: timecode %d %s\n", pp->lencode, pp->a_lastcode); #endif /* * We get down to business, check the timecode format and decode * its contents. If the timecode has invalid length or is not in * proper format, we declare bad format and exit. */ syncchar = leapchar = modechar = siglchar = ' '; switch (pp->lencode ) { case LEN33X: /* * First we check if the format is 33x or 325: * <CR><LF>S9+D 00 YYYY+DDDUTCS HH:MM:SSL+5 (33x) * <CR><LF>R5_1C00LYYYY+DDDUTCS HH:MM:SSL+5 (325) * simply by comparing if the signal level is 'S' or 'R' */ if (sscanf(pp->a_lastcode, "%c%*31c", &siglchar) == 1) { if(siglchar == SIGLCHAR325) { /* * decode for a Model 325 decoder. * Timecode format from January 23, 2004 datasheet is: * * <CR><LF>R5_1C00LYYYY+DDDUTCS HH:MM:SSL+5 * * R WWVB decodersignal readability R1 - R5 * 5 R1 is unreadable, R5 is best * space a space (0x20) * 1 Data bit 0, 1, M (pos mark), or ? (unknown). * C Reception from either (C)olorado or (H)awaii * 00 Hours since last good WWVB frame sync. Will * be 00-99 * space Space char (0x20) or (0xa5) if locked to wwvb * YYYY Current year, 2000-2099 * + Leap year indicator. '+' if a leap year, * a space (0x20) if not. * DDD Day of year, 000 - 365. * UTC Timezone (always 'UTC'). * S Daylight savings indicator * S - standard time (STD) in effect * O - during STD to DST day 0000-2400 * D - daylight savings time (DST) in effect * I - during DST to STD day 0000-2400 * space Space character (0x20) * HH Hours 00-23 * : This is the REAL in sync indicator (: = insync) * MM Minutes 00-59 * : : = in sync ? = NOT in sync * SS Seconds 00-59 * L Leap second flag. Changes from space (0x20) * to 'I' or 'D' during month preceding leap * second adjustment. (I)nsert or (D)elete * +5 UT1 correction (sign + digit )) */ if (sscanf(pp->a_lastcode, "%*2c %*2c%2c%*c%4d%*c%3d%*4c %2d%c%2d:%2d%c%*2c", char_quality, &pp->year, &pp->day, &pp->hour, &syncchar, &pp->minute, &pp->second, &leapchar) == 8) { if (char_quality[0] == '0') { quality = 0; } else if (char_quality[0] == '0') { quality = (char_quality[1] & 0x0f); } else { quality = 99; } if (leapchar == 'I' ) leapchar = '+'; if (leapchar == 'D' ) leapchar = '-'; /* #ifdef DEBUG if (debug) { printf("ulink: char_quality %c %c\n", char_quality[0], char_quality[1]); printf("ulink: quality %d\n", quality); printf("ulink: syncchar %x\n", syncchar); printf("ulink: leapchar %x\n", leapchar); } #endif */ } } if(siglchar == SIGLCHAR33x) { /* * We got a Model 33X decoder. * Timecode format from January 29, 2001 datasheet is: * <CR><LF>S9+D 00 YYYY+DDDUTCS HH:MM:SSL+5 * S WWVB decoder sync indicator. S for in-sync(?) * or N for noisy signal. * 9+ RF signal level in S-units, 0-9 followed by * a space (0x20). The space turns to '+' if the * level is over 9. * D Data bit 0, 1, 2 (position mark), or * 3 (unknown). * space Space character (0x20) * 00 Hours since last good WWVB frame sync. Will * be 00-23 hrs, or '1d' to '7d'. Will be 'Lk' * if currently in sync. * space Space character (0x20) * YYYY Current year, 1990-2089 * + Leap year indicator. '+' if a leap year, * a space (0x20) if not. * DDD Day of year, 001 - 366. * UTC Timezone (always 'UTC'). * S Daylight savings indicator * S - standard time (STD) in effect * O - during STD to DST day 0000-2400 * D - daylight savings time (DST) in effect * I - during DST to STD day 0000-2400 * space Space character (0x20) * HH Hours 00-23 * : This is the REAL in sync indicator (: = insync) * MM Minutes 00-59 * : : = in sync ? = NOT in sync * SS Seconds 00-59 * L Leap second flag. Changes from space (0x20) * to '+' or '-' during month preceding leap * second adjustment. * +5 UT1 correction (sign + digit )) */ if (sscanf(pp->a_lastcode, "%*4c %2c %4d%*c%3d%*4c %2d%c%2d:%2d%c%*2c", char_quality, &pp->year, &pp->day, &pp->hour, &syncchar, &pp->minute, &pp->second, &leapchar) == 8) { if (char_quality[0] == 'L') { quality = 0; } else if (char_quality[0] == '0') { quality = (char_quality[1] & 0x0f); } else { quality = 99; } /* #ifdef DEBUG if (debug) { printf("ulink: char_quality %c %c\n", char_quality[0], char_quality[1]); printf("ulink: quality %d\n", quality); printf("ulink: syncchar %x\n", syncchar); printf("ulink: leapchar %x\n", leapchar); } #endif */ } } break; } case LEN320: /* * Model 320 Decoder * The timecode format is: * * <cr><lf>SQRYYYYDDD+HH:MM:SS.mmLT<cr> * * where: * * S = 'S' -- sync'd in last hour, * '0'-'9' - hours x 10 since last update, * '?' -- not in sync * Q = Number of correlating time-frames, from 0 to 5 * R = 'R' -- reception in progress, * 'N' -- Noisy reception, * ' ' -- standby mode * YYYY = year from 1990 to 2089 * DDD = current day from 1 to 366 * + = '+' if current year is a leap year, else ' ' * HH = UTC hour 0 to 23 * MM = Minutes of current hour from 0 to 59 * SS = Seconds of current minute from 0 to 59 * mm = 10's milliseconds of the current second from 00 to 99 * L = Leap second pending at end of month * 'I' = insert, 'D'= delete * T = DST <-> STD transition indicators * */ if (sscanf(pp->a_lastcode, "%c%1d%c%4d%3d%*c%2d:%2d:%2d.%2ld%c", &syncchar, &quality, &modechar, &pp->year, &pp->day, &pp->hour, &pp->minute, &pp->second, &pp->nsec, &leapchar) == 10) { pp->nsec *= 10000000; /* M320 returns 10's of msecs */ if (leapchar == 'I' ) leapchar = '+'; if (leapchar == 'D' ) leapchar = '-'; if (syncchar != '?' ) syncchar = ':'; break; } default: refclock_report(peer, CEVNT_BADREPLY); return; } /* * Decode quality indicator * For the 325 & 33x series, the lower the number the "better" * the time is. I used the dispersion as the measure of time * quality. The quality indicator in the 320 is the number of * correlating time frames (the more the better) */ /* * The spec sheet for the 325 & 33x series states the clock will * maintain +/-0.002 seconds accuracy when locked to WWVB. This * is indicated by 'Lk' in the quality portion of the incoming * string. When not in lock, a drift of +/-0.015 seconds should * be allowed for. * With the quality indicator decoding scheme above, the 'Lk' * condition will produce a quality value of 0. If the quality * indicator starts with '0' then the second character is the * number of hours since we were last locked. If the first * character is anything other than 'L' or '0' then we have been * out of lock for more than 9 hours so we assume the worst and * force a quality value that selects the 'default' maximum * dispersion. The dispersion values below are what came with the * driver. They're not unreasonable so they've not been changed. */ if (pp->lencode == LEN33X) { switch (quality) { case 0 : pp->disp=.002; break; case 1 : pp->disp=.02; break; case 2 : pp->disp=.04; break; case 3 : pp->disp=.08; break; default: pp->disp=MAXDISPERSE; break; } } else { switch (quality) { case 5 : pp->disp=.002; break; case 4 : pp->disp=.02; break; case 3 : pp->disp=.04; break; case 2 : pp->disp=.08; break; case 1 : pp->disp=.16; break; default: pp->disp=MAXDISPERSE; break; } } /* * Decode synchronization, and leap characters. If * unsynchronized, set the leap bits accordingly and exit. * Otherwise, set the leap bits according to the leap character. */ if (syncchar != ':') pp->leap = LEAP_NOTINSYNC; else if (leapchar == '+') pp->leap = LEAP_ADDSECOND; else if (leapchar == '-') pp->leap = LEAP_DELSECOND; else pp->leap = LEAP_NOWARNING; /* * Process the new sample in the median filter and determine the * timecode timestamp. */ if (!refclock_process(pp)) { refclock_report(peer, CEVNT_BADTIME); } }
/* * true_receive - receive data from the serial interface on a clock */ static void true_receive( struct recvbuf *rbufp ) { register struct true_unit *up; struct refclockproc *pp; struct peer *peer; u_short new_station; char synced; int i; int lat, lon, off; /* GOES Satellite position */ /* Use these variable to hold data until we decide its worth keeping */ char rd_lastcode[BMAX]; l_fp rd_tmp; u_short rd_lencode; /* * Get the clock this applies to and pointers to the data. */ peer = (struct peer *)rbufp->recv_srcclock; pp = peer->procptr; up = (struct true_unit *)pp->unitptr; /* * Read clock output. Automatically handles STREAMS, CLKLDISC. */ rd_lencode = refclock_gtlin(rbufp, rd_lastcode, BMAX, &rd_tmp); rd_lastcode[rd_lencode] = '\0'; /* * There is a case where <cr><lf> generates 2 timestamps. */ if (rd_lencode == 0) return; pp->lencode = rd_lencode; strcpy(pp->a_lastcode, rd_lastcode); pp->lastrec = rd_tmp; true_debug(peer, "receive(%s) [%d]\n", pp->a_lastcode, pp->lencode); up->pollcnt = 2; record_clock_stats(&peer->srcadr, pp->a_lastcode); /* * We get down to business, check the timecode format and decode * its contents. This code decodes a multitude of different * clock messages. Timecodes are processed if needed. All replies * will be run through the state machine to tweak driver options * and program the clock. */ /* * Clock misunderstood our last command? */ if (pp->a_lastcode[0] == '?' || strcmp(pp->a_lastcode, "ERROR 05 NO SUCH FUNCTION") == 0) { true_doevent(peer, e_Huh); return; } /* * Timecode: "nnnnn+nnn-nnn" * (from GOES clock when asked about satellite position) */ if ((pp->a_lastcode[5] == '+' || pp->a_lastcode[5] == '-') && (pp->a_lastcode[9] == '+' || pp->a_lastcode[9] == '-') && sscanf(pp->a_lastcode, "%5d%*c%3d%*c%3d", &lon, &lat, &off) == 3 ) { const char *label = "Botch!"; /* * This is less than perfect. Call the (satellite) * either EAST or WEST and adjust slop accodingly * Perfectionists would recalculate the exact delay * and adjust accordingly... */ if (lon > 7000 && lon < 14000) { if (lon < 10000) { new_station = GOES_EAST; label = "EAST"; } else { new_station = GOES_WEST; label = "WEST"; } if (new_station != up->station) { double dtemp; dtemp = pp->fudgetime1; pp->fudgetime1 = pp->fudgetime2; pp->fudgetime2 = dtemp; up->station = new_station; } } else { /*refclock_report(peer, CEVNT_BADREPLY);*/ label = "UNKNOWN"; } true_debug(peer, "GOES: station %s\n", label); true_doevent(peer, e_Satellite); return; } /* * Timecode: "Fnn" * (from TM/TMD clock when it wants to tell us what it's up to.) */ if (sscanf(pp->a_lastcode, "F%2d", &i) == 1 && i > 0 && i < 80) { switch (i) { case 50: true_doevent(peer, e_F50); break; case 51: true_doevent(peer, e_F51); break; default: true_debug(peer, "got F%02d - ignoring\n", i); break; } return; } /* * Timecode: " TRUETIME Mk III" or " TRUETIME XL" * (from a TM/TMD/XL clock during initialization.) */ if (strcmp(pp->a_lastcode, " TRUETIME Mk III") == 0 || strncmp(pp->a_lastcode, " TRUETIME XL", 12) == 0) { true_doevent(peer, e_F18); NLOG(NLOG_CLOCKSTATUS) { msyslog(LOG_INFO, "TM/TMD/XL: %s", pp->a_lastcode); } return; }
static void hopfserial_receive ( struct recvbuf *rbufp ) { struct hopfclock_unit *up; struct refclockproc *pp; struct peer *peer; int synch; /* synchhronization indicator */ int DoW; /* Dow */ int day, month; /* ddd conversion */ /* * Initialize pointers and read the timecode and timestamp. */ peer = (struct peer *)rbufp->recv_srcclock; pp = peer->procptr; up = (struct hopfclock_unit *)pp->unitptr; if (up->rpt_next == 0 ) return; up->rpt_next = 0; /* wait until next poll interval occur */ pp->lencode = (u_short)refclock_gtlin(rbufp, pp->a_lastcode, BMAX, &pp->lastrec); if (pp->lencode == 0) return; sscanf(pp->a_lastcode, #if 1 "%1x%1x%2d%2d%2d%2d%2d%2d", /* ...cr,lf */ #else "%*c%1x%1x%2d%2d%2d%2d%2d%2d", /* stx...cr,lf,etx */ #endif &synch, &DoW, &pp->hour, &pp->minute, &pp->second, &day, &month, &pp->year); /* Validate received values at least enough to prevent internal array-bounds problems, etc. */ if((pp->hour < 0) || (pp->hour > 23) || (pp->minute < 0) || (pp->minute > 59) || (pp->second < 0) || (pp->second > 60) /*Allow for leap seconds.*/ || (day < 1) || (day > 31) || (month < 1) || (month > 12) || (pp->year < 0) || (pp->year > 99)) { /* Data out of range. */ refclock_report(peer, CEVNT_BADREPLY); return; } /* some preparations */ pp->day = ymd2yd(pp->year,month,day); pp->leap=0; /* Year-2000 check! */ /* wrap 2-digit date into 4-digit */ if(pp->year < YEAR_PIVOT) { pp->year += 100; } /* < 98 */ pp->year += 1900; /* preparation for timecode ntpq rl command ! */ #if 0 snprintf(pp->a_lastcode, sizeof(pp->a_lastcode), "STATUS: %1X%1X, DATE: %02d.%02d.%04d TIME: %02d:%02d:%02d", synch, DoW, day, month, pp->year, pp->hour, pp->minute, pp->second); pp->lencode = strlen(pp->a_lastcode); if ((synch && 0xc) == 0 ){ /* time ok? */ refclock_report(peer, CEVNT_BADTIME); pp->leap = LEAP_NOTINSYNC; return; } #endif /* * If clock has no valid status then report error and exit */ if ((synch & HOPF_OPMODE) == HOPF_INVALID ){ /* time ok? */ refclock_report(peer, CEVNT_BADTIME); pp->leap = LEAP_NOTINSYNC; return; } /* * Test if time is running on internal quarz * if CLK_FLAG1 is set, sychronize even if no radio operation */ if ((synch & HOPF_OPMODE) == HOPF_INTERNAL){ if ((pp->sloppyclockflag & CLK_FLAG1) == 0) { refclock_report(peer, CEVNT_BADTIME); pp->leap = LEAP_NOTINSYNC; return; } } if (!refclock_process(pp)) { refclock_report(peer, CEVNT_BADTIME); return; } pp->lastref = pp->lastrec; refclock_receive(peer); #if 0 msyslog(LOG_ERR, " D:%x D:%d D:%d",synch,pp->minute,pp->second); #endif record_clock_stats(&peer->srcadr, pp->a_lastcode); return; }
/* * arb_receive - receive data from the serial interface */ static void arb_receive( struct recvbuf *rbufp ) { register struct arbunit *up; struct refclockproc *pp; struct peer *peer; l_fp trtmp; int temp; u_char syncchar; /* synch indicator */ char tbuf[BMAX]; /* temp buffer */ /* * Initialize pointers and read the timecode and timestamp */ peer = rbufp->recv_peer; pp = peer->procptr; up = pp->unitptr; temp = refclock_gtlin(rbufp, tbuf, sizeof(tbuf), &trtmp); /* * Note we get a buffer and timestamp for both a <cr> and <lf>, * but only the <cr> timestamp is retained. The program first * sends a TQ and expects the echo followed by the time quality * character. It then sends a B5 starting the timecode broadcast * and expects the echo followed some time later by the on-time * character <cr> and then the <lf> beginning the timecode * itself. Finally, at the <cr> beginning the next timecode at * the next second, the program sends a B0 shutting down the * timecode broadcast. * * If flag4 is set, the program snatches the latitude, longitude * and elevation and writes it to the clockstats file. */ if (temp == 0) return; pp->lastrec = up->laststamp; up->laststamp = trtmp; if (temp < 3) return; if (up->tcswitch == 0) { /* * Collect statistics. If nothing is recogized, just * ignore; sometimes the clock doesn't stop spewing * timecodes for awhile after the B0 command. * * If flag4 is not set, send TQ, SR, B5. If flag4 is * sset, send TQ, SR, LA, LO, LH, DB, B5. When the * median filter is full, send B0. */ if (!strncmp(tbuf, "TQ", 2)) { up->qualchar = tbuf[2]; write(pp->io.fd, "SR", 2); return; } else if (!strncmp(tbuf, "SR", 2)) { strlcpy(up->status, tbuf + 2, sizeof(up->status)); if (pp->sloppyclockflag & CLK_FLAG4) write(pp->io.fd, "LA", 2); else write(pp->io.fd, COMMAND_START_BCAST, 2); return; } else if (!strncmp(tbuf, "LA", 2)) { strlcpy(up->latlon, tbuf + 2, sizeof(up->latlon)); write(pp->io.fd, "LO", 2); return; } else if (!strncmp(tbuf, "LO", 2)) { strlcat(up->latlon, " ", sizeof(up->latlon)); strlcat(up->latlon, tbuf + 2, sizeof(up->latlon)); write(pp->io.fd, "LH", 2); return; } else if (!strncmp(tbuf, "LH", 2)) { strlcat(up->latlon, " ", sizeof(up->latlon)); strlcat(up->latlon, tbuf + 2, sizeof(up->latlon)); write(pp->io.fd, "DB", 2); return; } else if (!strncmp(tbuf, "DB", 2)) { strlcat(up->latlon, " ", sizeof(up->latlon)); strlcat(up->latlon, tbuf + 2, sizeof(up->latlon)); record_clock_stats(&peer->srcadr, up->latlon); #ifdef DEBUG if (debug) printf("arbiter: %s\n", up->latlon); #endif write(pp->io.fd, COMMAND_START_BCAST, 2); } } /* * We get down to business, check the timecode format and decode * its contents. If the timecode has valid length, but not in * proper format, we declare bad format and exit. If the * timecode has invalid length, which sometimes occurs when the * B0 amputates the broadcast, we just quietly steal away. Note * that the time quality character and receiver status string is * tacked on the end for clockstats display. */ up->tcswitch++; if (up->tcswitch <= 1 || temp < LENARB) return; /* * Timecode format B5: "i yy ddd hh:mm:ss.000 " */ strlcpy(pp->a_lastcode, tbuf, sizeof(pp->a_lastcode)); pp->a_lastcode[LENARB - 2] = up->qualchar; strlcat(pp->a_lastcode, up->status, sizeof(pp->a_lastcode)); pp->lencode = strlen(pp->a_lastcode); syncchar = ' '; if (sscanf(pp->a_lastcode, "%c%2d %3d %2d:%2d:%2d", &syncchar, &pp->year, &pp->day, &pp->hour, &pp->minute, &pp->second) != 6) { refclock_report(peer, CEVNT_BADREPLY); write(pp->io.fd, COMMAND_HALT_BCAST, 2); return; } /* * We decode the clock dispersion from the time quality * character. */ switch (up->qualchar) { case '0': /* locked, max accuracy */ pp->disp = 1e-7; pp->lastref = pp->lastrec; break; case '4': /* unlock accuracy < 1 us */ pp->disp = 1e-6; break; case '5': /* unlock accuracy < 10 us */ pp->disp = 1e-5; break; case '6': /* unlock accuracy < 100 us */ pp->disp = 1e-4; break; case '7': /* unlock accuracy < 1 ms */ pp->disp = .001; break; case '8': /* unlock accuracy < 10 ms */ pp->disp = .01; break; case '9': /* unlock accuracy < 100 ms */ pp->disp = .1; break; case 'A': /* unlock accuracy < 1 s */ pp->disp = 1; break; case 'B': /* unlock accuracy < 10 s */ pp->disp = 10; break; case 'F': /* clock failure */ pp->disp = MAXDISPERSE; refclock_report(peer, CEVNT_FAULT); write(pp->io.fd, COMMAND_HALT_BCAST, 2); return; default: pp->disp = MAXDISPERSE; refclock_report(peer, CEVNT_BADREPLY); write(pp->io.fd, COMMAND_HALT_BCAST, 2); return; } if (syncchar != ' ') pp->leap = LEAP_NOTINSYNC; else pp->leap = LEAP_NOWARNING; /* * Process the new sample in the median filter and determine the * timecode timestamp. */ if (!refclock_process(pp)) refclock_report(peer, CEVNT_BADTIME); else if (peer->disp > MAXDISTANCE) refclock_receive(peer); /* if (up->tcswitch >= MAXSTAGE) { */ write(pp->io.fd, COMMAND_HALT_BCAST, 2); /* } */ }
/* * pst_receive - receive data from the serial interface */ static void pst_receive( struct recvbuf *rbufp ) { register struct pstunit *up; struct refclockproc *pp; struct peer *peer; l_fp trtmp; u_long ltemp; char ampmchar; /* AM/PM indicator */ char daychar; /* standard/daylight indicator */ char junque[10]; /* "yy/dd/mm/" discard */ char info[14]; /* "frdzycchhSSFT" clock info */ /* * Initialize pointers and read the timecode and timestamp */ peer = rbufp->recv_peer; pp = peer->procptr; up = pp->unitptr; up->lastptr += refclock_gtlin(rbufp, up->lastptr, pp->a_lastcode + BMAX - 2 - up->lastptr, &trtmp); *up->lastptr++ = ' '; *up->lastptr = '\0'; /* * Note we get a buffer and timestamp for each <cr>, but only * the first timestamp is retained. */ if (up->tcswitch == 0) pp->lastrec = trtmp; up->tcswitch++; pp->lencode = up->lastptr - pp->a_lastcode; if (up->tcswitch < 3) return; /* * We get down to business, check the timecode format and decode * its contents. If the timecode has invalid length or is not in * proper format, we declare bad format and exit. */ if (pp->lencode < LENPST) { refclock_report(peer, CEVNT_BADREPLY); return; } /* * Timecode format: * "ahh:mm:ss.fffs yy/dd/mm/ddd frdzycchhSSFTttttuuxx" */ if (sscanf(pp->a_lastcode, "%c%2d:%2d:%2d.%3ld%c %9s%3d%13s%4ld", &mchar, &pp->hour, &pp->minute, &pp->second, &pp->nsec, &daychar, junque, &pp->day, info, <emp) != 10) { refclock_report(peer, CEVNT_BADREPLY); return; } pp->nsec *= 1000000; /* * Decode synchronization, quality and last update. If * unsynchronized, set the leap bits accordingly and exit. Once * synchronized, the dispersion depends only on when the clock * was last heard, which depends on the time since last update, * as reported by the clock. */ if (info[9] != '8') pp->leap = LEAP_NOTINSYNC; if (info[12] == 'H') memcpy((char *)&pp->refid, WWVHREFID, 4); else memcpy((char *)&pp->refid, WWVREFID, 4); if (peer->stratum <= 1) peer->refid = pp->refid; if (ltemp == 0) pp->lastref = pp->lastrec; pp->disp = PST_PHI * ltemp * 60; /* * Process the new sample in the median filter and determine the * timecode timestamp. */ if (!refclock_process(pp)) refclock_report(peer, CEVNT_BADTIME); else if (peer->disp > MAXDISTANCE) refclock_receive(peer); }
/* * hpgps_receive - receive data from the serial interface */ static void hpgps_receive( struct recvbuf *rbufp ) { register struct hpgpsunit *up; struct refclockproc *pp; struct peer *peer; l_fp trtmp; char tcodechar1; /* identifies timecode format */ char tcodechar2; /* identifies timecode format */ char timequal; /* time figure of merit: 0-9 */ char freqqual; /* frequency figure of merit: 0-3 */ char leapchar; /* leapsecond: + or 0 or - */ char servchar; /* request for service: 0 = no, 1 = yes */ char syncchar; /* time info is invalid: 0 = no, 1 = yes */ short expectedsm; /* expected timecode byte checksum */ short tcodechksm; /* computed timecode byte checksum */ int i,m,n; int month, day, lastday; char *tcp; /* timecode pointer (skips over the prompt) */ char prompt[BMAX]; /* prompt in response from receiver */ /* * Initialize pointers and read the receiver response */ peer = (struct peer *)rbufp->recv_srcclock; pp = peer->procptr; up = (struct hpgpsunit *)pp->unitptr; *pp->a_lastcode = '\0'; pp->lencode = refclock_gtlin(rbufp, pp->a_lastcode, BMAX, &trtmp); #ifdef DEBUG if (debug) printf("hpgps: lencode: %d timecode:%s\n", pp->lencode, pp->a_lastcode); #endif /* * If there's no characters in the reply, we can quit now */ if (pp->lencode == 0) return; /* * If linecnt is greater than zero, we are getting information only, * such as the receiver identification string or the receiver status * screen, so put the receiver response at the end of the status * screen buffer. When we have the last line, write the buffer to * the clockstats file and return without further processing. * * If linecnt is zero, we are expecting either the timezone * or a timecode. At this point, also write the response * to the clockstats file, and go on to process the prompt (if any), * timezone, or timecode and timestamp. */ if (up->linecnt-- > 0) { if ((int)(pp->lencode + 2) <= (SMAX - (up->lastptr - up->statscrn))) { *up->lastptr++ = '\n'; (void)strcpy(up->lastptr, pp->a_lastcode); up->lastptr += pp->lencode; } if (up->linecnt == 0) record_clock_stats(&peer->srcadr, up->statscrn); return; } record_clock_stats(&peer->srcadr, pp->a_lastcode); pp->lastrec = trtmp; up->lastptr = up->statscrn; *up->lastptr = '\0'; up->pollcnt = 2; /* * We get down to business: get a prompt if one is there, issue * a clear status command if it contains an error indication. * Next, check for either the timezone reply or the timecode reply * and decode it. If we don't recognize the reply, or don't get the * proper number of decoded fields, or get an out of range timezone, * or if the timecode checksum is bad, then we declare bad format * and exit. * * Timezone format (including nominal prompt): * scpi > -H,-M<cr><lf> * * Timecode format (including nominal prompt): * scpi > T2yyyymmddhhmmssMFLRVcc<cr><lf> * */ (void)strcpy(prompt,pp->a_lastcode); tcp = strrchr(pp->a_lastcode,'>'); if (tcp == NULL) tcp = pp->a_lastcode; else tcp++; prompt[tcp - pp->a_lastcode] = '\0'; while ((*tcp == ' ') || (*tcp == '\t')) tcp++; /* * deal with an error indication in the prompt here */ if (strrchr(prompt,'E') > strrchr(prompt,'s')){ #ifdef DEBUG if (debug) printf("hpgps: error indicated in prompt: %s\n", prompt); #endif if (write(pp->io.fd, "*CLS\r\r", 6) != 6) refclock_report(peer, CEVNT_FAULT); } /* * make sure we got a timezone or timecode format and * then process accordingly */ m = sscanf(tcp,"%c%c", &tcodechar1, &tcodechar2); if (m != 2){ #ifdef DEBUG if (debug) printf("hpgps: no format indicator\n"); #endif refclock_report(peer, CEVNT_BADREPLY); return; } switch (tcodechar1) { case '+': case '-': m = sscanf(tcp,"%d,%d", &up->tzhour, &up->tzminute); if (m != MTZONE) { #ifdef DEBUG if (debug) printf("hpgps: only %d fields recognized in timezone\n", m); #endif refclock_report(peer, CEVNT_BADREPLY); return; } if ((up->tzhour < -12) || (up->tzhour > 13) || (up->tzminute < -59) || (up->tzminute > 59)){ #ifdef DEBUG if (debug) printf("hpgps: timezone %d, %d out of range\n", up->tzhour, up->tzminute); #endif refclock_report(peer, CEVNT_BADREPLY); return; } return; case 'T': break; default: #ifdef DEBUG if (debug) printf("hpgps: unrecognized reply format %c%c\n", tcodechar1, tcodechar2); #endif refclock_report(peer, CEVNT_BADREPLY); return; } /* end of tcodechar1 switch */ switch (tcodechar2) { case '2': m = sscanf(tcp,"%*c%*c%4d%2d%2d%2d%2d%2d%c%c%c%c%c%2hx", &pp->year, &month, &day, &pp->hour, &pp->minute, &pp->second, &timequal, &freqqual, &leapchar, &servchar, &syncchar, &expectedsm); n = NTCODET2; if (m != MTCODET2){ #ifdef DEBUG if (debug) printf("hpgps: only %d fields recognized in timecode\n", m); #endif refclock_report(peer, CEVNT_BADREPLY); return; } break; default: #ifdef DEBUG if (debug) printf("hpgps: unrecognized timecode format %c%c\n", tcodechar1, tcodechar2); #endif refclock_report(peer, CEVNT_BADREPLY); return; } /* end of tcodechar2 format switch */ /* * Compute and verify the checksum. * Characters are summed starting at tcodechar1, ending at just * before the expected checksum. Bail out if incorrect. */ tcodechksm = 0; while (n-- > 0) tcodechksm += *tcp++; tcodechksm &= 0x00ff; if (tcodechksm != expectedsm) { #ifdef DEBUG if (debug) printf("hpgps: checksum %2hX doesn't match %2hX expected\n", tcodechksm, expectedsm); #endif refclock_report(peer, CEVNT_BADREPLY); return; } /* * Compute the day of year from the yyyymmdd format. */ if (month < 1 || month > 12 || day < 1) { refclock_report(peer, CEVNT_BADTIME); return; } if ( ! isleap_4(pp->year) ) { /* Y2KFixes */ /* not a leap year */ if (day > day1tab[month - 1]) { refclock_report(peer, CEVNT_BADTIME); return; } for (i = 0; i < month - 1; i++) day += day1tab[i]; lastday = 365; } else { /* a leap year */ if (day > day2tab[month - 1]) { refclock_report(peer, CEVNT_BADTIME); return; } for (i = 0; i < month - 1; i++) day += day2tab[i]; lastday = 366; } /* * Deal with the timezone offset here. The receiver timecode is in * local time = UTC + :PTIME:TZONE, so SUBTRACT the timezone values. * For example, Pacific Standard Time is -8 hours , 0 minutes. * Deal with the underflows and overflows. */ pp->minute -= up->tzminute; pp->hour -= up->tzhour; if (pp->minute < 0) { pp->minute += 60; pp->hour--; } if (pp->minute > 59) { pp->minute -= 60; pp->hour++; } if (pp->hour < 0) { pp->hour += 24; day--; if (day < 1) { pp->year--; if ( isleap_4(pp->year) ) /* Y2KFixes */ day = 366; else day = 365; } } if (pp->hour > 23) { pp->hour -= 24; day++; if (day > lastday) { pp->year++; day = 1; } } pp->day = day; /* * Decode the MFLRV indicators. * NEED TO FIGURE OUT how to deal with the request for service, * time quality, and frequency quality indicators some day. */ if (syncchar != '0') { pp->leap = LEAP_NOTINSYNC; } else { pp->leap = LEAP_NOWARNING; switch (leapchar) { case '0': break; /* See http://bugs.ntp.org/1090 * Ignore leap announcements unless June or December. * Better would be to use :GPSTime? to find the month, * but that seems too likely to introduce other bugs. */ case '+': if ((month==6) || (month==12)) pp->leap = LEAP_ADDSECOND; break; case '-': if ((month==6) || (month==12)) pp->leap = LEAP_DELSECOND; break; default: #ifdef DEBUG if (debug) printf("hpgps: unrecognized leap indicator: %c\n", leapchar); #endif refclock_report(peer, CEVNT_BADTIME); return; } /* end of leapchar switch */ } /* * Process the new sample in the median filter and determine the * reference clock offset and dispersion. We use lastrec as both * the reference time and receive time in order to avoid being * cute, like setting the reference time later than the receive * time, which may cause a paranoid protocol module to chuck out * the data. */ if (!refclock_process(pp)) { refclock_report(peer, CEVNT_BADTIME); return; } pp->lastref = pp->lastrec; refclock_receive(peer); /* * If CLK_FLAG4 is set, ask for the status screen response. */ if (pp->sloppyclockflag & CLK_FLAG4){ up->linecnt = 22; if (write(pp->io.fd, ":SYSTEM:PRINT?\r", 15) != 15) refclock_report(peer, CEVNT_FAULT); } }