refclock_tsyncpci.c revision 293896
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*/
111typedef 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*/
126typedef 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*/
140typedef 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*/
156typedef struct NtpTimeObj {
157
158    TimeObj        timeObj;
159    struct timeval tv;
160    unsigned int   refId;
161
162} NtpTimeObj;
163/*
164** TSYNC Supervisor Reference Object
165*/
166typedef 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*/
176typedef struct SecTimeObj
177{
178    unsigned int seconds;
179    unsigned int ns;
180}
181SecTimeObj;
182
183/*
184** TSYNC DOY Time Object
185*/
186typedef 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}
195DoyTimeObj;
196
197/*
198** TSYNC Leap Second Object
199*/
200typedef struct LeapSecondObj
201{
202    int        offset;
203    DoyTimeObj utcDate;
204}
205LeapSecondObj;
206
207/*
208 * structures for ioctl interactions with driver
209 */
210#define DI_PAYLOADS_STARTER_LENGTH 4
211typedef 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 */
236typedef 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 */
246typedef 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*/
256static void tsync_poll     (int unit, struct peer *);
257static void tsync_shutdown (int, struct peer *);
258static int  tsync_start    (int, struct peer *);
259
260/*
261**  Helper functions
262*/
263static void ApplyTimeOffset    (DoyTimeObj* pDt, int off);
264static void SecTimeFromDoyTime (SecTimeObj* pSt, DoyTimeObj* pDt);
265static void DoyTimeFromSecTime (DoyTimeObj* pDt, SecTimeObj* pSt);
266
267/*
268**  Transfer vector
269*/
270struct 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 */
283static 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*******************************************************************************/
319static 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*******************************************************************************/
372static 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*******************************************************************************/
388static 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////////////////////////////////////////////////////////////////////////////////
779void 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////////////////////////////////////////////////////////////////////////////////
807void 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////////////////////////////////////////////////////////////////////////////////
846void 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
911int refclock_tsyncpci_bs;
912#endif /* REFCLOCK */
913