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