]> git.ipfire.org Git - thirdparty/ntp.git/commitdiff
chu cleanup from Dave Mills
authorHarlan Stenn <stenn@ntp.org>
Sat, 22 Mar 2003 01:27:39 +0000 (20:27 -0500)
committerHarlan Stenn <stenn@ntp.org>
Sat, 22 Mar 2003 01:27:39 +0000 (20:27 -0500)
bk: 3e7bbc0b1CGoWdmC9EI32Lm1Aqm_CA

ntpd/refclock_chu.c

index 025b6335c5f2e8215c1f5ed10cbe592fd18189c7..4c69c2fa2ce4ebd55d28f19281f316f8779cf3e0 100644 (file)
  * change throughout the day and night.
  *
  * The driver receives, demodulates and decodes the radio signals when
- * connected to the audio codec of a Sun workstation running SunOS or
- * Solaris, and with a little help, other workstations with similar
- * codecs or sound cards. In this implementation, only one audio driver
- * and codec can be supported on a single machine.
+ * connected to the audio codec of supported workstation hardware and
+ * operating system. These include Solaris, SunOS, FreeBSD, NetBSD and
+ * Linux. In this implementation, only one audio driver and codec can be
+ * supported on a single machine.
  *
  * The driver can be compiled to use a Bell 103 compatible modem or
  * modem chip to receive the radio signal and demodulate the data.
 #define TUNE           .001    /* offset for narrow filter (kHz) */
 #define DWELL          5       /* minutes in a probe cycle */
 #define NCHAN          3       /* number of channels */
+#undef  NSTAGE
 #define NSTAGE         3       /* number of integrator stages */
 #endif /* ICOM */
 
 #define BURST          11      /* max characters per burst */
 #define MINCHAR                9       /* min characters per burst */
 #define MINDIST                28      /* min burst distance (of 40)  */
+#define MINBURST       4       /* min bursts in minute */
 #define MINSYNC                8       /* min sync distance (of 16) */
 #define MINSTAMP       20      /* min timestamps (of 60) */
+#define METRIC         50.     /* min channel metric */
 #define PANIC          (4 * 1440) /* panic restart */
 
 /*
  * Hex extension codes (>= 16)
  */
-#define HEX_MISS       16      /* miss */
-#define HEX_SOFT       17      /* soft error */
-#define HEX_HARD       18      /* hard error */
+#define HEX_MISS       16      /* miss */
+#define HEX_SOFT       17      /* soft error * */
+#define HEX_HARD       18      /* hard error */
 
 /*
  * Status bits (status)
 #define AFORMAT                0x0020  /* invalid format A data */
 #define DECODE         0x0040  /* invalid data decode */
 #define STAMP          0x0080  /* too few timestamps */
-#define INSECOND       0x0100  /* valid A frame */
-#define INYEAR         0x0200  /* valid B frame */
+#define AVALID         0x0100  /* valid A frame */
+#define BVALID         0x0200  /* valid B frame */
 #define INSYNC         0x0400  /* clock synchronized */
 
 /*
 #define TSPERR         0x08    /* insufficient data */
 
 #ifdef HAVE_AUDIO
+/*
+ * Maximum likelihood UART structure. There are eight of these
+ * corresponding to the number of phases.
+ */ 
 struct surv {
        double  shift[12];      /* mark register */
        double  es_max, es_min; /* max/min envelope signals */
@@ -278,10 +285,15 @@ struct surv {
 #endif /* HAVE_AUDIO */
 
 #ifdef ICOM
+/*
+ * CHU station structure. There are three of these corresponding to the
+ * three frequencies.
+ */
 struct xmtr {
-       int     integ[NSTAGE];  /* circular integrator */
+       double  integ[NSTAGE];  /* circular integrator */
+       double  metric;         /* integrator sum */
        int     iptr;           /* integrator pointer */
-       int     metric;         /* integrator sum */
+       int     probe;          /* dwells since last probe */
 };
 #endif /* ICOM */
 
@@ -297,13 +309,12 @@ struct chuunit {
        l_fp    charstamp;      /* character time as a l_fp */
        int     errflg;         /* error flags */
        int     status;         /* status bits */
-       char    ident[10];      /* transmitter frequency */
+       char    ident[5];       /* station ID and channel */
 #ifdef ICOM
        int     fd_icom;        /* ICOM file descriptor */
        int     chan;           /* data channel */
-       int     pchan;          /* probe channel */
        int     achan;          /* active channel */
-       int     dwell;          /* dwell minutes at current frequency */
+       int     dwell;          /* dwell cycle */
        struct xmtr xmtr[NCHAN]; /* station metric */
 #endif /* ICOM */
 
@@ -315,7 +326,6 @@ struct chuunit {
        int     ndx;            /* buffer start index */
        int     prevsec;        /* previous burst second */
        int     burdist;        /* burst distance */
-       int     mindist;        /* minimum distance */
        int     syndist;        /* sync distance */
        int     burstcnt;       /* format A bursts this minute */
 
@@ -377,7 +387,7 @@ static      void    chu_clear       P((struct peer *));
 static void    chu_a           P((struct peer *, int));
 static void    chu_b           P((struct peer *, int));
 static int     chu_dist        P((int, int));
-static int     chu_major       P((struct peer *));
+static double  chu_major       P((struct peer *));
 #ifdef HAVE_AUDIO
 static void    chu_uart        P((struct surv *, double));
 static void    chu_rf          P((struct peer *, double));
@@ -385,14 +395,14 @@ static    void    chu_gain        P((struct peer *));
 static void    chu_audio_receive P((struct recvbuf *rbufp));
 #endif /* HAVE_AUDIO */
 #ifdef ICOM
-static void    chu_newchan     P((struct peer *, int));
+static int     chu_newchan     P((struct peer *, double));
 #endif /* ICOM */
 static void    chu_serial_receive P((struct recvbuf *rbufp));
 
 /*
  * Global variables
  */
-static char hexchar[] = "0123456789abcdef_-=";
+static char hexchar[] = "0123456789abcdef_*=";
 
 #ifdef ICOM
 /*
@@ -532,10 +542,9 @@ chu_start(
                            temp);
        }
        if (up->fd_icom > 0) {
-               if (icom_freq(up->fd_icom, peer->ttl & 0x7f, qsy[1] +
-                   TUNE) != 0) {
+               if (chu_newchan(peer, 0) != 0) {
                        NLOG(NLOG_SYNCEVENT | NLOG_SYSEVENT)
-                           msyslog(LOG_ERR,
+                           msyslog(LOG_NOTICE,
                            "icom: radio not found");
                        up->errflg = CEVNT_FAULT;
                        close(up->fd_icom);
@@ -576,6 +585,7 @@ chu_shutdown(
        free(up);
 }
 
+
 /*
  * chu_receive - receive data from the audio or serial device
  */
@@ -610,8 +620,8 @@ chu_receive(
 #endif /* HAVE_AUDIO */
 }
 
-#ifdef HAVE_AUDIO
 
+#ifdef HAVE_AUDIO
 /*
  * chu_audio_receive - receive data from the audio device
  */
@@ -1061,7 +1071,8 @@ chu_b(
         * In a format B burst, a character is considered valid only if
         * the first occurrence matches the last occurrence. The burst
         * is considered valid only if all characters are valid; that
-        * is, only if the distance is 40. 
+        * is, only if the distance is 40. Note that once a valid frame
+        * has been found errors are ignored.
         */
        sprintf(tbuf, "chuB %04x %2d %2d ", up->status, nchar,
            -up->burdist);
@@ -1077,7 +1088,7 @@ chu_b(
                up->status |= BFRAME;
                return;
        }
-       up->status |= INYEAR;
+       up->status |= BVALID;
 
        /*
         * Convert the burst data to internal format. If this succeeds,
@@ -1229,7 +1240,7 @@ chu_a(
                up->decode[i][(up->cbuf[j] >> 4) & 0xf]++;
                i++;
        }
-       up->status |= INSECOND;
+       up->status |= AVALID;
        up->burstcnt++;
 }
 
@@ -1247,7 +1258,7 @@ chu_poll(
        struct chuunit *up;
        char    synchar, qual, leapchar;
        int     minset;
-       int     temp;
+       double  dtemp;
 
        pp = peer->procptr;
        up = (struct chuunit *)pp->unitptr;
@@ -1265,7 +1276,12 @@ chu_poll(
 
        /*
         * Process the last burst, if still in the burst buffer.
-        * Don't mess with anything if nothing has been heard.
+        * Don't mess with anything if nothing has been heard. If the
+        * minute contains a valid A frame and valid B frame, assume
+        * synchronized; however, believe the time only if within metric
+        * threshold. Note the quality indicator is only for
+        * diagnostics; the data are used only if in sync and above
+        * metric threshold.
         */
        chu_burst(peer);
        if (up->burstcnt == 0) {
@@ -1274,9 +1290,7 @@ chu_poll(
 #endif /* ICOM */
                return;
        }
-       temp = chu_major(peer);
-       if (up->status & INYEAR)
-               up->status |= INSYNC;
+       dtemp = chu_major(peer);
        qual = 0;
        if (up->status & (BFRAME | AFRAME))
                qual |= SYNERR;
@@ -1286,6 +1300,8 @@ chu_poll(
                qual |= DECERR;
        if (up->status & STAMP)
                qual |= TSPERR;
+       if (up->status & AVALID && up->status & BVALID)
+               up->status |= INSYNC;
        synchar = leapchar = ' ';
        if (!(up->status & INSYNC)) {
                pp->leap = LEAP_NOTINSYNC;
@@ -1302,24 +1318,22 @@ chu_poll(
 #ifdef HAVE_AUDIO
        if (up->fd_audio)
                sprintf(pp->a_lastcode,
-                   "%c%1X %04d %3d %02d:%02d:%02d %c%x %+d %d %d %s %d %d %d %d",
+                   "%c%1X %04d %3d %02d:%02d:%02d %c%x %+d %d %d %s %.0f %d",
                    synchar, qual, pp->year, pp->day, pp->hour,
                    pp->minute, pp->second, leapchar, up->dst, up->dut,
-                   minset, up->gain, up->ident, up->tai, up->burstcnt,
-                   up->mindist, temp);
+                   minset, up->gain, up->ident, dtemp, up->ntstamp);
        else
                sprintf(pp->a_lastcode,
-                   "%c%1X %04d %3d %02d:%02d:%02d %c%x %+d %d %s %d %d %d %d",
+                   "%c%1X %04d %3d %02d:%02d:%02d %c%x %+d %d %s %.0f %d",
                    synchar, qual, pp->year, pp->day, pp->hour,
                    pp->minute, pp->second, leapchar, up->dst, up->dut,
-                   minset, up->ident, up->tai, up->burstcnt,
-                   up->mindist, temp);
+                   minset, up->ident, dtemp, up->ntstamp);
 #else
        sprintf(pp->a_lastcode,
-           "%c%1X %04d %3d %02d:%02d:%02d %c%x %+d %d %s %d %d %d %d",
+           "%c%1X %04d %3d %02d:%02d:%02d %c%x %+d %d %s %.0f %d",
            synchar, qual, pp->year, pp->day, pp->hour, pp->minute,
            pp->second, leapchar, up->dst, up->dut, minset, up->ident,
-           up->tai, up->burstcnt, up->mindist, temp);
+           dtemp, up->ntstamp);
 #endif /* HAVE_AUDIO */
        pp->lencode = strlen(pp->a_lastcode);
 
@@ -1327,7 +1341,7 @@ chu_poll(
         * If timestamps have been stuffed, the timecode is ipso fatso
         * correct and can be selected to discipline the clock.
         */
-       if (temp > 0 && qual == 0) {
+       if (up->status & INSYNC && dtemp > METRIC) {
                refclock_receive(peer);
                record_clock_stats(&peer->srcadr, pp->a_lastcode);
        } else if (pp->sloppyclockflag & CLK_FLAG4) {
@@ -1339,7 +1353,7 @@ chu_poll(
                    pp->a_lastcode);
 #endif
 #ifdef ICOM
-       chu_newchan(peer, temp);
+       chu_newchan(peer, dtemp);
 #endif /* ICOM */
        chu_clear(peer);
        if (up->errflg)
@@ -1351,7 +1365,7 @@ chu_poll(
 /*
  * chu_major - majority decoder
  */
-static int
+static double
 chu_major(
        struct peer *peer       /* peer structure pointer */
        )
@@ -1361,6 +1375,7 @@ chu_major(
 
        u_char  code[11];       /* decoded timecode */
        l_fp    offset;         /* l_fp temps */
+       int     mindist;        /* minimum distance */
        int     val1, val2;     /* maximum distance */
        int     synchar;        /* stray cat */
        int     temp;
@@ -1378,14 +1393,14 @@ chu_major(
         * and the corresponding digit is the maximumn likelihood
         * candidate. If the distance is zero, assume a miss '_'; if the
         * distance is not more than half the total number of
-        * occurences, assume a soft error '-'; if two different digits
+        * occurences, assume a soft error '*'; if two different digits
         * with the same distance are found, assume a hard error '='.
         * These will later cause a format error when the timecode is
         * interpreted. The decoding distance is defined as the minimum
         * distance over the first nine digits. The tenth digit varies
         * over the seconds, so we don't count it.
         */
-       up->mindist = 16;
+       mindist = 16;
        for (i = 0; i < 9; i++) {
                val1 = val2 = 0;
                k = 0;
@@ -1405,19 +1420,18 @@ chu_major(
                        code[i] = HEX_SOFT;
                else
                        code[i] = k;
-               if (val1 < up->mindist)
-                       up->mindist = val1;
+               if (val1 < mindist)
+                       mindist = val1;
                code[i] = hexchar[code[i]];
        }
        code[i] = 0;
 
        /*
-        * A valid timecode requires at least three bursts and a
-        * decoding distance greater than half the total number of
-        * occurences. A valid timecode also requires at least 20 valid
-        * timestamps.
+        * A valid timecode requires a minimum distance at least half
+        * the total number of occurences. A valid timecode also
+        * requires at least 20 valid timestamps.
         */
-       if (up->burstcnt < 3 || up->mindist <= up->burstcnt)
+       if (up->burstcnt < MINBURST || mindist < up->burstcnt)
                up->status |= DECODE;
        if (up->ntstamp < MINSTAMP)
                up->status |= STAMP;
@@ -1448,7 +1462,7 @@ chu_major(
                refclock_process_offset(pp, offset, up->tstamp[i],
                    FUDGE + pp->fudgetime1);
        pp->lastref = up->timestamp;
-       return (i);
+       return (mindist * 100. / (2. * up->burstcnt));
 }
 
 
@@ -1471,9 +1485,8 @@ chu_clear(
         * Clear stuff for the minute.
         */
        up->ndx = up->prevsec = 0;
-       up->burstcnt = up->mindist = up->ntstamp = 0;
-       up->status &= INSYNC | INYEAR | INSECOND;
-       up->burstcnt = 0;
+       up->burstcnt = up->ntstamp = 0;
+       up->status &= INSYNC;
        for (i = 0; i < 20; i++) {
                for (j = 0; j < 16; j++)
                        up->decode[i][j] = 0;
@@ -1482,38 +1495,39 @@ chu_clear(
 
 #ifdef ICOM
 /*
- * chu_newchan - called once per minute find the best channel
+ * chu_newchan - called once per minute to find the best channel;
+ * returns zero on success, nonzero if ICOM error.
  */
-void
+int
 chu_newchan(
        struct peer *peer,
-       int     met
+       double  met
        )
 {
        struct chuunit *up;
        struct refclockproc *pp;
        struct xmtr *sp;
-       int     metric;
-       int     i, j;
        char    tbuf[80];       /* trace buffer */
+       int     rval;
+       double  metric;
+       int     i, j;
 
        pp = peer->procptr;
        up = (struct chuunit *)pp->unitptr;
 
        /*
-        * Before INSECOND is set we dwell two minutes on each channel
-        * in turn. The seconds are synchronized when the first burst
-        * has been successfully decoded. After that the channels are
-        * evaluated and the winner becomes the data channel. Every five
-        * minutes we switch to a probe channel which rotates over all
-        * three frequencies. The next time the channels are evaluated a
-        * new winner may result.
+        * The radio can be tuned to three channels: 0 (3330 kHz), 1
+        * (7335 kHz) and 2 (14670 kHz). There are five one-minute
+        * dwells in each cycle. During the first dwell the radio is
+        * tuned to one of three probe channels; during the remaining
+        * four dwells the radio is tuned to the data channel. The probe
+        * channel is selects as the least recently used. At the end of
+        * each dwell the channel metrics are measured and the highest
+        * one is selected as the data channel. 
         */
        if (up->fd_icom <= 0)
-               return;
+               return (0);
 
-       if (up->burstcnt == 0)
-               up->dwell = 0;
        sp = &up->xmtr[up->achan];
        sp->metric -= sp->integ[sp->iptr];
        sp->integ[sp->iptr] = met;
@@ -1522,6 +1536,9 @@ chu_newchan(
        metric = 0;
        j = 0;
        for (i = 0; i < NCHAN; i++) {
+               up->xmtr[i].probe++;
+               if (i == up->achan)
+                       up->xmtr[i].probe = 0;
                if (up->xmtr[i].metric < metric)
                        continue;
                metric = up->xmtr[i].metric;
@@ -1529,33 +1546,49 @@ chu_newchan(
        }
        if (j != up->chan && metric > 0) {
                up->chan = j;
-               if (pp->sloppyclockflag & CLK_FLAG4) {
-                       sprintf(tbuf, "chu: QSY to %.3f MHz metric %d",
-                           qsy[up->chan], metric);
+               sprintf(tbuf, "chu: QSY to %.3f MHz metric %.0f",
+                   qsy[up->chan], metric);
+               if (pp->sloppyclockflag & CLK_FLAG4)
                        record_clock_stats(&peer->srcadr, tbuf);
 #ifdef DEBUG
                if (debug)
                        printf("%s\n", tbuf);
 #endif
-               }
        }
+
+       /*
+        * Start the next dwell. We speed up the initial sync a little.
+        * If not in sync and no bursts were heard the previous dwell,
+        * restart the probe.
+        */
+       rval = 0;
+       if (up->burstcnt == 0 && !(up->status & INSYNC))
+               up->dwell = 0;
 #ifdef DEBUG
        if (debug)
                printf(
-                   "chu: at %ld dwell %d achan %d metric %d chan %d pchan %d\n",
+                   "chu: at %ld dwell %d achan %d metric %.0f chan %d\n",
                    current_time, up->dwell, up->achan, sp->metric,
-                   up->chan, up->pchan);
+                   up->chan);
 #endif
        if (up->dwell == 0) {
-               if (up->achan == up->pchan)
-                       up->pchan = (up->pchan + 1) % NCHAN;
-               icom_freq(up->fd_icom, peer->ttl & 0x7f,
-                   qsy[up->pchan] + TUNE);
-               up->achan = up->pchan;
-               up->pchan = (up->pchan + 1) % NCHAN;
+               rval = 0;
+               for (i = 0; i < NCHAN; i++) {
+                       if (up->xmtr[i].probe < rval)
+                               continue;
+                       rval = up->xmtr[i].probe;
+                       up->achan = i;
+               }
+               rval = icom_freq(up->fd_icom, peer->ttl & 0x7f,
+                   qsy[up->achan] + TUNE);
+#ifdef DEBUG
+               if (debug)
+                       printf("chu: at %ld probe channel %d\n",
+                   current_time, up->achan);
+#endif
        } else {
                if (up->achan != up->chan) {
-                       icom_freq(up->fd_icom, peer->ttl & 0x7f,
+                       rval = icom_freq(up->fd_icom, peer->ttl & 0x7f,
                            qsy[up->chan] + TUNE);
                        up->achan = up->chan;
                }
@@ -1563,6 +1596,7 @@ chu_newchan(
        sprintf(up->ident, "CHU%d", up->achan);
        memcpy(&peer->refid, up->ident, 4); 
        up->dwell = (up->dwell + 1) % DWELL;
+       return (rval);
 }
 #endif /* ICOM */