PPS-Client  2.0.0
Client for synchronizing the system clock to a GPS PPS source
pps-client.cpp
Go to the documentation of this file.
1 
23 /*
24  * Copyright (C) 2016-2020 Raymond S. Connell
25  *
26  * This program is free software; you can redistribute it and/or modify
27  * it under the terms of the GNU General Public License as published by
28  * the Free Software Foundation; either version 2 of the License, or
29  * (at your option) any later version.
30  *
31  * This program is distributed in the hope that it will be useful,
32  * but WITHOUT ANY WARRANTY; without even the implied warranty of
33  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
34  * GNU General Public License for more details.
35  *
36  * You should have received a copy of the GNU General Public License along
37  * with this program; if not, write to the Free Software Foundation, Inc.,
38  * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
39  */
40 
41 #include "../client/pps-client.h"
42 
46 extern int adjtimex (struct timex *timex);
47 
51 //extern int gettimeofday(struct timeval *tv, struct timezone *tz);
52 
53 const char *version = "2.0.1";
54 
55 struct G g;
56 
57 extern struct ppsFiles f;
58 extern bool writeJitterDistrib;
59 extern bool writeErrorDistrib;
60 
61 static double rawErrorAvg = 0.0; // Variable cannot be in the G struct because
62  // it is cleared on every restart.
63 class List rawErr(SLEW_LEN);
64 bool threadIsRunning = false;
65 bool readState = false;
66 
67 
75 int initialize(bool verbose){
76  int rv;
77 
78  memset(&g, 0, sizeof(struct G));
79 
80  g.isVerbose = verbose;
84  g.exitOnLostPPS = true;
85 
86  g.t3.modes = ADJ_FREQUENCY; // Initialize system clock
87  g.t3.freq = 0; // frequency offset to zero.
88  adjtimex(&g.t3);
89 
90  rv = getConfigs();
91 
93  if (g.cpuVersion == 3){
95 
96  if (g.nCores > 0 && g.nCores != 4){
97  printf("Invalid value for segregate in pps-client.conf\n");
98  g.nCores = 0;
99  }
100  }
101  else if (g.cpuVersion == 4){
103 
104  if (g.nCores > 0 && g.nCores != 4){
105  printf("Invalid value for segregate in pps-client.conf\n");
106  g.nCores = 0;
107  }
108  }
109 
110  if (g.nCores > 0){
112  }
113 // printf("CPU zeroOffset: %d\n", g.zeroOffset);
114 
115  return rv;
116 }
117 
138 bool getAcquireState(void){
139 
140  if (! g.slewIsLow && g.slewAccum_cnt == 0
141  && fabs(g.avgSlew) < SLEW_MAX){ // SLEW_MAX only needs to be low enough
142  g.slewIsLow = true; // that the controller can begin locking
143  } // at limitValue == HARD_LIMIT_NONE
144 
145  return (g.slewIsLow && g.seq_num >= SECS_PER_MINUTE); // The g.seq_num requirement sets a limit on the
146 } // length of time to run the Type 1 controller
147  // that initially pushes avgSlew below SLEW_MAX.
162 void setHardLimit(double avgCorrection){
163 
164  double avgCorrectionMag = fabs(avgCorrection);
165 
168  return;
169  }
170 
171  if (abs(g.avgSlew) > SLEW_MAX){ // As long as average time slew is
172  int d_4 = abs(g.avgSlew) * 4; // outside of range SLEW_MAX this keeps
173  while (g.hardLimit < d_4 // g.hardLimit above 4 * g.avgSlew
174  && g.hardLimit < HARD_LIMIT_NONE){ // which is high enough to allow the
175  g.hardLimit = g.hardLimit << 1; // controller to pull avgSlew within
176  } // SLEW_MAX.
177  return;
178  }
179 
180  if (avgCorrectionMag < ((double)g.hardLimit * 0.25)){ // If avgCorrection is below 1/4 of limitValue
181  if (g.hardLimit > 1){ // and g.hardLimit not 1
182  g.hardLimit = g.hardLimit >> 1; // then halve limitValue.
183  }
184  }
185  else if (avgCorrectionMag > ((double)g.hardLimit * 0.5)){ // If avgCorrection is above 1/2 of limitValue
186  g.hardLimit = g.hardLimit << 1; // then double limitValue.
187 
188  if (g.hardLimit > HARD_LIMIT_NONE){
190  }
191  }
192 
193  return;
194 }
195 
204 void getTimeSlew(int rawError){
205 
206  rawErr.binaryInsert(rawError); // Store rawError samples in sorted order of absolute
207  // delay to allow rawErr.averageBelow() to ignore
208  g.slewAccum_cnt += 1; // the samples above the LARGE_SPIKE level.
209 
210  g.slewAccum += (double)rawError;
211 
212  if (g.slewAccum_cnt >= SLEW_LEN){
213  g.slewAccum_cnt = 0;
214 
215  double avg = g.slewAccum / (double)SLEW_LEN;
216  double avgBelow = rawErr.averageBelow(LARGE_SPIKE); // Do the average only below the LARGE_SPIKE level
217 
218  if (fabs(avg) < fabs(avgBelow)){
219  g.avgSlew = avg;
220  }
221  else {
222  g.avgSlew = avgBelow;
223  }
224 
225  g.slewAccum = 0.0;
226  rawErr.clear();
227  }
228 }
229 
248 int clampJitter(int rawError){
249  int maxClamp, posClamp, negClamp;
250 
251  maxClamp = g.hardLimit;
252 
253  int zeroError = rawError;
254 
255  if (rawErrorAvg < 1.0 && g.hardLimit <= 4){
256  g.clampAbsolute = true;
257  }
258  else if (g.hardLimit >= 16){
259  g.clampAbsolute = false;
260  }
261 
262  if (g.clampAbsolute){
263  posClamp = maxClamp;
264  negClamp = -maxClamp;
265  }
266  else {
267  posClamp = (int)rawErrorAvg + maxClamp;
268  negClamp = (int)rawErrorAvg - maxClamp;
269  }
270 
271  if (rawError > posClamp){
272  zeroError = posClamp;
273  }
274  else if (rawError < negClamp){
275  zeroError = negClamp;
276  }
277 
278  return zeroError;
279 }
280 
295 void makeAverageIntegral(double avgCorrection){
296 
297  int indexOffset = SECS_PER_MINUTE - NUM_INTEGRALS;
298 
299  if (g.correctionFifo_idx >= indexOffset){ // Over the last NUM_INTEGRALS seconds in each minute
300 
301  int i = g.correctionFifo_idx - indexOffset;
302  if (i == 0){
303  g.avgIntegral = 0.0;
304  g.integralCount = 0;
305  }
306 
307  g.integral[i] = g.integral[i] + avgCorrection; // avgCorrection sums into g.integral[i] once each
308  // minute forming the ith integral over the last minute.
309  if (g.hardLimit == HARD_LIMIT_1){
310  g.avgIntegral += g.integral[i]; // Accumulate each integral that is being formed
311  g.integralCount += 1; // into g.avgIntegral for averaging.
312  }
313  }
314 
315  if (g.correctionFifo_idx == SECS_PER_MINUTE - 1 // just before the minute rolls over.
317 
318  g.avgIntegral *= PER_NUM_INTEGRALS; // Normalize g.avgIntegral.
319  }
320 }
321 
333 bool integralIsReady(void){
334  bool isReady = false;
335 
336  if (g.correctionFifo_idx == 0){
337  isReady = true;
338  }
339 
340  g.correctionFifo_idx += 1;
342  g.correctionFifo_idx = 0;
343  }
344 
345  return isReady;
346 }
347 
365 double getMovingAverage(int timeCorrection){
366 
367  double avgCorrection;
368 
369  g.correctionAccum += timeCorrection; // Add the new timeCorrection into the error accumulator.
370 
371  if (g.correctionFifoCount == SECS_PER_MINUTE){ // Once the FIFO is full, maintain the continuous
372  // rolling sum accumulator by subtracting the
373  int oldError = g.correctionFifo[g.correctionFifo_idx];
374  g.correctionAccum -= oldError; // old timeCorrection value at the current correctionFifo_idx.
375  }
376 
377  g.correctionFifo[g.correctionFifo_idx] = timeCorrection; // and replacing the old value in the FIFO with the new.
378 
379  if (g.correctionFifoCount < SECS_PER_MINUTE){ // When correctionFifoCount == SECS_PER_MINUTE
380  g.correctionFifoCount += 1; // the FIFO is full and ready to use.
381  }
382 
383  avgCorrection = g.correctionAccum * PER_MINUTE;
384  return avgCorrection;
385 }
386 
396  int roundedTime;
397  struct timespec t_now;
398 
399  clock_gettime(CLOCK_REALTIME, &t_now);
400  roundedTime = (int)round((double)t_now.tv_sec + 1e-9 * (double)t_now.tv_nsec);
401  return roundedTime;
402 }
403 
416  memset(&g.t3, 0, sizeof(struct timex));
417 
418  g.t3.modes = ADJ_SETOFFSET | ADJ_STATUS;
419  g.t3.status = STA_PLL;
420 
421  g.t3.time.tv_sec = g.consensusTimeError;
422  g.t3.time.tv_usec = 0;
423 
424  int rv = adjtimex(&g.t3);
425  if (rv == -1){
426  sprintf(g.logbuf, "In setClocktoNISTtime() adjtimex() returned: errno: %d, %s\n", errno, strerror(errno));
427  writeToLog(g.logbuf, "setClocktoNISTtime()");
428  }
429  else {
430  sprintf(g.logbuf, "adjtimex(): Requested correction: %d secs\n", g.consensusTimeError);
431  writeToLog(g.logbuf, "setClocktoNISTtime()");
432 
433  sprintf(g.logbuf, "adjtimex(): Log message will have a timestamp resulting from this correction\n");
434  writeToLog(g.logbuf, "setClocktoNISTtime()");
435  }
436 
437  g.consensusTimeError = 0;
438 
439  g.nistTimeUpdated = true;
440 
441  return;
442 }
443 
454 void setClockToGPStime(void){
455  memset(&g.t3, 0, sizeof(struct timex));
456 
457  g.t3.modes = ADJ_SETOFFSET | ADJ_STATUS;
458  g.t3.status = STA_PLL;
459 
460  g.t3.time.tv_sec = g.serialTimeError;
461  g.t3.time.tv_usec = 0;
462 
463  int rv = adjtimex(&g.t3);
464  if (rv == -1){
465  sprintf(g.logbuf, "adjtimex() returned: errno: %d, %s\n", errno, strerror(errno));
466  writeToLog(g.logbuf, "setClockToGPStime()");
467  }
468  else {
469  sprintf(g.logbuf, "adjtimex(): Requested correction: %d secs\n", g.serialTimeError);
470  writeToLog(g.logbuf, "setClockToGPStime()");
471  sprintf(g.logbuf, "adjtimex(): Log message will have a timestamp resulting from this correction\n");
472  writeToLog(g.logbuf, "setClockToGPStime()");
473  }
474 
475  g.serialTimeError = 0;
476 
477  g.serialTimeUpdated = true;
478 
479  return;
480 }
481 
482 
492 void buildRawErrorDistrib(int rawError, double errorDistrib[], unsigned int *count){
493  int len = ERROR_DISTRIB_LEN - 1;
494 
495  int idx = rawError + RAW_ERROR_ZERO;
496  if (idx > len){
497  idx = len;
498  }
499  else if (idx < 0){
500  idx = 0;
501  }
502 
503  if (g.hardLimit == HARD_LIMIT_1){
504 
505  if (*count > 600 && *count % 60 == 0){ // About once a minute
506 
507  for (int i = 0; i < len; i++){ // Scale errorDistrib to allow older
508  errorDistrib[i] *= RAW_ERROR_DECAY; // values to decay (halflife 1 hour).
509  }
510  }
511  errorDistrib[idx] += 1.0;
512  }
513 
514  *count += 1;
515 }
516 
525 void getAvgNoiseLevel(int rawError){
526  double diff = ((double)rawError - rawErrorAvg) * NOISE_ACCUM_RATE;
527  rawErrorAvg += diff;
528 
529  double absdiff = ((double)abs(g.jitter) - g.noiseLevel) * NOISE_ACCUM_RATE;
530  g.noiseLevel += absdiff;
531 }
532 
544 bool detectDelaySpike(int rawError){
545  bool isDelaySpike = false;
546  bool limitCondition;
547 
548  if (g.clampAbsolute){
549  limitCondition = g.hardLimit == 1 && rawError >= NOISE_LEVEL_MIN;
550  }
551  else {
552  limitCondition = g.isControlling && (rawError - rawErrorAvg) >= LARGE_SPIKE;
553  }
554 
555  if (limitCondition){
556 
557  if (g.nDelaySpikes < MAX_SPIKES) {
558  if (g.nDelaySpikes == 0){
560  }
561  else {
562  if (rawError < g.minSustainedDelay){
563  g.minSustainedDelay = rawError;
564  }
565  }
566  g.nDelaySpikes += 1; // Record unbroken sequence of delay spikes
567 
568  isDelaySpike = true;
569  }
570  else { // If nDelaySpikes == MAX_SPIKES stop the
571  isDelaySpike = false; // suspend even if spikes continue.
572 
574  g.clockChanged = true;
575  }
576  }
577  }
578  else {
579 
580  if (g.clampAbsolute == false){
581  getAvgNoiseLevel(rawError);
582  }
583 
584  isDelaySpike = false;
585 
586  if (g.nDelaySpikes > 0){
587  g.nDelaySpikes = 0;
588  }
589  }
590  return isDelaySpike;
591 }
592 
601 int removeNoise(int rawError){
602 
603  int zeroError;
604 
606 
607  g.jitter = rawError;
608  g.isDelaySpike = detectDelaySpike(rawError); // g.isDelaySpike == true will prevent time and
609  // frequency updates during a delay spike.
610  getTimeSlew(rawError);
611 
613  buildJitterDistrib(rawError);
614  }
615 
616  if (g.isDelaySpike){
617  return 0;
618  }
619 
621 
622  zeroError = clampJitter(rawError); // Recover the time error by
623  // limiting away the jitter.
624  if (g.clampAbsolute == true){
625  getAvgNoiseLevel(zeroError);
626  }
627 
628  if (g.isControlling){
630  }
631 
633  buildErrorDistrib(zeroError);
634  }
635 
636  return zeroError;
637 }
638 
648 double getIntegral(void){
649  double integral;
650 
651  if (g.hardLimit == HARD_LIMIT_1
653  integral = g.avgIntegral; // Use average of last 10 integrals
654  } // in the last minute.
655  else {
656  integral = g.integral[9]; // Use only the last integral from
657  // the last minute
658  }
659 
661 
662  return integral;
663 }
664 
674 void savePPStime(int timeCorrection){
675 
676  struct timeval tv1;
677  gettimeofday(&tv1, NULL); // Will always be after rollover of second
678 
679  g.pps_t_sec = tv1.tv_sec; // So the unmodified second will be correct
680 
681  g.pps_t_usec = -timeCorrection;
682  if (g.pps_t_usec < 0){
683  g.pps_t_usec = 1000000 - timeCorrection;
684  g.pps_t_sec -= 1;
685  }
686 
687  double timestamp = (double)g.pps_t_sec + 1e-6 * (double)g.pps_t_usec;
688 
689 // printf("\ntimestamp: %lf timeCorrection: %d\n", timestamp, timeCorrection);
690 
691  writeTimestamp(timestamp);
692 }
693 
704 int signedFractionalSeconds(int fracSec){
705 
706  if (fracSec > 500000){
707  fracSec -= USECS_PER_SEC;
708  }
709  return fracSec;
710 }
711 
720 void detectMissedPPS(void){
721  struct timespec t_mono;
722 
723  g.t_now = getNearestSecond(); // Current time seconds
724 
725  if (g.blockDetectClockChange > 0){
726  g.blockDetectClockChange -= 1;
727 
728  if (g.blockDetectClockChange == 0){
729  g.t_count = g.t_now;
730  }
731  }
732 
733  clock_gettime(CLOCK_MONOTONIC, &t_mono);
734  g.t_mono_now = (double)t_mono.tv_sec + 1e-9 * (double)t_mono.tv_nsec;
735 
736  if (g.seq_num < 2 || g.startingFromRestore != 0){ // Initialize g.t_mono_last to
737  g.t_mono_last = g.t_mono_now - 1.0; // prevent incorrect detection
738  }
739 
740  if (g.seq_num == 0 || g.startingFromRestore != 0){ // Initialize g.t_count at start
741  g.t_count = g.t_now;
742  }
743 
744  double diff = g.t_mono_now - g.t_mono_last;
745  int iDiff = (int)round(diff);
746  // test. For normal operation uncomment the following section:
747  if (iDiff > 1){
748  sprintf(g.logbuf, "detectMissedPPS(): Missed PPS %d time(s)\n", iDiff-1);
749  writeToLog(g.logbuf, "detectMissedPPS()");
750  }
751 
752  g.t_count += iDiff; // The counter is advanced only if monotonic clock advanced.
753 
754  g.t_mono_last = g.t_mono_now; // Set value of g.t_mono_last to be used next
755 }
756 
766 bool detectExteralSystemClockChange(struct timeval pps_t){
767  bool clockChanged = false;
768 
769  if (g.startingFromRestore != 0){
770  return clockChanged;
771  }
772 
773  if (g.isControlling && g.seq_num > SLEW_LEN && fabs(g.avgSlew) < SLEW_MAX) {
774 
775  if (g.t_now != g.t_count){
776  int change = g.t_now - g.t_count;
777  sprintf(g.logbuf, "detectExteralSystemClockChange() System time changed externally by %d seconds\n", change);
778  writeToLog(g.logbuf, "detectExteralSystemClockChange()");
779 
780  clockChanged = true; // The clock was set externally.
781  g.t_count = g.t_now; // Update the seconds counter.
782  }
783  else if (g.hardLimit == HARD_LIMIT_1 && g.clockChanged){
784  g.clockChanged = false;
785 
786  sprintf(g.logbuf, "detectExteralSystemClockChange() Error in fractional second of %ld microseconds\n", pps_t.tv_usec);
787  writeToLog(g.logbuf, "detectExteralSystemClockChange()");
788 
789  clockChanged = true; // The clock was set externally.
790  g.t_count = g.t_now; // Update the seconds counter.
791  }
792  }
793  return clockChanged;
794 }
795 
830 void setClockFractionalSecond(int correction){ // The correction is always a positive number.
831 
832  memset(&g.t3, 0, sizeof(struct timex));
833  // Not possible to assign a negative number to fractional part
834  g.t3.modes = ADJ_SETOFFSET | ADJ_STATUS; // given to adjtimex(). So must give it (1e6 - correction) instead.
835  g.t3.status = STA_PLL;
836 
837  if (correction < 500000){ // (1e6 - correction) is greater than a half second. That would
838  // push the system clock one second ahead of the PPS second.
839  g.t3.time.tv_sec = -1; // Compensate by subtracting a whole second.
840  g.t3.time.tv_usec = USECS_PER_SEC - correction;
841  }
842  else if (correction > 1000000){ // Positive g.zeroOffset could push correction into a range where
843  g.t3.time.tv_sec = -1; // (1e6 - correction) is less than zero which is an invalid range
844  g.t3.time.tv_usec = 2 * USECS_PER_SEC - correction; // for adjtimex() so add 1000000 usec and subtract 1 sec
845  }
846  else { // In this case, (1e6 - correction) is less than half a second
847  // so adding the difference would not push the system clock
848  g.t3.time.tv_sec = 0; // time ahead by a second. So no whole second is subtracted.
849  g.t3.time.tv_usec = USECS_PER_SEC - correction;
850  }
851 
852  g.t_now = (int)g.t3.time.tv_sec; // Reconcile the g.t_count monotonic counter to prevent
853  g.t_count = g.t_now; // triggering detectExteralSystemClockChange().
854 
855  int rv = adjtimex(&g.t3);
856  if (rv == -1){
857  sprintf(g.logbuf, "adjtimex() returned: errno: %d, %s\n", errno, strerror(errno));
858  writeToLog(g.logbuf, "setClockFractionalSecond()");
859  }
860 
861  return;
862 }
863 
873 int correctFractionalSecond(struct timeval *pps_t){
874 
875  int correction = pps_t->tv_usec;
876 
877  int relCorrection = correction;
878  if (relCorrection >= 1000000){
879  relCorrection = correction - 1000000;
880  }
881  else if (relCorrection > 500000){
882  relCorrection = -(1000000 - correction);
883  }
884 
885  if (abs(relCorrection) < 15){ // No correction needed for less than 15 usec since
886  return 1; // the servo can remove an error of this magnitude.
887  }
888 
889  setClockFractionalSecond(correction);
890 
891  pps_t->tv_usec = pps_t->tv_usec - correction; // Correct the interrut delay time that will be processed.
892 
893  return 0;
894 }
895 
903 void doTimeFixups(struct timeval pps_t){
904  int rv;
905 
906  if (g.serialTimeUpdated == true){
908  g.t_count = g.t_now;
909  g.serialTimeUpdated = false;
910  }
911 
912  if (g.nistTimeUpdated == true){
914  g.t_count = g.t_now;
915  g.nistTimeUpdated = false;
916  }
917 
918  if (g.doNISTsettime && g.consensusTimeError != 0){ // When a NIST time correction is needed
919  setClocktoNISTtime(); // it is done here.
920  }
921 
922  if (g.doSerialsettime && g.serialTimeError != 0){ // When a serial time correction is needed
923  setClockToGPStime(); // it is done here.
924  }
925 
926  if (g.blockDetectClockChange == 0 &&
928 
929  if (g.serialTimeUpdated == true){ // If the clock was changed because of serial time update,
930  return; // don't need to correct the fractional second.
931  }
932 
933  if (g.nistTimeUpdated == true){ // Same for NIST
934  return;
935  }
936 
937  rv = correctFractionalSecond(&pps_t);
938  if (rv == 1){
939  return;
940  }
941 
942  g.blockDetectClockChange = SECS_PER_MINUTE; // Block detectExteralSystemClockChange() for one minute
943  // after it has been triggered.
944  sysCommand("systemctl stop systemd-timesyncd.service"); // If systemctl was used to set the system time, kill it.
945  }
946  else if (g.blockDetectClockChange > SECS_PER_MINUTE - 4){
947  correctFractionalSecond(&pps_t); // Continue fixing the fractional seconds for awhile.
948  }
949 
950  return;
951 }
952 
976 int makeTimeCorrection(struct timeval pps_t){
977 
978  g.interruptReceived = true;
979 
980  g.seq_num += 1;
981 
982  if (g.isControlling && g.startingFromRestore == 0){
983  doTimeFixups(pps_t);
984  }
985 
986  g.ppsTimestamp = (int)pps_t.tv_usec;
987 
988  int time0 = g.ppsTimestamp - g.zeroOffset;
989 
990  g.rawError = signedFractionalSeconds(time0); // g.rawError is set to zero by the feedback loop causing
991  // pps_t.tv_usec == g.zeroOffset so that the timestamp
992  g.zeroError = removeNoise(g.rawError); // is equal to the PPS delay.
993 
994  if (g.isDelaySpike){ // Skip a delay spike.
995  savePPStime(0);
996  return 0;
997  }
998 
999  g.timeCorrection = -g.zeroError // The sign of g.zeroError is chosen to provide negative feedback.
1000  / g.invProportionalGain; // Apply controller proportional gain factor.
1001 
1002  g.t3.status = 0;
1003  g.t3.modes = ADJ_OFFSET_SINGLESHOT;
1004  g.t3.offset = g.timeCorrection;
1005 
1006  adjtimex(&g.t3);
1007 
1008  g.isControlling = getAcquireState(); // Provides enough time to reduce time slew on startup.
1009  if (g.isControlling){
1010 
1012 
1013  makeAverageIntegral(g.avgCorrection); // Constructs an average of integrals of one
1014  // minute rolling averages of time corrections.
1015  if (integralIsReady()){ // Get a new frequency offset.
1018 
1019  g.t3.status = 0;
1020  g.t3.modes = ADJ_FREQUENCY;
1021  g.t3.freq = (long)round(ADJTIMEX_SCALE * g.freqOffset);
1022  adjtimex(&g.t3); // Adjust the system clock frequency.
1023  }
1024 
1026 
1027  g.activeCount += 1;
1028  }
1029  else {
1030  g.t_count = g.t_now; // Unless g.isControlling let g.t_count copy pps_t.tv_sec.
1031  } // If g.isControlling then g.t_count is an independent counter.
1032 
1034  return 0;
1035 }
1036 
1045 
1046  if (g.seq_num > 0 && g.exit_requested == false){ // Start looking for lost PPS
1047  if (g.interruptReceived == false){
1048  g.interruptLossCount += 1;
1049 
1051  sprintf(g.logbuf, "WARNING: PPS interrupt lost\n");
1052  writeToLog(g.logbuf, "checkPPSInterrupt()");
1053  }
1054  if (g.exitOnLostPPS && g.interruptLossCount >= SECS_PER_HOUR){
1055  sprintf(g.logbuf, "ERROR: Lost PPS for one hour.");
1056  writeToLog(g.logbuf, "checkPPSInterrupt()");
1057  return -1;
1058  }
1059  }
1060  else {
1062  sprintf(g.logbuf, "PPS interrupt resumed\n");
1063  writeToLog(g.logbuf, "checkPPSInterrupt()");
1064  }
1065  g.interruptLossCount = 0;
1066  }
1067  }
1068 
1069  g.interruptReceived = false;
1070 
1071  return 0;
1072 }
1073 
1086 struct timespec setSyncDelay(int timeAt, int fracSec){
1087 
1088  struct timespec ts2;
1089 
1090  int timerVal = USECS_PER_SEC - fracSec + timeAt;
1091 
1092  if (timerVal >= USECS_PER_SEC){
1093  ts2.tv_sec = 1;
1094  ts2.tv_nsec = (timerVal - USECS_PER_SEC) * 1000;
1095  }
1096  else if (timerVal < 0){
1097  ts2.tv_sec = 0;
1098  ts2.tv_nsec = (USECS_PER_SEC + timerVal) * 1000;
1099  }
1100  else {
1101  ts2.tv_sec = 0;
1102  ts2.tv_nsec = timerVal * 1000;
1103  }
1104 
1105  return ts2;
1106 }
1107 
1134 int readPPS_SetTime(bool verbose, timeCheckParams *tcp, pps_handle_t *pps_handle, int *pps_mode){
1135  int restart = 0;
1136 
1137  int rv = readPPSTimestamp(pps_handle, pps_mode, g.tm);
1138 
1139  detectMissedPPS();
1140 
1141  g.interruptLost = false;
1142  if (rv < 0){
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");
1148  bufferStatusMsg(g.strbuf);
1149  }
1150  else {
1151  sprintf(g.logbuf, "gps-pps-io PPS read() returned: %d Error: %s\n", rv, strerror(errno));
1152  writeToLog(g.logbuf, "readPPS_SetTime()");
1153  }
1154  g.interruptLost = true;
1155  }
1156  else {
1157 
1158  g.t.tv_sec = g.tm[0]; // Seconds value read by gps-pps-io driver from system clock
1159  // at rising edge of PPS signal.
1160  g.t.tv_usec = g.tm[1]; // Fractional seconds read from the timestamp of the PPS signal
1161 
1162  rv = makeTimeCorrection(g.t);
1163  if (rv == -1){
1164  sprintf(g.logbuf, "%s\n", "makeTimeCorrection() returned -1");
1165  writeToLog(g.logbuf, "readPPS_SetTime()");
1166  return -1;
1167  }
1168 
1169  if (g.startingFromRestore == 0){
1170 
1171  if ((!g.isControlling && g.seq_num >= SECS_PER_MINUTE) // If time slew on startup is too large
1172  || (g.isControlling && g.hardLimit > HARD_LIMIT_1024 // or if g.avgSlew becomes too large
1173  && abs(g.avgSlew) > SLEW_MAX)){ // after acquiring
1174 
1175  sprintf(g.logbuf, "pps-client is restarting from SLEW_MAX...\n");
1176  writeToLog(g.logbuf, "readPPS_SetTime() 1");
1177 
1178  initialize(verbose); // then restart the controller.
1179 
1180  restart = 1;
1181  }
1182  }
1183  else {
1184  if (g.isControlling && abs(g.avgSlew) > SLEW_MAX){
1185 
1186  sprintf(g.logbuf, "pps-client is restarting from restore...\n");
1187  writeToLog(g.logbuf, "readPPS_SetTime() 2");
1188 
1189  initialize(verbose); // then restart the controller.
1190  restart = 1;
1191  }
1192  }
1193  }
1194  return restart;
1195 }
1196 
1197 //void reportLeak(const char *msg){
1198 // sprintf(g.logbuf, msg);
1199 // writeToLog(g.logbuf);
1200 //}
1201 
1202 //void testForArrayLeaks(void){
1203 // if (g.seq_num % SECS_PER_10_MIN == 0){
1204 // for (int i = 0; i < 100; i++){
1205 // if (g.pad1[i] != 0){
1206 // reportLeak("Leak in g.pad1 .................................\n");
1207 // }
1208 //
1209 // if (g.pad2[i] != 0){
1210 // reportLeak("Leak in g.pad2 .................................\n");
1211 // }
1212 //
1213 // }
1214 // }
1215 //}
1216 
1217 //int printDuration(struct timeval *tv2, struct timeval *tv1){
1218 // int usec;
1219 // int sec;
1220 //
1221 // tv1->tv_sec = g.tm[0];
1222 // tv1->tv_usec = g.tm[1];
1223 //
1224 // if (tv2->tv_usec >= tv1->tv_usec){
1225 // usec = tv2->tv_usec - tv1->tv_usec;
1226 // sec = tv2->tv_sec - tv1->tv_sec;
1227 // }
1228 // else {
1229 // usec = tv2->tv_usec - tv1->tv_usec + 1000000;
1230 // sec = tv2->tv_sec - tv1->tv_sec - 1;
1231 // }
1232 //
1233 // printf(" Runtime: %d, rawErrorAvg: %lf, g.noiseLevel: %lf, g.avgSlew: %lf, g.isDelaySpike: %d\n", usec + 1000000 * sec, rawErrorAvg, g.noiseLevel, g.avgSlew, g.isDelaySpike);
1234 // return usec;
1235 //}
1236 
1250 void waitForPPS(bool verbose, pps_handle_t *pps_handle, int *pps_mode){
1251  struct timeval tv1;
1252  struct timespec ts2;
1253  int timePPS;
1254  int rv;
1255  timeCheckParams tcp;
1256  int restart = 0;
1257 
1258  if (g.doNISTsettime){
1259  rv = allocInitializeNISTThreads(&tcp);
1260  if (rv == -1){
1261  goto end;
1262  }
1263  }
1264  if (g.doSerialsettime){
1265  char cmd[80];
1266  strcpy(cmd, "stty -F ");
1267 
1268  sprintf(g.logbuf, "\nSerial port, %s, is providing time of day from GPS Satellites\n\n", g.serialPort);
1269  writeToLog(g.logbuf, "waitForPPS() 1");
1270 
1271  strcat(cmd, g.serialPort);
1272  strcat(cmd, " raw 9600 cs8 clocal -cstopb");
1273  rv = sysCommand(cmd);
1274  if (rv == -1){
1275  return;
1276  }
1278  }
1279 
1280  signal(SIGHUP, HUPhandler); // Handler used to ignore SIGHUP.
1281  signal(SIGTERM, TERMhandler); // Handler for the termination signal.
1282 
1283  sprintf(g.logbuf, "PPS-Client v%s is starting ...\n", version);
1284  writeToLog(g.logbuf, "waitForPPS() 1");
1285  // Set up a one-second delay loop that stays in synch by
1286  timePPS = -PPS_WINDOW; // continuously re-timing to before the roll-over of the second.
1287  // timePPS allows for a time window in which to look for the PPS
1289 
1290  for (;;){ // Look for the PPS time returned by the PPS driver
1291 
1292  if (readState == false){
1293 
1294  rv = loadLastState(); // Attempts to use the previous state to avoid long restarts.
1295  if (rv == -1){ // If the previous state is from a recent state can save restart time.
1296  break;
1297  }
1298  readState = true;
1299  }
1300 
1301  if (g.startingFromRestore > 0){ // Only used on restore
1302  g.startingFromRestore -= 1; // Stops decrementing when g.startingFromRestore == 0
1303 
1304  g.t_now = getNearestSecond();
1305  g.t_count = g.t_now;
1306  }
1307 
1308  g.isVerbose = verbose;
1309 
1310  if (g.exit_requested){
1311  sprintf(g.logbuf, "PPS-Client stopped.\n");
1312  writeToLog(g.logbuf, "waitForPPS() 2");
1313  break;
1314  }
1315 
1316  gettimeofday(&tv1, NULL);
1317  ts2 = setSyncDelay(timePPS, tv1.tv_usec);
1318 
1319  nanosleep(&ts2, NULL); // Sleep until ready to look for PPS interrupt
1320 
1321  restart = readPPS_SetTime(verbose, &tcp, pps_handle, pps_mode);
1322  if (restart == -1){
1323  break;
1324  }
1325 
1326  if (restart == 0){
1327 
1328  if (g.doSerialsettime && threadIsRunning == false && g.isControlling){
1329  threadIsRunning = true;
1330 
1331  int rv = pthread_create(&((tcp.tid)[0]), &(tcp.attr), (void* (*)(void*))&saveGPSTime, &tcp);
1332  if (rv != 0){
1333  sprintf(g.logbuf, "Can't create thread : %s\n", strerror(errno));
1334  writeToLog(g.logbuf, "waitForPPS()");
1335  break;
1336  }
1337  }
1338 
1339  if (checkPPSInterrupt() != 0){
1340  sprintf(g.logbuf, "Lost PPS or system error. pps-client is exiting.\n");
1341  writeToLog(g.logbuf, "waitForPPS() 3");
1342  break;
1343  }
1344 
1345  if (bufferStateParams() == -1){
1346  break;
1347  }
1348 
1349  if (g.doNISTsettime && g.isControlling){
1350  makeNISTTimeQuery(&tcp);
1351  }
1352 
1353  if (g.doSerialsettime && g.isControlling){
1354  makeSerialTimeQuery(&tcp);
1355  }
1356 
1358 
1359  if (! g.interruptLost && ! g.isDelaySpike){
1360  if (getConfigs() == -1){
1361  break;
1362  }
1363  }
1364  }
1365 
1366 // if (verbose){
1367 // printDuration(&tv1, &tst);
1368 // }
1369  }
1370 
1371  saveLastState();
1372 
1373 end:
1374  if (g.doNISTsettime){
1375  freeNISTThreads(&tcp);
1376  }
1377  if (g.doSerialsettime){
1378  pthread_cancel((tcp.tid)[0]);
1379  }
1380 
1381  if (g.doSerialsettime){
1382  freeSerialThread(&tcp);
1383  }
1384  return;
1385 }
1386 
1407 int main(int argc, char *argv[])
1408 {
1409  int rv = 0;
1410  int ppid;
1411  bool verbose = false;
1412 
1413  if (argc > 1){
1414  if (strcmp(argv[1], "-v") == 0){
1415  verbose = true;
1416  }
1417  }
1418 
1419  int prStat = accessDaemon(argc, argv); // Send commands to the daemon.
1420  if (prStat == 0 || prStat == -1){ // Program is running or an error occurred.
1421  return rv;
1422  }
1423 
1424  if (geteuid() != 0){ // Superuser only!
1425  printf("pps-client is not running. \"sudo pps-client\" to start.\n");
1426  return rv;
1427  }
1428 
1429  pid_t pid = fork(); // Fork a duplicate child of this process.
1430 
1431  if (pid > 0){ // This is the parent process.
1432  bufferStatusMsg("Spawning pps-client daemon.\n");
1433  return rv; // Return from the parent process and leave
1434  } // the child running.
1435 
1436  if (pid == -1){ // Error: unable to fork a child from parent,
1437  sprintf(g.logbuf, "Fork in main() failed: %s\n", strerror(errno));
1438  writeToLog(g.logbuf, "main()");
1439  return pid;
1440  }
1441  // pid == 0 for the child process which now will run this code as a daemon
1442 
1443  getRootHome();
1444 
1445  pps_handle_t pps_handle;
1446  int pps_mode;
1447 
1448  initialize(verbose);
1449  if (rv == -1){
1450  goto end0;
1451  }
1452 
1453  rv = find_source(f.pps_device, &pps_handle, &pps_mode);
1454  if (rv < 0){
1455  sprintf(g.logbuf, "Unable to get PPS source. Exiting.\n");
1456  fprintf(stderr, "%s", g.logbuf);
1457  writeToLog(g.logbuf, "main()");
1458  goto end0;
1459  }
1460 
1461  ppid = createPIDfile(); // Create the PID file for this process.
1462  if (ppid == -1){ // Either already running or could not
1463  rv = -1; // create a pid file. In either case, exit.
1464  goto end0;
1465  }
1466 
1467  struct sched_param param; // Process must be run as root
1468  mlockall(MCL_CURRENT | MCL_FUTURE);
1469 
1470  rv = sysCommand("timedatectl set-ntp 0"); // Disable NTP, to prevent it from disciolining the clock.
1471  if (g.doNISTsettime && rv != 0){
1472  goto end0;
1473  } // Also disable systemd-timesyncd.
1474  rv = sysCommand("systemctl stop systemd-timesyncd.service");
1475  if (rv != 0){
1476  goto end0;
1477  }
1478 
1479  param.sched_priority = 99; // to get real-time priority.
1480  sched_setscheduler(0, SCHED_FIFO, &param); // SCHED_FIFO: Don't yield to scheduler until sleep.
1481 
1482  sprintf(g.msgbuf, "Process PID: %d\n", ppid); // PPS client is starting.
1483  bufferStatusMsg(g.msgbuf);
1484 
1485  waitForPPS(verbose, &pps_handle, &pps_mode); // Synchronize to the PPS.
1486 
1487  time_pps_destroy(pps_handle);
1488 
1489  sysCommand("rm /run/pps-client.pid"); // Remove the PID file with system() which blocks until
1490  // rm completes keeping shutdown correctly sequenced.
1491 // sysCommand("timedatectl set-ntp 1"); // Re-enable a system time service on shutdown.
1492 end0:
1493  return rv;
1494 }
1495 
setHardLimit
void setHardLimit(double avgCorrection)
Definition: pps-client.cpp:162
G::slewIsLow
bool slewIsLow
Set to "true" in getAcquireState() when G.avgSlew is less than SLEW_MAX. This is a precondition for g...
Definition: pps-client.h:214
G::rawErrorDistrib
double rawErrorDistrib[ERROR_DISTRIB_LEN]
The distribution of rawError values accumulated in buildRawErrorDistrib().
Definition: pps-client.h:203
savePPStime
void savePPStime(int timeCorrection)
Definition: pps-client.cpp:674
G::integral
double integral[NUM_INTEGRALS]
Array of integrals constructed by makeAverageIntegral().
Definition: pps-client.h:229
G::correctionAccum
int correctionAccum
Accumulates G.timeCorrection values from G.correctionFifo in getMovingAverage() in order to generate ...
Definition: pps-client.h:227
integralIsReady
bool integralIsReady(void)
Definition: pps-client.cpp:333
INV_GAIN_0
#define INV_GAIN_0
Controller inverse proportional gain constant at startup.
Definition: pps-client.h:58
SECS_PER_HOUR
#define SECS_PER_HOUR
Definition: pps-client.h:51
makeAverageIntegral
void makeAverageIntegral(double avgCorrection)
Definition: pps-client.cpp:295
PER_NUM_INTEGRALS
#define PER_NUM_INTEGRALS
Inverse of NUM_INTEGRALS.
Definition: pps-client.h:69
initialize
int initialize(bool verbose)
Definition: pps-client.cpp:75
getAvgNoiseLevel
void getAvgNoiseLevel(int rawError)
Definition: pps-client.cpp:525
MAX_SPIKE_LEVEL
#define MAX_SPIKE_LEVEL
An initialization value for G.minSustainedDelay.
Definition: pps-client.h:85
timeCheckParams
Definition: pps-client.h:146
HUPhandler
void HUPhandler(int sig)
Definition: pps-files.cpp:2350
G::hardLimit
int hardLimit
An adaptive limit value determined by setHardLimit() and applied to G.rawError by clampJitter() as th...
Definition: pps-client.h:217
accessDaemon
int accessDaemon(int argc, char *argv[])
Definition: pps-files.cpp:2238
G::activeCount
unsigned int activeCount
Advancing count of active (not skipped) controller cycles once G.isControlling is "true".
Definition: pps-client.h:181
recordFrequencyVars
void recordFrequencyVars(void)
Definition: pps-files.cpp:2366
G::correctionFifoCount
int correctionFifoCount
Signals that G.correctionFifo contains a full count of G.timeCorrection values.
Definition: pps-client.h:226
clampJitter
int clampJitter(int rawError)
Definition: pps-client.cpp:248
freeSerialThread
void freeSerialThread(timeCheckParams *tcp)
Definition: pps-serial.cpp:464
G::invProportionalGain
int invProportionalGain
Controller proportional gain configured inversely to use as an int divisor.
Definition: pps-client.h:220
CLK_CHANGED_LEVEL
#define CLK_CHANGED_LEVEL
Definition: pps-client.h:86
G::freqOffset
double freqOffset
System clock frequency correction calculated as G.integralTimeCorrection * G.integralGain.
Definition: pps-client.h:236
threadIsRunning
bool threadIsRunning
Definition: pps-client.cpp:64
LARGE_SPIKE
#define LARGE_SPIKE
Level above which spikes are are disruptive.
Definition: pps-client.h:88
G::doNISTsettime
bool doNISTsettime
Definition: pps-client.h:238
NUM_INTEGRALS
#define NUM_INTEGRALS
Number of integrals used by makeAverageIntegral() to calculate the one minute clock frequency correct...
Definition: pps-client.h:68
writeErrorDistrib
bool writeErrorDistrib
Definition: pps-files.cpp:58
MAX_SPIKES
#define MAX_SPIKES
Maximum microseconds to suppress a burst of continuous positive jitter.
Definition: pps-client.h:84
sysCommand
int sysCommand(const char *cmd)
Definition: pps-files.cpp:193
freeNISTThreads
void freeNISTThreads(timeCheckParams *tcp)
Definition: pps-sntp.cpp:412
SLEW_MAX
#define SLEW_MAX
Jitter slew value below which the controller will begin to frequency lock.
Definition: pps-client.h:92
HARD_LIMIT_NONE
#define HARD_LIMIT_NONE
Definition: pps-client.h:106
timeCheckParams::attr
pthread_attr_t attr
Thread attribute object.
Definition: pps-client.h:148
doTimeFixups
void doTimeFixups(struct timeval pps_t)
Definition: pps-client.cpp:903
writeToLog
void writeToLog(char *logbuf, const char *location)
Definition: pps-files.cpp:364
removeNoise
int removeNoise(int rawError)
Definition: pps-client.cpp:601
writeTimestamp
void writeTimestamp(double timestamp)
Definition: pps-files.cpp:1581
rawErr
class List rawErr(SLEW_LEN)
allocInitializeNISTThreads
int allocInitializeNISTThreads(timeCheckParams *tcp)
Definition: pps-sntp.cpp:371
SETTLE_TIME
#define SETTLE_TIME
The PPS-Client up time required before saving performance data.
Definition: pps-client.h:56
timeCheckParams::tid
pthread_t * tid
Thread id.
Definition: pps-client.h:147
NOISE_ACCUM_RATE
#define NOISE_ACCUM_RATE
Sets the rate at which G.noiseLevel adjusts to G.rawError.
Definition: pps-client.h:89
G::interruptLossCount
int interruptLossCount
Records the number of consecutive lost PPS interrupt times.
Definition: pps-client.h:185
G::timeCorrection
int timeCorrection
Time correction value constructed in makeTimeCorrection().
Definition: pps-client.h:221
G::seq_num
unsigned int seq_num
Advancing count of the number of PPS interrupt timings that have been received.
Definition: pps-client.h:176
find_source
int find_source(const char *path, pps_handle_t *handle, int *avail_mode)
Definition: pps-files.cpp:2433
G::zeroError
int zeroError
The controller error resulting from removing jitter noise from G.rawError in removeNoise().
Definition: pps-client.h:216
ADJTIMEX_SCALE
#define ADJTIMEX_SCALE
Frequency scaling required by adjtimex().
Definition: pps-client.h:71
G::nDelaySpikes
int nDelaySpikes
Current count of continuous delay spikes made by detectDelaySpike().
Definition: pps-client.h:206
makeSerialTimeQuery
int makeSerialTimeQuery(timeCheckParams *tcp)
Definition: pps-serial.cpp:312
detectExteralSystemClockChange
bool detectExteralSystemClockChange(struct timeval pps_t)
Definition: pps-client.cpp:766
PPS_WINDOW
#define PPS_WINDOW
WaitForPPS delay loop time window in which to look for a PPS.
Definition: pps-client.h:61
G::integralTimeCorrection
double integralTimeCorrection
Integral or average integral of G.timeCorrection returned by getIntegral();.
Definition: pps-client.h:235
buildRawErrorDistrib
void buildRawErrorDistrib(int rawError, double errorDistrib[], unsigned int *count)
Definition: pps-client.cpp:492
PER_MINUTE
#define PER_MINUTE
Definition: pps-client.h:55
INTERRUPT_LOST
#define INTERRUPT_LOST
Number of consecutive lost interrupts at which a warning starts.
Definition: pps-client.h:76
g
struct G g
Declares the global variables defined in pps-client.h.
Definition: pps-client.cpp:55
G::slewAccum
double slewAccum
Accumulates G.rawError in getTimeSlew() and is used to determine G.avgSlew.
Definition: pps-client.h:211
f
struct ppsFiles f
PPS-Client internal files.
Definition: pps-files.cpp:63
ZERO_OFFSET_RPI4
#define ZERO_OFFSET_RPI4
Definition: pps-client.h:65
G::t_mono_last
double t_mono_last
Last recorded monotonic time.
Definition: pps-client.h:194
buildJitterDistrib
void buildJitterDistrib(int rawError)
Definition: pps-files.cpp:2316
saveGPSTime
void saveGPSTime(timeCheckParams *tcp)
Definition: pps-serial.cpp:228
G::consensusTimeError
int consensusTimeError
Consensus value of whole-second time corrections for DST or leap seconds from Internet NIST servers.
Definition: pps-client.h:240
assignProcessorAffinity
int assignProcessorAffinity(void)
Definition: pps-files.cpp:1877
G::correctionFifo_idx
int correctionFifo_idx
Advances G.correctionFifo on each controller cycle in integralIsReady() which returns "true" every 60...
Definition: pps-client.h:232
setClockToGPStime
void setClockToGPStime(void)
Definition: pps-client.cpp:454
correctFractionalSecond
int correctFractionalSecond(struct timeval *pps_t)
Definition: pps-client.cpp:873
adjtimex
int adjtimex(struct timex *timex)
makeNISTTimeQuery
void makeNISTTimeQuery(timeCheckParams *tcp)
Definition: pps-sntp.cpp:300
G::ppsCount
unsigned int ppsCount
Advancing count of G.rawErrorDistrib[] entries made by buildRawErrorDistrib().
Definition: pps-client.h:204
G::clampAbsolute
bool clampAbsolute
Hard limit relative to zero if true else relative to average G.rawError.
Definition: pps-client.h:218
ERROR_DISTRIB_LEN
#define ERROR_DISTRIB_LEN
Definition: pps-client.h:102
G::t_count
int t_count
Rounded seconds counted at the time of G.t_now.
Definition: pps-client.h:192
getAcquireState
bool getAcquireState(void)
Definition: pps-client.cpp:138
INTEGRAL_GAIN
#define INTEGRAL_GAIN
Controller integral gain constant in active controller operation.
Definition: pps-client.h:59
readState
bool readState
Definition: pps-client.cpp:65
HARD_LIMIT_1024
#define HARD_LIMIT_1024
Definition: pps-client.h:107
makeTimeCorrection
int makeTimeCorrection(struct timeval pps_t)
Definition: pps-client.cpp:976
G::integralCount
int integralCount
Counts the integrals formed over the last 10 controller cycles and signals when all integrals in G....
Definition: pps-client.h:231
writeJitterDistrib
bool writeJitterDistrib
Definition: pps-files.cpp:57
readPPSTimestamp
int readPPSTimestamp(pps_handle_t *handle, int *avail_mode, int *tm)
Definition: pps-files.cpp:2505
getRPiCPU
int getRPiCPU(void)
Definition: pps-files.cpp:1834
RAW_ERROR_ZERO
#define RAW_ERROR_ZERO
Index corresponding to rawError == 0 in buildRawErrorDistrib().
Definition: pps-client.h:73
bufferStatusMsg
void bufferStatusMsg(const char *msg)
Definition: pps-files.cpp:407
G::noiseLevel
double noiseLevel
PPS time delay value beyond which a delay is defined to be a delay spike.
Definition: pps-client.h:198
G::interruptReceived
bool interruptReceived
Set "true" when makeTimeCorrection() processes an interrupt time from the Linux PPS device driver.
Definition: pps-client.h:183
createPIDfile
int createPIDfile(void)
Definition: pps-files.cpp:875
G::doSerialsettime
bool doSerialsettime
Definition: pps-client.h:242
setClockFractionalSecond
void setClockFractionalSecond(int correction)
Definition: pps-client.cpp:830
G::slewAccum_cnt
int slewAccum_cnt
Count of the number of times G.rawError has been summed into G.slewAccum.
Definition: pps-client.h:212
getRootHome
int getRootHome(void)
Definition: pps-files.cpp:2545
getMovingAverage
double getMovingAverage(int timeCorrection)
Definition: pps-client.cpp:365
readPPS_SetTime
int readPPS_SetTime(bool verbose, timeCheckParams *tcp, pps_handle_t *pps_handle, int *pps_mode)
Definition: pps-client.cpp:1134
STRBUF_SZ
#define STRBUF_SZ
Definition: pps-client.h:95
ZERO_OFFSET_RPI3
#define ZERO_OFFSET_RPI3
Definition: pps-client.h:64
G::t_mono_now
double t_mono_now
Current monotonic time.
Definition: pps-client.h:193
pps_handle_t
int pps_handle_t
Definition: timepps.h:60
G::cpuVersion
int cpuVersion
The principle CPU version number for Raspberry Pi processors else 0.
Definition: pps-client.h:170
waitForPPS
void waitForPPS(bool verbose, pps_handle_t *pps_handle, int *pps_mode)
Definition: pps-client.cpp:1250
G::t_now
int t_now
Rounded seconds of current time reported by gettimeofday().
Definition: pps-client.h:191
version
const char * version
Program v2.0.0 updated on 9 Jul 2020.
Definition: pps-client.cpp:53
G::t3
struct timex t3
Passes G.timeCorrection to the system function adjtimex() in makeTimeCorrection().
Definition: pps-client.h:222
G::zeroOffset
int zeroOffset
Definition: pps-client.h:196
G::avgSlew
double avgSlew
Average slew value determined by getTimeSlew() from the average of G.slewAccum each time G....
Definition: pps-client.h:213
G::clockChanged
bool clockChanged
Set true if an external clock change is detected.
Definition: pps-client.h:209
getConfigs
int getConfigs(void)
Definition: pps-files.cpp:1356
G::minSustainedDelay
int minSustainedDelay
The observed minimum delay value of a sustained sequence of delay spikes.
Definition: pps-client.h:208
G::serialTimeUpdated
bool serialTimeUpdated
Definition: pps-client.h:243
getIntegral
double getIntegral(void)
Definition: pps-client.cpp:648
getNearestSecond
int getNearestSecond(void)
Definition: pps-client.cpp:395
G::nCores
int nCores
If PPS-Client is segregated, identifies the number of processor cores.
Definition: pps-client.h:168
setSyncDelay
struct timespec setSyncDelay(int timeAt, int fracSec)
Definition: pps-client.cpp:1086
G::avgCorrection
double avgCorrection
A one-minute rolling average of G.timeCorrection values generated by getMovingAverage().
Definition: pps-client.h:224
signedFractionalSeconds
int signedFractionalSeconds(int fracSec)
Definition: pps-client.cpp:704
SLEW_LEN
#define SLEW_LEN
The slew accumulator (slewAccum) update interval.
Definition: pps-client.h:91
bufferStateParams
int bufferStateParams(void)
Definition: pps-files.cpp:1693
TERMhandler
void TERMhandler(int sig)
Definition: pps-files.cpp:2337
RAW_ERROR_DECAY
#define RAW_ERROR_DECAY
Decay rate for G.rawError samples (1 hour half life)
Definition: pps-client.h:74
G::integralGain
double integralGain
Current controller integral gain.
Definition: pps-client.h:234
setClocktoNISTtime
void setClocktoNISTtime(void)
Definition: pps-client.cpp:415
recordOffsets
void recordOffsets(int timeCorrection)
Definition: pps-files.cpp:2412
detectDelaySpike
bool detectDelaySpike(int rawError)
Definition: pps-client.cpp:544
getTimeSlew
void getTimeSlew(int rawError)
Definition: pps-client.cpp:204
loadLastState
int loadLastState(void)
Definition: pps-files.cpp:610
writeStatusStrings
int writeStatusStrings(void)
Definition: pps-files.cpp:435
buildErrorDistrib
void buildErrorDistrib(int timeCorrection)
Definition: pps-files.cpp:2294
main
int main(int argc, char *argv[])
Definition: pps-client.cpp:1407
G::interruptLost
bool interruptLost
Set "true" when a PPS interrupt time fails to be received.
Definition: pps-client.h:184
G::tm
int tm[6]
Returns the timestamp from the Linux PPS device driver as a pair of ints.
Definition: pps-client.h:189
HARD_LIMIT_1
#define HARD_LIMIT_1
Definition: pps-client.h:109
G::isDelaySpike
bool isDelaySpike
Set "true" by detectDelaySpike() when G.rawError exceeds G.noiseLevel.
Definition: pps-client.h:207
allocInitializeSerialThread
int allocInitializeSerialThread(timeCheckParams *tcp)
Definition: pps-serial.cpp:417
G::ppsTimestamp
int ppsTimestamp
Fractional second value of the PPS timestamp from the kernel driver.
Definition: pps-client.h:178
USECS_PER_SEC
#define USECS_PER_SEC
Definition: pps-client.h:47
G
Struct for program-wide global variables showing those important to the controller.
Definition: pps-client.h:167
INV_GAIN_1
#define INV_GAIN_1
Controller inverse proportional gain constant during active controller operation.
Definition: pps-client.h:57
SECS_PER_MINUTE
#define SECS_PER_MINUTE
Definition: pps-client.h:48
detectMissedPPS
void detectMissedPPS(void)
Definition: pps-client.cpp:720
G::rawError
int rawError
Signed difference: G.ppsTimestamp - G.zeroOffset in makeTimeCorrection().
Definition: pps-client.h:201
G::avgIntegral
double avgIntegral
One-minute average of the integrals in G.integral[].
Definition: pps-client.h:230
G::isControlling
bool isControlling
Set "true" by getAcquireState() when the control loop can begin to control the system clock frequency...
Definition: pps-client.h:180
NOISE_LEVEL_MIN
#define NOISE_LEVEL_MIN
The minimum level at which interrupt delays are delay spikes.
Definition: pps-client.h:90
G::correctionFifo
int correctionFifo[OFFSETFIFO_LEN]
Contains the G.timeCorrection values from over the previous 60 seconds.
Definition: pps-client.h:225
G::t
struct timeval t
Time of system response to the PPS interrupt received from the Linux PPS device driver.
Definition: pps-client.h:187
saveLastState
int saveLastState(void)
Definition: pps-files.cpp:509
G::serialTimeError
int serialTimeError
Error reported by GPS serial port.S.
Definition: pps-client.h:244
checkPPSInterrupt
int checkPPSInterrupt(void)
Definition: pps-client.cpp:1044
G::nistTimeUpdated
bool nistTimeUpdated
Definition: pps-client.h:239
G::isVerbose
bool isVerbose
Enables continuous printing of PPS-Client status params when "true".
Definition: pps-client.h:172