home *** CD-ROM | disk | FTP | other *** search
/ Source Code 1992 March / Source_Code_CD-ROM_Walnut_Creek_March_1992.iso / unix_c / sysadmin / chu.c < prev    next >
Encoding:
C/C++ Source or Header  |  1989-03-21  |  9.4 KB  |  291 lines

  1. Date: Thursday, 16 June 1988  16:55-MDT
  2. From: attcan!utzoo!dciem!nrcaer!cognos!bruce at uunet.uu.net (Dwayne Bruce)
  3. Re:   N.B.S. Time Service
  4.  
  5. Well kids, after reading about the NBS telephone time service, and
  6. having read about an expensive commerical system from Precision
  7. Standard Time, Inc., I just had to tell you about the system I cooked
  8. up.
  9.  
  10. We've been keeping our VAXen in sync with a simple system that uses
  11. the AFSK time code produced by CHU.  CHU is Canada's provider of
  12. brodcast time, and it has a signal that can be heard regularly all
  13. over the western hemisphere.  During seconds 31..39 of each minute,
  14. they transmit a time code in 103-type AFSK.  I built a modem, plugged
  15. it into an HF receiver, and wrote a piece of code to handle the
  16. output. The code is enclosed.
  17.  
  18. If you want to know more about the hardware, consult the article I
  19. wrote in the April, 1988 edition of "The Canadian Amateur" (published
  20. by the Canadian Amateur Radio Federation).
  21.  
  22. Anyway, here's the code.  It runs under UNIX and VMS.
  23.  
  24. -------------------------
  25. /* CHU      Marcus Leech  VE3MDL   Aug 1987 */
  26. /* This program reads and understands the timecode format
  27.  *  produced by the CHU broadcast signal.  This signal provides
  28.  *  an atomic time standard to most places in North and South
  29.  *  America on 3.330 7.335 and 14.670 MHZ, mode A3. During
  30.  *  seconds 31 thru 39 the time is broadcast using Bell 103 AFSK.
  31.  *  The time code looks like this:
  32.  *  6.d|d.d|h.h|m.m|s.s|6.d|d.d|h.h|m.m|s.s
  33.  *  The time code is repeated twice for error detection. Each . represents
  34.  *    one nibble boundary.  The nibbles get transmitted reversed, so you
  35.  *    have to flip them to make sense of the time code.  Each nibble is a BCD
  36.  *    digit.
  37.  * It takes one argument, the input device, and some optional flags:
  38.  *  chu [-adst] <device>
  39.  *  -
  40.  *  a adjust  attempt to adjust the system clock to CHU time
  41.  *  d daemon  run continuously as a daemon
  42.  *  s show    show semi-raw timecode
  43.  *  t tune    show chars that wouldn't get considered for time code
  44.  *
  45.  * When used as a system-time-adjuster, you need to set the system time
  46.  *  to within 2mins of the correct time. The program will drag the
  47.  *  clock into exact synchronism.
  48.  */
  49. #include <stdio.h>
  50. #ifndef VMS
  51. #include <sys/types.h>
  52. #include <sgtty.h>
  53. #include <time.h>
  54. #else VMS
  55. #include <types.h>
  56. #include <time.h>
  57. #endif VMS
  58. /* Macros to fetch individual nibbles. */
  59. #define hinib(x) (((x) >> 4) & 0x0F)
  60. #define lonib(x) ((x) & 0x0F)
  61. /* Macro to restore correct ordering within a byte. */
  62. #define byte(h,l) (((l) << 4)|(h))
  63. #define ADJINT 27
  64. #define TWEAK 50 + 12     /* Fudge factor in centiseconds. */
  65.                           /* CHU code is skewed by 0.5 seconds. */
  66. #define iabs(x) ((x < 0) ? -x : x)
  67. char sample1[5];  /* The first time code. */
  68. char sample2[5];  /* The second (error checking) time code. */
  69. main (argc, argv)
  70. int argc;
  71. char **argv;
  72. {
  73.     char c, *p;
  74. #ifndef VMS
  75.     struct sgttyb sgb;            /* For fiddling with tty line params. */
  76. #endif VMS
  77.     int line;                     /* Fd for the tty line.*/
  78.     int i;
  79.     int adjcnt;                     /* Number of samples before we adjust.*/
  80.     struct tm *localtime(), *ltp, *gmtime();  /* To break out the time. */
  81.     time_t now, time();
  82. #ifdef VMS
  83.     long t0[2], t1[2], t2[2];
  84. #endif VMS
  85.     int amm, ass, mmss, diff;
  86.     char *devstr;
  87.     int adjust;
  88.     int show;
  89.     int tune;
  90.     int daemon;
  91.  
  92.     setbuf (stdout, NULL);
  93.     adjust = show = tune = 0;
  94.     devstr = "/dev/null";
  95.     for (i = 1; i < argc; i++)
  96.     {
  97.         if (argv[i][0] == '-')
  98.         {
  99.             p = &argv[i][1];
  100.             while (*p)
  101.             {
  102.                 switch (*p)
  103.                 {
  104.                 case 'a':
  105.                     adjust++;
  106.                     break;
  107.                 case 't':
  108.                     tune++;
  109.                     break;
  110.                 case 's':
  111.                     show++;
  112.                     break;
  113.                 case 'd':
  114.                     daemon++;
  115.                     break;
  116.                 default:
  117.                     fprintf (stdout, "unknown flag '%c'\n", *p);
  118.                     exit (1);
  119.                 }
  120.                 *p++;
  121.             }
  122.         }
  123.         else
  124.         {
  125.             strcpy (devstr, argv[i]);
  126.         }
  127.     }
  128.     line = open (devstr, 0);
  129. #ifndef VMS
  130.     /* Set up 8-bit datapath, at 30CPS. */
  131.     gtty (line, &sgb);
  132.     sgb.sg_ispeed = sgb.sg_ospeed = B300;
  133.     sgb.sg_flags |= RAW;
  134.     stty (line, &sgb);
  135. #endif VMS
  136.     adjcnt = 0;
  137.     if (!daemon)
  138.     {
  139.         adjcnt = 19;
  140.     }
  141.     /* Read forever, waiting for the synchronizing BCD 6 digit to appear. */
  142.     for (;;)
  143.     {
  144.         read (line, &c, 1);
  145.         /* We have a syncronizing digit. Grab two samples and compare. */
  146.         if (lonib(c) == 6)
  147.         {
  148.             /* Get first sample. */
  149.             sample1[0] = byte(hinib(c),lonib(c));
  150.             for (i = 1; i < 5; i++)
  151.             {
  152.                 read (line, &c, 1);
  153.                 sample1[i] = byte(hinib(c),lonib(c));
  154.             }
  155.             /* Get second sample. */
  156.             for (i = 0; i < 5; i++)
  157.             {
  158.                 read (line, &c, 1);
  159.                 sample2[i] = byte(hinib(c),lonib(c));
  160.             }
  161.             /* If samples match, we have a valid time code. */
  162.             if (compare (sample1, sample2, 5) == 0)
  163.             {
  164.                 /* Show the code (if -s).  The high-order nibble in the
  165.                  *  first byte is the synch digit, so it gets masked out
  166.                  *  for printing.
  167.                  */
  168.                 if (show)
  169.                 {
  170.                     fprintf (stdout, "TC: ");
  171.                     for (i = 0; i < 5; i++)
  172.                     {
  173.                         fprintf (stdout, "%02x", sample1[i]);
  174.                     }
  175.                     fprintf (stdout, "\n");
  176.                 }
  177.                 if (adjcnt++ >= ADJINT)
  178.                 {
  179.                     adjcnt = 0;
  180.                     /* Fetch UTC (GMT). */
  181.                     time (&now);
  182.                     ltp = gmtime (&now);
  183.                     /* Convert time code minutes and seconds into
  184.                      *   binary.
  185.                      */
  186.                     amm = (hinib(sample1[3]) * 10) + lonib(sample1[3]);
  187.                     ass = (hinib(sample1[4]) * 10) + lonib(sample1[4]);
  188.                     /* Convert minutes and seconds portion of system time. */
  189.                     mmss = (ltp->tm_min * 60) + ltp->tm_sec;
  190.                     /* Compute the difference. */
  191.                     diff = ((amm * 60) + ass) - mmss;
  192.                     /* Adjust the system time. */
  193.                     now += (long)diff;
  194.                     if (iabs(diff) > 120)
  195.                     {
  196.                         fprintf (stdout, "%02d-%02d-%02d %02d:%02d:%02d ",
  197.                             ltp->tm_year, ltp->tm_mon + 1, ltp->tm_mday,
  198.                             ltp->tm_hour, ltp->tm_min, ltp->tm_sec);
  199.                         fprintf (stdout, "TERROR %d\n", diff);
  200.                         if (!daemon)
  201.                         {
  202.                             break;
  203.                         }
  204.                         continue;
  205.                     }
  206.                     /* Only do it if there IS a (reasonable) difference. */
  207.                     if ((diff != 0) && (adjust))
  208.                     {
  209.                         ltp = localtime (&now);
  210.                         fprintf (stdout, "%02d-%02d-%02d %02d:%02d:%02d ",
  211.                             ltp->tm_year, ltp->tm_mon + 1, ltp->tm_mday,
  212.                             ltp->tm_hour, ltp->tm_min, ltp->tm_sec);
  213.                         fprintf (stdout, "ADJUST %d\n", diff);
  214.                         /* What we'd REALLY like here is a system call of
  215.                          * the form:   stime (*time, ticks)
  216.                          * that would allow you to set the system tick
  217.                          * counter to <ticks> in centiseconds.  Too bad.
  218.                          *-->stime (&now, TWEAK);<--
  219.                          */
  220. #ifndef VMS
  221. #ifndef EUNICE
  222.                         stime (&now);
  223. #endif EUNICE
  224. #else VMS
  225.                         t1[0] = 100000 * ((diff * 100) + TWEAK);
  226.                         t1[1] = 0;
  227.                         if (diff < 0)
  228.                         {
  229.                             t1[1] = -1;
  230.                         }
  231.                         sys$gettim (t0);
  232.                         lib$addx (t0, t1, t2);
  233.                         sys$setime (t2);
  234. #endif VMS
  235.                     }
  236.                     ltp = localtime (&now);
  237.                     fprintf (stdout, "%02d-%02d-%02d %02d:%02d:%02d TVALID\n",
  238.                         ltp->tm_year, ltp->tm_mon + 1, ltp->tm_mday,
  239.                         ltp->tm_hour, ltp->tm_min, ltp->tm_sec);
  240.                     if (!daemon)
  241.                     {
  242.                         break;
  243.                     }
  244.                 }
  245.             }
  246.         }
  247.         else if (tune)
  248.         {
  249.                 fprintf (stdout, "FT: %c (%02x)\n", c, (unsigned)c);
  250.         }
  251.     }
  252. }
  253. /* Compare two byte-arrays (s1, s2) of length cnt. */
  254. compare (s1, s2, cnt)
  255. char *s1;
  256. char *s2;
  257. int cnt;
  258. {
  259.     int i;
  260.     for (i = 0; i < cnt; i++)
  261.     {
  262.         if (*s1++ != *s2++)
  263.         {
  264.             return (1);
  265.         }
  266.     }
  267.     return (0);
  268. }
  269. #ifdef VMS
  270. struct tm *
  271. gmtime (tod)
  272. time_t *tod;
  273. {
  274.     int hh;
  275.     int mm;
  276.     char s;
  277.     int secdiff, sgn;
  278.     time_t utc;
  279.     char *p, *getenv ();
  280.     p = getenv ("UTCDISP");
  281.     sscanf (p, "%c %02d:%02d", &s, &hh, &mm);
  282.     utc = *tod;
  283.     secdiff = (hh * 3600) + (mm * 60);
  284.     sgn = (s == '+') ? 1 : -1;
  285.     secdiff *= sgn;
  286.     utc += secdiff;
  287.     return (localtime (&utc));
  288. }
  289. #endif VMS
  290.  
  291.