Home | History | Annotate | Line # | Download | only in ntpd
refclock_tsyncpci.c revision 1.1.1.4
      1 /*******************************************************************************
      2 *
      3 *  Module  : refclock_tsyncpci.c
      4 *  Date    : 09/08/08
      5 *  Purpose : Implements a reference clock driver for the NTP daemon.  This
      6 *            reference clock driver provides a means to communicate with
      7 *            the Spectracom TSYNC PCI timing devices and use them as a time
      8 *            source.
      9 *
     10 *  (C) Copyright 2008 Spectracom Corporation
     11 *
     12 *  This software is provided by Spectracom Corporation 'as is' and
     13 *  any express or implied warranties, including, but not limited to, the
     14 *  implied warranties of merchantability and fitness for a particular purpose
     15 *  are disclaimed.  In no event shall Spectracom Corporation be liable
     16 *  for any direct, indirect, incidental, special, exemplary, or consequential
     17 *  damages (including, but not limited to, procurement of substitute goods
     18 *  or services; loss of use, data, or profits; or business interruption)
     19 *  however caused and on any theory of liability, whether in contract, strict
     20 *  liability, or tort (including negligence or otherwise) arising in any way
     21 *  out of the use of this software, even if advised of the possibility of
     22 *  such damage.
     23 *
     24 *  This software is released for distribution according to the NTP copyright
     25 *  and license contained in html/copyright.html of NTP source.
     26 *
     27 *******************************************************************************/
     28 #ifdef HAVE_CONFIG_H
     29 #include <config.h>
     30 #endif
     31 
     32 #if defined(REFCLOCK) && defined(CLOCK_TSYNCPCI)
     33 
     34 #include <asm/ioctl.h>
     35 #ifdef HAVE_SYS_IOCTL_H
     36 # include <sys/ioctl.h>
     37 #endif
     38 
     39 #include <stdio.h>
     40 #include <ctype.h>
     41 #include <netinet/in.h>
     42 
     43 
     44 #include "ntpd.h"
     45 #include "ntp_io.h"
     46 #include "ntp_refclock.h"
     47 #include "ntp_unixtime.h"
     48 #include "ntp_stdlib.h"
     49 #include "ntp_calendar.h"
     50 
     51 
     52 /*******************************************************************************
     53 **
     54 ** This driver supports the Spectracom TSYNC PCI GPS receiver.  It requires
     55 ** that the tsyncpci.o device driver be installed and loaded.
     56 **
     57 *******************************************************************************/
     58 
     59 #define TSYNC_PCI_REVISION "1.11"
     60 
     61 /*
     62 ** TPRO interface definitions
     63 */
     64 #define DEVICE      "/dev/tsyncpci"             /* device name */
     65 #define PRECISION   (-20)                       /* precision assumed (1 us) */
     66 #define DESCRIPTION "Spectracom TSYNC-PCI"      /* WRU */
     67 
     68 #define SECONDS_1900_TO_1970 (2208988800U)
     69 
     70 #define TSYNC_REF_IID               (0x2500)    // SS CAI, REF IID
     71 #define TSYNC_REF_DEST_ID           (0x0001)    // KTS Firmware
     72 #define TSYNC_REF_IN_PYLD_OFF       (0)
     73 #define TSYNC_REF_IN_LEN            (0)
     74 #define TSYNC_REF_OUT_PYLD_OFF      (0)
     75 #define TSYNC_REF_OUT_LEN           (8)
     76 #define TSYNC_REF_MAX_OUT_LEN       (16)
     77 #define TSYNC_REF_PYLD_LEN          (TSYNC_REF_IN_LEN +                     \
     78                                      TSYNC_REF_MAX_OUT_LEN)
     79 #define TSYNC_REF_LEN               (4)
     80 #define TSYNC_REF_LOCAL             ("LOCL")
     81 
     82 #define TSYNC_TMSCL_IID              (0x2301)    // CS CAI, TIMESCALE IID
     83 #define TSYNC_TMSCL_DEST_ID          (0x0001)    // KTS Firmware
     84 #define TSYNC_TMSCL_IN_PYLD_OFF      (0)
     85 #define TSYNC_TMSCL_IN_LEN           (0)
     86 #define TSYNC_TMSCL_OUT_PYLD_OFF     (0)
     87 #define TSYNC_TMSCL_OUT_LEN          (4)
     88 #define TSYNC_TMSCL_MAX_OUT_LEN      (12)
     89 #define TSYNC_TMSCL_PYLD_LEN         (TSYNC_TMSCL_IN_LEN +                    \
     90                                      TSYNC_TMSCL_MAX_OUT_LEN)
     91 
     92 #define TSYNC_LEAP_IID              (0x2307)    // CS CAI, LEAP SEC IID
     93 #define TSYNC_LEAP_DEST_ID          (0x0001)    // KTS Firmware
     94 #define TSYNC_LEAP_IN_PYLD_OFF      (0)
     95 #define TSYNC_LEAP_IN_LEN           (0)
     96 #define TSYNC_LEAP_OUT_PYLD_OFF     (0)
     97 #define TSYNC_LEAP_OUT_LEN          (28)
     98 #define TSYNC_LEAP_MAX_OUT_LEN      (36)
     99 #define TSYNC_LEAP_PYLD_LEN         (TSYNC_LEAP_IN_LEN +                    \
    100                                      TSYNC_LEAP_MAX_OUT_LEN)
    101 
    102 // These define the base date/time of the system clock.  The system time will
    103 // be tracked as the number of seconds from this date/time.
    104 #define TSYNC_TIME_BASE_YEAR        (1970) // earliest acceptable year
    105 
    106 #define TSYNC_LCL_STRATUM           (0)
    107 
    108 /*
    109 ** TSYNC Time Scales type
    110 */
    111 typedef enum
    112 {
    113     TIME_SCALE_UTC    = 0,   // Universal Coordinated Time
    114     TIME_SCALE_TAI    = 1,   // International Atomic Time
    115     TIME_SCALE_GPS    = 2,   // Global Positioning System
    116     TIME_SCALE_LOCAL  = 3,   // UTC w/local rules for time zone and DST
    117     NUM_TIME_SCALES   = 4,   // Number of time scales
    118 
    119     TIME_SCALE_MAX    = 15   // Maximum number of timescales
    120 
    121 } TIME_SCALE;
    122 
    123 /*
    124 ** TSYNC Board Object
    125 */
    126 typedef struct BoardObj {
    127 
    128   int            file_descriptor;
    129   unsigned short devid;
    130   unsigned short options;
    131   unsigned char  firmware[5];
    132   unsigned char  FPGA[5];
    133   unsigned char  driver[7];
    134 
    135 } BoardObj;
    136 
    137 /*
    138 ** TSYNC Time Object
    139 */
    140 typedef struct TimeObj {
    141 
    142   unsigned char  syncOption;  /* -M option */
    143   unsigned int   secsDouble;  /* seconds floating pt */
    144   unsigned char  seconds;     /* seconds whole num */
    145   unsigned char  minutes;
    146   unsigned char  hours;
    147   unsigned short days;
    148   unsigned short year;
    149   unsigned short flags;      /* bit 2 SYNC, bit 1 TCODE; all others 0 */
    150 
    151 } TimeObj;
    152 
    153 /*
    154 ** NTP Time Object
    155 */
    156 typedef struct NtpTimeObj {
    157 
    158     TimeObj        timeObj;
    159     struct timeval tv;
    160     unsigned int   refId;
    161 
    162 } NtpTimeObj;
    163 /*
    164 ** TSYNC Supervisor Reference Object
    165 */
    166 typedef struct ReferenceObj {
    167 
    168     char time[TSYNC_REF_LEN];
    169     char pps[TSYNC_REF_LEN];
    170 
    171 } ReferenceObj;
    172 
    173 /*
    174 ** TSYNC Seconds Time Object
    175 */
    176 typedef struct SecTimeObj
    177 {
    178     unsigned int seconds;
    179     unsigned int ns;
    180 }
    181 SecTimeObj;
    182 
    183 /*
    184 ** TSYNC DOY Time Object
    185 */
    186 typedef struct DoyTimeObj
    187 {
    188     unsigned int year;
    189     unsigned int doy;
    190     unsigned int hour;
    191     unsigned int minute;
    192     unsigned int second;
    193     unsigned int ns;
    194 }
    195 DoyTimeObj;
    196 
    197 /*
    198 ** TSYNC Leap Second Object
    199 */
    200 typedef struct LeapSecondObj
    201 {
    202     int        offset;
    203     DoyTimeObj utcDate;
    204 }
    205 LeapSecondObj;
    206 
    207 /*
    208  * structures for ioctl interactions with driver
    209  */
    210 #define DI_PAYLOADS_STARTER_LENGTH 4
    211 typedef struct ioctl_trans_di {
    212 
    213     // input parameters
    214     uint16_t        dest;
    215     uint16_t        iid;
    216 
    217     uint32_t        inPayloadOffset;
    218     uint32_t        inLength;
    219     uint32_t        outPayloadOffset;
    220     uint32_t        maxOutLength;
    221 
    222     // output parameters
    223     uint32_t        actualOutLength;
    224     int32_t         status;
    225 
    226     // Input and output
    227 
    228     // The payloads field MUST be last in ioctl_trans_di.
    229     uint8_t         payloads[DI_PAYLOADS_STARTER_LENGTH];
    230 
    231 }ioctl_trans_di;
    232 
    233 /*
    234  * structure for looking up a reference ID from a reference name
    235  */
    236 typedef struct
    237 {
    238     const char* pRef;           // KTS Reference Name
    239     const char* pRefId;         // NTP Reference ID
    240 
    241 } RefIdLookup;
    242 
    243 /*
    244  * unit control structure
    245  */
    246 typedef struct  {
    247     uint32_t refPrefer;         // Reference prefer flag
    248     uint32_t refId;             // Host peer reference ID
    249     uint8_t  refStratum;        // Host peer reference stratum
    250 
    251 } TsyncUnit;
    252 
    253 /*
    254 **  Function prototypes
    255 */
    256 static void tsync_poll     (int unit, struct peer *);
    257 static void tsync_shutdown (int, struct peer *);
    258 static int  tsync_start    (int, struct peer *);
    259 
    260 /*
    261 **  Helper functions
    262 */
    263 static void ApplyTimeOffset    (DoyTimeObj* pDt, int off);
    264 static void SecTimeFromDoyTime (SecTimeObj* pSt, DoyTimeObj* pDt);
    265 static void DoyTimeFromSecTime (DoyTimeObj* pDt, SecTimeObj* pSt);
    266 
    267 /*
    268 **  Transfer vector
    269 */
    270 struct refclock refclock_tsyncpci = {
    271     tsync_start,    /* start up driver */
    272     tsync_shutdown, /* shut down driver */
    273     tsync_poll,     /* transmit poll message */
    274     noentry,        /* not used (old tsync_control) */
    275     noentry,        /* initialize driver (not used) */
    276     noentry,        /* not used (old tsync_buginfo) */
    277     NOFLAGS         /* not used */
    278 };
    279 
    280 /*
    281  * Reference ID lookup table
    282  */
    283 static RefIdLookup RefIdLookupTbl[] =
    284 {
    285     {"gps",  "GPS"},
    286     {"ir",   "IRIG"},
    287     {"hvq",  "HVQ"},
    288     {"frq",  "FREQ"},
    289     {"mdm",  "ACTS"},
    290     {"epp",  "PPS"},
    291     {"ptp",  "PTP"},
    292     {"asc",  "ATC"},
    293     {"hst0", "USER"},
    294     {"hst",  TSYNC_REF_LOCAL},
    295     {"self", TSYNC_REF_LOCAL},
    296     {NULL,   NULL}
    297 };
    298 
    299 /*******************************************************************************
    300 **          IOCTL DEFINITIONS
    301 *******************************************************************************/
    302 #define IOCTL_TPRO_ID            't'
    303 #define IOCTL_TPRO_OPEN          _IOWR(IOCTL_TPRO_ID, 0,  BoardObj)
    304 #define IOCTL_TPRO_GET_NTP_TIME  _IOWR(IOCTL_TPRO_ID, 25, NtpTimeObj)
    305 #define IOCTL_TSYNC_GET          _IOWR(IOCTL_TPRO_ID, 26, ioctl_trans_di)
    306 
    307 /******************************************************************************
    308  *
    309  * Function:    tsync_start()
    310  * Description: Used to intialize the Spectracom TSYNC reference driver.
    311  *
    312  * Parameters:
    313  *     IN:  unit - not used.
    314  *         *peer - pointer to this reference clock's peer structure
    315  *     Returns: 0 - unsuccessful
    316  *              1 - successful
    317  *
    318 *******************************************************************************/
    319 static int tsync_start(int unit, struct peer *peer)
    320 {
    321     struct refclockproc *pp;
    322     TsyncUnit           *up;
    323 
    324 
    325     /*
    326     **  initialize reference clock and peer parameters
    327     */
    328     pp                = peer->procptr;
    329     pp->clockdesc     = DESCRIPTION;
    330     pp->io.clock_recv = noentry;
    331     pp->io.srcclock   = peer;
    332     pp->io.datalen    = 0;
    333     peer->precision   = PRECISION;
    334 
    335     // Allocate and initialize unit structure
    336     if (!(up = (TsyncUnit*)emalloc(sizeof(TsyncUnit))))
    337     {
    338         return (0);
    339     }
    340 
    341     // Store reference preference
    342     up->refPrefer = peer->flags & FLAG_PREFER;
    343 
    344     // Initialize reference stratum level and ID
    345     up->refStratum = STRATUM_UNSPEC;
    346     strncpy((char *)&up->refId, TSYNC_REF_LOCAL, TSYNC_REF_LEN);
    347 
    348     // Attach unit structure
    349     pp->unitptr = (caddr_t)up;
    350 
    351     /* Declare our refId as local in the beginning because we do not know
    352      * what our actual refid is yet.
    353      */
    354     strncpy((char *)&pp->refid, TSYNC_REF_LOCAL, TSYNC_REF_LEN);
    355 
    356     return (1);
    357 
    358 } /* End - tsync_start() */
    359 
    360 /*******************************************************************************
    361 **
    362 ** Function:    tsync_shutdown()
    363 ** Description: Handles anything related to shutting down the reference clock
    364 **              driver. Nothing at this point in time.
    365 **
    366 ** Parameters:
    367 **     IN:  unit - not used.
    368 **         *peer - pointer to this reference clock's peer structure
    369 **     Returns: none.
    370 **
    371 *******************************************************************************/
    372 static void tsync_shutdown(int unit, struct peer *peer)
    373 {
    374 
    375 } /* End - tsync_shutdown() */
    376 
    377 /******************************************************************************
    378  *
    379  * Function:    tsync_poll()
    380  * Description: Retrieve time from the TSYNC device.
    381  *
    382  * Parameters:
    383  *     IN:  unit - not used.
    384  *         *peer - pointer to this reference clock's peer structure
    385  *     Returns: none.
    386  *
    387 *******************************************************************************/
    388 static void tsync_poll(int unit, struct peer *peer)
    389 {
    390     char                 device[32];
    391     struct refclockproc *pp;
    392     struct calendar      jt;
    393     TsyncUnit           *up;
    394     unsigned char        synch;
    395     double               seconds;
    396     int                  err;
    397     int                  err1;
    398     int                  err2;
    399     int                  err3;
    400     int                  i;
    401     int                  j;
    402     unsigned int         itAllocationLength;
    403     unsigned int         itAllocationLength1;
    404     unsigned int         itAllocationLength2;
    405     NtpTimeObj           TimeContext;
    406     BoardObj             hBoard;
    407     char                 timeRef[TSYNC_REF_LEN + 1];
    408     char                 ppsRef [TSYNC_REF_LEN + 1];
    409     TIME_SCALE           tmscl = TIME_SCALE_UTC;
    410     LeapSecondObj        leapSec;
    411     ioctl_trans_di      *it;
    412     ioctl_trans_di      *it1;
    413     ioctl_trans_di      *it2;
    414     l_fp                 offset;
    415     l_fp                 ltemp;
    416     ReferenceObj *	 pRefObj;
    417 
    418 
    419     /* Construct the device name */
    420     sprintf(device, "%s%d", DEVICE, (int)peer->refclkunit);
    421 
    422     printf("Polling device number %d...\n", (int)peer->refclkunit);
    423 
    424     /* Open the TSYNC device */
    425     hBoard.file_descriptor = open(device, O_RDONLY | O_NDELAY, 0777);
    426 
    427     /* If error opening TSYNC device... */
    428     if (hBoard.file_descriptor < 0)
    429     {
    430         msyslog(LOG_ERR, "Couldn't open device");
    431         return;
    432     }
    433 
    434     /* If error while initializing the board... */
    435     if (ioctl(hBoard.file_descriptor, IOCTL_TPRO_OPEN, &hBoard) < 0)
    436     {
    437         msyslog(LOG_ERR, "Couldn't initialize device");
    438         close(hBoard.file_descriptor);
    439         return;
    440     }
    441 
    442     /* Allocate memory for ioctl message */
    443     itAllocationLength =
    444         (sizeof(ioctl_trans_di) - DI_PAYLOADS_STARTER_LENGTH) +
    445         TSYNC_REF_IN_LEN + TSYNC_REF_MAX_OUT_LEN;
    446 
    447     it = (ioctl_trans_di*)alloca(itAllocationLength);
    448     if (it == NULL) {
    449         msyslog(LOG_ERR, "Couldn't allocate transaction memory - Reference");
    450         return;
    451     }
    452 
    453     /* Build SS_GetRef ioctl message */
    454     it->dest             = TSYNC_REF_DEST_ID;
    455     it->iid              = TSYNC_REF_IID;
    456     it->inPayloadOffset  = TSYNC_REF_IN_PYLD_OFF;
    457     it->inLength         = TSYNC_REF_IN_LEN;
    458     it->outPayloadOffset = TSYNC_REF_OUT_PYLD_OFF;
    459     it->maxOutLength     = TSYNC_REF_MAX_OUT_LEN;
    460     it->actualOutLength  = 0;
    461     it->status           = 0;
    462     memset(it->payloads, 0, TSYNC_REF_MAX_OUT_LEN);
    463 
    464     /* Read the reference from the TSYNC-PCI device */
    465     err = ioctl(hBoard.file_descriptor,
    466                  IOCTL_TSYNC_GET,
    467                 (char *)it);
    468 
    469     /* Allocate memory for ioctl message */
    470     itAllocationLength1 =
    471         (sizeof(ioctl_trans_di) - DI_PAYLOADS_STARTER_LENGTH) +
    472         TSYNC_TMSCL_IN_LEN + TSYNC_TMSCL_MAX_OUT_LEN;
    473 
    474     it1 = (ioctl_trans_di*)alloca(itAllocationLength1);
    475     if (it1 == NULL) {
    476         msyslog(LOG_ERR, "Couldn't allocate transaction memory - Time Scale");
    477         return;
    478     }
    479 
    480     /* Build CS_GetTimeScale ioctl message */
    481     it1->dest             = TSYNC_TMSCL_DEST_ID;
    482     it1->iid              = TSYNC_TMSCL_IID;
    483     it1->inPayloadOffset  = TSYNC_TMSCL_IN_PYLD_OFF;
    484     it1->inLength         = TSYNC_TMSCL_IN_LEN;
    485     it1->outPayloadOffset = TSYNC_TMSCL_OUT_PYLD_OFF;
    486     it1->maxOutLength     = TSYNC_TMSCL_MAX_OUT_LEN;
    487     it1->actualOutLength  = 0;
    488     it1->status           = 0;
    489     memset(it1->payloads, 0, TSYNC_TMSCL_MAX_OUT_LEN);
    490 
    491     /* Read the Time Scale info from the TSYNC-PCI device */
    492     err1 = ioctl(hBoard.file_descriptor,
    493                  IOCTL_TSYNC_GET,
    494                  (char *)it1);
    495 
    496     /* Allocate memory for ioctl message */
    497     itAllocationLength2 =
    498         (sizeof(ioctl_trans_di) - DI_PAYLOADS_STARTER_LENGTH) +
    499         TSYNC_LEAP_IN_LEN + TSYNC_LEAP_MAX_OUT_LEN;
    500 
    501     it2 = (ioctl_trans_di*)alloca(itAllocationLength2);
    502     if (it2 == NULL) {
    503         msyslog(LOG_ERR, "Couldn't allocate transaction memory - Leap Second");
    504         return;
    505     }
    506 
    507     /* Build CS_GetLeapSec ioctl message */
    508     it2->dest             = TSYNC_LEAP_DEST_ID;
    509     it2->iid              = TSYNC_LEAP_IID;
    510     it2->inPayloadOffset  = TSYNC_LEAP_IN_PYLD_OFF;
    511     it2->inLength         = TSYNC_LEAP_IN_LEN;
    512     it2->outPayloadOffset = TSYNC_LEAP_OUT_PYLD_OFF;
    513     it2->maxOutLength     = TSYNC_LEAP_MAX_OUT_LEN;
    514     it2->actualOutLength  = 0;
    515     it2->status           = 0;
    516     memset(it2->payloads, 0, TSYNC_LEAP_MAX_OUT_LEN);
    517 
    518     /* Read the leap seconds info from the TSYNC-PCI device */
    519     err2 = ioctl(hBoard.file_descriptor,
    520                  IOCTL_TSYNC_GET,
    521                  (char *)it2);
    522 
    523     pp = peer->procptr;
    524     up = (TsyncUnit*)pp->unitptr;
    525 
    526     /* Read the time from the TSYNC-PCI device */
    527     err3 = ioctl(hBoard.file_descriptor,
    528                  IOCTL_TPRO_GET_NTP_TIME,
    529                  (char *)&TimeContext);
    530 
    531     /* Close the TSYNC device */
    532     close(hBoard.file_descriptor);
    533 
    534     // Check for errors
    535     if ((err < 0) ||(err1 < 0) || (err2 < 0) || (err3 < 0) ||
    536         (it->status != 0) || (it1->status != 0) || (it2->status != 0) ||
    537         (it->actualOutLength  != TSYNC_REF_OUT_LEN) ||
    538         (it1->actualOutLength != TSYNC_TMSCL_OUT_LEN) ||
    539         (it2->actualOutLength != TSYNC_LEAP_OUT_LEN)) {
    540         refclock_report(peer, CEVNT_FAULT);
    541         return;
    542     }
    543 
    544     // Extract reference identifiers from ioctl payload
    545     memset(timeRef, '\0', sizeof(timeRef));
    546     memset(ppsRef, '\0', sizeof(ppsRef));
    547     pRefObj = (void *)it->payloads;
    548     memcpy(timeRef, pRefObj->time, TSYNC_REF_LEN);
    549     memcpy(ppsRef, pRefObj->pps, TSYNC_REF_LEN);
    550 
    551     // Extract the Clock Service Time Scale and convert to correct byte order
    552     memcpy(&tmscl, it1->payloads, sizeof(tmscl));
    553     tmscl = ntohl(tmscl);
    554 
    555     // Extract leap second info from ioctl payload and perform byte swapping
    556     for (i = 0; i < (sizeof(leapSec) / 4); i++)
    557     {
    558         for (j = 0; j < 4; j++)
    559         {
    560             ((unsigned char*)&leapSec)[(i * 4) + j] =
    561                     ((unsigned char*)(it2->payloads))[(i * 4) + (3 - j)];
    562         }
    563     }
    564 
    565     // Determine time reference ID from reference name
    566     for (i = 0; RefIdLookupTbl[i].pRef != NULL; i++)
    567     {
    568        // Search RefID table
    569        if (strstr(timeRef, RefIdLookupTbl[i].pRef) != NULL)
    570        {
    571           // Found the matching string
    572           break;
    573        }
    574     }
    575 
    576     // Determine pps reference ID from reference name
    577     for (j = 0; RefIdLookupTbl[j].pRef != NULL; j++)
    578     {
    579        // Search RefID table
    580        if (strstr(ppsRef, RefIdLookupTbl[j].pRef) != NULL)
    581        {
    582           // Found the matching string
    583           break;
    584        }
    585     }
    586 
    587     // Determine synchronization state from flags
    588     synch = (TimeContext.timeObj.flags == 0x4) ? 1 : 0;
    589 
    590     // Pull seconds information from time object
    591     seconds = (double) (TimeContext.timeObj.secsDouble);
    592     seconds /= (double) 1000000.0;
    593 
    594     /*
    595     ** Convert the number of microseconds to double and then place in the
    596     ** peer's last received long floating point format.
    597     */
    598     DTOLFP(((double)TimeContext.tv.tv_usec / 1000000.0), &pp->lastrec);
    599 
    600     /*
    601     ** The specTimeStamp is the number of seconds since 1/1/1970, while the
    602     ** peer's lastrec time should be compatible with NTP which is seconds since
    603     ** 1/1/1900.  So Add the number of seconds between 1900 and 1970 to the
    604     ** specTimeStamp and place in the peer's lastrec long floating point struct.
    605     */
    606     pp->lastrec.Ul_i.Xl_ui += (unsigned int)TimeContext.tv.tv_sec +
    607                                             SECONDS_1900_TO_1970;
    608 
    609     pp->polls++;
    610 
    611     /*
    612     **  set the reference clock object
    613     */
    614     sprintf(pp->a_lastcode, "%03d %02d:%02d:%02.6f",
    615             TimeContext.timeObj.days, TimeContext.timeObj.hours,
    616             TimeContext.timeObj.minutes, seconds);
    617 
    618     pp->lencode = strlen (pp->a_lastcode);
    619     pp->day     = TimeContext.timeObj.days;
    620     pp->hour    = TimeContext.timeObj.hours;
    621     pp->minute  = TimeContext.timeObj.minutes;
    622     pp->second  = (int) seconds;
    623     seconds     = (seconds - (double) (pp->second / 1.0)) * 1000000000;
    624     pp->nsec    = (long) seconds;
    625 
    626     /*
    627     **  calculate year start
    628     */
    629     jt.year       = TimeContext.timeObj.year;
    630     jt.yearday    = 1;
    631     jt.monthday   = 1;
    632     jt.month      = 1;
    633     jt.hour       = 0;
    634     jt.minute     = 0;
    635     jt.second     = 0;
    636     pp->yearstart = caltontp(&jt);
    637 
    638     // Calculate and report reference clock offset
    639     offset.l_ui = (long)(((pp->day - 1) * 24) + pp->hour + GMT);
    640     offset.l_ui = (offset.l_ui * 60) + (long)pp->minute;
    641     offset.l_ui = (offset.l_ui * 60) + (long)pp->second;
    642     offset.l_ui = offset.l_ui + (long)pp->yearstart;
    643     offset.l_uf = 0;
    644     DTOLFP(pp->nsec / 1e9, &ltemp);
    645     L_ADD(&offset, &ltemp);
    646     refclock_process_offset(pp, offset, pp->lastrec,
    647                             pp->fudgetime1);
    648 
    649     // KTS in sync
    650     if (synch) {
    651         // Subtract leap second info by one second to determine effective day
    652         ApplyTimeOffset(&(leapSec.utcDate), -1);
    653 
    654         // If there is a leap second today and the KTS is using a time scale
    655         // which handles leap seconds then
    656         if ((tmscl != TIME_SCALE_GPS) && (tmscl != TIME_SCALE_TAI) &&
    657             (leapSec.utcDate.year == (unsigned int)TimeContext.timeObj.year) &&
    658             (leapSec.utcDate.doy  == (unsigned int)TimeContext.timeObj.days))
    659         {
    660             // If adding a second
    661             if (leapSec.offset == 1)
    662             {
    663                 pp->leap = LEAP_ADDSECOND;
    664             }
    665             // Else if removing a second
    666             else if (leapSec.offset == -1)
    667             {
    668                 pp->leap = LEAP_DELSECOND;
    669             }
    670             // Else report no leap second pending (no handling of offsets
    671             // other than +1 or -1)
    672             else
    673             {
    674                 pp->leap = LEAP_NOWARNING;
    675             }
    676         }
    677         // Else report no leap second pending
    678         else
    679         {
    680             pp->leap = LEAP_NOWARNING;
    681         }
    682 
    683         peer->leap = pp->leap;
    684         refclock_report(peer, CEVNT_NOMINAL);
    685 
    686         // If reference name reported, then not in holdover
    687         if ((RefIdLookupTbl[i].pRef != NULL) &&
    688             (RefIdLookupTbl[j].pRef != NULL))
    689         {
    690             // Determine if KTS being synchronized by host (identified as
    691             // "LOCL")
    692             if ((strcmp(RefIdLookupTbl[i].pRefId, TSYNC_REF_LOCAL) == 0) ||
    693                 (strcmp(RefIdLookupTbl[j].pRefId, TSYNC_REF_LOCAL) == 0))
    694             {
    695                 // Clear prefer flag
    696                 peer->flags &= ~FLAG_PREFER;
    697 
    698                 // Set reference clock stratum level as unusable
    699                 pp->stratum   = STRATUM_UNSPEC;
    700                 peer->stratum = pp->stratum;
    701 
    702                 // If a valid peer is available
    703                 if ((sys_peer != NULL) && (sys_peer != peer))
    704                 {
    705                     // Store reference peer stratum level and ID
    706                     up->refStratum = sys_peer->stratum;
    707                     up->refId      = addr2refid(&sys_peer->srcadr);
    708                 }
    709             }
    710             else
    711             {
    712                 // Restore prefer flag
    713                 peer->flags |= up->refPrefer;
    714 
    715                 // Store reference stratum as local clock
    716                 up->refStratum = TSYNC_LCL_STRATUM;
    717                 strncpy((char *)&up->refId, RefIdLookupTbl[j].pRefId,
    718                     TSYNC_REF_LEN);
    719 
    720                 // Set reference clock stratum level as local clock
    721                 pp->stratum   = TSYNC_LCL_STRATUM;
    722                 peer->stratum = pp->stratum;
    723             }
    724 
    725             // Update reference name
    726             strncpy((char *)&pp->refid, RefIdLookupTbl[j].pRefId,
    727                 TSYNC_REF_LEN);
    728             peer->refid = pp->refid;
    729         }
    730         // Else in holdover
    731         else
    732         {
    733             // Restore prefer flag
    734             peer->flags |= up->refPrefer;
    735 
    736             // Update reference ID to saved ID
    737             pp->refid   = up->refId;
    738             peer->refid = pp->refid;
    739 
    740             // Update stratum level to saved stratum level
    741             pp->stratum   = up->refStratum;
    742             peer->stratum = pp->stratum;
    743         }
    744     }
    745     // Else KTS not in sync
    746     else {
    747         // Place local identifier in peer RefID
    748         strncpy((char *)&pp->refid, TSYNC_REF_LOCAL, TSYNC_REF_LEN);
    749         peer->refid = pp->refid;
    750 
    751         // Report not in sync
    752         pp->leap   = LEAP_NOTINSYNC;
    753         peer->leap = pp->leap;
    754     }
    755 
    756     if (pp->coderecv == pp->codeproc) {
    757         refclock_report(peer, CEVNT_TIMEOUT);
    758         return;
    759     }
    760 
    761     record_clock_stats(&peer->srcadr, pp->a_lastcode);
    762     refclock_receive(peer);
    763 
    764     /* Increment the number of times the reference has been polled */
    765     pp->polls++;
    766 
    767 } /* End - tsync_poll() */
    768 
    769 
    770 ////////////////////////////////////////////////////////////////////////////////
    771 // Function:    ApplyTimeOffset
    772 // Description: The ApplyTimeOffset function adds an offset (in seconds) to a
    773 //              specified date and time.  The specified date and time is passed
    774 //              back after being modified.
    775 //
    776 // Assumptions: 1. Every fourth year is a leap year.  Therefore, this function
    777 //                 is only accurate through Feb 28, 2100.
    778 ////////////////////////////////////////////////////////////////////////////////
    779 void ApplyTimeOffset(DoyTimeObj* pDt, int off)
    780 {
    781     SecTimeObj st;                  // Time, in seconds
    782 
    783 
    784     // Convert date and time to seconds
    785     SecTimeFromDoyTime(&st, pDt);
    786 
    787     // Apply offset
    788     st.seconds = (int)((signed long long)st.seconds + (signed long long)off);
    789 
    790     // Convert seconds to date and time
    791     DoyTimeFromSecTime(pDt, &st);
    792 
    793 } // End ApplyTimeOffset
    794 
    795 
    796 ////////////////////////////////////////////////////////////////////////////////
    797 // Function:    SecTimeFromDoyTime
    798 // Description: The SecTimeFromDoyTime function converts a specified date
    799 //              and time into a count of seconds since the base time.  This
    800 //              function operates across the range Base Time to Max Time for
    801 //              the system.
    802 //
    803 // Assumptions: 1. A leap year is any year evenly divisible by 4.  Therefore,
    804 //                 this function is only accurate through Feb 28, 2100.
    805 //              2. Conversion does not account for leap seconds.
    806 ////////////////////////////////////////////////////////////////////////////////
    807 void SecTimeFromDoyTime(SecTimeObj* pSt, DoyTimeObj* pDt)
    808 {
    809     unsigned int yrs;               // Years
    810     unsigned int lyrs;              // Leap years
    811 
    812 
    813     // Start with accumulated time of 0
    814     pSt->seconds  = 0;
    815 
    816     // Calculate the number of years and leap years
    817     yrs           = pDt->year - TSYNC_TIME_BASE_YEAR;
    818     lyrs          = (yrs + 1) / 4;
    819 
    820     // Convert leap years and years
    821     pSt->seconds += lyrs           * SECSPERLEAPYEAR;
    822     pSt->seconds += (yrs - lyrs)   * SECSPERYEAR;
    823 
    824     // Convert days, hours, minutes and seconds
    825     pSt->seconds += (pDt->doy - 1) * SECSPERDAY;
    826     pSt->seconds += pDt->hour      * SECSPERHR;
    827     pSt->seconds += pDt->minute    * SECSPERMIN;
    828     pSt->seconds += pDt->second;
    829 
    830     // Copy the subseconds count
    831     pSt->ns       = pDt->ns;
    832 
    833 } // End SecTimeFromDoyTime
    834 
    835 
    836 ////////////////////////////////////////////////////////////////////////////////
    837 // Function:    DoyTimeFromSecTime
    838 // Description: The DoyTimeFromSecTime function converts a specified count
    839 //              of seconds since the start of our base time into a SecTimeObj
    840 //              structure.
    841 //
    842 // Assumptions: 1. A leap year is any year evenly divisible by 4.  Therefore,
    843 //                 this function is only accurate through Feb 28, 2100.
    844 //              2. Conversion does not account for leap seconds.
    845 ////////////////////////////////////////////////////////////////////////////////
    846 void DoyTimeFromSecTime(DoyTimeObj* pDt, SecTimeObj* pSt)
    847 {
    848     signed long long secs;          // Seconds accumulator variable
    849     unsigned int     yrs;           // Years accumulator variable
    850     unsigned int     doys;          // Days accumulator variable
    851     unsigned int     hrs;           // Hours accumulator variable
    852     unsigned int     mins;          // Minutes accumulator variable
    853 
    854 
    855     // Convert the seconds count into a signed 64-bit number for calculations
    856     secs  = (signed long long)(pSt->seconds);
    857 
    858     // Calculate the number of 4 year chunks
    859     yrs   = (unsigned int)((secs /
    860                            ((SECSPERYEAR * 3) + SECSPERLEAPYEAR)) * 4);
    861     secs %= ((SECSPERYEAR * 3) + SECSPERLEAPYEAR);
    862 
    863     // If there is at least a normal year worth of time left
    864     if (secs >= SECSPERYEAR)
    865     {
    866         // Increment the number of years and subtract a normal year of time
    867         yrs++;
    868         secs -= SECSPERYEAR;
    869     }
    870 
    871     // If there is still at least a normal year worth of time left
    872     if (secs >= SECSPERYEAR)
    873     {
    874         // Increment the number of years and subtract a normal year of time
    875         yrs++;
    876         secs -= SECSPERYEAR;
    877     }
    878 
    879     // If there is still at least a leap year worth of time left
    880     if (secs >= SECSPERLEAPYEAR)
    881     {
    882         // Increment the number of years and subtract a leap year of time
    883         yrs++;
    884         secs -= SECSPERLEAPYEAR;
    885     }
    886 
    887     // Calculate the day of year as the number of days left, then add 1
    888     // because months start on the 1st.
    889     doys  = (unsigned int)((secs / SECSPERDAY) + 1);
    890     secs %= SECSPERDAY;
    891 
    892     // Calculate the hour
    893     hrs   = (unsigned int)(secs / SECSPERHR);
    894     secs %= SECSPERHR;
    895 
    896     // Calculate the minute
    897     mins  = (unsigned int)(secs / SECSPERMIN);
    898     secs %= SECSPERMIN;
    899 
    900     // Fill in the doytime structure
    901     pDt->year   = yrs + TSYNC_TIME_BASE_YEAR;
    902     pDt->doy    = doys;
    903     pDt->hour   = hrs;
    904     pDt->minute = mins;
    905     pDt->second = (unsigned int)secs;
    906     pDt->ns     = pSt->ns;
    907 
    908 } // End DoyTimeFromSecTime
    909 
    910 #else
    911 int refclock_tsyncpci_bs;
    912 #endif /* REFCLOCK */
    913