/******************************************************************************* * * Module : refclock_tsyncpci.c * Date : 09/08/08 * Purpose : Implements a reference clock driver for the NTP daemon. This * reference clock driver provides a means to communicate with * the Spectracom TSYNC PCI timing devices and use them as a time * source. * * (C) Copyright 2008 Spectracom Corporation * * This software is provided by Spectracom Corporation 'as is' and * any express or implied warranties, including, but not limited to, the * implied warranties of merchantability and fitness for a particular purpose * are disclaimed. In no event shall Spectracom Corporation be liable * for any direct, indirect, incidental, special, exemplary, or consequential * damages (including, but not limited to, procurement of substitute goods * or services; loss of use, data, or profits; or business interruption) * however caused and on any theory of liability, whether in contract, strict * liability, or tort (including negligence or otherwise) arising in any way * out of the use of this software, even if advised of the possibility of * such damage. * * This software is released for distribution according to the NTP copyright * and license contained in html/copyright.html of NTP source. * *******************************************************************************/ #ifdef HAVE_CONFIG_H #include #endif #if defined(REFCLOCK) && defined(CLOCK_TSYNCPCI) #include #ifdef HAVE_SYS_IOCTL_H # include #endif #include #include #include #include "ntpd.h" #include "ntp_io.h" #include "ntp_refclock.h" #include "ntp_unixtime.h" #include "ntp_stdlib.h" #include "ntp_calendar.h" /******************************************************************************* ** ** This driver supports the Spectracom TSYNC PCI GPS receiver. It requires ** that the tsyncpci.o device driver be installed and loaded. ** *******************************************************************************/ #define TSYNC_PCI_REVISION "1.11" /* ** TPRO interface definitions */ #define DEVICE "/dev/tsyncpci" /* device name */ #define PRECISION (-20) /* precision assumed (1 us) */ #define DESCRIPTION "Spectracom TSYNC-PCI" /* WRU */ #define SECONDS_1900_TO_1970 (2208988800U) #define TSYNC_REF_IID (0x2500) // SS CAI, REF IID #define TSYNC_REF_DEST_ID (0x0001) // KTS Firmware #define TSYNC_REF_IN_PYLD_OFF (0) #define TSYNC_REF_IN_LEN (0) #define TSYNC_REF_OUT_PYLD_OFF (0) #define TSYNC_REF_OUT_LEN (8) #define TSYNC_REF_MAX_OUT_LEN (16) #define TSYNC_REF_PYLD_LEN (TSYNC_REF_IN_LEN + \ TSYNC_REF_MAX_OUT_LEN) #define TSYNC_REF_LEN (4) #define TSYNC_REF_LOCAL ("LOCL") #define TSYNC_TMSCL_IID (0x2301) // CS CAI, TIMESCALE IID #define TSYNC_TMSCL_DEST_ID (0x0001) // KTS Firmware #define TSYNC_TMSCL_IN_PYLD_OFF (0) #define TSYNC_TMSCL_IN_LEN (0) #define TSYNC_TMSCL_OUT_PYLD_OFF (0) #define TSYNC_TMSCL_OUT_LEN (4) #define TSYNC_TMSCL_MAX_OUT_LEN (12) #define TSYNC_TMSCL_PYLD_LEN (TSYNC_TMSCL_IN_LEN + \ TSYNC_TMSCL_MAX_OUT_LEN) #define TSYNC_LEAP_IID (0x2307) // CS CAI, LEAP SEC IID #define TSYNC_LEAP_DEST_ID (0x0001) // KTS Firmware #define TSYNC_LEAP_IN_PYLD_OFF (0) #define TSYNC_LEAP_IN_LEN (0) #define TSYNC_LEAP_OUT_PYLD_OFF (0) #define TSYNC_LEAP_OUT_LEN (28) #define TSYNC_LEAP_MAX_OUT_LEN (36) #define TSYNC_LEAP_PYLD_LEN (TSYNC_LEAP_IN_LEN + \ TSYNC_LEAP_MAX_OUT_LEN) // These define the base date/time of the system clock. The system time will // be tracked as the number of seconds from this date/time. #define TSYNC_TIME_BASE_YEAR (1970) // earliest acceptable year #define TSYNC_LCL_STRATUM (0) /* ** TSYNC Time Scales type */ typedef enum { TIME_SCALE_UTC = 0, // Universal Coordinated Time TIME_SCALE_TAI = 1, // International Atomic Time TIME_SCALE_GPS = 2, // Global Positioning System TIME_SCALE_LOCAL = 3, // UTC w/local rules for time zone and DST NUM_TIME_SCALES = 4, // Number of time scales TIME_SCALE_MAX = 15 // Maximum number of timescales } TIME_SCALE; /* ** TSYNC Board Object */ typedef struct BoardObj { int file_descriptor; unsigned short devid; unsigned short options; unsigned char firmware[5]; unsigned char FPGA[5]; unsigned char driver[7]; } BoardObj; /* ** TSYNC Time Object */ typedef struct TimeObj { unsigned char syncOption; /* -M option */ unsigned int secsDouble; /* seconds floating pt */ unsigned char seconds; /* seconds whole num */ unsigned char minutes; unsigned char hours; unsigned short days; unsigned short year; unsigned short flags; /* bit 2 SYNC, bit 1 TCODE; all others 0 */ } TimeObj; /* ** NTP Time Object */ typedef struct NtpTimeObj { TimeObj timeObj; struct timeval tv; unsigned int refId; } NtpTimeObj; /* ** TSYNC Supervisor Reference Object */ typedef struct ReferenceObj { char time[TSYNC_REF_LEN]; char pps[TSYNC_REF_LEN]; } ReferenceObj; /* ** TSYNC Seconds Time Object */ typedef struct SecTimeObj { unsigned int seconds; unsigned int ns; } SecTimeObj; /* ** TSYNC DOY Time Object */ typedef struct DoyTimeObj { unsigned int year; unsigned int doy; unsigned int hour; unsigned int minute; unsigned int second; unsigned int ns; } DoyTimeObj; /* ** TSYNC Leap Second Object */ typedef struct LeapSecondObj { int offset; DoyTimeObj utcDate; } LeapSecondObj; /* * structures for ioctl interactions with driver */ #define DI_PAYLOADS_STARTER_LENGTH 4 typedef struct ioctl_trans_di { // input parameters uint16_t dest; uint16_t iid; uint32_t inPayloadOffset; uint32_t inLength; uint32_t outPayloadOffset; uint32_t maxOutLength; // output parameters uint32_t actualOutLength; int32_t status; // Input and output // The payloads field MUST be last in ioctl_trans_di. uint8_t payloads[DI_PAYLOADS_STARTER_LENGTH]; }ioctl_trans_di; /* * structure for looking up a reference ID from a reference name */ typedef struct { const char* pRef; // KTS Reference Name const char* pRefId; // NTP Reference ID } RefIdLookup; /* * unit control structure */ typedef struct { uint32_t refPrefer; // Reference prefer flag uint32_t refId; // Host peer reference ID uint8_t refStratum; // Host peer reference stratum } TsyncUnit; /* ** Function prototypes */ static void tsync_poll (int unit, struct peer *); static void tsync_shutdown (int, struct peer *); static int tsync_start (int, struct peer *); /* ** Helper functions */ static void ApplyTimeOffset (DoyTimeObj* pDt, int off); static void SecTimeFromDoyTime (SecTimeObj* pSt, DoyTimeObj* pDt); static void DoyTimeFromSecTime (DoyTimeObj* pDt, SecTimeObj* pSt); /* ** Transfer vector */ struct refclock refclock_tsyncpci = { tsync_start, /* start up driver */ tsync_shutdown, /* shut down driver */ tsync_poll, /* transmit poll message */ noentry, /* not used (old tsync_control) */ noentry, /* initialize driver (not used) */ noentry, /* not used (old tsync_buginfo) */ NOFLAGS /* not used */ }; /* * Reference ID lookup table */ static RefIdLookup RefIdLookupTbl[] = { {"gps", "GPS"}, {"ir", "IRIG"}, {"hvq", "HVQ"}, {"frq", "FREQ"}, {"mdm", "ACTS"}, {"epp", "PPS"}, {"ptp", "PTP"}, {"asc", "ATC"}, {"hst0", "USER"}, {"hst", TSYNC_REF_LOCAL}, {"self", TSYNC_REF_LOCAL}, {NULL, NULL} }; /******************************************************************************* ** IOCTL DEFINITIONS *******************************************************************************/ #define IOCTL_TPRO_ID 't' #define IOCTL_TPRO_OPEN _IOWR(IOCTL_TPRO_ID, 0, BoardObj) #define IOCTL_TPRO_GET_NTP_TIME _IOWR(IOCTL_TPRO_ID, 25, NtpTimeObj) #define IOCTL_TSYNC_GET _IOWR(IOCTL_TPRO_ID, 26, ioctl_trans_di) /****************************************************************************** * * Function: tsync_start() * Description: Used to intialize the Spectracom TSYNC reference driver. * * Parameters: * IN: unit - not used. * *peer - pointer to this reference clock's peer structure * Returns: 0 - unsuccessful * 1 - successful * *******************************************************************************/ static int tsync_start(int unit, struct peer *peer) { struct refclockproc *pp; TsyncUnit *up; /* ** initialize reference clock and peer parameters */ pp = peer->procptr; pp->clockdesc = DESCRIPTION; pp->io.clock_recv = noentry; pp->io.srcclock = peer; pp->io.datalen = 0; peer->precision = PRECISION; // Allocate and initialize unit structure if (!(up = (TsyncUnit*)emalloc(sizeof(TsyncUnit)))) { return (0); } // Store reference preference up->refPrefer = peer->flags & FLAG_PREFER; // Initialize reference stratum level and ID up->refStratum = STRATUM_UNSPEC; strncpy((char *)&up->refId, TSYNC_REF_LOCAL, TSYNC_REF_LEN); // Attach unit structure pp->unitptr = (caddr_t)up; /* Declare our refId as local in the beginning because we do not know * what our actual refid is yet. */ strncpy((char *)&pp->refid, TSYNC_REF_LOCAL, TSYNC_REF_LEN); return (1); } /* End - tsync_start() */ /******************************************************************************* ** ** Function: tsync_shutdown() ** Description: Handles anything related to shutting down the reference clock ** driver. Nothing at this point in time. ** ** Parameters: ** IN: unit - not used. ** *peer - pointer to this reference clock's peer structure ** Returns: none. ** *******************************************************************************/ static void tsync_shutdown(int unit, struct peer *peer) { } /* End - tsync_shutdown() */ /****************************************************************************** * * Function: tsync_poll() * Description: Retrieve time from the TSYNC device. * * Parameters: * IN: unit - not used. * *peer - pointer to this reference clock's peer structure * Returns: none. * *******************************************************************************/ static void tsync_poll(int unit, struct peer *peer) { char device[32]; struct refclockproc *pp; struct calendar jt; TsyncUnit *up; unsigned char synch; double seconds; int err; int err1; int err2; int err3; int i; int j; unsigned int itAllocationLength; unsigned int itAllocationLength1; unsigned int itAllocationLength2; NtpTimeObj TimeContext; BoardObj hBoard; char timeRef[TSYNC_REF_LEN + 1]; char ppsRef [TSYNC_REF_LEN + 1]; TIME_SCALE tmscl = TIME_SCALE_UTC; LeapSecondObj leapSec; ioctl_trans_di *it; ioctl_trans_di *it1; ioctl_trans_di *it2; l_fp offset; l_fp ltemp; ReferenceObj * pRefObj; /* Construct the device name */ sprintf(device, "%s%d", DEVICE, (int)peer->refclkunit); printf("Polling device number %d...\n", (int)peer->refclkunit); /* Open the TSYNC device */ hBoard.file_descriptor = open(device, O_RDONLY | O_NDELAY, 0777); /* If error opening TSYNC device... */ if (hBoard.file_descriptor < 0) { msyslog(LOG_ERR, "Couldn't open device"); return; } /* If error while initializing the board... */ if (ioctl(hBoard.file_descriptor, IOCTL_TPRO_OPEN, &hBoard) < 0) { msyslog(LOG_ERR, "Couldn't initialize device"); close(hBoard.file_descriptor); return; } /* Allocate memory for ioctl message */ itAllocationLength = (sizeof(ioctl_trans_di) - DI_PAYLOADS_STARTER_LENGTH) + TSYNC_REF_IN_LEN + TSYNC_REF_MAX_OUT_LEN; it = (ioctl_trans_di*)alloca(itAllocationLength); if (it == NULL) { msyslog(LOG_ERR, "Couldn't allocate transaction memory - Reference"); return; } /* Build SS_GetRef ioctl message */ it->dest = TSYNC_REF_DEST_ID; it->iid = TSYNC_REF_IID; it->inPayloadOffset = TSYNC_REF_IN_PYLD_OFF; it->inLength = TSYNC_REF_IN_LEN; it->outPayloadOffset = TSYNC_REF_OUT_PYLD_OFF; it->maxOutLength = TSYNC_REF_MAX_OUT_LEN; it->actualOutLength = 0; it->status = 0; memset(it->payloads, 0, TSYNC_REF_MAX_OUT_LEN); /* Read the reference from the TSYNC-PCI device */ err = ioctl(hBoard.file_descriptor, IOCTL_TSYNC_GET, (char *)it); /* Allocate memory for ioctl message */ itAllocationLength1 = (sizeof(ioctl_trans_di) - DI_PAYLOADS_STARTER_LENGTH) + TSYNC_TMSCL_IN_LEN + TSYNC_TMSCL_MAX_OUT_LEN; it1 = (ioctl_trans_di*)alloca(itAllocationLength1); if (it1 == NULL) { msyslog(LOG_ERR, "Couldn't allocate transaction memory - Time Scale"); return; } /* Build CS_GetTimeScale ioctl message */ it1->dest = TSYNC_TMSCL_DEST_ID; it1->iid = TSYNC_TMSCL_IID; it1->inPayloadOffset = TSYNC_TMSCL_IN_PYLD_OFF; it1->inLength = TSYNC_TMSCL_IN_LEN; it1->outPayloadOffset = TSYNC_TMSCL_OUT_PYLD_OFF; it1->maxOutLength = TSYNC_TMSCL_MAX_OUT_LEN; it1->actualOutLength = 0; it1->status = 0; memset(it1->payloads, 0, TSYNC_TMSCL_MAX_OUT_LEN); /* Read the Time Scale info from the TSYNC-PCI device */ err1 = ioctl(hBoard.file_descriptor, IOCTL_TSYNC_GET, (char *)it1); /* Allocate memory for ioctl message */ itAllocationLength2 = (sizeof(ioctl_trans_di) - DI_PAYLOADS_STARTER_LENGTH) + TSYNC_LEAP_IN_LEN + TSYNC_LEAP_MAX_OUT_LEN; it2 = (ioctl_trans_di*)alloca(itAllocationLength2); if (it2 == NULL) { msyslog(LOG_ERR, "Couldn't allocate transaction memory - Leap Second"); return; } /* Build CS_GetLeapSec ioctl message */ it2->dest = TSYNC_LEAP_DEST_ID; it2->iid = TSYNC_LEAP_IID; it2->inPayloadOffset = TSYNC_LEAP_IN_PYLD_OFF; it2->inLength = TSYNC_LEAP_IN_LEN; it2->outPayloadOffset = TSYNC_LEAP_OUT_PYLD_OFF; it2->maxOutLength = TSYNC_LEAP_MAX_OUT_LEN; it2->actualOutLength = 0; it2->status = 0; memset(it2->payloads, 0, TSYNC_LEAP_MAX_OUT_LEN); /* Read the leap seconds info from the TSYNC-PCI device */ err2 = ioctl(hBoard.file_descriptor, IOCTL_TSYNC_GET, (char *)it2); pp = peer->procptr; up = (TsyncUnit*)pp->unitptr; /* Read the time from the TSYNC-PCI device */ err3 = ioctl(hBoard.file_descriptor, IOCTL_TPRO_GET_NTP_TIME, (char *)&TimeContext); /* Close the TSYNC device */ close(hBoard.file_descriptor); // Check for errors if ((err < 0) ||(err1 < 0) || (err2 < 0) || (err3 < 0) || (it->status != 0) || (it1->status != 0) || (it2->status != 0) || (it->actualOutLength != TSYNC_REF_OUT_LEN) || (it1->actualOutLength != TSYNC_TMSCL_OUT_LEN) || (it2->actualOutLength != TSYNC_LEAP_OUT_LEN)) { refclock_report(peer, CEVNT_FAULT); return; } // Extract reference identifiers from ioctl payload memset(timeRef, '\0', sizeof(timeRef)); memset(ppsRef, '\0', sizeof(ppsRef)); pRefObj = (void *)it->payloads; memcpy(timeRef, pRefObj->time, TSYNC_REF_LEN); memcpy(ppsRef, pRefObj->pps, TSYNC_REF_LEN); // Extract the Clock Service Time Scale and convert to correct byte order memcpy(&tmscl, it1->payloads, sizeof(tmscl)); tmscl = ntohl(tmscl); // Extract leap second info from ioctl payload and perform byte swapping for (i = 0; i < (sizeof(leapSec) / 4); i++) { for (j = 0; j < 4; j++) { ((unsigned char*)&leapSec)[(i * 4) + j] = ((unsigned char*)(it2->payloads))[(i * 4) + (3 - j)]; } } // Determine time reference ID from reference name for (i = 0; RefIdLookupTbl[i].pRef != NULL; i++) { // Search RefID table if (strstr(timeRef, RefIdLookupTbl[i].pRef) != NULL) { // Found the matching string break; } } // Determine pps reference ID from reference name for (j = 0; RefIdLookupTbl[j].pRef != NULL; j++) { // Search RefID table if (strstr(ppsRef, RefIdLookupTbl[j].pRef) != NULL) { // Found the matching string break; } } // Determine synchronization state from flags synch = (TimeContext.timeObj.flags == 0x4) ? 1 : 0; // Pull seconds information from time object seconds = (double) (TimeContext.timeObj.secsDouble); seconds /= (double) 1000000.0; /* ** Convert the number of microseconds to double and then place in the ** peer's last received long floating point format. */ DTOLFP(((double)TimeContext.tv.tv_usec / 1000000.0), &pp->lastrec); /* ** The specTimeStamp is the number of seconds since 1/1/1970, while the ** peer's lastrec time should be compatible with NTP which is seconds since ** 1/1/1900. So Add the number of seconds between 1900 and 1970 to the ** specTimeStamp and place in the peer's lastrec long floating point struct. */ pp->lastrec.Ul_i.Xl_ui += (unsigned int)TimeContext.tv.tv_sec + SECONDS_1900_TO_1970; pp->polls++; /* ** set the reference clock object */ sprintf(pp->a_lastcode, "%03d %02d:%02d:%02.6f", TimeContext.timeObj.days, TimeContext.timeObj.hours, TimeContext.timeObj.minutes, seconds); pp->lencode = strlen (pp->a_lastcode); pp->day = TimeContext.timeObj.days; pp->hour = TimeContext.timeObj.hours; pp->minute = TimeContext.timeObj.minutes; pp->second = (int) seconds; seconds = (seconds - (double) (pp->second / 1.0)) * 1000000000; pp->nsec = (long) seconds; /* ** calculate year start */ jt.year = TimeContext.timeObj.year; jt.yearday = 1; jt.monthday = 1; jt.month = 1; jt.hour = 0; jt.minute = 0; jt.second = 0; pp->yearstart = caltontp(&jt); // Calculate and report reference clock offset offset.l_ui = (long)(((pp->day - 1) * 24) + pp->hour + GMT); offset.l_ui = (offset.l_ui * 60) + (long)pp->minute; offset.l_ui = (offset.l_ui * 60) + (long)pp->second; offset.l_ui = offset.l_ui + (long)pp->yearstart; offset.l_uf = 0; DTOLFP(pp->nsec / 1e9, <emp); L_ADD(&offset, <emp); refclock_process_offset(pp, offset, pp->lastrec, pp->fudgetime1); // KTS in sync if (synch) { // Subtract leap second info by one second to determine effective day ApplyTimeOffset(&(leapSec.utcDate), -1); // If there is a leap second today and the KTS is using a time scale // which handles leap seconds then if ((tmscl != TIME_SCALE_GPS) && (tmscl != TIME_SCALE_TAI) && (leapSec.utcDate.year == (unsigned int)TimeContext.timeObj.year) && (leapSec.utcDate.doy == (unsigned int)TimeContext.timeObj.days)) { // If adding a second if (leapSec.offset == 1) { pp->leap = LEAP_ADDSECOND; } // Else if removing a second else if (leapSec.offset == -1) { pp->leap = LEAP_DELSECOND; } // Else report no leap second pending (no handling of offsets // other than +1 or -1) else { pp->leap = LEAP_NOWARNING; } } // Else report no leap second pending else { pp->leap = LEAP_NOWARNING; } peer->leap = pp->leap; refclock_report(peer, CEVNT_NOMINAL); // If reference name reported, then not in holdover if ((RefIdLookupTbl[i].pRef != NULL) && (RefIdLookupTbl[j].pRef != NULL)) { // Determine if KTS being synchronized by host (identified as // "LOCL") if ((strcmp(RefIdLookupTbl[i].pRefId, TSYNC_REF_LOCAL) == 0) || (strcmp(RefIdLookupTbl[j].pRefId, TSYNC_REF_LOCAL) == 0)) { // Clear prefer flag peer->flags &= ~FLAG_PREFER; // Set reference clock stratum level as unusable pp->stratum = STRATUM_UNSPEC; peer->stratum = pp->stratum; // If a valid peer is available if ((sys_peer != NULL) && (sys_peer != peer)) { // Store reference peer stratum level and ID up->refStratum = sys_peer->stratum; up->refId = addr2refid(&sys_peer->srcadr); } } else { // Restore prefer flag peer->flags |= up->refPrefer; // Store reference stratum as local clock up->refStratum = TSYNC_LCL_STRATUM; strncpy((char *)&up->refId, RefIdLookupTbl[j].pRefId, TSYNC_REF_LEN); // Set reference clock stratum level as local clock pp->stratum = TSYNC_LCL_STRATUM; peer->stratum = pp->stratum; } // Update reference name strncpy((char *)&pp->refid, RefIdLookupTbl[j].pRefId, TSYNC_REF_LEN); peer->refid = pp->refid; } // Else in holdover else { // Restore prefer flag peer->flags |= up->refPrefer; // Update reference ID to saved ID pp->refid = up->refId; peer->refid = pp->refid; // Update stratum level to saved stratum level pp->stratum = up->refStratum; peer->stratum = pp->stratum; } } // Else KTS not in sync else { // Place local identifier in peer RefID strncpy((char *)&pp->refid, TSYNC_REF_LOCAL, TSYNC_REF_LEN); peer->refid = pp->refid; // Report not in sync pp->leap = LEAP_NOTINSYNC; peer->leap = pp->leap; } if (pp->coderecv == pp->codeproc) { refclock_report(peer, CEVNT_TIMEOUT); return; } record_clock_stats(&peer->srcadr, pp->a_lastcode); refclock_receive(peer); /* Increment the number of times the reference has been polled */ pp->polls++; } /* End - tsync_poll() */ //////////////////////////////////////////////////////////////////////////////// // Function: ApplyTimeOffset // Description: The ApplyTimeOffset function adds an offset (in seconds) to a // specified date and time. The specified date and time is passed // back after being modified. // // Assumptions: 1. Every fourth year is a leap year. Therefore, this function // is only accurate through Feb 28, 2100. //////////////////////////////////////////////////////////////////////////////// void ApplyTimeOffset(DoyTimeObj* pDt, int off) { SecTimeObj st; // Time, in seconds // Convert date and time to seconds SecTimeFromDoyTime(&st, pDt); // Apply offset st.seconds = (int)((signed long long)st.seconds + (signed long long)off); // Convert seconds to date and time DoyTimeFromSecTime(pDt, &st); } // End ApplyTimeOffset //////////////////////////////////////////////////////////////////////////////// // Function: SecTimeFromDoyTime // Description: The SecTimeFromDoyTime function converts a specified date // and time into a count of seconds since the base time. This // function operates across the range Base Time to Max Time for // the system. // // Assumptions: 1. A leap year is any year evenly divisible by 4. Therefore, // this function is only accurate through Feb 28, 2100. // 2. Conversion does not account for leap seconds. //////////////////////////////////////////////////////////////////////////////// void SecTimeFromDoyTime(SecTimeObj* pSt, DoyTimeObj* pDt) { unsigned int yrs; // Years unsigned int lyrs; // Leap years // Start with accumulated time of 0 pSt->seconds = 0; // Calculate the number of years and leap years yrs = pDt->year - TSYNC_TIME_BASE_YEAR; lyrs = (yrs + 1) / 4; // Convert leap years and years pSt->seconds += lyrs * SECSPERLEAPYEAR; pSt->seconds += (yrs - lyrs) * SECSPERYEAR; // Convert days, hours, minutes and seconds pSt->seconds += (pDt->doy - 1) * SECSPERDAY; pSt->seconds += pDt->hour * SECSPERHR; pSt->seconds += pDt->minute * SECSPERMIN; pSt->seconds += pDt->second; // Copy the subseconds count pSt->ns = pDt->ns; } // End SecTimeFromDoyTime //////////////////////////////////////////////////////////////////////////////// // Function: DoyTimeFromSecTime // Description: The DoyTimeFromSecTime function converts a specified count // of seconds since the start of our base time into a SecTimeObj // structure. // // Assumptions: 1. A leap year is any year evenly divisible by 4. Therefore, // this function is only accurate through Feb 28, 2100. // 2. Conversion does not account for leap seconds. //////////////////////////////////////////////////////////////////////////////// void DoyTimeFromSecTime(DoyTimeObj* pDt, SecTimeObj* pSt) { signed long long secs; // Seconds accumulator variable unsigned int yrs; // Years accumulator variable unsigned int doys; // Days accumulator variable unsigned int hrs; // Hours accumulator variable unsigned int mins; // Minutes accumulator variable // Convert the seconds count into a signed 64-bit number for calculations secs = (signed long long)(pSt->seconds); // Calculate the number of 4 year chunks yrs = (unsigned int)((secs / ((SECSPERYEAR * 3) + SECSPERLEAPYEAR)) * 4); secs %= ((SECSPERYEAR * 3) + SECSPERLEAPYEAR); // If there is at least a normal year worth of time left if (secs >= SECSPERYEAR) { // Increment the number of years and subtract a normal year of time yrs++; secs -= SECSPERYEAR; } // If there is still at least a normal year worth of time left if (secs >= SECSPERYEAR) { // Increment the number of years and subtract a normal year of time yrs++; secs -= SECSPERYEAR; } // If there is still at least a leap year worth of time left if (secs >= SECSPERLEAPYEAR) { // Increment the number of years and subtract a leap year of time yrs++; secs -= SECSPERLEAPYEAR; } // Calculate the day of year as the number of days left, then add 1 // because months start on the 1st. doys = (unsigned int)((secs / SECSPERDAY) + 1); secs %= SECSPERDAY; // Calculate the hour hrs = (unsigned int)(secs / SECSPERHR); secs %= SECSPERHR; // Calculate the minute mins = (unsigned int)(secs / SECSPERMIN); secs %= SECSPERMIN; // Fill in the doytime structure pDt->year = yrs + TSYNC_TIME_BASE_YEAR; pDt->doy = doys; pDt->hour = hrs; pDt->minute = mins; pDt->second = (unsigned int)secs; pDt->ns = pSt->ns; } // End DoyTimeFromSecTime #else NONEMPTY_TRANSLATION_UNIT #endif /* REFCLOCK */