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