PPS-Client
2.0.0
Client for synchronizing the system clock to a GPS PPS source
|
Go to the documentation of this file.
41 #include "../client/pps-client.h"
57 extern struct ppsFiles
f;
61 static double rawErrorAvg = 0.0;
78 memset(&
g, 0,
sizeof(
struct G));
84 g.exitOnLostPPS =
true;
86 g.
t3.modes = ADJ_FREQUENCY;
97 printf(
"Invalid value for segregate in pps-client.conf\n");
105 printf(
"Invalid value for segregate in pps-client.conf\n");
164 double avgCorrectionMag = fabs(avgCorrection);
180 if (avgCorrectionMag < ((
double)
g.
hardLimit * 0.25)){
185 else if (avgCorrectionMag > ((
double)
g.
hardLimit * 0.5)){
206 rawErr.binaryInsert(rawError);
218 if (fabs(avg) < fabs(avgBelow)){
249 int maxClamp, posClamp, negClamp;
253 int zeroError = rawError;
264 negClamp = -maxClamp;
267 posClamp = (int)rawErrorAvg + maxClamp;
268 negClamp = (int)rawErrorAvg - maxClamp;
271 if (rawError > posClamp){
272 zeroError = posClamp;
274 else if (rawError < negClamp){
275 zeroError = negClamp;
334 bool isReady =
false;
367 double avgCorrection;
384 return avgCorrection;
397 struct timespec t_now;
399 clock_gettime(CLOCK_REALTIME, &t_now);
400 roundedTime = (int)round((
double)t_now.tv_sec + 1e-9 * (double)t_now.tv_nsec);
416 memset(&
g.
t3, 0,
sizeof(
struct timex));
418 g.
t3.modes = ADJ_SETOFFSET | ADJ_STATUS;
419 g.
t3.status = STA_PLL;
422 g.
t3.time.tv_usec = 0;
426 sprintf(
g.logbuf,
"In setClocktoNISTtime() adjtimex() returned: errno: %d, %s\n", errno, strerror(errno));
433 sprintf(
g.logbuf,
"adjtimex(): Log message will have a timestamp resulting from this correction\n");
455 memset(&
g.
t3, 0,
sizeof(
struct timex));
457 g.
t3.modes = ADJ_SETOFFSET | ADJ_STATUS;
458 g.
t3.status = STA_PLL;
461 g.
t3.time.tv_usec = 0;
465 sprintf(
g.logbuf,
"adjtimex() returned: errno: %d, %s\n", errno, strerror(errno));
469 sprintf(
g.logbuf,
"adjtimex(): Requested correction: %d secs\n",
g.
serialTimeError);
471 sprintf(
g.logbuf,
"adjtimex(): Log message will have a timestamp resulting from this correction\n");
505 if (*count > 600 && *count % 60 == 0){
507 for (
int i = 0; i < len; i++){
511 errorDistrib[idx] += 1.0;
545 bool isDelaySpike =
false;
571 isDelaySpike =
false;
584 isDelaySpike =
false;
677 gettimeofday(&tv1, NULL);
679 g.pps_t_sec = tv1.tv_sec;
681 g.pps_t_usec = -timeCorrection;
682 if (
g.pps_t_usec < 0){
683 g.pps_t_usec = 1000000 - timeCorrection;
687 double timestamp = (double)
g.pps_t_sec + 1e-6 * (
double)
g.pps_t_usec;
706 if (fracSec > 500000){
721 struct timespec t_mono;
725 if (
g.blockDetectClockChange > 0){
726 g.blockDetectClockChange -= 1;
728 if (
g.blockDetectClockChange == 0){
733 clock_gettime(CLOCK_MONOTONIC, &t_mono);
734 g.
t_mono_now = (double)t_mono.tv_sec + 1e-9 * (
double)t_mono.tv_nsec;
736 if (
g.
seq_num < 2 ||
g.startingFromRestore != 0){
740 if (
g.
seq_num == 0 ||
g.startingFromRestore != 0){
745 int iDiff = (int)round(diff);
748 sprintf(
g.logbuf,
"detectMissedPPS(): Missed PPS %d time(s)\n", iDiff-1);
767 bool clockChanged =
false;
769 if (
g.startingFromRestore != 0){
777 sprintf(
g.logbuf,
"detectExteralSystemClockChange() System time changed externally by %d seconds\n", change);
778 writeToLog(
g.logbuf,
"detectExteralSystemClockChange()");
786 sprintf(
g.logbuf,
"detectExteralSystemClockChange() Error in fractional second of %ld microseconds\n", pps_t.tv_usec);
787 writeToLog(
g.logbuf,
"detectExteralSystemClockChange()");
832 memset(&
g.
t3, 0,
sizeof(
struct timex));
834 g.
t3.modes = ADJ_SETOFFSET | ADJ_STATUS;
835 g.
t3.status = STA_PLL;
837 if (correction < 500000){
839 g.
t3.time.tv_sec = -1;
842 else if (correction > 1000000){
843 g.
t3.time.tv_sec = -1;
848 g.
t3.time.tv_sec = 0;
857 sprintf(
g.logbuf,
"adjtimex() returned: errno: %d, %s\n", errno, strerror(errno));
858 writeToLog(
g.logbuf,
"setClockFractionalSecond()");
875 int correction = pps_t->tv_usec;
877 int relCorrection = correction;
878 if (relCorrection >= 1000000){
879 relCorrection = correction - 1000000;
881 else if (relCorrection > 500000){
882 relCorrection = -(1000000 - correction);
885 if (abs(relCorrection) < 15){
891 pps_t->tv_usec = pps_t->tv_usec - correction;
926 if (
g.blockDetectClockChange == 0 &&
944 sysCommand(
"systemctl stop systemd-timesyncd.service");
1003 g.
t3.modes = ADJ_OFFSET_SINGLESHOT;
1020 g.
t3.modes = ADJ_FREQUENCY;
1046 if (
g.
seq_num > 0 &&
g.exit_requested ==
false){
1051 sprintf(
g.logbuf,
"WARNING: PPS interrupt lost\n");
1055 sprintf(
g.logbuf,
"ERROR: Lost PPS for one hour.");
1062 sprintf(
g.logbuf,
"PPS interrupt resumed\n");
1088 struct timespec ts2;
1096 else if (timerVal < 0){
1102 ts2.tv_nsec = timerVal * 1000;
1143 if (!
g.exit_requested){
1144 time_t t = time(NULL);
1145 struct tm *tmp = localtime(&t);
1146 strftime(
g.strbuf,
STRBUF_SZ-1,
"%F %H:%M:%S ", tmp);
1147 strcat(
g.strbuf,
"Read PPS interrupt failed\n");
1151 sprintf(
g.logbuf,
"gps-pps-io PPS read() returned: %d Error: %s\n", rv, strerror(errno));
1158 g.
t.tv_sec =
g.
tm[0];
1160 g.
t.tv_usec =
g.
tm[1];
1164 sprintf(
g.logbuf,
"%s\n",
"makeTimeCorrection() returned -1");
1169 if (
g.startingFromRestore == 0){
1175 sprintf(
g.logbuf,
"pps-client is restarting from SLEW_MAX...\n");
1186 sprintf(
g.logbuf,
"pps-client is restarting from restore...\n");
1252 struct timespec ts2;
1266 strcpy(cmd,
"stty -F ");
1268 sprintf(
g.logbuf,
"\nSerial port, %s, is providing time of day from GPS Satellites\n\n",
g.serialPort);
1271 strcat(cmd,
g.serialPort);
1272 strcat(cmd,
" raw 9600 cs8 clocal -cstopb");
1283 sprintf(
g.logbuf,
"PPS-Client v%s is starting ...\n",
version);
1301 if (
g.startingFromRestore > 0){
1302 g.startingFromRestore -= 1;
1310 if (
g.exit_requested){
1311 sprintf(
g.logbuf,
"PPS-Client stopped.\n");
1316 gettimeofday(&tv1, NULL);
1319 nanosleep(&ts2, NULL);
1331 int rv = pthread_create(&((tcp.
tid)[0]), &(tcp.
attr), (
void* (*)(
void*))&
saveGPSTime, &tcp);
1333 sprintf(
g.logbuf,
"Can't create thread : %s\n", strerror(errno));
1340 sprintf(
g.logbuf,
"Lost PPS or system error. pps-client is exiting.\n");
1378 pthread_cancel((tcp.
tid)[0]);
1411 bool verbose =
false;
1414 if (strcmp(argv[1],
"-v") == 0){
1420 if (prStat == 0 || prStat == -1){
1424 if (geteuid() != 0){
1425 printf(
"pps-client is not running. \"sudo pps-client\" to start.\n");
1437 sprintf(
g.logbuf,
"Fork in main() failed: %s\n", strerror(errno));
1453 rv =
find_source(
f.pps_device, &pps_handle, &pps_mode);
1455 sprintf(
g.logbuf,
"Unable to get PPS source. Exiting.\n");
1456 fprintf(stderr,
"%s",
g.logbuf);
1467 struct sched_param param;
1468 mlockall(MCL_CURRENT | MCL_FUTURE);
1474 rv =
sysCommand(
"systemctl stop systemd-timesyncd.service");
1479 param.sched_priority = 99;
1480 sched_setscheduler(0, SCHED_FIFO, ¶m);
1482 sprintf(
g.msgbuf,
"Process PID: %d\n", ppid);
1487 time_pps_destroy(pps_handle);
void setHardLimit(double avgCorrection)
bool slewIsLow
Set to "true" in getAcquireState() when G.avgSlew is less than SLEW_MAX. This is a precondition for g...
double rawErrorDistrib[ERROR_DISTRIB_LEN]
The distribution of rawError values accumulated in buildRawErrorDistrib().
void savePPStime(int timeCorrection)
double integral[NUM_INTEGRALS]
Array of integrals constructed by makeAverageIntegral().
int correctionAccum
Accumulates G.timeCorrection values from G.correctionFifo in getMovingAverage() in order to generate ...
bool integralIsReady(void)
#define INV_GAIN_0
Controller inverse proportional gain constant at startup.
void makeAverageIntegral(double avgCorrection)
#define PER_NUM_INTEGRALS
Inverse of NUM_INTEGRALS.
int initialize(bool verbose)
void getAvgNoiseLevel(int rawError)
#define MAX_SPIKE_LEVEL
An initialization value for G.minSustainedDelay.
int hardLimit
An adaptive limit value determined by setHardLimit() and applied to G.rawError by clampJitter() as th...
int accessDaemon(int argc, char *argv[])
unsigned int activeCount
Advancing count of active (not skipped) controller cycles once G.isControlling is "true".
void recordFrequencyVars(void)
int correctionFifoCount
Signals that G.correctionFifo contains a full count of G.timeCorrection values.
int clampJitter(int rawError)
void freeSerialThread(timeCheckParams *tcp)
int invProportionalGain
Controller proportional gain configured inversely to use as an int divisor.
#define CLK_CHANGED_LEVEL
double freqOffset
System clock frequency correction calculated as G.integralTimeCorrection * G.integralGain.
#define LARGE_SPIKE
Level above which spikes are are disruptive.
#define NUM_INTEGRALS
Number of integrals used by makeAverageIntegral() to calculate the one minute clock frequency correct...
#define MAX_SPIKES
Maximum microseconds to suppress a burst of continuous positive jitter.
int sysCommand(const char *cmd)
void freeNISTThreads(timeCheckParams *tcp)
#define SLEW_MAX
Jitter slew value below which the controller will begin to frequency lock.
pthread_attr_t attr
Thread attribute object.
void doTimeFixups(struct timeval pps_t)
void writeToLog(char *logbuf, const char *location)
int removeNoise(int rawError)
void writeTimestamp(double timestamp)
class List rawErr(SLEW_LEN)
int allocInitializeNISTThreads(timeCheckParams *tcp)
#define SETTLE_TIME
The PPS-Client up time required before saving performance data.
pthread_t * tid
Thread id.
#define NOISE_ACCUM_RATE
Sets the rate at which G.noiseLevel adjusts to G.rawError.
int interruptLossCount
Records the number of consecutive lost PPS interrupt times.
int timeCorrection
Time correction value constructed in makeTimeCorrection().
unsigned int seq_num
Advancing count of the number of PPS interrupt timings that have been received.
int find_source(const char *path, pps_handle_t *handle, int *avail_mode)
int zeroError
The controller error resulting from removing jitter noise from G.rawError in removeNoise().
#define ADJTIMEX_SCALE
Frequency scaling required by adjtimex().
int nDelaySpikes
Current count of continuous delay spikes made by detectDelaySpike().
int makeSerialTimeQuery(timeCheckParams *tcp)
bool detectExteralSystemClockChange(struct timeval pps_t)
#define PPS_WINDOW
WaitForPPS delay loop time window in which to look for a PPS.
double integralTimeCorrection
Integral or average integral of G.timeCorrection returned by getIntegral();.
void buildRawErrorDistrib(int rawError, double errorDistrib[], unsigned int *count)
#define INTERRUPT_LOST
Number of consecutive lost interrupts at which a warning starts.
struct G g
Declares the global variables defined in pps-client.h.
double slewAccum
Accumulates G.rawError in getTimeSlew() and is used to determine G.avgSlew.
struct ppsFiles f
PPS-Client internal files.
double t_mono_last
Last recorded monotonic time.
void buildJitterDistrib(int rawError)
void saveGPSTime(timeCheckParams *tcp)
int consensusTimeError
Consensus value of whole-second time corrections for DST or leap seconds from Internet NIST servers.
int assignProcessorAffinity(void)
int correctionFifo_idx
Advances G.correctionFifo on each controller cycle in integralIsReady() which returns "true" every 60...
void setClockToGPStime(void)
int correctFractionalSecond(struct timeval *pps_t)
int adjtimex(struct timex *timex)
void makeNISTTimeQuery(timeCheckParams *tcp)
unsigned int ppsCount
Advancing count of G.rawErrorDistrib[] entries made by buildRawErrorDistrib().
bool clampAbsolute
Hard limit relative to zero if true else relative to average G.rawError.
#define ERROR_DISTRIB_LEN
int t_count
Rounded seconds counted at the time of G.t_now.
bool getAcquireState(void)
#define INTEGRAL_GAIN
Controller integral gain constant in active controller operation.
int makeTimeCorrection(struct timeval pps_t)
int integralCount
Counts the integrals formed over the last 10 controller cycles and signals when all integrals in G....
int readPPSTimestamp(pps_handle_t *handle, int *avail_mode, int *tm)
#define RAW_ERROR_ZERO
Index corresponding to rawError == 0 in buildRawErrorDistrib().
void bufferStatusMsg(const char *msg)
double noiseLevel
PPS time delay value beyond which a delay is defined to be a delay spike.
bool interruptReceived
Set "true" when makeTimeCorrection() processes an interrupt time from the Linux PPS device driver.
void setClockFractionalSecond(int correction)
int slewAccum_cnt
Count of the number of times G.rawError has been summed into G.slewAccum.
double getMovingAverage(int timeCorrection)
int readPPS_SetTime(bool verbose, timeCheckParams *tcp, pps_handle_t *pps_handle, int *pps_mode)
double t_mono_now
Current monotonic time.
int cpuVersion
The principle CPU version number for Raspberry Pi processors else 0.
void waitForPPS(bool verbose, pps_handle_t *pps_handle, int *pps_mode)
int t_now
Rounded seconds of current time reported by gettimeofday().
const char * version
Program v2.0.0 updated on 9 Jul 2020.
struct timex t3
Passes G.timeCorrection to the system function adjtimex() in makeTimeCorrection().
double avgSlew
Average slew value determined by getTimeSlew() from the average of G.slewAccum each time G....
bool clockChanged
Set true if an external clock change is detected.
int minSustainedDelay
The observed minimum delay value of a sustained sequence of delay spikes.
int getNearestSecond(void)
int nCores
If PPS-Client is segregated, identifies the number of processor cores.
struct timespec setSyncDelay(int timeAt, int fracSec)
double avgCorrection
A one-minute rolling average of G.timeCorrection values generated by getMovingAverage().
int signedFractionalSeconds(int fracSec)
#define SLEW_LEN
The slew accumulator (slewAccum) update interval.
int bufferStateParams(void)
void TERMhandler(int sig)
#define RAW_ERROR_DECAY
Decay rate for G.rawError samples (1 hour half life)
double integralGain
Current controller integral gain.
void setClocktoNISTtime(void)
void recordOffsets(int timeCorrection)
bool detectDelaySpike(int rawError)
void getTimeSlew(int rawError)
int writeStatusStrings(void)
void buildErrorDistrib(int timeCorrection)
int main(int argc, char *argv[])
bool interruptLost
Set "true" when a PPS interrupt time fails to be received.
int tm[6]
Returns the timestamp from the Linux PPS device driver as a pair of ints.
bool isDelaySpike
Set "true" by detectDelaySpike() when G.rawError exceeds G.noiseLevel.
int allocInitializeSerialThread(timeCheckParams *tcp)
int ppsTimestamp
Fractional second value of the PPS timestamp from the kernel driver.
Struct for program-wide global variables showing those important to the controller.
#define INV_GAIN_1
Controller inverse proportional gain constant during active controller operation.
void detectMissedPPS(void)
int rawError
Signed difference: G.ppsTimestamp - G.zeroOffset in makeTimeCorrection().
double avgIntegral
One-minute average of the integrals in G.integral[].
bool isControlling
Set "true" by getAcquireState() when the control loop can begin to control the system clock frequency...
#define NOISE_LEVEL_MIN
The minimum level at which interrupt delays are delay spikes.
int correctionFifo[OFFSETFIFO_LEN]
Contains the G.timeCorrection values from over the previous 60 seconds.
struct timeval t
Time of system response to the PPS interrupt received from the Linux PPS device driver.
int serialTimeError
Error reported by GPS serial port.S.
int checkPPSInterrupt(void)
bool isVerbose
Enables continuous printing of PPS-Client status params when "true".