Annotation of embedaddon/ntp/ntpd/refclock_ulink.c, revision 1.1.1.1

1.1       misho       1: /*
                      2:  * refclock_ulink - clock driver for Ultralink  WWVB receiver
                      3:  */
                      4: 
                      5: /***********************************************************************
                      6:  *                                                                     *
                      7:  * Copyright (c) David L. Mills 1992-1998                              *
                      8:  *                                                                     *
                      9:  * Permission to use, copy, modify, and distribute this software and   *
                     10:  * its documentation for any purpose and without fee is hereby         *
                     11:  * granted, provided that the above copyright notice appears in all    *
                     12:  * copies and that both the copyright notice and this permission       *
                     13:  * notice appear in supporting documentation, and that the name        *
                     14:  * University of Delaware not be used in advertising or publicity      *
                     15:  * pertaining to distribution of the software without specific,        *
                     16:  * written prior permission. The University of Delaware makes no       *
                     17:  * representations about the suitability this software for any         *
                     18:  * purpose. It is provided "as is" without express or implied          *
                     19:  * warranty.                                                           *
                     20:  **********************************************************************/
                     21: 
                     22: #ifdef HAVE_CONFIG_H
                     23: #include <config.h>
                     24: #endif
                     25: 
                     26: #if defined(REFCLOCK) && defined(CLOCK_ULINK)
                     27: 
                     28: #include <stdio.h>
                     29: #include <ctype.h>
                     30: 
                     31: #include "ntpd.h"
                     32: #include "ntp_io.h"
                     33: #include "ntp_refclock.h"
                     34: #include "ntp_stdlib.h"
                     35: 
                     36: /* This driver supports ultralink Model 320,325,330,331,332 WWVB radios
                     37:  *
                     38:  * this driver was based on the refclock_wwvb.c driver
                     39:  * in the ntp distribution.
                     40:  *
                     41:  * Fudge Factors
                     42:  *
                     43:  * fudge flag1 0 don't poll clock
                     44:  *             1 send poll character
                     45:  *
                     46:  * revision history:
                     47:  *             99/9/09 j.c.lang        original edit's
                     48:  *             99/9/11 j.c.lang        changed timecode parse to 
                     49:  *                                      match what the radio actually
                     50:  *                                      sends. 
                     51:  *              99/10/11 j.c.lang       added support for continous
                     52:  *                                      time code mode (dipsw2)
                     53:  *             99/11/26 j.c.lang       added support for 320 decoder
                     54:  *                                      (taken from Dave Strout's
                     55:  *                                      Model 320 driver)
                     56:  *             99/11/29 j.c.lang       added fudge flag 1 to control
                     57:  *                                     clock polling
                     58:  *             99/12/15 j.c.lang       fixed 320 quality flag
                     59:  *             01/02/21 s.l.smith      fixed 33x quality flag
                     60:  *                                     added more debugging stuff
                     61:  *                                     updated 33x time code explanation
                     62:  *             04/01/23 frank migge    added support for 325 decoder
                     63:  *                                      (tested with ULM325.F)
                     64:  *
                     65:  * Questions, bugs, ideas send to:
                     66:  *     Joseph C. Lang
                     67:  *     tcnojl1@earthlink.net
                     68:  *
                     69:  *     Dave Strout
                     70:  *     dstrout@linuxfoundry.com
                     71:  *
                     72:  *      Frank Migge
                     73:  *      frank.migge@oracle.com
                     74:  *
                     75:  *
                     76:  * on the Ultralink model 33X decoder Dip switch 2 controls
                     77:  * polled or continous timecode 
                     78:  * set fudge flag1 if using polled (needed for model 320 and 325)
                     79:  * dont set fudge flag1 if dip switch 2 is set on model 33x decoder
                     80: */
                     81: 
                     82: 
                     83: /*
                     84:  * Interface definitions
                     85:  */
                     86: #define        DEVICE          "/dev/wwvb%d" /* device name and unit */
                     87: #define        SPEED232        B9600   /* uart speed (9600 baud) */
                     88: #define        PRECISION       (-10)   /* precision assumed (about 10 ms) */
                     89: #define        REFID           "WWVB"  /* reference ID */
                     90: #define        DESCRIPTION     "Ultralink WWVB Receiver" /* WRU */
                     91: 
                     92: #define        LEN33X          32      /* timecode length Model 33X and 325 */
                     93: #define LEN320         24      /* timecode length Model 320 */
                     94: 
                     95: #define        SIGLCHAR33x     'S'     /* signal strength identifier char 325 */
                     96: #define        SIGLCHAR325     'R'     /* signal strength identifier char 33x */
                     97: 
                     98: /*
                     99:  *  unit control structure
                    100:  */
                    101: struct ulinkunit {
                    102:        u_char  tcswitch;       /* timecode switch */
                    103:        l_fp    laststamp;      /* last receive timestamp */
                    104: };
                    105: 
                    106: /*
                    107:  * Function prototypes
                    108:  */
                    109: static int     ulink_start     (int, struct peer *);
                    110: static void    ulink_shutdown  (int, struct peer *);
                    111: static void    ulink_receive   (struct recvbuf *);
                    112: static void    ulink_poll      (int, struct peer *);
                    113: 
                    114: /*
                    115:  * Transfer vector
                    116:  */
                    117: struct refclock refclock_ulink = {
                    118:        ulink_start,            /* start up driver */
                    119:        ulink_shutdown,         /* shut down driver */
                    120:        ulink_poll,             /* transmit poll message */
                    121:        noentry,                /* not used  */
                    122:        noentry,                /* not used  */
                    123:        noentry,                /* not used  */
                    124:        NOFLAGS
                    125: };
                    126: 
                    127: 
                    128: /*
                    129:  * ulink_start - open the devices and initialize data for processing
                    130:  */
                    131: static int
                    132: ulink_start(
                    133:        int unit,
                    134:        struct peer *peer
                    135:        )
                    136: {
                    137:        register struct ulinkunit *up;
                    138:        struct refclockproc *pp;
                    139:        int fd;
                    140:        char device[20];
                    141: 
                    142:        /*
                    143:         * Open serial port. Use CLK line discipline, if available.
                    144:         */
                    145:        (void)sprintf(device, DEVICE, unit);
                    146:        if (!(fd = refclock_open(device, SPEED232, LDISC_CLK)))
                    147:                return (0);
                    148: 
                    149:        /*
                    150:         * Allocate and initialize unit structure
                    151:         */
                    152:        if (!(up = (struct ulinkunit *)
                    153:              emalloc(sizeof(struct ulinkunit)))) {
                    154:                (void) close(fd);
                    155:                return (0);
                    156:        }
                    157:        memset((char *)up, 0, sizeof(struct ulinkunit));
                    158:        pp = peer->procptr;
                    159:        pp->unitptr = (caddr_t)up;
                    160:        pp->io.clock_recv = ulink_receive;
                    161:        pp->io.srcclock = (caddr_t)peer;
                    162:        pp->io.datalen = 0;
                    163:        pp->io.fd = fd;
                    164:        if (!io_addclock(&pp->io)) {
                    165:                (void) close(fd);
                    166:                free(up);
                    167:                return (0);
                    168:        }
                    169: 
                    170:        /*
                    171:         * Initialize miscellaneous variables
                    172:         */
                    173:        peer->precision = PRECISION;
                    174:        peer->burst = NSTAGE;
                    175:        pp->clockdesc = DESCRIPTION;
                    176:        memcpy((char *)&pp->refid, REFID, 4);
                    177:        return (1);
                    178: }
                    179: 
                    180: 
                    181: /*
                    182:  * ulink_shutdown - shut down the clock
                    183:  */
                    184: static void
                    185: ulink_shutdown(
                    186:        int unit,
                    187:        struct peer *peer
                    188:        )
                    189: {
                    190:        register struct ulinkunit *up;
                    191:        struct refclockproc *pp;
                    192: 
                    193:        pp = peer->procptr;
                    194:        up = (struct ulinkunit *)pp->unitptr;
                    195:        io_closeclock(&pp->io);
                    196:        free(up);
                    197: }
                    198: 
                    199: 
                    200: /*
                    201:  * ulink_receive - receive data from the serial interface
                    202:  */
                    203: static void
                    204: ulink_receive(
                    205:        struct recvbuf *rbufp
                    206:        )
                    207: {
                    208:        struct ulinkunit *up;
                    209:        struct refclockproc *pp;
                    210:        struct peer *peer;
                    211: 
                    212:        l_fp    trtmp;          /* arrival timestamp */
                    213:        int     quality;        /* quality indicator */
                    214:        int     temp;           /* int temp */
                    215:        char    syncchar;       /* synchronization indicator */
                    216:        char    leapchar;       /* leap indicator */
                    217:        char    modechar;       /* model 320 mode flag */
                    218:         char   siglchar;       /* model difference between 33x/325 */
                    219:        char    char_quality[2];        /* temp quality flag */
                    220: 
                    221:        /*
                    222:         * Initialize pointers and read the timecode and timestamp
                    223:         */
                    224:        peer = (struct peer *)rbufp->recv_srcclock;
                    225:        pp = peer->procptr;
                    226:        up = (struct ulinkunit *)pp->unitptr;
                    227:        temp = refclock_gtlin(rbufp, pp->a_lastcode, BMAX, &trtmp);
                    228: 
                    229:        /*
                    230:         * Note we get a buffer and timestamp for both a <cr> and <lf>,
                    231:         * but only the <cr> timestamp is retained. 
                    232:         */
                    233:        if (temp == 0) {
                    234:                if (up->tcswitch == 0) {
                    235:                        up->tcswitch = 1;
                    236:                        up->laststamp = trtmp;
                    237:                } else
                    238:                    up->tcswitch = 0;
                    239:                return;
                    240:        }
                    241:        pp->lencode = temp;
                    242:        pp->lastrec = up->laststamp;
                    243:        up->laststamp = trtmp;
                    244:        up->tcswitch = 1;
                    245: #ifdef DEBUG
                    246:        if (debug)
                    247:                printf("ulink: timecode %d %s\n", pp->lencode,
                    248:                    pp->a_lastcode);
                    249: #endif
                    250: 
                    251:        /*
                    252:         * We get down to business, check the timecode format and decode
                    253:         * its contents. If the timecode has invalid length or is not in
                    254:         * proper format, we declare bad format and exit.
                    255:         */
                    256:        syncchar = leapchar = modechar = siglchar = ' ';
                    257:        switch (pp->lencode ) {
                    258:                case LEN33X:
                    259: 
                    260:                /*
                    261:                  * First we check if the format is 33x or 325:
                    262:                 *   <CR><LF>S9+D 00 YYYY+DDDUTCS HH:MM:SSL+5 (33x)
                    263:                 *   <CR><LF>R5_1C00LYYYY+DDDUTCS HH:MM:SSL+5 (325)
                    264:                 * simply by comparing if the signal level is 'S' or 'R'
                    265:                  */
                    266: 
                    267:                  if (sscanf(pp->a_lastcode, "%c%*31c",
                    268:                             &siglchar) == 1) {
                    269: 
                    270:                     if(siglchar == SIGLCHAR325) {
                    271: 
                    272:                           /*
                    273:                    * decode for a Model 325 decoder.
                    274:                    * Timecode format from January 23, 2004 datasheet is:
                    275:                     *
                    276:                    *   <CR><LF>R5_1C00LYYYY+DDDUTCS HH:MM:SSL+5
                    277:                     *
                    278:                    *   R      WWVB decodersignal readability R1 - R5
                    279:                    *   5      R1 is unreadable, R5 is best
                    280:                    *   space  a space (0x20)
                    281:                    *   1      Data bit 0, 1, M (pos mark), or ? (unknown).
                    282:                    *   C      Reception from either (C)olorado or (H)awaii 
                    283:                    *   00     Hours since last good WWVB frame sync. Will 
                    284:                    *          be 00-99
                    285:                    *   space  Space char (0x20) or (0xa5) if locked to wwvb
                    286:                    *   YYYY   Current year, 2000-2099
                    287:                    *   +      Leap year indicator. '+' if a leap year,
                    288:                    *          a space (0x20) if not.
                    289:                    *   DDD    Day of year, 000 - 365.
                    290:                    *   UTC    Timezone (always 'UTC').
                    291:                    *   S      Daylight savings indicator
                    292:                    *             S - standard time (STD) in effect
                    293:                    *             O - during STD to DST day 0000-2400
                    294:                    *             D - daylight savings time (DST) in effect
                    295:                    *             I - during DST to STD day 0000-2400
                    296:                    *   space  Space character (0x20)
                    297:                    *   HH     Hours 00-23
                    298:                    *   :      This is the REAL in sync indicator (: = insync)  
                    299:                    *   MM     Minutes 00-59
                    300:                    *   :      : = in sync ? = NOT in sync
                    301:                    *   SS     Seconds 00-59
                    302:                    *   L      Leap second flag. Changes from space (0x20)
                    303:                    *          to 'I' or 'D' during month preceding leap
                    304:                    *          second adjustment. (I)nsert or (D)elete
                    305:                    *   +5     UT1 correction (sign + digit ))
                    306:                    */
                    307: 
                    308:                       if (sscanf(pp->a_lastcode, 
                    309:                           "%*2c %*2c%2c%*c%4d%*c%3d%*4c %2d%c%2d:%2d%c%*2c",
                    310:                          char_quality, &pp->year, &pp->day, 
                    311:                           &pp->hour, &syncchar, &pp->minute, &pp->second, 
                    312:                           &leapchar) == 8) { 
                    313:                
                    314:                          if (char_quality[0] == '0') {
                    315:                                quality = 0;
                    316:                          } else if (char_quality[0] == '0') {
                    317:                                quality = (char_quality[1] & 0x0f);
                    318:                          } else  {
                    319:                                quality = 99;
                    320:                          }
                    321: 
                    322:                          if (leapchar == 'I' ) leapchar = '+';
                    323:                          if (leapchar == 'D' ) leapchar = '-';
                    324: 
                    325:                          /*
                    326:                          #ifdef DEBUG
                    327:                          if (debug) {
                    328:                             printf("ulink: char_quality %c %c\n", 
                    329:                                     char_quality[0], char_quality[1]);
                    330:                             printf("ulink: quality %d\n", quality);
                    331:                             printf("ulink: syncchar %x\n", syncchar);
                    332:                             printf("ulink: leapchar %x\n", leapchar);
                    333:                           }
                    334:                           #endif
                    335:                           */
                    336: 
                    337:                        }
                    338:                
                    339:                     } 
                    340:                     if(siglchar == SIGLCHAR33x) {
                    341:                 
                    342:                   /*
                    343:                    * We got a Model 33X decoder.
                    344:                    * Timecode format from January 29, 2001 datasheet is:
                    345:                    *   <CR><LF>S9+D 00 YYYY+DDDUTCS HH:MM:SSL+5
                    346:                    *   S      WWVB decoder sync indicator. S for in-sync(?)
                    347:                    *          or N for noisy signal.
                    348:                    *   9+     RF signal level in S-units, 0-9 followed by
                    349:                    *          a space (0x20). The space turns to '+' if the
                    350:                    *          level is over 9.
                    351:                    *   D      Data bit 0, 1, 2 (position mark), or
                    352:                    *          3 (unknown).
                    353:                    *   space  Space character (0x20)
                    354:                    *   00     Hours since last good WWVB frame sync. Will 
                    355:                    *          be 00-23 hrs, or '1d' to '7d'. Will be 'Lk'
                    356:                     *          if currently in sync. 
                    357:                    *   space  Space character (0x20)
                    358:                    *   YYYY   Current year, 1990-2089
                    359:                    *   +      Leap year indicator. '+' if a leap year,
                    360:                    *          a space (0x20) if not.
                    361:                    *   DDD    Day of year, 001 - 366.
                    362:                    *   UTC    Timezone (always 'UTC').
                    363:                    *   S      Daylight savings indicator
                    364:                    *             S - standard time (STD) in effect
                    365:                    *             O - during STD to DST day 0000-2400
                    366:                    *             D - daylight savings time (DST) in effect
                    367:                    *             I - during DST to STD day 0000-2400
                    368:                    *   space  Space character (0x20)
                    369:                    *   HH     Hours 00-23
                    370:                    *   :      This is the REAL in sync indicator (: = insync)  
                    371:                    *   MM     Minutes 00-59
                    372:                    *   :      : = in sync ? = NOT in sync
                    373:                    *   SS     Seconds 00-59
                    374:                    *   L      Leap second flag. Changes from space (0x20)
                    375:                    *          to '+' or '-' during month preceding leap
                    376:                    *          second adjustment.
                    377:                    *   +5     UT1 correction (sign + digit ))
                    378:                    */
                    379: 
                    380:                       if (sscanf(pp->a_lastcode, 
                    381:                            "%*4c %2c %4d%*c%3d%*4c %2d%c%2d:%2d%c%*2c",
                    382:                           char_quality, &pp->year, &pp->day, 
                    383:                            &pp->hour, &syncchar, &pp->minute, &pp->second, 
                    384:                            &leapchar) == 8) { 
                    385:                
                    386:                           if (char_quality[0] == 'L') {
                    387:                                quality = 0;
                    388:                           } else if (char_quality[0] == '0') {
                    389:                                quality = (char_quality[1] & 0x0f);
                    390:                           } else  {
                    391:                                quality = 99;
                    392:                           }
                    393:        
                    394:                            /*
                    395:                            #ifdef DEBUG
                    396:                           if (debug) {
                    397:                                printf("ulink: char_quality %c %c\n", 
                    398:                                         char_quality[0], char_quality[1]);
                    399:                                printf("ulink: quality %d\n", quality);
                    400:                                printf("ulink: syncchar %x\n", syncchar);
                    401:                                printf("ulink: leapchar %x\n", leapchar);
                    402:                            }
                    403:                            #endif
                    404:                            */
                    405: 
                    406:                        }
                    407:                     }
                    408:                    break;
                    409:                }
                    410: 
                    411:                case LEN320:
                    412: 
                    413:                /*
                    414:                 * Model 320 Decoder
                    415:                 * The timecode format is:
                    416:                 *
                    417:                 *  <cr><lf>SQRYYYYDDD+HH:MM:SS.mmLT<cr>
                    418:                 *
                    419:                 * where:
                    420:                 *
                    421:                 * S = 'S' -- sync'd in last hour,
                    422:                 *     '0'-'9' - hours x 10 since last update,
                    423:                 *     '?' -- not in sync
                    424:                 * Q = Number of correlating time-frames, from 0 to 5
                    425:                 * R = 'R' -- reception in progress,
                    426:                 *     'N' -- Noisy reception,
                    427:                 *     ' ' -- standby mode
                    428:                 * YYYY = year from 1990 to 2089
                    429:                 * DDD = current day from 1 to 366
                    430:                 * + = '+' if current year is a leap year, else ' '
                    431:                 * HH = UTC hour 0 to 23
                    432:                 * MM = Minutes of current hour from 0 to 59
                    433:                 * SS = Seconds of current minute from 0 to 59
                    434:                 * mm = 10's milliseconds of the current second from 00 to 99
                    435:                 * L  = Leap second pending at end of month
                    436:                 *     'I' = insert, 'D'= delete
                    437:                 * T  = DST <-> STD transition indicators
                    438:                 *
                    439:                 */
                    440: 
                    441:                if (sscanf(pp->a_lastcode, "%c%1d%c%4d%3d%*c%2d:%2d:%2d.%2ld%c",
                    442:                       &syncchar, &quality, &modechar, &pp->year, &pp->day,
                    443:                       &pp->hour, &pp->minute, &pp->second,
                    444:                        &pp->nsec, &leapchar) == 10) {
                    445:                pp->nsec *= 10000000; /* M320 returns 10's of msecs */
                    446:                if (leapchar == 'I' ) leapchar = '+';
                    447:                if (leapchar == 'D' ) leapchar = '-';
                    448:                if (syncchar != '?' ) syncchar = ':';
                    449: 
                    450:                break;
                    451:                }
                    452: 
                    453:                default:
                    454:                refclock_report(peer, CEVNT_BADREPLY);
                    455:                return;
                    456:        }
                    457: 
                    458:        /*
                    459:         * Decode quality indicator
                    460:         * For the 325 & 33x series, the lower the number the "better" 
                    461:         * the time is. I used the dispersion as the measure of time 
                    462:         * quality. The quality indicator in the 320 is the number of 
                    463:         * correlating time frames (the more the better)
                    464:         */
                    465: 
                    466:        /* 
                    467:         * The spec sheet for the 325 & 33x series states the clock will
                    468:         * maintain +/-0.002 seconds accuracy when locked to WWVB. This 
                    469:         * is indicated by 'Lk' in the quality portion of the incoming 
                    470:         * string. When not in lock, a drift of +/-0.015 seconds should 
                    471:         * be allowed for.
                    472:         * With the quality indicator decoding scheme above, the 'Lk' 
                    473:         * condition will produce a quality value of 0. If the quality 
                    474:         * indicator starts with '0' then the second character is the 
                    475:         * number of hours since we were last locked. If the first 
                    476:         * character is anything other than 'L' or '0' then we have been 
                    477:         * out of lock for more than 9 hours so we assume the worst and 
                    478:         * force a quality value that selects the 'default' maximum 
                    479:         * dispersion. The dispersion values below are what came with the
                    480:         * driver. They're not unreasonable so they've not been changed.
                    481:         */
                    482: 
                    483:        if (pp->lencode == LEN33X) {
                    484:                switch (quality) {
                    485:                        case 0 :
                    486:                                pp->disp=.002;
                    487:                                break;
                    488:                        case 1 :
                    489:                                pp->disp=.02;
                    490:                                break;
                    491:                        case 2 :
                    492:                                pp->disp=.04;
                    493:                                break;
                    494:                        case 3 :
                    495:                                pp->disp=.08;
                    496:                                break;
                    497:                        default:
                    498:                                pp->disp=MAXDISPERSE;
                    499:                                break;
                    500:                }
                    501:        } else {
                    502:                switch (quality) {
                    503:                        case 5 :
                    504:                                pp->disp=.002;
                    505:                                break;
                    506:                        case 4 :
                    507:                                pp->disp=.02;
                    508:                                break;
                    509:                        case 3 :
                    510:                                pp->disp=.04;
                    511:                                break;
                    512:                        case 2 :
                    513:                                pp->disp=.08;
                    514:                                break;
                    515:                        case 1 :
                    516:                                pp->disp=.16;
                    517:                                break;
                    518:                        default:
                    519:                                pp->disp=MAXDISPERSE;
                    520:                                break;
                    521:                }
                    522: 
                    523:        }
                    524: 
                    525:        /*
                    526:         * Decode synchronization, and leap characters. If
                    527:         * unsynchronized, set the leap bits accordingly and exit.
                    528:         * Otherwise, set the leap bits according to the leap character.
                    529:         */
                    530: 
                    531:        if (syncchar != ':')
                    532:                pp->leap = LEAP_NOTINSYNC;
                    533:        else if (leapchar == '+')
                    534:                pp->leap = LEAP_ADDSECOND;
                    535:        else if (leapchar == '-')
                    536:                pp->leap = LEAP_DELSECOND;
                    537:        else
                    538:                pp->leap = LEAP_NOWARNING;
                    539: 
                    540:        /*
                    541:         * Process the new sample in the median filter and determine the
                    542:         * timecode timestamp.
                    543:         */
                    544:        if (!refclock_process(pp)) {
                    545:                refclock_report(peer, CEVNT_BADTIME);
                    546:        }
                    547: 
                    548: }
                    549: 
                    550: /*
                    551:  * ulink_poll - called by the transmit procedure
                    552:  */
                    553: 
                    554: static void
                    555: ulink_poll(
                    556:        int unit,
                    557:        struct peer *peer
                    558:        )
                    559: {
                    560:         struct refclockproc *pp;
                    561:         char pollchar;
                    562: 
                    563:         pp = peer->procptr;
                    564:         pollchar = 'T';
                    565:        if (pp->sloppyclockflag & CLK_FLAG1) {
                    566:                if (write(pp->io.fd, &pollchar, 1) != 1)
                    567:                        refclock_report(peer, CEVNT_FAULT);
                    568:                else
                    569:                    pp->polls++;
                    570:        }
                    571:        else
                    572:                    pp->polls++;
                    573: 
                    574:         if (peer->burst > 0)
                    575:                 return;
                    576:         if (pp->coderecv == pp->codeproc) {
                    577:                 refclock_report(peer, CEVNT_TIMEOUT);
                    578:                 return;
                    579:         }
                    580:         pp->lastref = pp->lastrec;
                    581:        refclock_receive(peer);
                    582:        record_clock_stats(&peer->srcadr, pp->a_lastcode);
                    583:         peer->burst = NSTAGE;
                    584: 
                    585: }
                    586: 
                    587: #else
                    588: int refclock_ulink_bs;
                    589: #endif /* REFCLOCK */

FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>