xref: /netbsd-src/external/bsd/ntp/dist/ntpd/refclock_tsyncpci.c (revision eabc0478de71e4e011a5b4e0392741e01d491794)
1*eabc0478Schristos /*	$NetBSD: refclock_tsyncpci.c,v 1.6 2024/08/18 20:47:19 christos Exp $	*/
28585484eSchristos 
38585484eSchristos /*******************************************************************************
48585484eSchristos *
58585484eSchristos *  Module  : refclock_tsyncpci.c
68585484eSchristos *  Date    : 09/08/08
78585484eSchristos *  Purpose : Implements a reference clock driver for the NTP daemon.  This
88585484eSchristos *            reference clock driver provides a means to communicate with
98585484eSchristos *            the Spectracom TSYNC PCI timing devices and use them as a time
108585484eSchristos *            source.
118585484eSchristos *
128585484eSchristos *  (C) Copyright 2008 Spectracom Corporation
138585484eSchristos *
148585484eSchristos *  This software is provided by Spectracom Corporation 'as is' and
158585484eSchristos *  any express or implied warranties, including, but not limited to, the
168585484eSchristos *  implied warranties of merchantability and fitness for a particular purpose
178585484eSchristos *  are disclaimed.  In no event shall Spectracom Corporation be liable
188585484eSchristos *  for any direct, indirect, incidental, special, exemplary, or consequential
198585484eSchristos *  damages (including, but not limited to, procurement of substitute goods
208585484eSchristos *  or services; loss of use, data, or profits; or business interruption)
218585484eSchristos *  however caused and on any theory of liability, whether in contract, strict
228585484eSchristos *  liability, or tort (including negligence or otherwise) arising in any way
238585484eSchristos *  out of the use of this software, even if advised of the possibility of
248585484eSchristos *  such damage.
258585484eSchristos *
268585484eSchristos *  This software is released for distribution according to the NTP copyright
278585484eSchristos *  and license contained in html/copyright.html of NTP source.
288585484eSchristos *
298585484eSchristos *******************************************************************************/
308585484eSchristos #ifdef HAVE_CONFIG_H
318585484eSchristos #include <config.h>
328585484eSchristos #endif
338585484eSchristos 
348585484eSchristos #if defined(REFCLOCK) && defined(CLOCK_TSYNCPCI)
358585484eSchristos 
368585484eSchristos #include <asm/ioctl.h>
378585484eSchristos #ifdef HAVE_SYS_IOCTL_H
388585484eSchristos # include <sys/ioctl.h>
398585484eSchristos #endif
408585484eSchristos 
418585484eSchristos #include <stdio.h>
428585484eSchristos #include <ctype.h>
438585484eSchristos #include <netinet/in.h>
448585484eSchristos 
458585484eSchristos 
468585484eSchristos #include "ntpd.h"
478585484eSchristos #include "ntp_io.h"
488585484eSchristos #include "ntp_refclock.h"
498585484eSchristos #include "ntp_unixtime.h"
508585484eSchristos #include "ntp_stdlib.h"
518585484eSchristos #include "ntp_calendar.h"
528585484eSchristos 
538585484eSchristos 
548585484eSchristos /*******************************************************************************
558585484eSchristos **
568585484eSchristos ** This driver supports the Spectracom TSYNC PCI GPS receiver.  It requires
578585484eSchristos ** that the tsyncpci.o device driver be installed and loaded.
588585484eSchristos **
598585484eSchristos *******************************************************************************/
608585484eSchristos 
618585484eSchristos #define TSYNC_PCI_REVISION "1.11"
628585484eSchristos 
638585484eSchristos /*
648585484eSchristos ** TPRO interface definitions
658585484eSchristos */
668585484eSchristos #define DEVICE      "/dev/tsyncpci"             /* device name */
678585484eSchristos #define PRECISION   (-20)                       /* precision assumed (1 us) */
688585484eSchristos #define DESCRIPTION "Spectracom TSYNC-PCI"      /* WRU */
698585484eSchristos 
708585484eSchristos #define SECONDS_1900_TO_1970 (2208988800U)
718585484eSchristos 
728585484eSchristos #define TSYNC_REF_IID               (0x2500)    // SS CAI, REF IID
738585484eSchristos #define TSYNC_REF_DEST_ID           (0x0001)    // KTS Firmware
748585484eSchristos #define TSYNC_REF_IN_PYLD_OFF       (0)
758585484eSchristos #define TSYNC_REF_IN_LEN            (0)
768585484eSchristos #define TSYNC_REF_OUT_PYLD_OFF      (0)
778585484eSchristos #define TSYNC_REF_OUT_LEN           (8)
788585484eSchristos #define TSYNC_REF_MAX_OUT_LEN       (16)
798585484eSchristos #define TSYNC_REF_PYLD_LEN          (TSYNC_REF_IN_LEN +                     \
808585484eSchristos                                      TSYNC_REF_MAX_OUT_LEN)
818585484eSchristos #define TSYNC_REF_LEN               (4)
828585484eSchristos #define TSYNC_REF_LOCAL             ("LOCL")
838585484eSchristos 
848585484eSchristos #define TSYNC_TMSCL_IID              (0x2301)    // CS CAI, TIMESCALE IID
858585484eSchristos #define TSYNC_TMSCL_DEST_ID          (0x0001)    // KTS Firmware
868585484eSchristos #define TSYNC_TMSCL_IN_PYLD_OFF      (0)
878585484eSchristos #define TSYNC_TMSCL_IN_LEN           (0)
888585484eSchristos #define TSYNC_TMSCL_OUT_PYLD_OFF     (0)
898585484eSchristos #define TSYNC_TMSCL_OUT_LEN          (4)
908585484eSchristos #define TSYNC_TMSCL_MAX_OUT_LEN      (12)
918585484eSchristos #define TSYNC_TMSCL_PYLD_LEN         (TSYNC_TMSCL_IN_LEN +                    \
928585484eSchristos                                      TSYNC_TMSCL_MAX_OUT_LEN)
938585484eSchristos 
948585484eSchristos #define TSYNC_LEAP_IID              (0x2307)    // CS CAI, LEAP SEC IID
958585484eSchristos #define TSYNC_LEAP_DEST_ID          (0x0001)    // KTS Firmware
968585484eSchristos #define TSYNC_LEAP_IN_PYLD_OFF      (0)
978585484eSchristos #define TSYNC_LEAP_IN_LEN           (0)
988585484eSchristos #define TSYNC_LEAP_OUT_PYLD_OFF     (0)
998585484eSchristos #define TSYNC_LEAP_OUT_LEN          (28)
1008585484eSchristos #define TSYNC_LEAP_MAX_OUT_LEN      (36)
1018585484eSchristos #define TSYNC_LEAP_PYLD_LEN         (TSYNC_LEAP_IN_LEN +                    \
1028585484eSchristos                                      TSYNC_LEAP_MAX_OUT_LEN)
1038585484eSchristos 
1048585484eSchristos // These define the base date/time of the system clock.  The system time will
1058585484eSchristos // be tracked as the number of seconds from this date/time.
1068585484eSchristos #define TSYNC_TIME_BASE_YEAR        (1970) // earliest acceptable year
1078585484eSchristos 
1088585484eSchristos #define TSYNC_LCL_STRATUM           (0)
1098585484eSchristos 
1108585484eSchristos /*
1118585484eSchristos ** TSYNC Time Scales type
1128585484eSchristos */
1138585484eSchristos typedef enum
1148585484eSchristos {
1158585484eSchristos     TIME_SCALE_UTC    = 0,   // Universal Coordinated Time
1168585484eSchristos     TIME_SCALE_TAI    = 1,   // International Atomic Time
1178585484eSchristos     TIME_SCALE_GPS    = 2,   // Global Positioning System
1188585484eSchristos     TIME_SCALE_LOCAL  = 3,   // UTC w/local rules for time zone and DST
1198585484eSchristos     NUM_TIME_SCALES   = 4,   // Number of time scales
1208585484eSchristos 
1218585484eSchristos     TIME_SCALE_MAX    = 15   // Maximum number of timescales
1228585484eSchristos 
1238585484eSchristos } TIME_SCALE;
1248585484eSchristos 
1258585484eSchristos /*
1268585484eSchristos ** TSYNC Board Object
1278585484eSchristos */
1288585484eSchristos typedef struct BoardObj {
1298585484eSchristos 
1308585484eSchristos   int            file_descriptor;
1318585484eSchristos   unsigned short devid;
1328585484eSchristos   unsigned short options;
1338585484eSchristos   unsigned char  firmware[5];
1348585484eSchristos   unsigned char  FPGA[5];
1358585484eSchristos   unsigned char  driver[7];
1368585484eSchristos 
1378585484eSchristos } BoardObj;
1388585484eSchristos 
1398585484eSchristos /*
1408585484eSchristos ** TSYNC Time Object
1418585484eSchristos */
1428585484eSchristos typedef struct TimeObj {
1438585484eSchristos 
1448585484eSchristos   unsigned char  syncOption;  /* -M option */
1458585484eSchristos   unsigned int   secsDouble;  /* seconds floating pt */
1468585484eSchristos   unsigned char  seconds;     /* seconds whole num */
1478585484eSchristos   unsigned char  minutes;
1488585484eSchristos   unsigned char  hours;
1498585484eSchristos   unsigned short days;
1508585484eSchristos   unsigned short year;
1518585484eSchristos   unsigned short flags;      /* bit 2 SYNC, bit 1 TCODE; all others 0 */
1528585484eSchristos 
1538585484eSchristos } TimeObj;
1548585484eSchristos 
1558585484eSchristos /*
1568585484eSchristos ** NTP Time Object
1578585484eSchristos */
1588585484eSchristos typedef struct NtpTimeObj {
1598585484eSchristos 
1608585484eSchristos     TimeObj        timeObj;
1618585484eSchristos     struct timeval tv;
1628585484eSchristos     unsigned int   refId;
1638585484eSchristos 
1648585484eSchristos } NtpTimeObj;
1658585484eSchristos /*
1668585484eSchristos ** TSYNC Supervisor Reference Object
1678585484eSchristos */
1688585484eSchristos typedef struct ReferenceObj {
1698585484eSchristos 
1708585484eSchristos     char time[TSYNC_REF_LEN];
1718585484eSchristos     char pps[TSYNC_REF_LEN];
1728585484eSchristos 
1738585484eSchristos } ReferenceObj;
1748585484eSchristos 
1758585484eSchristos /*
1768585484eSchristos ** TSYNC Seconds Time Object
1778585484eSchristos */
1788585484eSchristos typedef struct SecTimeObj
1798585484eSchristos {
1808585484eSchristos     unsigned int seconds;
1818585484eSchristos     unsigned int ns;
1828585484eSchristos }
1838585484eSchristos SecTimeObj;
1848585484eSchristos 
1858585484eSchristos /*
1868585484eSchristos ** TSYNC DOY Time Object
1878585484eSchristos */
1888585484eSchristos typedef struct DoyTimeObj
1898585484eSchristos {
1908585484eSchristos     unsigned int year;
1918585484eSchristos     unsigned int doy;
1928585484eSchristos     unsigned int hour;
1938585484eSchristos     unsigned int minute;
1948585484eSchristos     unsigned int second;
1958585484eSchristos     unsigned int ns;
1968585484eSchristos }
1978585484eSchristos DoyTimeObj;
1988585484eSchristos 
1998585484eSchristos /*
2008585484eSchristos ** TSYNC Leap Second Object
2018585484eSchristos */
2028585484eSchristos typedef struct LeapSecondObj
2038585484eSchristos {
2048585484eSchristos     int        offset;
2058585484eSchristos     DoyTimeObj utcDate;
2068585484eSchristos }
2078585484eSchristos LeapSecondObj;
2088585484eSchristos 
2098585484eSchristos /*
2108585484eSchristos  * structures for ioctl interactions with driver
2118585484eSchristos  */
2128585484eSchristos #define DI_PAYLOADS_STARTER_LENGTH 4
2138585484eSchristos typedef struct ioctl_trans_di {
2148585484eSchristos 
2158585484eSchristos     // input parameters
2168585484eSchristos     uint16_t        dest;
2178585484eSchristos     uint16_t        iid;
2188585484eSchristos 
2198585484eSchristos     uint32_t        inPayloadOffset;
2208585484eSchristos     uint32_t        inLength;
2218585484eSchristos     uint32_t        outPayloadOffset;
2228585484eSchristos     uint32_t        maxOutLength;
2238585484eSchristos 
2248585484eSchristos     // output parameters
2258585484eSchristos     uint32_t        actualOutLength;
2268585484eSchristos     int32_t         status;
2278585484eSchristos 
2288585484eSchristos     // Input and output
2298585484eSchristos 
2308585484eSchristos     // The payloads field MUST be last in ioctl_trans_di.
2318585484eSchristos     uint8_t         payloads[DI_PAYLOADS_STARTER_LENGTH];
2328585484eSchristos 
2338585484eSchristos }ioctl_trans_di;
2348585484eSchristos 
2358585484eSchristos /*
2368585484eSchristos  * structure for looking up a reference ID from a reference name
2378585484eSchristos  */
2388585484eSchristos typedef struct
2398585484eSchristos {
2408585484eSchristos     const char* pRef;           // KTS Reference Name
2418585484eSchristos     const char* pRefId;         // NTP Reference ID
2428585484eSchristos 
2438585484eSchristos } RefIdLookup;
2448585484eSchristos 
2458585484eSchristos /*
2468585484eSchristos  * unit control structure
2478585484eSchristos  */
2488585484eSchristos typedef struct  {
2498585484eSchristos     uint32_t refPrefer;         // Reference prefer flag
2508585484eSchristos     uint32_t refId;             // Host peer reference ID
2518585484eSchristos     uint8_t  refStratum;        // Host peer reference stratum
2528585484eSchristos 
2538585484eSchristos } TsyncUnit;
2548585484eSchristos 
2558585484eSchristos /*
2568585484eSchristos **  Function prototypes
2578585484eSchristos */
2588585484eSchristos static void tsync_poll     (int unit, struct peer *);
2598585484eSchristos static void tsync_shutdown (int, struct peer *);
2608585484eSchristos static int  tsync_start    (int, struct peer *);
2618585484eSchristos 
2628585484eSchristos /*
2638585484eSchristos **  Helper functions
2648585484eSchristos */
2658585484eSchristos static void ApplyTimeOffset    (DoyTimeObj* pDt, int off);
2668585484eSchristos static void SecTimeFromDoyTime (SecTimeObj* pSt, DoyTimeObj* pDt);
2678585484eSchristos static void DoyTimeFromSecTime (DoyTimeObj* pDt, SecTimeObj* pSt);
2688585484eSchristos 
2698585484eSchristos /*
2708585484eSchristos **  Transfer vector
2718585484eSchristos */
2728585484eSchristos struct refclock refclock_tsyncpci = {
2738585484eSchristos     tsync_start,    /* start up driver */
2748585484eSchristos     tsync_shutdown, /* shut down driver */
2758585484eSchristos     tsync_poll,     /* transmit poll message */
2768585484eSchristos     noentry,        /* not used (old tsync_control) */
2778585484eSchristos     noentry,        /* initialize driver (not used) */
2788585484eSchristos     noentry,        /* not used (old tsync_buginfo) */
2798585484eSchristos     NOFLAGS         /* not used */
2808585484eSchristos };
2818585484eSchristos 
2828585484eSchristos /*
2838585484eSchristos  * Reference ID lookup table
2848585484eSchristos  */
2858585484eSchristos static RefIdLookup RefIdLookupTbl[] =
2868585484eSchristos {
2878585484eSchristos     {"gps",  "GPS"},
2888585484eSchristos     {"ir",   "IRIG"},
2898585484eSchristos     {"hvq",  "HVQ"},
2908585484eSchristos     {"frq",  "FREQ"},
2918585484eSchristos     {"mdm",  "ACTS"},
2928585484eSchristos     {"epp",  "PPS"},
2938585484eSchristos     {"ptp",  "PTP"},
2948585484eSchristos     {"asc",  "ATC"},
2958585484eSchristos     {"hst0", "USER"},
2968585484eSchristos     {"hst",  TSYNC_REF_LOCAL},
2978585484eSchristos     {"self", TSYNC_REF_LOCAL},
2988585484eSchristos     {NULL,   NULL}
2998585484eSchristos };
3008585484eSchristos 
3018585484eSchristos /*******************************************************************************
3028585484eSchristos **          IOCTL DEFINITIONS
3038585484eSchristos *******************************************************************************/
3048585484eSchristos #define IOCTL_TPRO_ID            't'
3058585484eSchristos #define IOCTL_TPRO_OPEN          _IOWR(IOCTL_TPRO_ID, 0,  BoardObj)
3068585484eSchristos #define IOCTL_TPRO_GET_NTP_TIME  _IOWR(IOCTL_TPRO_ID, 25, NtpTimeObj)
3078585484eSchristos #define IOCTL_TSYNC_GET          _IOWR(IOCTL_TPRO_ID, 26, ioctl_trans_di)
3088585484eSchristos 
3098585484eSchristos /******************************************************************************
3108585484eSchristos  *
3118585484eSchristos  * Function:    tsync_start()
3128585484eSchristos  * Description: Used to intialize the Spectracom TSYNC reference driver.
3138585484eSchristos  *
3148585484eSchristos  * Parameters:
3158585484eSchristos  *     IN:  unit - not used.
3168585484eSchristos  *         *peer - pointer to this reference clock's peer structure
3178585484eSchristos  *     Returns: 0 - unsuccessful
3188585484eSchristos  *              1 - successful
3198585484eSchristos  *
3208585484eSchristos *******************************************************************************/
3218585484eSchristos static int tsync_start(int unit, struct peer *peer)
3228585484eSchristos {
3238585484eSchristos     struct refclockproc *pp;
3248585484eSchristos     TsyncUnit           *up;
3258585484eSchristos 
3268585484eSchristos 
3278585484eSchristos     /*
3288585484eSchristos     **  initialize reference clock and peer parameters
3298585484eSchristos     */
3308585484eSchristos     pp                = peer->procptr;
3318585484eSchristos     pp->clockdesc     = DESCRIPTION;
3328585484eSchristos     pp->io.clock_recv = noentry;
3338585484eSchristos     pp->io.srcclock   = peer;
3348585484eSchristos     pp->io.datalen    = 0;
3358585484eSchristos     peer->precision   = PRECISION;
3368585484eSchristos 
3378585484eSchristos     // Allocate and initialize unit structure
3388585484eSchristos     if (!(up = (TsyncUnit*)emalloc(sizeof(TsyncUnit))))
3398585484eSchristos     {
3408585484eSchristos         return (0);
3418585484eSchristos     }
3428585484eSchristos 
3438585484eSchristos     // Store reference preference
3448585484eSchristos     up->refPrefer = peer->flags & FLAG_PREFER;
3458585484eSchristos 
3468585484eSchristos     // Initialize reference stratum level and ID
3478585484eSchristos     up->refStratum = STRATUM_UNSPEC;
3488585484eSchristos     strncpy((char *)&up->refId, TSYNC_REF_LOCAL, TSYNC_REF_LEN);
3498585484eSchristos 
3508585484eSchristos     // Attach unit structure
3518585484eSchristos     pp->unitptr = (caddr_t)up;
3528585484eSchristos 
3538585484eSchristos     /* Declare our refId as local in the beginning because we do not know
3548585484eSchristos      * what our actual refid is yet.
3558585484eSchristos      */
3568585484eSchristos     strncpy((char *)&pp->refid, TSYNC_REF_LOCAL, TSYNC_REF_LEN);
3578585484eSchristos 
3588585484eSchristos     return (1);
3598585484eSchristos 
3608585484eSchristos } /* End - tsync_start() */
3618585484eSchristos 
3628585484eSchristos /*******************************************************************************
3638585484eSchristos **
3648585484eSchristos ** Function:    tsync_shutdown()
3658585484eSchristos ** Description: Handles anything related to shutting down the reference clock
3668585484eSchristos **              driver. Nothing at this point in time.
3678585484eSchristos **
3688585484eSchristos ** Parameters:
3698585484eSchristos **     IN:  unit - not used.
3708585484eSchristos **         *peer - pointer to this reference clock's peer structure
3718585484eSchristos **     Returns: none.
3728585484eSchristos **
3738585484eSchristos *******************************************************************************/
3748585484eSchristos static void tsync_shutdown(int unit, struct peer *peer)
3758585484eSchristos {
3768585484eSchristos 
3778585484eSchristos } /* End - tsync_shutdown() */
3788585484eSchristos 
3798585484eSchristos /******************************************************************************
3808585484eSchristos  *
3818585484eSchristos  * Function:    tsync_poll()
3828585484eSchristos  * Description: Retrieve time from the TSYNC device.
3838585484eSchristos  *
3848585484eSchristos  * Parameters:
3858585484eSchristos  *     IN:  unit - not used.
3868585484eSchristos  *         *peer - pointer to this reference clock's peer structure
3878585484eSchristos  *     Returns: none.
3888585484eSchristos  *
3898585484eSchristos *******************************************************************************/
3908585484eSchristos static void tsync_poll(int unit, struct peer *peer)
3918585484eSchristos {
3928585484eSchristos     char                 device[32];
3938585484eSchristos     struct refclockproc *pp;
3948585484eSchristos     struct calendar      jt;
3958585484eSchristos     TsyncUnit           *up;
3968585484eSchristos     unsigned char        synch;
3978585484eSchristos     double               seconds;
3988585484eSchristos     int                  err;
3998585484eSchristos     int                  err1;
4008585484eSchristos     int                  err2;
4018585484eSchristos     int                  err3;
4028585484eSchristos     int                  i;
4038585484eSchristos     int                  j;
4048585484eSchristos     unsigned int         itAllocationLength;
4058585484eSchristos     unsigned int         itAllocationLength1;
4068585484eSchristos     unsigned int         itAllocationLength2;
4078585484eSchristos     NtpTimeObj           TimeContext;
4088585484eSchristos     BoardObj             hBoard;
4098585484eSchristos     char                 timeRef[TSYNC_REF_LEN + 1];
4108585484eSchristos     char                 ppsRef [TSYNC_REF_LEN + 1];
4118585484eSchristos     TIME_SCALE           tmscl = TIME_SCALE_UTC;
4128585484eSchristos     LeapSecondObj        leapSec;
4138585484eSchristos     ioctl_trans_di      *it;
4148585484eSchristos     ioctl_trans_di      *it1;
4158585484eSchristos     ioctl_trans_di      *it2;
4168585484eSchristos     l_fp                 offset;
4178585484eSchristos     l_fp                 ltemp;
4188585484eSchristos     ReferenceObj *	 pRefObj;
4198585484eSchristos 
4208585484eSchristos 
4218585484eSchristos     /* Construct the device name */
4228585484eSchristos     sprintf(device, "%s%d", DEVICE, (int)peer->refclkunit);
4238585484eSchristos 
4248585484eSchristos     printf("Polling device number %d...\n", (int)peer->refclkunit);
4258585484eSchristos 
4268585484eSchristos     /* Open the TSYNC device */
4278585484eSchristos     hBoard.file_descriptor = open(device, O_RDONLY | O_NDELAY, 0777);
4288585484eSchristos 
4298585484eSchristos     /* If error opening TSYNC device... */
4308585484eSchristos     if (hBoard.file_descriptor < 0)
4318585484eSchristos     {
4328585484eSchristos         msyslog(LOG_ERR, "Couldn't open device");
4338585484eSchristos         return;
4348585484eSchristos     }
4358585484eSchristos 
4368585484eSchristos     /* If error while initializing the board... */
4378585484eSchristos     if (ioctl(hBoard.file_descriptor, IOCTL_TPRO_OPEN, &hBoard) < 0)
4388585484eSchristos     {
4398585484eSchristos         msyslog(LOG_ERR, "Couldn't initialize device");
4408585484eSchristos         close(hBoard.file_descriptor);
4418585484eSchristos         return;
4428585484eSchristos     }
4438585484eSchristos 
4448585484eSchristos     /* Allocate memory for ioctl message */
4458585484eSchristos     itAllocationLength =
4468585484eSchristos         (sizeof(ioctl_trans_di) - DI_PAYLOADS_STARTER_LENGTH) +
4478585484eSchristos         TSYNC_REF_IN_LEN + TSYNC_REF_MAX_OUT_LEN;
4488585484eSchristos 
4498585484eSchristos     it = (ioctl_trans_di*)alloca(itAllocationLength);
4508585484eSchristos     if (it == NULL) {
4518585484eSchristos         msyslog(LOG_ERR, "Couldn't allocate transaction memory - Reference");
4528585484eSchristos         return;
4538585484eSchristos     }
4548585484eSchristos 
4558585484eSchristos     /* Build SS_GetRef ioctl message */
4568585484eSchristos     it->dest             = TSYNC_REF_DEST_ID;
4578585484eSchristos     it->iid              = TSYNC_REF_IID;
4588585484eSchristos     it->inPayloadOffset  = TSYNC_REF_IN_PYLD_OFF;
4598585484eSchristos     it->inLength         = TSYNC_REF_IN_LEN;
4608585484eSchristos     it->outPayloadOffset = TSYNC_REF_OUT_PYLD_OFF;
4618585484eSchristos     it->maxOutLength     = TSYNC_REF_MAX_OUT_LEN;
4628585484eSchristos     it->actualOutLength  = 0;
4638585484eSchristos     it->status           = 0;
4648585484eSchristos     memset(it->payloads, 0, TSYNC_REF_MAX_OUT_LEN);
4658585484eSchristos 
4668585484eSchristos     /* Read the reference from the TSYNC-PCI device */
4678585484eSchristos     err = ioctl(hBoard.file_descriptor,
4688585484eSchristos                  IOCTL_TSYNC_GET,
4698585484eSchristos                 (char *)it);
4708585484eSchristos 
4718585484eSchristos     /* Allocate memory for ioctl message */
4728585484eSchristos     itAllocationLength1 =
4738585484eSchristos         (sizeof(ioctl_trans_di) - DI_PAYLOADS_STARTER_LENGTH) +
4748585484eSchristos         TSYNC_TMSCL_IN_LEN + TSYNC_TMSCL_MAX_OUT_LEN;
4758585484eSchristos 
4768585484eSchristos     it1 = (ioctl_trans_di*)alloca(itAllocationLength1);
4778585484eSchristos     if (it1 == NULL) {
4788585484eSchristos         msyslog(LOG_ERR, "Couldn't allocate transaction memory - Time Scale");
4798585484eSchristos         return;
4808585484eSchristos     }
4818585484eSchristos 
4828585484eSchristos     /* Build CS_GetTimeScale ioctl message */
4838585484eSchristos     it1->dest             = TSYNC_TMSCL_DEST_ID;
4848585484eSchristos     it1->iid              = TSYNC_TMSCL_IID;
4858585484eSchristos     it1->inPayloadOffset  = TSYNC_TMSCL_IN_PYLD_OFF;
4868585484eSchristos     it1->inLength         = TSYNC_TMSCL_IN_LEN;
4878585484eSchristos     it1->outPayloadOffset = TSYNC_TMSCL_OUT_PYLD_OFF;
4888585484eSchristos     it1->maxOutLength     = TSYNC_TMSCL_MAX_OUT_LEN;
4898585484eSchristos     it1->actualOutLength  = 0;
4908585484eSchristos     it1->status           = 0;
4918585484eSchristos     memset(it1->payloads, 0, TSYNC_TMSCL_MAX_OUT_LEN);
4928585484eSchristos 
4938585484eSchristos     /* Read the Time Scale info from the TSYNC-PCI device */
4948585484eSchristos     err1 = ioctl(hBoard.file_descriptor,
4958585484eSchristos                  IOCTL_TSYNC_GET,
4968585484eSchristos                  (char *)it1);
4978585484eSchristos 
4988585484eSchristos     /* Allocate memory for ioctl message */
4998585484eSchristos     itAllocationLength2 =
5008585484eSchristos         (sizeof(ioctl_trans_di) - DI_PAYLOADS_STARTER_LENGTH) +
5018585484eSchristos         TSYNC_LEAP_IN_LEN + TSYNC_LEAP_MAX_OUT_LEN;
5028585484eSchristos 
5038585484eSchristos     it2 = (ioctl_trans_di*)alloca(itAllocationLength2);
5048585484eSchristos     if (it2 == NULL) {
5058585484eSchristos         msyslog(LOG_ERR, "Couldn't allocate transaction memory - Leap Second");
5068585484eSchristos         return;
5078585484eSchristos     }
5088585484eSchristos 
5098585484eSchristos     /* Build CS_GetLeapSec ioctl message */
5108585484eSchristos     it2->dest             = TSYNC_LEAP_DEST_ID;
5118585484eSchristos     it2->iid              = TSYNC_LEAP_IID;
5128585484eSchristos     it2->inPayloadOffset  = TSYNC_LEAP_IN_PYLD_OFF;
5138585484eSchristos     it2->inLength         = TSYNC_LEAP_IN_LEN;
5148585484eSchristos     it2->outPayloadOffset = TSYNC_LEAP_OUT_PYLD_OFF;
5158585484eSchristos     it2->maxOutLength     = TSYNC_LEAP_MAX_OUT_LEN;
5168585484eSchristos     it2->actualOutLength  = 0;
5178585484eSchristos     it2->status           = 0;
5188585484eSchristos     memset(it2->payloads, 0, TSYNC_LEAP_MAX_OUT_LEN);
5198585484eSchristos 
5208585484eSchristos     /* Read the leap seconds info from the TSYNC-PCI device */
5218585484eSchristos     err2 = ioctl(hBoard.file_descriptor,
5228585484eSchristos                  IOCTL_TSYNC_GET,
5238585484eSchristos                  (char *)it2);
5248585484eSchristos 
5258585484eSchristos     pp = peer->procptr;
5268585484eSchristos     up = (TsyncUnit*)pp->unitptr;
5278585484eSchristos 
5288585484eSchristos     /* Read the time from the TSYNC-PCI device */
5298585484eSchristos     err3 = ioctl(hBoard.file_descriptor,
5308585484eSchristos                  IOCTL_TPRO_GET_NTP_TIME,
5318585484eSchristos                  (char *)&TimeContext);
5328585484eSchristos 
5338585484eSchristos     /* Close the TSYNC device */
5348585484eSchristos     close(hBoard.file_descriptor);
5358585484eSchristos 
5368585484eSchristos     // Check for errors
5378585484eSchristos     if ((err < 0) ||(err1 < 0) || (err2 < 0) || (err3 < 0) ||
5388585484eSchristos         (it->status != 0) || (it1->status != 0) || (it2->status != 0) ||
5398585484eSchristos         (it->actualOutLength  != TSYNC_REF_OUT_LEN) ||
5408585484eSchristos         (it1->actualOutLength != TSYNC_TMSCL_OUT_LEN) ||
5418585484eSchristos         (it2->actualOutLength != TSYNC_LEAP_OUT_LEN)) {
5428585484eSchristos         refclock_report(peer, CEVNT_FAULT);
5438585484eSchristos         return;
5448585484eSchristos     }
5458585484eSchristos 
5468585484eSchristos     // Extract reference identifiers from ioctl payload
5478585484eSchristos     memset(timeRef, '\0', sizeof(timeRef));
5488585484eSchristos     memset(ppsRef, '\0', sizeof(ppsRef));
5498585484eSchristos     pRefObj = (void *)it->payloads;
5508585484eSchristos     memcpy(timeRef, pRefObj->time, TSYNC_REF_LEN);
5518585484eSchristos     memcpy(ppsRef, pRefObj->pps, TSYNC_REF_LEN);
5528585484eSchristos 
5538585484eSchristos     // Extract the Clock Service Time Scale and convert to correct byte order
5548b8da087Schristos     memcpy(&tmscl, it1->payloads, sizeof(tmscl));
5558585484eSchristos     tmscl = ntohl(tmscl);
5568585484eSchristos 
5578585484eSchristos     // Extract leap second info from ioctl payload and perform byte swapping
5588585484eSchristos     for (i = 0; i < (sizeof(leapSec) / 4); i++)
5598585484eSchristos     {
5608585484eSchristos         for (j = 0; j < 4; j++)
5618585484eSchristos         {
5628585484eSchristos             ((unsigned char*)&leapSec)[(i * 4) + j] =
5638585484eSchristos                     ((unsigned char*)(it2->payloads))[(i * 4) + (3 - j)];
5648585484eSchristos         }
5658585484eSchristos     }
5668585484eSchristos 
5678585484eSchristos     // Determine time reference ID from reference name
5688585484eSchristos     for (i = 0; RefIdLookupTbl[i].pRef != NULL; i++)
5698585484eSchristos     {
5708585484eSchristos        // Search RefID table
5718585484eSchristos        if (strstr(timeRef, RefIdLookupTbl[i].pRef) != NULL)
5728585484eSchristos        {
5738585484eSchristos           // Found the matching string
5748585484eSchristos           break;
5758585484eSchristos        }
5768585484eSchristos     }
5778585484eSchristos 
5788585484eSchristos     // Determine pps reference ID from reference name
5798585484eSchristos     for (j = 0; RefIdLookupTbl[j].pRef != NULL; j++)
5808585484eSchristos     {
5818585484eSchristos        // Search RefID table
5828585484eSchristos        if (strstr(ppsRef, RefIdLookupTbl[j].pRef) != NULL)
5838585484eSchristos        {
5848585484eSchristos           // Found the matching string
5858585484eSchristos           break;
5868585484eSchristos        }
5878585484eSchristos     }
5888585484eSchristos 
5898585484eSchristos     // Determine synchronization state from flags
5908585484eSchristos     synch = (TimeContext.timeObj.flags == 0x4) ? 1 : 0;
5918585484eSchristos 
5928585484eSchristos     // Pull seconds information from time object
5938585484eSchristos     seconds = (double) (TimeContext.timeObj.secsDouble);
5948585484eSchristos     seconds /= (double) 1000000.0;
5958585484eSchristos 
5968585484eSchristos     /*
5978585484eSchristos     ** Convert the number of microseconds to double and then place in the
5988585484eSchristos     ** peer's last received long floating point format.
5998585484eSchristos     */
6008585484eSchristos     DTOLFP(((double)TimeContext.tv.tv_usec / 1000000.0), &pp->lastrec);
6018585484eSchristos 
6028585484eSchristos     /*
6038585484eSchristos     ** The specTimeStamp is the number of seconds since 1/1/1970, while the
6048585484eSchristos     ** peer's lastrec time should be compatible with NTP which is seconds since
6058585484eSchristos     ** 1/1/1900.  So Add the number of seconds between 1900 and 1970 to the
6068585484eSchristos     ** specTimeStamp and place in the peer's lastrec long floating point struct.
6078585484eSchristos     */
6088585484eSchristos     pp->lastrec.Ul_i.Xl_ui += (unsigned int)TimeContext.tv.tv_sec +
6098585484eSchristos                                             SECONDS_1900_TO_1970;
6108585484eSchristos 
6118585484eSchristos     pp->polls++;
6128585484eSchristos 
6138585484eSchristos     /*
6148585484eSchristos     **  set the reference clock object
6158585484eSchristos     */
6168585484eSchristos     sprintf(pp->a_lastcode, "%03d %02d:%02d:%02.6f",
6178585484eSchristos             TimeContext.timeObj.days, TimeContext.timeObj.hours,
6188585484eSchristos             TimeContext.timeObj.minutes, seconds);
6198585484eSchristos 
6208585484eSchristos     pp->lencode = strlen (pp->a_lastcode);
6218585484eSchristos     pp->day     = TimeContext.timeObj.days;
6228585484eSchristos     pp->hour    = TimeContext.timeObj.hours;
6238585484eSchristos     pp->minute  = TimeContext.timeObj.minutes;
6248585484eSchristos     pp->second  = (int) seconds;
6258585484eSchristos     seconds     = (seconds - (double) (pp->second / 1.0)) * 1000000000;
6268585484eSchristos     pp->nsec    = (long) seconds;
6278585484eSchristos 
6288585484eSchristos     /*
6298585484eSchristos     **  calculate year start
6308585484eSchristos     */
6318585484eSchristos     jt.year       = TimeContext.timeObj.year;
6328585484eSchristos     jt.yearday    = 1;
6338585484eSchristos     jt.monthday   = 1;
6348585484eSchristos     jt.month      = 1;
6358585484eSchristos     jt.hour       = 0;
6368585484eSchristos     jt.minute     = 0;
6378585484eSchristos     jt.second     = 0;
6388585484eSchristos     pp->yearstart = caltontp(&jt);
6398585484eSchristos 
6408585484eSchristos     // Calculate and report reference clock offset
6418585484eSchristos     offset.l_ui = (long)(((pp->day - 1) * 24) + pp->hour + GMT);
6428585484eSchristos     offset.l_ui = (offset.l_ui * 60) + (long)pp->minute;
6438585484eSchristos     offset.l_ui = (offset.l_ui * 60) + (long)pp->second;
6448585484eSchristos     offset.l_ui = offset.l_ui + (long)pp->yearstart;
6458585484eSchristos     offset.l_uf = 0;
6468585484eSchristos     DTOLFP(pp->nsec / 1e9, &ltemp);
6478585484eSchristos     L_ADD(&offset, &ltemp);
6488585484eSchristos     refclock_process_offset(pp, offset, pp->lastrec,
6498585484eSchristos                             pp->fudgetime1);
6508585484eSchristos 
6518585484eSchristos     // KTS in sync
6528585484eSchristos     if (synch) {
6538585484eSchristos         // Subtract leap second info by one second to determine effective day
6548585484eSchristos         ApplyTimeOffset(&(leapSec.utcDate), -1);
6558585484eSchristos 
6568585484eSchristos         // If there is a leap second today and the KTS is using a time scale
6578585484eSchristos         // which handles leap seconds then
6588585484eSchristos         if ((tmscl != TIME_SCALE_GPS) && (tmscl != TIME_SCALE_TAI) &&
6598585484eSchristos             (leapSec.utcDate.year == (unsigned int)TimeContext.timeObj.year) &&
6608585484eSchristos             (leapSec.utcDate.doy  == (unsigned int)TimeContext.timeObj.days))
6618585484eSchristos         {
6628585484eSchristos             // If adding a second
6638585484eSchristos             if (leapSec.offset == 1)
6648585484eSchristos             {
6658585484eSchristos                 pp->leap = LEAP_ADDSECOND;
6668585484eSchristos             }
6678585484eSchristos             // Else if removing a second
6688585484eSchristos             else if (leapSec.offset == -1)
6698585484eSchristos             {
6708585484eSchristos                 pp->leap = LEAP_DELSECOND;
6718585484eSchristos             }
6728585484eSchristos             // Else report no leap second pending (no handling of offsets
6738585484eSchristos             // other than +1 or -1)
6748585484eSchristos             else
6758585484eSchristos             {
6768585484eSchristos                 pp->leap = LEAP_NOWARNING;
6778585484eSchristos             }
6788585484eSchristos         }
6798585484eSchristos         // Else report no leap second pending
6808585484eSchristos         else
6818585484eSchristos         {
6828585484eSchristos             pp->leap = LEAP_NOWARNING;
6838585484eSchristos         }
6848585484eSchristos 
6858585484eSchristos         peer->leap = pp->leap;
6868585484eSchristos         refclock_report(peer, CEVNT_NOMINAL);
6878585484eSchristos 
6888585484eSchristos         // If reference name reported, then not in holdover
6898585484eSchristos         if ((RefIdLookupTbl[i].pRef != NULL) &&
6908585484eSchristos             (RefIdLookupTbl[j].pRef != NULL))
6918585484eSchristos         {
6928585484eSchristos             // Determine if KTS being synchronized by host (identified as
6938585484eSchristos             // "LOCL")
6948585484eSchristos             if ((strcmp(RefIdLookupTbl[i].pRefId, TSYNC_REF_LOCAL) == 0) ||
6958585484eSchristos                 (strcmp(RefIdLookupTbl[j].pRefId, TSYNC_REF_LOCAL) == 0))
6968585484eSchristos             {
6978585484eSchristos                 // Clear prefer flag
6988585484eSchristos                 peer->flags &= ~FLAG_PREFER;
6998585484eSchristos 
7008585484eSchristos                 // Set reference clock stratum level as unusable
7018585484eSchristos                 pp->stratum   = STRATUM_UNSPEC;
7028585484eSchristos                 peer->stratum = pp->stratum;
7038585484eSchristos 
7048585484eSchristos                 // If a valid peer is available
7058585484eSchristos                 if ((sys_peer != NULL) && (sys_peer != peer))
7068585484eSchristos                 {
7078585484eSchristos                     // Store reference peer stratum level and ID
7088585484eSchristos                     up->refStratum = sys_peer->stratum;
7098585484eSchristos                     up->refId      = addr2refid(&sys_peer->srcadr);
7108585484eSchristos                 }
7118585484eSchristos             }
7128585484eSchristos             else
7138585484eSchristos             {
7148585484eSchristos                 // Restore prefer flag
7158585484eSchristos                 peer->flags |= up->refPrefer;
7168585484eSchristos 
7178585484eSchristos                 // Store reference stratum as local clock
7188585484eSchristos                 up->refStratum = TSYNC_LCL_STRATUM;
7198585484eSchristos                 strncpy((char *)&up->refId, RefIdLookupTbl[j].pRefId,
7208585484eSchristos                     TSYNC_REF_LEN);
7218585484eSchristos 
7228585484eSchristos                 // Set reference clock stratum level as local clock
7238585484eSchristos                 pp->stratum   = TSYNC_LCL_STRATUM;
7248585484eSchristos                 peer->stratum = pp->stratum;
7258585484eSchristos             }
7268585484eSchristos 
7278585484eSchristos             // Update reference name
7288585484eSchristos             strncpy((char *)&pp->refid, RefIdLookupTbl[j].pRefId,
7298585484eSchristos                 TSYNC_REF_LEN);
7308585484eSchristos             peer->refid = pp->refid;
7318585484eSchristos         }
7328585484eSchristos         // Else in holdover
7338585484eSchristos         else
7348585484eSchristos         {
7358585484eSchristos             // Restore prefer flag
7368585484eSchristos             peer->flags |= up->refPrefer;
7378585484eSchristos 
7388585484eSchristos             // Update reference ID to saved ID
7398585484eSchristos             pp->refid   = up->refId;
7408585484eSchristos             peer->refid = pp->refid;
7418585484eSchristos 
7428585484eSchristos             // Update stratum level to saved stratum level
7438585484eSchristos             pp->stratum   = up->refStratum;
7448585484eSchristos             peer->stratum = pp->stratum;
7458585484eSchristos         }
7468585484eSchristos     }
7478585484eSchristos     // Else KTS not in sync
7488585484eSchristos     else {
7498585484eSchristos         // Place local identifier in peer RefID
7508585484eSchristos         strncpy((char *)&pp->refid, TSYNC_REF_LOCAL, TSYNC_REF_LEN);
7518585484eSchristos         peer->refid = pp->refid;
7528585484eSchristos 
7538585484eSchristos         // Report not in sync
7548585484eSchristos         pp->leap   = LEAP_NOTINSYNC;
7558585484eSchristos         peer->leap = pp->leap;
7568585484eSchristos     }
7578585484eSchristos 
7588585484eSchristos     if (pp->coderecv == pp->codeproc) {
7598585484eSchristos         refclock_report(peer, CEVNT_TIMEOUT);
7608585484eSchristos         return;
7618585484eSchristos     }
7628585484eSchristos 
7638585484eSchristos     record_clock_stats(&peer->srcadr, pp->a_lastcode);
7648585484eSchristos     refclock_receive(peer);
7658585484eSchristos 
7668585484eSchristos     /* Increment the number of times the reference has been polled */
7678585484eSchristos     pp->polls++;
7688585484eSchristos 
7698585484eSchristos } /* End - tsync_poll() */
7708585484eSchristos 
7718585484eSchristos 
7728585484eSchristos ////////////////////////////////////////////////////////////////////////////////
7738585484eSchristos // Function:    ApplyTimeOffset
7748585484eSchristos // Description: The ApplyTimeOffset function adds an offset (in seconds) to a
7758585484eSchristos //              specified date and time.  The specified date and time is passed
7768585484eSchristos //              back after being modified.
7778585484eSchristos //
7788585484eSchristos // Assumptions: 1. Every fourth year is a leap year.  Therefore, this function
7798585484eSchristos //                 is only accurate through Feb 28, 2100.
7808585484eSchristos ////////////////////////////////////////////////////////////////////////////////
7818585484eSchristos void ApplyTimeOffset(DoyTimeObj* pDt, int off)
7828585484eSchristos {
7838585484eSchristos     SecTimeObj st;                  // Time, in seconds
7848585484eSchristos 
7858585484eSchristos 
7868585484eSchristos     // Convert date and time to seconds
7878585484eSchristos     SecTimeFromDoyTime(&st, pDt);
7888585484eSchristos 
7898585484eSchristos     // Apply offset
7908585484eSchristos     st.seconds = (int)((signed long long)st.seconds + (signed long long)off);
7918585484eSchristos 
7928585484eSchristos     // Convert seconds to date and time
7938585484eSchristos     DoyTimeFromSecTime(pDt, &st);
7948585484eSchristos 
7958585484eSchristos } // End ApplyTimeOffset
7968585484eSchristos 
7978585484eSchristos 
7988585484eSchristos ////////////////////////////////////////////////////////////////////////////////
7998585484eSchristos // Function:    SecTimeFromDoyTime
8008585484eSchristos // Description: The SecTimeFromDoyTime function converts a specified date
8018585484eSchristos //              and time into a count of seconds since the base time.  This
8028585484eSchristos //              function operates across the range Base Time to Max Time for
8038585484eSchristos //              the system.
8048585484eSchristos //
8058585484eSchristos // Assumptions: 1. A leap year is any year evenly divisible by 4.  Therefore,
8068585484eSchristos //                 this function is only accurate through Feb 28, 2100.
8078585484eSchristos //              2. Conversion does not account for leap seconds.
8088585484eSchristos ////////////////////////////////////////////////////////////////////////////////
8098585484eSchristos void SecTimeFromDoyTime(SecTimeObj* pSt, DoyTimeObj* pDt)
8108585484eSchristos {
8118585484eSchristos     unsigned int yrs;               // Years
8128585484eSchristos     unsigned int lyrs;              // Leap years
8138585484eSchristos 
8148585484eSchristos 
8158585484eSchristos     // Start with accumulated time of 0
8168585484eSchristos     pSt->seconds  = 0;
8178585484eSchristos 
8188585484eSchristos     // Calculate the number of years and leap years
8198585484eSchristos     yrs           = pDt->year - TSYNC_TIME_BASE_YEAR;
8208585484eSchristos     lyrs          = (yrs + 1) / 4;
8218585484eSchristos 
8228585484eSchristos     // Convert leap years and years
8238585484eSchristos     pSt->seconds += lyrs           * SECSPERLEAPYEAR;
8248585484eSchristos     pSt->seconds += (yrs - lyrs)   * SECSPERYEAR;
8258585484eSchristos 
8268585484eSchristos     // Convert days, hours, minutes and seconds
8278585484eSchristos     pSt->seconds += (pDt->doy - 1) * SECSPERDAY;
8288585484eSchristos     pSt->seconds += pDt->hour      * SECSPERHR;
8298585484eSchristos     pSt->seconds += pDt->minute    * SECSPERMIN;
8308585484eSchristos     pSt->seconds += pDt->second;
8318585484eSchristos 
8328585484eSchristos     // Copy the subseconds count
8338585484eSchristos     pSt->ns       = pDt->ns;
8348585484eSchristos 
8358585484eSchristos } // End SecTimeFromDoyTime
8368585484eSchristos 
8378585484eSchristos 
8388585484eSchristos ////////////////////////////////////////////////////////////////////////////////
8398585484eSchristos // Function:    DoyTimeFromSecTime
8408585484eSchristos // Description: The DoyTimeFromSecTime function converts a specified count
8418585484eSchristos //              of seconds since the start of our base time into a SecTimeObj
8428585484eSchristos //              structure.
8438585484eSchristos //
8448585484eSchristos // Assumptions: 1. A leap year is any year evenly divisible by 4.  Therefore,
8458585484eSchristos //                 this function is only accurate through Feb 28, 2100.
8468585484eSchristos //              2. Conversion does not account for leap seconds.
8478585484eSchristos ////////////////////////////////////////////////////////////////////////////////
8488585484eSchristos void DoyTimeFromSecTime(DoyTimeObj* pDt, SecTimeObj* pSt)
8498585484eSchristos {
8508585484eSchristos     signed long long secs;          // Seconds accumulator variable
8518585484eSchristos     unsigned int     yrs;           // Years accumulator variable
8528585484eSchristos     unsigned int     doys;          // Days accumulator variable
8538585484eSchristos     unsigned int     hrs;           // Hours accumulator variable
8548585484eSchristos     unsigned int     mins;          // Minutes accumulator variable
8558585484eSchristos 
8568585484eSchristos 
8578585484eSchristos     // Convert the seconds count into a signed 64-bit number for calculations
8588585484eSchristos     secs  = (signed long long)(pSt->seconds);
8598585484eSchristos 
8608585484eSchristos     // Calculate the number of 4 year chunks
8618585484eSchristos     yrs   = (unsigned int)((secs /
8628585484eSchristos                            ((SECSPERYEAR * 3) + SECSPERLEAPYEAR)) * 4);
8638585484eSchristos     secs %= ((SECSPERYEAR * 3) + SECSPERLEAPYEAR);
8648585484eSchristos 
8658585484eSchristos     // If there is at least a normal year worth of time left
8668585484eSchristos     if (secs >= SECSPERYEAR)
8678585484eSchristos     {
8688585484eSchristos         // Increment the number of years and subtract a normal year of time
8698585484eSchristos         yrs++;
8708585484eSchristos         secs -= SECSPERYEAR;
8718585484eSchristos     }
8728585484eSchristos 
8738585484eSchristos     // If there is still at least a normal year worth of time left
8748585484eSchristos     if (secs >= SECSPERYEAR)
8758585484eSchristos     {
8768585484eSchristos         // Increment the number of years and subtract a normal year of time
8778585484eSchristos         yrs++;
8788585484eSchristos         secs -= SECSPERYEAR;
8798585484eSchristos     }
8808585484eSchristos 
8818585484eSchristos     // If there is still at least a leap year worth of time left
8828585484eSchristos     if (secs >= SECSPERLEAPYEAR)
8838585484eSchristos     {
8848585484eSchristos         // Increment the number of years and subtract a leap year of time
8858585484eSchristos         yrs++;
8868585484eSchristos         secs -= SECSPERLEAPYEAR;
8878585484eSchristos     }
8888585484eSchristos 
8898585484eSchristos     // Calculate the day of year as the number of days left, then add 1
8908585484eSchristos     // because months start on the 1st.
8918585484eSchristos     doys  = (unsigned int)((secs / SECSPERDAY) + 1);
8928585484eSchristos     secs %= SECSPERDAY;
8938585484eSchristos 
8948585484eSchristos     // Calculate the hour
8958585484eSchristos     hrs   = (unsigned int)(secs / SECSPERHR);
8968585484eSchristos     secs %= SECSPERHR;
8978585484eSchristos 
8988585484eSchristos     // Calculate the minute
8998585484eSchristos     mins  = (unsigned int)(secs / SECSPERMIN);
9008585484eSchristos     secs %= SECSPERMIN;
9018585484eSchristos 
9028585484eSchristos     // Fill in the doytime structure
9038585484eSchristos     pDt->year   = yrs + TSYNC_TIME_BASE_YEAR;
9048585484eSchristos     pDt->doy    = doys;
9058585484eSchristos     pDt->hour   = hrs;
9068585484eSchristos     pDt->minute = mins;
9078585484eSchristos     pDt->second = (unsigned int)secs;
9088585484eSchristos     pDt->ns     = pSt->ns;
9098585484eSchristos 
9108585484eSchristos } // End DoyTimeFromSecTime
9118585484eSchristos 
9128585484eSchristos #else
913*eabc0478Schristos NONEMPTY_TRANSLATION_UNIT
9148585484eSchristos #endif /* REFCLOCK */
915