3311ff84ea
ntp 4.2.8p5 Reviewed by: cy, roberto Relnotes: yes Differential Revision: https://reviews.freebsd.org/D4828
913 lines
28 KiB
C
913 lines
28 KiB
C
/*******************************************************************************
|
|
*
|
|
* 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 <config.h>
|
|
#endif
|
|
|
|
#if defined(REFCLOCK) && defined(CLOCK_TSYNCPCI)
|
|
|
|
#include <asm/ioctl.h>
|
|
#ifdef HAVE_SYS_IOCTL_H
|
|
# include <sys/ioctl.h>
|
|
#endif
|
|
|
|
#include <stdio.h>
|
|
#include <ctype.h>
|
|
#include <netinet/in.h>
|
|
|
|
|
|
#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
|
|
int refclock_tsyncpci_bs;
|
|
#endif /* REFCLOCK */
|