home *** CD-ROM | disk | FTP | other *** search
- Date: Thursday, 16 June 1988 16:55-MDT
- From: attcan!utzoo!dciem!nrcaer!cognos!bruce at uunet.uu.net (Dwayne Bruce)
- Re: N.B.S. Time Service
-
- Well kids, after reading about the NBS telephone time service, and
- having read about an expensive commerical system from Precision
- Standard Time, Inc., I just had to tell you about the system I cooked
- up.
-
- We've been keeping our VAXen in sync with a simple system that uses
- the AFSK time code produced by CHU. CHU is Canada's provider of
- brodcast time, and it has a signal that can be heard regularly all
- over the western hemisphere. During seconds 31..39 of each minute,
- they transmit a time code in 103-type AFSK. I built a modem, plugged
- it into an HF receiver, and wrote a piece of code to handle the
- output. The code is enclosed.
-
- If you want to know more about the hardware, consult the article I
- wrote in the April, 1988 edition of "The Canadian Amateur" (published
- by the Canadian Amateur Radio Federation).
-
- Anyway, here's the code. It runs under UNIX and VMS.
-
- -------------------------
- /* CHU Marcus Leech VE3MDL Aug 1987 */
- /* This program reads and understands the timecode format
- * produced by the CHU broadcast signal. This signal provides
- * an atomic time standard to most places in North and South
- * America on 3.330 7.335 and 14.670 MHZ, mode A3. During
- * seconds 31 thru 39 the time is broadcast using Bell 103 AFSK.
- * The time code looks like this:
- * 6.d|d.d|h.h|m.m|s.s|6.d|d.d|h.h|m.m|s.s
- * The time code is repeated twice for error detection. Each . represents
- * one nibble boundary. The nibbles get transmitted reversed, so you
- * have to flip them to make sense of the time code. Each nibble is a BCD
- * digit.
- * It takes one argument, the input device, and some optional flags:
- * chu [-adst] <device>
- * -
- * a adjust attempt to adjust the system clock to CHU time
- * d daemon run continuously as a daemon
- * s show show semi-raw timecode
- * t tune show chars that wouldn't get considered for time code
- *
- * When used as a system-time-adjuster, you need to set the system time
- * to within 2mins of the correct time. The program will drag the
- * clock into exact synchronism.
- */
- #include <stdio.h>
- #ifndef VMS
- #include <sys/types.h>
- #include <sgtty.h>
- #include <time.h>
- #else VMS
- #include <types.h>
- #include <time.h>
- #endif VMS
- /* Macros to fetch individual nibbles. */
- #define hinib(x) (((x) >> 4) & 0x0F)
- #define lonib(x) ((x) & 0x0F)
- /* Macro to restore correct ordering within a byte. */
- #define byte(h,l) (((l) << 4)|(h))
- #define ADJINT 27
- #define TWEAK 50 + 12 /* Fudge factor in centiseconds. */
- /* CHU code is skewed by 0.5 seconds. */
- #define iabs(x) ((x < 0) ? -x : x)
- char sample1[5]; /* The first time code. */
- char sample2[5]; /* The second (error checking) time code. */
- main (argc, argv)
- int argc;
- char **argv;
- {
- char c, *p;
- #ifndef VMS
- struct sgttyb sgb; /* For fiddling with tty line params. */
- #endif VMS
- int line; /* Fd for the tty line.*/
- int i;
- int adjcnt; /* Number of samples before we adjust.*/
- struct tm *localtime(), *ltp, *gmtime(); /* To break out the time. */
- time_t now, time();
- #ifdef VMS
- long t0[2], t1[2], t2[2];
- #endif VMS
- int amm, ass, mmss, diff;
- char *devstr;
- int adjust;
- int show;
- int tune;
- int daemon;
-
- setbuf (stdout, NULL);
- adjust = show = tune = 0;
- devstr = "/dev/null";
- for (i = 1; i < argc; i++)
- {
- if (argv[i][0] == '-')
- {
- p = &argv[i][1];
- while (*p)
- {
- switch (*p)
- {
- case 'a':
- adjust++;
- break;
- case 't':
- tune++;
- break;
- case 's':
- show++;
- break;
- case 'd':
- daemon++;
- break;
- default:
- fprintf (stdout, "unknown flag '%c'\n", *p);
- exit (1);
- }
- *p++;
- }
- }
- else
- {
- strcpy (devstr, argv[i]);
- }
- }
- line = open (devstr, 0);
- #ifndef VMS
- /* Set up 8-bit datapath, at 30CPS. */
- gtty (line, &sgb);
- sgb.sg_ispeed = sgb.sg_ospeed = B300;
- sgb.sg_flags |= RAW;
- stty (line, &sgb);
- #endif VMS
- adjcnt = 0;
- if (!daemon)
- {
- adjcnt = 19;
- }
- /* Read forever, waiting for the synchronizing BCD 6 digit to appear. */
- for (;;)
- {
- read (line, &c, 1);
- /* We have a syncronizing digit. Grab two samples and compare. */
- if (lonib(c) == 6)
- {
- /* Get first sample. */
- sample1[0] = byte(hinib(c),lonib(c));
- for (i = 1; i < 5; i++)
- {
- read (line, &c, 1);
- sample1[i] = byte(hinib(c),lonib(c));
- }
- /* Get second sample. */
- for (i = 0; i < 5; i++)
- {
- read (line, &c, 1);
- sample2[i] = byte(hinib(c),lonib(c));
- }
- /* If samples match, we have a valid time code. */
- if (compare (sample1, sample2, 5) == 0)
- {
- /* Show the code (if -s). The high-order nibble in the
- * first byte is the synch digit, so it gets masked out
- * for printing.
- */
- if (show)
- {
- fprintf (stdout, "TC: ");
- for (i = 0; i < 5; i++)
- {
- fprintf (stdout, "%02x", sample1[i]);
- }
- fprintf (stdout, "\n");
- }
- if (adjcnt++ >= ADJINT)
- {
- adjcnt = 0;
- /* Fetch UTC (GMT). */
- time (&now);
- ltp = gmtime (&now);
- /* Convert time code minutes and seconds into
- * binary.
- */
- amm = (hinib(sample1[3]) * 10) + lonib(sample1[3]);
- ass = (hinib(sample1[4]) * 10) + lonib(sample1[4]);
- /* Convert minutes and seconds portion of system time. */
- mmss = (ltp->tm_min * 60) + ltp->tm_sec;
- /* Compute the difference. */
- diff = ((amm * 60) + ass) - mmss;
- /* Adjust the system time. */
- now += (long)diff;
- if (iabs(diff) > 120)
- {
- fprintf (stdout, "%02d-%02d-%02d %02d:%02d:%02d ",
- ltp->tm_year, ltp->tm_mon + 1, ltp->tm_mday,
- ltp->tm_hour, ltp->tm_min, ltp->tm_sec);
- fprintf (stdout, "TERROR %d\n", diff);
- if (!daemon)
- {
- break;
- }
- continue;
- }
- /* Only do it if there IS a (reasonable) difference. */
- if ((diff != 0) && (adjust))
- {
- ltp = localtime (&now);
- fprintf (stdout, "%02d-%02d-%02d %02d:%02d:%02d ",
- ltp->tm_year, ltp->tm_mon + 1, ltp->tm_mday,
- ltp->tm_hour, ltp->tm_min, ltp->tm_sec);
- fprintf (stdout, "ADJUST %d\n", diff);
- /* What we'd REALLY like here is a system call of
- * the form: stime (*time, ticks)
- * that would allow you to set the system tick
- * counter to <ticks> in centiseconds. Too bad.
- *-->stime (&now, TWEAK);<--
- */
- #ifndef VMS
- #ifndef EUNICE
- stime (&now);
- #endif EUNICE
- #else VMS
- t1[0] = 100000 * ((diff * 100) + TWEAK);
- t1[1] = 0;
- if (diff < 0)
- {
- t1[1] = -1;
- }
- sys$gettim (t0);
- lib$addx (t0, t1, t2);
- sys$setime (t2);
- #endif VMS
- }
- ltp = localtime (&now);
- fprintf (stdout, "%02d-%02d-%02d %02d:%02d:%02d TVALID\n",
- ltp->tm_year, ltp->tm_mon + 1, ltp->tm_mday,
- ltp->tm_hour, ltp->tm_min, ltp->tm_sec);
- if (!daemon)
- {
- break;
- }
- }
- }
- }
- else if (tune)
- {
- fprintf (stdout, "FT: %c (%02x)\n", c, (unsigned)c);
- }
- }
- }
- /* Compare two byte-arrays (s1, s2) of length cnt. */
- compare (s1, s2, cnt)
- char *s1;
- char *s2;
- int cnt;
- {
- int i;
- for (i = 0; i < cnt; i++)
- {
- if (*s1++ != *s2++)
- {
- return (1);
- }
- }
- return (0);
- }
- #ifdef VMS
- struct tm *
- gmtime (tod)
- time_t *tod;
- {
- int hh;
- int mm;
- char s;
- int secdiff, sgn;
- time_t utc;
- char *p, *getenv ();
- p = getenv ("UTCDISP");
- sscanf (p, "%c %02d:%02d", &s, &hh, &mm);
- utc = *tod;
- secdiff = (hh * 3600) + (mm * 60);
- sgn = (s == '+') ? 1 : -1;
- secdiff *= sgn;
- utc += secdiff;
- return (localtime (&utc));
- }
- #endif VMS
-
-