/* * refclock_leitch.c - clock driver for Leitch 5300 Clock System Driver * Ralph Siemsen Nov 16, 2001 * based heavily on refclock_nmea.c * Could be improved by using all three leitch timestamps. * * Leitch only provides two digits of year information, so this driver * currently assumes that the year is between 2000 and 2099. */ #ifdef HAVE_CONFIG_H #include #endif #if defined(REFCLOCK) && defined(CLOCK_LEITCH) #include "ntpd.h" #include "ntp_io.h" #include "ntp_unixtime.h" #include "ntp_refclock.h" #include "ntp_stdlib.h" #include #include /* * This driver supports the Leitch 5300 Clock System Driver * It is a total rewrite of the existing driver. This one is * based heavily on refclock_nmea.c. * * COMMANDS: * DATE: D * TIME: T * STATUS: S * LOOP: L * * FORMAT: * DATE: YYMMDD * TIME: /HHMMSS /HHMMSS /HHMMSS / * second bondaried on the stop bit of the * second boundaries at '/' above. * STATUS: G (good), D (diag fail), T (time not provided) or * P (last phone update failed) */ /* * Definitions */ # define DEVICE "/dev/leitch%d" /* name of radio device */ #define SPEED232 B300 /* uart speed (300 bps) */ #define PRECISION (-9) /* precision assumed (about 2 ms) */ #define PPS_PRECISION (-20) /* precision assumed (about 1 us) */ #define REFID "ATOM" /* reference id */ #define DESCRIPTION "Leitch 5300 Clock System Driver" /* who we are */ /* * Tables to compute the ddd of year form icky dd/mm timecode. Viva la * leap. */ static int day1tab[] = {31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31}; static int day2tab[] = {31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31}; /* * Unit control structure */ struct leitchunit { short state; /* serial state machine counter */ l_fp tstamp; /* timestamp of last poll */ u_short year, yearday; /* date from last leitch poll */ u_short hour, minute, second; /* time form last leitch poll */ }; /* * Possible states for leitchunit->state */ #define LEITCH_STATE_IDLE 0 #define LEITCH_STATE_DATE 1 #define LEITCH_STATE_TIME0 2 #define LEITCH_STATE_TIME1 3 #define LEITCH_STATE_TIME2 4 #define LEITCH_STATE_TIME3 5 /* * Function prototypes */ static int leitch_start P((int, struct peer *)); static void leitch_shutdown P((int, struct peer *)); static void leitch_receive P((struct recvbuf *)); static int leitch_get_date P((char *, struct peer *)); static int leitch_get_time P((char *, struct peer *)); static void leitch_process P((struct peer *, char *, int)); static void leitch_poll P((int, struct peer *)); static void leitch_send P((int, const char *, struct peer *)); /* * Transfer vector */ struct refclock refclock_leitch = { leitch_start, /* start up driver */ leitch_shutdown, /* shut down driver */ leitch_poll, /* transmit poll message */ noentry, /* fudge control */ noentry, /* initialize driver */ noentry, /* buginfo */ NOFLAGS /* not used */ }; /* * leitch_start - open the Leitch devices and initialize data for processing */ static int leitch_start( int unit, struct peer *peer ) { register struct leitchunit *up; struct refclockproc *pp; int fd; char device[20]; /* * Open serial port. Use CLK line discipline, if available. */ (void)sprintf(device, DEVICE, unit); if (!(fd = refclock_open(device, SPEED232, LDISC_CLK))) return (0); /* * Allocate and initialize unit structure */ if (!(up = (struct leitchunit *) emalloc(sizeof(struct leitchunit)))) { (void) close(fd); return (0); } memset((char *)up, 0, sizeof(struct leitchunit)); pp = peer->procptr; pp->io.clock_recv = leitch_receive; pp->io.srcclock = (caddr_t)peer; pp->io.datalen = 0; pp->io.fd = fd; if (!io_addclock(&pp->io)) { (void) close(fd); free(up); return (0); } pp->unitptr = (caddr_t)up; /* * Initialize miscellaneous variables */ peer->precision = PRECISION; pp->clockdesc = DESCRIPTION; memcpy((char *)&pp->refid, REFID, 4); up->state = LEITCH_STATE_IDLE; return (1); } /* * leitch_shutdown - shut down the Leitch clock */ static void leitch_shutdown( int unit, struct peer *peer ) { register struct leitchunit *up; struct refclockproc *pp; pp = peer->procptr; up = (struct leitchunit *)pp->unitptr; io_closeclock(&pp->io); free(up); } /* * leitch_receive - receive data from the serial interface */ static void leitch_receive( struct recvbuf *rbufp ) { register struct leitchunit *up; struct refclockproc *pp; struct peer *peer; /* Use these variables to hold data until we decide its worth keeping */ char rd_lastcode[BMAX]; l_fp rd_tmp; u_short rd_lencode; /* * Initialize pointers and read the timecode and timestamp */ peer = (struct peer *)rbufp->recv_srcclock; pp = peer->procptr; up = (struct leitchunit *)pp->unitptr; rd_lencode = refclock_gtlin(rbufp, rd_lastcode, BMAX, &rd_tmp); #ifdef DEBUG if (debug) printf("leitch: leitchread %d %s\n", rd_lencode, rd_lastcode); #endif /* * We check the timecode format and decode its contents. */ switch(up->state) { case LEITCH_STATE_IDLE: /* Unexpected, discard */ printf("RFS: unexpected call to leitch_receive\n"); return; case LEITCH_STATE_DATE: /* Validate the date and store it into up->yearday */ if (!leitch_get_date(rd_lastcode, peer)) { refclock_report(peer, CEVNT_BADREPLY); up->state = LEITCH_STATE_IDLE; return; } /* Advance to next state, reading the time */ leitch_send(pp->io.fd,"T\r", peer); up->state = LEITCH_STATE_TIME0; break; case LEITCH_STATE_TIME0: /* Expect an empty line (only CR before the time) */ if (rd_lencode != 0) { refclock_report(peer, CEVNT_BADREPLY); up->state = LEITCH_STATE_IDLE; printf("RFS: didn't get CR before timestamp\n"); } up->state = LEITCH_STATE_TIME1; break; case LEITCH_STATE_TIME1: case LEITCH_STATE_TIME2: case LEITCH_STATE_TIME3: /* Validate time and store into up->TIME1 */ if (!leitch_get_time(rd_lastcode, peer)) { refclock_report(peer, CEVNT_BADREPLY); return; } up->tstamp = rd_tmp; /* Advance to next state */ if (up->state++ == LEITCH_STATE_TIME3) { /* Transfer time to pp struct and report to caller */ leitch_process(peer, rd_lastcode, rd_lencode); /* Return state machine to idle */ up->state = LEITCH_STATE_IDLE; } break; default: syslog(LOG_ERR, "leitch_receive: invalid state %d ", up->state); } } /* * Parse the date from the Leitch clock. * Returns 0 on error and -1 if successful. * Date is written to the *yearday parameter * NOTE: year value isn't really used. */ static int leitch_get_date( char *rd_lastcode, struct peer *peer ) { register struct leitchunit *up; struct refclockproc *pp; int year, month, day; pp = peer->procptr; up = (struct leitchunit *)pp->unitptr; if (sscanf(rd_lastcode, "%02d%02d%02d", &year, &month, &day) != 3) { refclock_report(peer, CEVNT_BADREPLY); return 0; } if (month < 1 || month > 12 || day < 1) { refclock_report(peer, CEVNT_BADTIME); return 0; } /* * Leitch only provides two digits of year. * Simple-mindedly we'll assume it is after 2000. */ if (year > 99) { refclock_report(peer, CEVNT_BADTIME); return 0; } year = year + 2000; /* * Determine day-of-year. * Only works up until 2099. */ if (year % 4) { int i; if (day > day1tab[month - 1]) { refclock_report(peer, CEVNT_BADTIME); return 0; } for (i = 0; i < month - 1; i++) day += day1tab[i]; } else { int i; if (day > day2tab[month - 1]) { refclock_report(peer, CEVNT_BADTIME); return 0; } for (i = 0; i < month - 1; i++) day += day2tab[i]; } /* Store the results into leitchunit struct */ up->year = year; up->yearday = day; return 1; } /* * Parse time from the leitch clock. * Returns 0 on error and 1 on success. */ static int leitch_get_time( char *rd_lastcode, struct peer *peer ) { register struct leitchunit *up; struct refclockproc *pp; int hour, minute, second; pp = peer->procptr; up = (struct leitchunit *)pp->unitptr; if (sscanf(rd_lastcode, "%02d%02d%02d", &hour, &minute, &second) != 3) { refclock_report(peer, CEVNT_BADREPLY); return 0; } if (hour > 23 || minute > 59 || second > 59) { refclock_report(peer, CEVNT_BADTIME); return 0; } up->hour = hour; up->minute = minute; up->second = second; return 1; } /* * Called when a complete time/date sequence has been read from leitch. * At this time, the last reported date/time and the timestamp are all * contained in fields of the leitchunit structure. */ static void leitch_process( struct peer *peer, char *rd_lastcode, int rd_lencode ) { register struct leitchunit *up; struct refclockproc *pp; pp = peer->procptr; up = (struct leitchunit *)pp->unitptr; #ifdef DEBUG if (debug) printf("leitch_process: called for %d/%03d %02d:%02d:%02d\n", up->year, up->yearday, up->hour, up->minute, up->second); #endif /* Copy last serial stream to pp, for logging purposes */ pp->lencode = rd_lencode; strcpy(pp->a_lastcode,rd_lastcode); /* Set reception time stamp */ pp->lastrec = up->tstamp; /* Copy time out of leitchunit, assume 0 milliseconds */ pp->msec = 0; pp->second = up->second; pp->minute = up->minute; pp->hour = up->hour; pp->day = up->yearday; pp->year = up->year; /* Leap second notification is enabled by fudge time2 */ pp->leap = LEAP_NOWARNING; if (pp->fudgetime2 > 0) pp->leap = LEAP_ADDSECOND; if (pp->fudgetime2 < 0) pp->leap = LEAP_DELSECOND; /* * 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; } refclock_receive(peer); /* If we get here - what we got from the clock is OK, so say so */ refclock_report(peer, CEVNT_NOMINAL); record_clock_stats(&peer->srcadr, pp->a_lastcode); } /* * leitch_poll - called by the transmit procedure */ static void leitch_poll( int unit, struct peer *peer ) { register struct leitchunit *up; struct refclockproc *pp; pp = peer->procptr; up = (struct leitchunit *)pp->unitptr; if (up->state != LEITCH_STATE_IDLE) { refclock_report(peer, CEVNT_TIMEOUT); up->state = LEITCH_STATE_IDLE; } else { leitch_send(pp->io.fd,"D\r", peer); up->state = LEITCH_STATE_DATE; } } /* * leitch_send(fd,cmd, peer) send command to Leitch. */ static void leitch_send( int fd, const char *cmd, struct peer *peer ) { if (write(fd, cmd, strlen(cmd)) == -1) { refclock_report(peer, CEVNT_FAULT); } } #else int refclock_leitch_bs; #endif /* REFCLOCK */