PPS-Client  1.4.0
Client for synchronizing the system clock to a GPS PPS source
pps-client.cpp
Go to the documentation of this file.
1 
44 #include "../client/pps-client.h"
45 
49 extern int adjtimex (struct timex *timex);
50 
51 const char *version = "1.4.0";
52 
53 struct G g;
54 
58 void setDelayTrackers(void){
59  g.noiseLevel = (int)round((double)g.sysDelay * NOISE_FACTOR) + 1;
62  }
63 }
64 
72 void initialize(bool verbose){
73  memset(&g, 0, sizeof(struct G));
74 
75  g.isVerbose = verbose;
81  g.exitOnLostPPS = true;
82  g.doCalibration = true;
83  g.doNTPsettime = true;
84 
85  g.t3.modes = ADJ_FREQUENCY; // Initialize system clock
86  g.t3.freq = 0; // frequency offset to zero.
87 }
88 
109 bool getAcquireState(void){
110 
111  if (! g.slewIsLow && g.slewAccum_cnt == 0
112  && fabs(g.avgSlew) < SLEW_MAX){ // SLEW_MAX only needs to be low enough
113  g.slewIsLow = true; // that the controller can begin locking
114  } // at limitValue == HARD_LIMIT_NONE
115 
116  return (g.slewIsLow && g.seq_num >= SECS_PER_MINUTE); // The g.seq_num requirement sets a limit on the
117 } // length of time to run the Type 1 controller
118  // that initially pushes avgSlew below SLEW_MAX.
119 
135 
136  double avgMedianMag = fabs(avgCorrection);
137 
140  return;
141  }
142 
143  if (abs(g.avgSlew) > SLEW_MAX){ // As long as average time slew is
144  int d_4 = abs(g.avgSlew) * 4; // outside of range SLEW_MAX this keeps
145  while (g.hardLimit < d_4 // g.hardLimit above 4 * g.avgSlew
146  && g.hardLimit < HARD_LIMIT_NONE){ // which is high enough to allow the
147  g.hardLimit = g.hardLimit << 1; // controller to pull avgSlew within
148  } // SLEW_MAX.
149  return;
150  }
151 
152  if (g.hardLimit == HARD_LIMIT_1){
153  if (avgMedianMag > HARD_LIMIT_05){
154  g.hardLimit = g.hardLimit << 1;
155  }
156  }
157  else if (avgMedianMag < HARD_LIMIT_05){
159  }
160  else if (avgMedianMag < (g.hardLimit >> 2)){ // If avgCorrection is below 1/4 of limitValue
161  g.hardLimit = g.hardLimit >> 1; // then halve limitValue.
162  }
163  else if (avgMedianMag > (g.hardLimit >> 1)){ // If avgCorrection is above 1/2 of limitValue
164  g.hardLimit = g.hardLimit << 1; // then double limitValue.
165 
166  if (g.hardLimit > HARD_LIMIT_NONE){
168  }
169  }
170 }
171 
184  bool isDelaySpike = false;
185 
187 
188  if (g.nDelaySpikes < MAX_SPIKES) {
189  g.nDelaySpikes += 1; // Record unbroken sequence of delay spikes
190 
191  isDelaySpike = true;
192  }
193  else { // If nDelaySpikes == MAX_SPIKES stop the
194  isDelaySpike = false; // suspend even if spikes continue.
195  }
196  }
197  else {
198  isDelaySpike = false;
199 
200  if (g.nDelaySpikes > 0){
201  g.nDelaySpikes = 0;
202  }
203  }
204  return isDelaySpike;
205 }
206 
216 
217  g.slewAccum += (double)rawError;
218 
219  g.slewAccum_cnt += 1;
220  if (g.slewAccum_cnt >= SLEW_LEN){
221  g.slewAccum_cnt = 0;
222 
223  g.avgSlew = g.slewAccum / (double)SLEW_LEN;
224 
225  g.slewAccum = 0.0;
226  }
227 }
228 
247 
248  int zeroError = rawError;
249 
250  if (rawError > g.hardLimit){
252  }
253  else if (rawError < -g.hardLimit){
254  zeroError = -g.hardLimit;
255  }
256 
257  return zeroError;
258 }
259 
275 
276  int indexOffset = SECS_PER_MINUTE - NUM_INTEGRALS;
277 
278  if (g.correctionFifo_idx >= indexOffset){ // Over the last NUM_INTEGRALS seconds in each minute
279 
280  int i = g.correctionFifo_idx - indexOffset;
281  if (i == 0){
282  g.avgIntegral = 0.0;
283  g.integralCount = 0;
284  }
285 
286  g.integral[i] = g.integral[i] + avgCorrection; // avgCorrection sums into g.integral[i] once each
287  // minute forming the ith integral over the last minute.
288  if (g.hardLimit == HARD_LIMIT_1){
289  g.avgIntegral += g.integral[i]; // Accumulate each integral that is being formed
290  g.integralCount += 1; // into g.avgIntegral for averaging.
291  }
292  }
293 
294  if (g.correctionFifo_idx == SECS_PER_MINUTE - 1 // just before the minute rolls over.
296 
297  g.avgIntegral *= PER_NUM_INTEGRALS; // Normalize g.avgIntegral.
298  }
299 }
300 
312 bool integralIsReady(void){
313  bool isReady = false;
314 
315  if (g.correctionFifo_idx == 0){
317  isReady = true;
318  }
319 
320  g.correctionFifo_idx += 1;
322  g.correctionFifo_idx = 0;
323  }
324 
325  return isReady;
326 }
327 
348 
349  double avgCorrection;
350 
351  if (g.seq_num > SETTLE_TIME && (g.config_select & ERROR_DISTRIB)){
353  }
354 
355  g.correctionAccum += timeCorrection; // Add the new timeCorrection into the error accumulator.
356 
357  if (g.correctionFifoCount == SECS_PER_MINUTE){ // Once the FIFO is full, maintain the continuous
358  // rolling sum accumulator by subtracting the
359  int oldError = g.correctionFifo[g.correctionFifo_idx];
360  g.correctionAccum -= oldError; // old timeCorrection value at the current correctionFifo_idx.
361  }
362 
363  g.correctionFifo[g.correctionFifo_idx] = timeCorrection; // and replacing the old value in the FIFO with the new.
364 
365  if (g.correctionFifoCount < SECS_PER_MINUTE){ // When correctionFifoCount == SECS_PER_MINUTE
366  g.correctionFifoCount += 1; // the FIFO is full and ready to use.
367  }
368 
370  return avgCorrection;
371 }
372 
389 int setClockToNTPtime(int pps_fd){
390 
391  sprintf(g.logbuf, "seq_num: %d consensusTimeError: %d\n", g.seq_num, g.consensusTimeError);
392  writeToLog(g.logbuf);
393 
394  int msg[2];
395  msg[0] = 3;
396  msg[1] = g.consensusTimeError;
397  int rv = write(pps_fd, msg, 2 * sizeof(int));
398  if (rv == -1){
399  sprintf(g.logbuf, "setClockToNTPtime() write to driver failed with msg: %s\n", strerror(errno));
400  writeToLog(g.logbuf);
401  return -1;
402  }
403 
404  g.consensusTimeError = 0;
405  return 0;
406 }
407 
422 int setClockToSerialTime(int pps_fd){
423  sprintf(g.logbuf, "setClockToSerialTime() Corrected time by %d seconds\n", g.serialTimeError);
424  writeToLog(g.logbuf);
425 
426  int msg[2];
427  msg[0] = 3;
428  msg[1] = g.serialTimeError;
429  int rv = write(pps_fd, msg, 2 * sizeof(int));
430  if (rv == -1){
431  sprintf(g.logbuf, "setClockToSerialTime() write to driver failed with msg: %s\n", strerror(errno));
432  writeToLog(g.logbuf);
433  return -1;
434  }
435 
436  g.t_count += g.serialTimeError;
437  g.serialTimeError = 0;
438 
439  return 0;
440 }
441 
460 int setClockFractionalSecond(int correction, int pps_fd){
461 
462  sprintf(g.logbuf, "setClockFractionalSecond() Made correction: %d\n", correction);
463  writeToLog(g.logbuf);
464 
465  int msg[2];
466  msg[0] = 2;
467  msg[1] = correction; // Make a correction equal and opposite to the fractional
468  // second that was set externally in order to cancel it.
469  int rv = write(pps_fd, msg, 2 * sizeof(int));
470  if (rv == -1){
471  sprintf(g.logbuf, "setClockFractionalSecond() write to driver failed with msg: %s\n", strerror(errno));
472  writeToLog(g.logbuf);
473  return -1;
474  }
475  return 0;
476 }
477 
487 void buildRawErrorDistrib(int rawError, double errorDistrib[], unsigned int *count){
488  int len = ERROR_DISTRIB_LEN - 1;
489 
490  int idx = rawError + RAW_ERROR_ZERO;
491  if (idx > len){
492  idx = len;
493  }
494  else if (idx < 0){
495  idx = 0;
496  }
497 
498  if (g.hardLimit == HARD_LIMIT_1){
499 
500  if (*count > 600 && *count % 60 == 0){ // About once a minute
501 
502  for (int i = 0; i < len; i++){ // Scale errorDistrib to allow older
503  errorDistrib[i] *= RAW_ERROR_DECAY; // values to decay (halflife 1 hour).
504  }
505  }
506  errorDistrib[idx] += 1.0;
507  }
508 
509  *count += 1;
510 }
511 
521 
522  int zeroError;
523 
525 
526  g.sysDelayShift = 0;
527 
528  g.jitter = rawError;
529 
530  if ((g.config_select & JITTER_DISTRIB) && g.seq_num > SETTLE_TIME){
532  }
533 
534  g.isDelaySpike = detectDelaySpike(rawError); // g.isDelaySpike == true will prevent time and
535  // frequency updates during a delay spike.
536  if (g.isDelaySpike){
537  return 0;
538  }
539 
541 
543  zeroError = clampJitter(rawError); // Recover the time error by
544  // limiting away the jitter.
545  if (g.isControlling){
547  }
548  return zeroError;
549 }
550 
560 double getIntegral(void){
561  double integral;
562 
563  if (g.hardLimit == HARD_LIMIT_1
565  integral = g.avgIntegral; // Use average of last 10 integrals
566  } // in the last minute.
567  else {
568  integral = g.integral[9]; // Use only the last integral from
569  // the last minute
570  }
571 
573 
574  return integral;
575 }
576 
589 void getPPStime(timeval t, int timeCorrection){
590  g.pps_t_sec = t.tv_sec;
591  g.pps_t_usec = -timeCorrection;
592  if (timeCorrection > 0){
593  g.pps_t_sec -= 1;
594  g.pps_t_usec = 1000000 - timeCorrection;
595  }
596 
597  double timestamp = t.tv_sec - 1e-6 * (double)timeCorrection;
598  writeTimestamp(timestamp);
599 }
600 
611 int getFractionalSeconds(timeval pps_t){
612  int interruptTime = pps_t.tv_usec;
613 
614  if (interruptTime > 500000){
616  }
617  return interruptTime;
618 }
619 
628  struct timespec t_mono;
629  struct timeval t_now;
630 
631  clock_gettime(CLOCK_MONOTONIC, &t_mono);
632  g.t_mono_now = (double)t_mono.tv_sec + 1e-9 * (double)t_mono.tv_nsec;
633 
634  if (g.seq_num < 2){ // Initialize g.t_mono_last
635  g.t_mono_last = g.t_mono_now - 1;
636  }
637 
638  gettimeofday(&t_now, NULL);
639  g.t_now = (int)t_now.tv_sec;
640 
641  if (g.seq_num == 0){ // Initialize g.t_count
642  g.t_count = g.t_now;
643  }
644 
645  double diff = g.t_mono_now - g.t_mono_last;
646  int iDiff = round(diff);
647 
648  if (iDiff != 1){
649  sprintf(g.logbuf, "increaseMonotonicCount() lost a cycle. diff: %lf\n", diff);
650  writeToLog(g.logbuf);
651  }
652 
653  g.t_count += iDiff; // Advance the counter
654  g.t_mono_last = g.t_mono_now; // Set value of g.t_mono_last to be used next
655 
656  if (g.blockDetectClockChange > 0){
657  g.blockDetectClockChange -= 1;
658  }
659 }
660 
661 
672 bool isNearZero(double val){
673  double accFrac = 0.9;
674  g.zeroAccum = accFrac * g.zeroAccum + (1.0 - accFrac) * val;
675 
676  if (g.hardLimit == HARD_LIMIT_1 && fabs(g.zeroAccum) < 0.05){ // NEAR_ZERO
677  return true;
678  }
679  return false;
680 }
681 
682 
690  bool clockChanged = false;
691 
692  if (isNearZero(g.avgCorrection)) {
693 
694  if (g.t_now != g.t_count){
695 
696  sprintf(g.logbuf, "detectExteralSystemClockChange() Got error g.t_now: %d g.t_count: %d\n", g.t_now, g.t_count);
697  writeToLog(g.logbuf);
698 
699  clockChanged = true; // The clock was set externally.
700  g.t_count = g.t_now; // Update the seconds counter.
701  }
702  }
703  return clockChanged;
704 }
705 
723 int makeTimeCorrection(struct timeval pps_t, int pps_fd){
724  int rv = 0;
725  g.interruptReceived = true;
726 
727  if (g.doNTPsettime && g.consensusTimeError != 0){ // When an NTP time correction is needed
728  rv = setClockToNTPtime(pps_fd); // apply it here.
729  if (rv == -1){
730  return rv;
731  }
732  }
733 
734  if (g.doSerialsettime && g.serialTimeError != 0){
735  rv = setClockToSerialTime(pps_fd);
736  if (rv == -1){
737  return rv;
738  }
739  }
740 
741  if (g.blockDetectClockChange == 0 &&
742  detectExteralSystemClockChange()){ // If the time was changed by an external clock setting,
743  int correction = -getFractionalSeconds(pps_t); // this cancels the change that may have
744  rv = setClockFractionalSecond(correction, pps_fd); // been made to the fractional second.
745  if (rv == -1){
746  return rv;
747  }
748  pps_t.tv_usec = g.sysDelay; // Temporarily zero the time correction.
749  }
750 
751  g.seq_num += 1;
752 
754  g.rawError = g.interruptTime - g.sysDelay; // References the controller to g.sysDelay which sets the time
755  // of the PPS rising edge to zero at the start of each second.
757 
758  if (g.isDelaySpike){ // Skip a delay spike.
759  getPPStime(pps_t, 0);
760  return 0;
761  }
762 
764  / g.invProportionalGain; // Apply controller proportional gain factor.
765 
766  g.t3.modes = ADJ_OFFSET_SINGLESHOT; // Adjust the time slew. adjtimex() limits the maximum
767  g.t3.offset = g.timeCorrection; // correction to about 500 microseconds each second so
768  // it can take up to 20 minutes to start pps-client.
769  adjtimex(&g.t3);
770 
771  g.isControlling = getAcquireState(); // Provides enough time to reduce time slew on startup.
772  if (g.isControlling){
773 
775 
776  makeAverageIntegral(g.avgCorrection); // Constructs an average of integrals of one
777  // minute rolling averages of time corrections.
778  if (integralIsReady()){ // Get a new frequency offset.
781 
782  g.t3.modes = ADJ_FREQUENCY;
783  g.t3.freq = (long)round(ADJTIMEX_SCALE * g.freqOffset);
784  adjtimex(&g.t3); // Adjust the system clock frequency.
785  }
786 
788 
789  g.activeCount += 1;
790  writeSysDelay();
791  }
792  else {
793  g.t_count = g.t_now; // Unless g.isControlling let g.t_count copy pps_t.tv_sec.
794  } // If g.isControlling then g.t_count is an independent counter.
795 
796  getPPStime(pps_t, g.timeCorrection);
797  return 0;
798 }
799 
810 int checkPPSInterrupt(int pps_fd){
811  int rv = 0;
812  int output;
813 
814  if (g.seq_num > 0 && g.exit_requested == false){
815  if (g.interruptReceived == false){
816  g.interruptLossCount += 1;
817 
819  sprintf(g.logbuf, "WARNING: PPS interrupt lost\n");
820  writeToLog(g.logbuf);
821 
822  if (g.config_select & ALERT_PPS_LOST){
823  output = HIGH;
824  rv = write(pps_fd, &output, sizeof(int));
825  if (rv == -1){
826  sprintf(g.logbuf, "checkPPSInterrupt() write to driver failed with msg: %s\n", strerror(errno));
827  writeToLog(g.logbuf);
828  return rv;
829  }
830  }
831  }
832  if (g.exitOnLostPPS && g.interruptLossCount >= SECS_PER_HOUR){
833  sprintf(g.logbuf, "ERROR: Lost PPS for one hour.");
834  writeToLog(g.logbuf);
835  return -1;
836  }
837  }
838  else {
840  sprintf(g.logbuf, "PPS interrupt resumed\n");
841  writeToLog(g.logbuf);
842 
843  if (g.config_select & ALERT_PPS_LOST){
844  output = LOW;
845  rv = write(pps_fd, &output, sizeof(int));
846  if (rv == -1){
847  sprintf(g.logbuf, "checkPPSInterrupt() write to driver failed with msg: %s\n", strerror(errno));
848  writeToLog(g.logbuf);
849  return -1;
850  }
851  }
852  }
853  g.interruptLossCount = 0;
854  }
855  }
856 
857  g.interruptReceived = false;
858 
859  return 0;
860 }
861 
874 bool detectIntrptDelaySpike(int intrptError){
875  bool isDelaySpike = false;
876 
877  if (g.hardLimit <= HARD_LIMIT_4 && intrptError >= g.noiseLevel){
879  g.nIntrptDelaySpikes += 1; // Record unbroken sequence of delay spikes
880 
881  isDelaySpike = true;
882  }
883  else { // If nIntrptDelaySpikes == MAX_SPIKES stop the
884  isDelaySpike = false; // suspend even if spikes continue.
885  }
886  }
887  else {
888  isDelaySpike = false;
889 
890  if (g.nIntrptDelaySpikes > 0){
891  g.nIntrptDelaySpikes = 0;
892  }
893  }
894  return isDelaySpike;
895 }
896 
906 int removeIntrptNoise(int intrptError){
907 
908  int zeroError;
909 
910  buildRawErrorDistrib(intrptError, g.intrptErrorDistrib, &(g.intrptCount));
911 
912  bool isDelaySpike = detectIntrptDelaySpike(intrptError);
913  if (isDelaySpike){
914  return 0;
915  }
916 
917  zeroError = clampJitter(intrptError); // Recover the time error by
918  // limiting away the jitter.
919  return zeroError;
920 }
921 
937 struct timespec setSyncDelay(int timeAt, int fracSec){
938 
939  struct timespec ts2;
940 
941  int timerVal = USECS_PER_SEC + timeAt - fracSec;
942 
943  if (timerVal >= USECS_PER_SEC){
944  ts2.tv_sec = 1;
945  ts2.tv_nsec = (timerVal - USECS_PER_SEC) * 1000;
946  }
947  else if (timerVal < 0){
948  ts2.tv_sec = 0;
949  ts2.tv_nsec = (USECS_PER_SEC + timerVal) * 1000;
950  }
951  else {
952  ts2.tv_sec = 0;
953  ts2.tv_nsec = timerVal * 1000;
954  }
955 
956  return ts2;
957 }
958 
977 int getInterruptDelay(int pps_fd){
978  ssize_t rv;
979 
980  int out = 1;
981  rv = write(pps_fd, &out, sizeof(int)); // Set the output pin to generate an interrupt
982  if (rv == -1){ // and disable reads of the PPS interrupt.
983  sprintf(g.logbuf, "getInterruptDelay() write to driver failed with msg: %s\n", strerror(errno));
984  writeToLog(g.logbuf);
985  return -1;
986  }
987 
988  rv = read(pps_fd, (void *)g.tm, 6 * sizeof(int)); // Read the interrupt write and response times.
989  if (rv > 0){
990 
991  g.intrptDelay = g.tm[5] - g.tm[3];
992 
994 
995  if (g.seq_num > SETTLE_TIME && (g.config_select & INTERRUPT_DISTRIB)){
997  }
998 
999  int zeroError = removeIntrptNoise(g.intrptError);
1000 
1001  g.delayMedian += (double)zeroError * INV_DELAY_SAMPLES_PER_MIN;
1002  g.sysDelay = (int)round(g.delayMedian);
1003 
1004  if (g.activeCount > SETTLE_TIME && g.hardLimit == HARD_LIMIT_1 && (g.config_select & SYSDELAY_DISTRIB)){
1006  }
1007 
1008  if (g.activeCount % SHOW_INTRPT_DATA_INTVL == 0 && g.activeCount != g.lastActiveCount){
1009  g.lastActiveCount = g.activeCount;
1010 
1011  sprintf(g.msgbuf, "Interrupt delay: %d usec, Delay median: %lf usec sysDelay: %d usec\n",
1013  bufferStatusMsg(g.msgbuf);
1014  }
1015  }
1016  else {
1017  sprintf(g.logbuf, "getInterruptDelay() Device driver read returned: %d Error: %s\n", rv, strerror(errno));
1018  writeToLog(g.logbuf);
1019  return -1;
1020  }
1021 
1022  out = 0;
1023  rv = write(pps_fd, &out, sizeof(int)); // Reset the output pin and resume PPS interrupt reads.
1024  if (rv == -1){
1025  sprintf(g.logbuf, "getInterruptDelay() write to driver failed with msg: %s\n", strerror(errno));
1026  writeToLog(g.logbuf);
1027  return -1;
1028  }
1029 
1030  return 0;
1031 }
1032 
1064 int readPPS_SetTime(bool verbose, int pps_fd){
1065  int restart = 0;
1066 
1067  ssize_t rv = read(pps_fd, (void *)g.tm, 2 * sizeof(int));
1068 
1070 
1071  g.interruptLost = false;
1072  if (rv <= 0){
1073  if (rv == 0 && ! g.exit_requested){
1074  time_t t = time(NULL);
1075  struct tm *tmp = localtime(&t);
1076  strftime(g.strbuf, STRBUF_SZ, "%F %H:%M:%S ", tmp);
1077  strcat(g.strbuf, "Read PPS interrupt failed\n");
1078  bufferStatusMsg(g.strbuf);
1079  }
1080  else {
1081  sprintf(g.logbuf, "gps-pps-io PPS read() returned: %d Error: %s\n", rv, strerror(errno));
1082  writeToLog(g.logbuf);
1083  }
1084  g.interruptLost = true;
1085  }
1086  else {
1087  g.t.tv_sec = g.tm[0]; // Seconds value read by gps-pps-io driver from system clock
1088  // at rising edge of PPS signal.
1089  g.t.tv_usec = g.tm[1]; // Fractional seconds read from clock at rising edge of PPS signal.
1090 
1091  if (makeTimeCorrection(g.t, pps_fd) == -1)
1092  return -1;
1093 
1094  if ((! g.isControlling && g.seq_num >= SECS_PER_MINUTE) // If time slew on startup is too large
1095  || (g.isControlling && g.hardLimit > HARD_LIMIT_1024 // or if g.avgSlew becomes too large
1096  && abs(g.avgSlew) > SLEW_MAX)){ // after acquiring
1097 
1098  sprintf(g.logbuf, "pps-client is restarting...\n");
1099  writeToLog(g.logbuf);
1100 
1101  initialize(verbose); // then restart the controller.
1102  adjtimex(&g.t3);
1103  setDelayTrackers();
1104 
1105  restart = 1;
1106  }
1107  }
1108  return restart;
1109 }
1110 
1111 //void reportLeak(const char *msg){
1112 // sprintf(g.logbuf, msg);
1113 // writeToLog(g.logbuf);
1114 //}
1115 
1116 //void testForArrayLeaks(void){
1117 // if (g.seq_num % SECS_PER_10_MIN == 0){
1118 // for (int i = 0; i < 100; i++){
1119 // if (g.pad1[i] != 0){
1120 // reportLeak("Leak in g.pad1 .................................\n");
1121 // }
1122 //
1123 // if (g.pad2[i] != 0){
1124 // reportLeak("Leak in g.pad2 .................................\n");
1125 // }
1126 //
1127 // }
1128 // }
1129 //}
1130 
1143 void waitForPPS(bool verbose, int pps_fd){
1144  struct timeval tv1;
1145  struct timespec ts2;
1146  int timePPS;
1147  int rv;
1148  timeCheckParams tcp;
1149  int restart = 0;
1150 
1151  adjtimex(&g.t3);
1152  setDelayTrackers();
1153 
1155 
1156  if (g.doNTPsettime){
1157  rv = allocInitializeSNTPThreads(&tcp);
1158  if (rv == -1){
1159  goto end;
1160  }
1161  }
1162  if (g.doSerialsettime){
1163  char cmd[80];
1164  strcpy(cmd, "stty -F ");
1165 
1166  printf("g.serialPort: %s\n", g.serialPort);
1167 
1168  strcat(cmd, g.serialPort);
1169  strcat(cmd, " raw 9600 cs8 clocal -cstopb");
1170  rv = sysCommand(cmd);
1171  if (rv == -1){
1172  return;
1173  }
1175  }
1176 
1177 
1178  signal(SIGHUP, HUPhandler); // Handler used to ignore SIGHUP.
1179  signal(SIGTERM, TERMhandler); // Handler for the termination signal.
1180 
1181  sprintf(g.logbuf, "PPS-Client v%s is starting ...\n", version);
1182  writeToLog(g.logbuf);
1183  // Set up a one-second delay loop that stays in synch by
1184  timePPS = -150; // continuously re-timing to before the roll-over of the second.
1185  // This allows for a delay of about 50 microseconds coming out
1186  // of sleep plus interrupt latencies up to 100 microseconds.
1187  gettimeofday(&tv1, NULL);
1188  ts2 = setSyncDelay(timePPS, tv1.tv_usec);
1189 
1191 
1192  for (;;){ // Delay loop
1193  if (g.exit_requested){
1194  sprintf(g.logbuf, "PPS-Client stopped.\n");
1195  writeToLog(g.logbuf);
1196  break;
1197  }
1198 
1199  nanosleep(&ts2, NULL); // Sleep until ready to look for PPS interrupt
1200 
1201  restart = readPPS_SetTime(verbose, pps_fd);
1202  if (restart == -1){
1203  break;
1204  }
1205  if (restart == 1) {
1206  readConfigFile();
1207  }
1208  else{
1209  if (checkPPSInterrupt(pps_fd) != 0){
1210  sprintf(g.logbuf, "Lost PPS or system error. pps-client is exiting.\n");
1211  writeToLog(g.logbuf);
1212  break;
1213  }
1214 
1215  if (bufferStateParams() == -1){
1216  break;
1217  }
1218 
1219  if (g.doNTPsettime){
1220  g.blockDetectClockChange = BLOCK_FOR_10; // Prevent interaction with detectExteralSystemClockChange()
1221  makeSNTPTimeQuery(&tcp);
1222  }
1223 
1224  if (g.doSerialsettime){
1225  rv = makeSerialTimeQuery(&tcp);
1226  if (rv == -1){
1227  break;
1228  }
1229  }
1230 
1232 
1233  if (! g.interruptLost && ! g.isDelaySpike){
1234  if (g.doCalibration && g.hardLimit == HARD_LIMIT_1){
1235  rv = getInterruptDelay(pps_fd);
1236  if (rv == -1){
1237  break;
1238  }
1239  }
1240 
1241  processFiles();
1242  }
1243  }
1244 
1245  gettimeofday(&tv1, NULL);
1246 
1247  ts2 = setSyncDelay(timePPS, tv1.tv_usec);
1248  }
1249 end:
1250  if (g.doNTPsettime){
1251  freeSNTPThreads(&tcp);
1252  }
1253  if (g.doSerialsettime){
1254  freeSerialThread(&tcp);
1255  }
1256  return;
1257 }
1258 
1279 int main(int argc, char *argv[])
1280 {
1281  int rv = 0;
1282  int ppid, pps_fd;
1283  bool verbose = false;
1284 
1285  if (argc > 1){
1286  if (strcmp(argv[1], "-v") == 0){
1287  verbose = true;
1288  }
1289  }
1290 
1291  int prStat = accessDaemon(argc, argv); // Send commands to the daemon.
1292  if (prStat == 0 || prStat == -2){ // Program is running or an error occurred.
1293  return rv;
1294  }
1295 
1296  if (geteuid() != 0){ // Superuser only!
1297  printf("pps-client is not running. \"sudo pps-client\" to start.\n");
1298  return rv;
1299  }
1300 
1301  pid_t pid = fork(); // Fork a duplicate child of this process.
1302 
1303  if (pid > 0){ // This is the parent process.
1304  bufferStatusMsg("Spawning pps-client daemon.\n");
1305  return rv; // Return from the parent process and leave
1306  } // the child running.
1307 
1308  if (pid == -1){ // Error: unable to fork a child from parent,
1309  sprintf(g.logbuf, "Fork in main() failed: %s\n", strerror(errno));
1310  writeToLog(g.logbuf);
1311  return pid;
1312  }
1313  // pid == 0 for the child process which now will run this code as a daemon
1314 
1315  struct sched_param param; // Process must be run as root
1316  mlockall(MCL_CURRENT | MCL_FUTURE);
1317 
1318  initialize(verbose);
1319  rv = processFiles();
1320  if (rv == -1){
1321  goto end0;
1322  }
1323 
1324  rv = disableNTP(); // Always disable NTP, to prevent it from disciolining the clock.
1325  if (g.doNTPsettime && rv != 0){ // But disableNTP() warns if it fails.
1326  goto end0;
1327  }
1328 
1329  param.sched_priority = 99; // to get real-time priority.
1330  sched_setscheduler(0, SCHED_FIFO, &param); // SCHED_FIFO: Don't yield to scheduler until sleep.
1331 
1332  if(getDriverGPIOvals() == -1){
1333  sprintf(g.logbuf, "Could not get GPIO vals for driver. Exiting.\n");
1334  fprintf(stderr, "%s", g.logbuf);
1335  writeToLog(g.logbuf);
1336  goto end0;
1337  }
1338 
1339  if (driver_load(g.ppsGPIO, g.outputGPIO, g.intrptGPIO) == -1){
1340  sprintf(g.logbuf, "Could not load PPS-Client driver. Exiting.\n");
1341  fprintf(stderr, "%s", g.logbuf);
1342  writeToLog(g.logbuf);
1343  rv = -1;
1344  goto end0;
1345  }
1346 
1347  ppid = createPIDfile(); // Create the PID file for this process.
1348  if (ppid == -1){ // Either already running or could not
1349  rv = -1; // create a pid file. In either case, exit.
1350  goto end1;
1351  }
1352 
1353  pps_fd = open_logerr("/dev/gps-pps-io", O_RDWR); // Open the gps-pps-io device driver.
1354  if (pps_fd == -1){
1355  rv = -1;
1356  goto end2;
1357  }
1358 
1359  sprintf(g.msgbuf, "Process PID: %d\n", ppid); // PPS client is starting.
1360  bufferStatusMsg(g.msgbuf);
1361 
1362  waitForPPS(verbose, pps_fd); // Synchronize to the PPS.
1363 
1364  close(pps_fd); // Close the interrupt device driver.
1365 
1366  sprintf(g.logbuf, "PPS-Client closed driver\n");
1367  writeToLog(g.logbuf);
1368 
1369 end2:
1370  sysCommand("rm /var/run/pps-client.pid"); // Remove PID file with system() which blocks until
1371  // rm completes keeping shutdown correctly sequenced.
1372  end1:
1373  enableNTP(); // Always try to re-enable NTP on shutdown.
1374 
1375  driver_unload(); // Driver will not be unloaded until a timeout occurs to
1376  // prevent driver from being unloaded before being closed
1377  sprintf(g.logbuf, "PPS-Client unloaded driver.\n"); // by OS.
1378  writeToLog(g.logbuf);
1379 end0:
1380  return rv;
1381 }
1382 
#define SLEW_LEN
The slew accumulator (slewAccum) update interval.
Definition: pps-client.h:84
void writeToLog(char *logbuf)
Definition: pps-files.cpp:247
#define HIGH
Definition: pps-client.h:105
double slewAccum
Accumulates G.rawError in getTimeSlew() and is used to determine G.avgSlew.
Definition: pps-client.h:202
void buildInterruptDistrib(int intrptDelay)
Definition: pps-files.cpp:2137
bool detectIntrptDelaySpike(int intrptError)
Definition: pps-client.cpp:874
void recordOffsets(int timeCorrection)
Definition: pps-files.cpp:2231
bool isControlling
Set "true" by getAcquireState() when the control loop can begin to control the system clock frequency...
Definition: pps-client.h:157
#define INV_DELAY_SAMPLES_PER_MIN
Constant for calculating G.sysDelay.
Definition: pps-client.h:56
#define JITTER_DISTRIB
Definition: pps-client.h:112
void recordFrequencyVars(void)
Definition: pps-files.cpp:2185
int tm[6]
Returns the interrupt calibration reception and response times from the PPS-Client device driver...
Definition: pps-client.h:167
struct timex t3
Passes G.timeCorrection to the system function adjtimex() in makeTimeCorrection().
Definition: pps-client.h:211
double freqOffset
System clock frequency correction calculated as G.integralTimeCorrection * G.integralGain.
Definition: pps-client.h:226
#define SHOW_INTRPT_DATA_INTVL
The number of seconds between displays of interrupt delay in the PPS-Client status line...
Definition: pps-client.h:55
int makeTimeCorrection(struct timeval pps_t, int pps_fd)
Definition: pps-client.cpp:723
double integral[NUM_INTEGRALS]
Array of integrals constructed by makeAverageIntegral().
Definition: pps-client.h:218
int removeNoise(int rawError)
Definition: pps-client.cpp:520
double t_mono_now
Current monotonic count of passing seconds.
Definition: pps-client.h:171
void setHardLimit(double avgCorrection)
Definition: pps-client.cpp:134
void writeTimestamp(double timestamp)
Definition: pps-files.cpp:1179
double integralTimeCorrection
Integral or average integral of G.timeCorrection returned by getIntegral();.
Definition: pps-client.h:225
unsigned int ppsCount
Advancing count of G.rawErrorDistrib[] entries made by detectDelayPeak().
Definition: pps-client.h:194
int getFractionalSeconds(timeval pps_t)
Definition: pps-client.cpp:611
#define ALERT_PPS_LOST
Definition: pps-client.h:111
int rawError
Set equal to G.interruptTime - G.sysDelay in makeTimeCorrection().
Definition: pps-client.h:182
void freeSNTPThreads(timeCheckParams *tcp)
Definition: pps-sntp.cpp:456
#define HARD_LIMIT_4
Definition: pps-client.h:101
bool integralIsReady(void)
Definition: pps-client.cpp:312
#define INTEGRAL_GAIN
Controller integral gain constant in active controller operation.
Definition: pps-client.h:54
#define LOW
Definition: pps-client.h:106
bool interruptLost
Set "true" when a PPS interrupt time fails to be received.
Definition: pps-client.h:161
int adjtimex(struct timex *timex)
int zeroError
The controller error resulting from removing jitter noise from G.rawError in removeNoise().
Definition: pps-client.h:207
void getTimeSlew(int rawError)
Definition: pps-client.cpp:215
unsigned int activeCount
Advancing count of controller cycles once G.isControlling is "true".
Definition: pps-client.h:158
void makeSNTPTimeQuery(timeCheckParams *tcp)
Definition: pps-sntp.cpp:341
void makeAverageIntegral(double avgCorrection)
Definition: pps-client.cpp:274
int integralCount
Counts the integrals formed over the last 10 controller cycles and signals when all integrals in G...
Definition: pps-client.h:220
double getAverageCorrection(int timeCorrection)
Definition: pps-client.cpp:347
#define SYSDELAY_DISTRIB
Definition: pps-client.h:115
int correctionAccum
Accumulates G.timeCorrection values from G.correctionFifo in getAverageCorrection() in order to gener...
Definition: pps-client.h:216
double avgSlew
Average slew value determined by getTimeSlew() from the average of G.slewAccum each time G...
Definition: pps-client.h:204
int disableNTP(void)
Definition: pps-files.cpp:1462
int checkPPSInterrupt(int pps_fd)
Definition: pps-client.cpp:810
void increaseMonotonicCount(void)
Definition: pps-client.cpp:627
int sysCommand(const char *cmd)
Definition: pps-files.cpp:89
#define RAW_ERROR_DECAY
Decay rate for G.rawError samples (1 hour half life)
Definition: pps-client.h:70
#define RAW_ERROR_ZERO
Index corresponding to rawError == 0 in detectDelayPeak().
Definition: pps-client.h:67
void buildSysDelayDistrib(int sysDelay)
Definition: pps-files.cpp:2161
double avgCorrection
A one-minute rolling average of G.timeCorrection values generated by getAverageCorrection().
Definition: pps-client.h:213
unsigned int intrptCount
Advancing count of intrptErrorDistrib[] entries made by detectDelayPeak().
Definition: pps-client.h:177
const char * version
Program version 1.4.0 created on 17 Dec 2017.
Definition: pps-client.cpp:51
#define HARD_LIMIT_1024
Definition: pps-client.h:100
void initialize(bool verbose)
Definition: pps-client.cpp:72
void writeSysDelay(void)
Definition: pps-files.cpp:1157
#define BLOCK_FOR_10
Blocks detection of external system clock changes for 10 seconds.
Definition: pps-client.h:76
#define PER_MINUTE
Inverse seconds per minute.
Definition: pps-client.h:50
int consensusTimeError
Consensus value of whole-second time corrections for DST or leap seconds from Internet SNTP servers...
Definition: pps-client.h:228
void HUPhandler(int sig)
Definition: pps-files.cpp:2101
int timeCorrection
Time correction value constructed in makeTimeCorrection() by dividing G.zeroError by G...
Definition: pps-client.h:210
#define NUM_INTEGRALS
Number of integrals used by makeAverageIntegral() to calculate the one minute clock frequency correct...
Definition: pps-client.h:60
int processFiles(void)
Definition: pps-files.cpp:1086
int accessDaemon(int argc, char *argv[])
Definition: pps-files.cpp:1989
int allocInitializeSerialThread(timeCheckParams *tcp)
Definition: pps-serial.cpp:423
double avgIntegral
One-minute average of the integrals in G.integral[].
Definition: pps-client.h:219
int correctionFifo[OFFSETFIFO_LEN]
Contains the G.timeCorrection values from over the previous 60 seconds.
Definition: pps-client.h:214
double delayMedian
Median of G.intrptDelay values calculated in getInterruptDelay().
Definition: pps-client.h:178
void TERMhandler(int sig)
Definition: pps-files.cpp:2088
#define SECS_PER_HOUR
Definition: pps-client.h:46
bool isVerbose
Enables continuous printing of PPS-Client status params when "true".
Definition: pps-client.h:151
#define HARD_LIMIT_NONE
Definition: pps-client.h:99
void buildErrorDistrib(int timeCorrection)
Definition: pps-files.cpp:2043
#define INV_GAIN_0
Controller inverse proportional gain constant at startup.
Definition: pps-client.h:53
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:205
int getDriverGPIOvals(void)
Definition: pps-files.cpp:575
int clampJitter(int rawError)
Definition: pps-client.cpp:246
int nIntrptDelaySpikes
Definition: pps-client.h:196
int setClockFractionalSecond(int correction, int pps_fd)
Definition: pps-client.cpp:460
int removeIntrptNoise(int intrptError)
Definition: pps-client.cpp:906
int correctionFifo_idx
Advances G.correctionFifo on each controller cycle in integralIsReady() which returns "true" every 60...
Definition: pps-client.h:222
#define STRBUF_SZ
Definition: pps-client.h:88
void freeSerialThread(timeCheckParams *tcp)
Definition: pps-serial.cpp:473
int setClockToNTPtime(int pps_fd)
Definition: pps-client.cpp:389
#define ERROR_DISTRIB_LEN
Definition: pps-client.h:95
int slewAccum_cnt
Count of the number of times G.rawError has been summed into G.slewAccum.
Definition: pps-client.h:203
#define SECS_PER_MINUTE
Definition: pps-client.h:43
int readConfigFile(void)
Definition: pps-files.cpp:626
int sysDelayShift
Assigned from G.delayShift and subtracted from G.rawError in correctDelayPeak() when a delay shift oc...
Definition: pps-client.h:185
int noiseLevel
PPS time delay value beyond which a delay is defined to be a delay spike.
Definition: pps-client.h:198
int bufferStateParams(void)
Definition: pps-files.cpp:1291
bool isDelaySpike
Set "true" by detectDelaySpike() when G.rawError exceeds G.noiseLevel.
Definition: pps-client.h:200
int enableNTP(void)
Definition: pps-files.cpp:1527
#define USECS_PER_SEC
Definition: pps-client.h:42
unsigned int seq_num
Advancing count of the number of PPS interrupt timings that have been received.
Definition: pps-client.h:155
int t_count
Whole seconds counted at the time of G.t_now.
Definition: pps-client.h:170
#define HARD_LIMIT_05
Definition: pps-client.h:103
#define SLEW_MAX
Jitter slew value below which the controller will begin to frequency lock.
Definition: pps-client.h:85
double rawErrorDistrib[ERROR_DISTRIB_LEN]
The distribution used to detect a delay shift in detectDelayPeak().
Definition: pps-client.h:192
#define INTERRUPT_LOST
Number of consecutive lost interrupts at which a warning starts.
Definition: pps-client.h:72
#define MAX_SPIKES
Maximum microseconds to suppress a burst of continuous positive jitter.
Definition: pps-client.h:80
void setDelayTrackers(void)
Definition: pps-client.cpp:58
int getInterruptDelay(int pps_fd)
Definition: pps-client.cpp:977
double getIntegral(void)
Definition: pps-client.cpp:560
Struct for passing arguments to and from threads querying SNTP time servers or GPS receivers...
Definition: pps-client.h:128
int intrptDelay
Value of the interrupt delay calibration measurement received from the PPS-Client device driver...
Definition: pps-client.h:175
void buildRawErrorDistrib(int rawError, double errorDistrib[], unsigned int *count)
Definition: pps-client.cpp:487
int sysDelay
Definition: pps-client.h:179
#define SETTLE_TIME
The PPS-Client up time required before saving performance data.
Definition: pps-client.h:51
int invProportionalGain
Controller proportional gain configured inversely to use as an int divisor.
Definition: pps-client.h:209
int nDelaySpikes
Current count of continuous delay spikes made by detectDelaySpike().
Definition: pps-client.h:199
Struct for program-wide global variables showing those important to the controller.
Definition: pps-client.h:146
void driver_unload()
Definition: pps-files.cpp:1733
int outputGPIO
The calibrate GPIO output number read from pps-client.conf and passed to the driver.
Definition: pps-client.h:148
#define NOISE_LEVEL_MIN
The minimum level at which interrupt delays are delay spikes.
Definition: pps-client.h:83
int t_now
Whole seconds of current time reported by gettimeofday().
Definition: pps-client.h:169
int intrptError
Set equal to "intrptDelay - sysDelay" in getInterruptDelay().
Definition: pps-client.h:176
int allocInitializeSNTPThreads(timeCheckParams *tcp)
Definition: pps-sntp.cpp:416
bool getAcquireState(void)
Definition: pps-client.cpp:109
double t_mono_last
Last recorded monotonic count used to determine a lost PPS update.
Definition: pps-client.h:172
#define HARD_LIMIT_1
Definition: pps-client.h:102
bool detectDelaySpike(int rawError)
Definition: pps-client.cpp:183
int open_logerr(const char *filename, int flags)
Definition: pps-files.cpp:366
int makeSerialTimeQuery(timeCheckParams *tcp)
Definition: pps-serial.cpp:365
int createPIDfile(void)
Definition: pps-files.cpp:515
bool detectExteralSystemClockChange(void)
Definition: pps-client.cpp:689
int interruptLossCount
Records the number of consecutive lost PPS interrupt times.
Definition: pps-client.h:162
#define INTERRUPT_LATENCY
Default interrupt latency assigned to sysDelay (microseconds).
Definition: pps-client.h:65
#define INTERRUPT_DISTRIB
Definition: pps-client.h:114
int setClockToSerialTime(int pps_fd)
Definition: pps-client.cpp:422
bool isNearZero(double val)
Definition: pps-client.cpp:672
int readPPS_SetTime(bool verbose, int pps_fd)
#define NOISE_FACTOR
Adjusts G.noiseLevel to track G.sysDelay.
Definition: pps-client.h:82
struct timeval t
Time of system response to the PPS interrupt. Received from the PPS-Client device driver...
Definition: pps-client.h:164
int main(int argc, char *argv[])
int hardLimit
An adaptive limit value determined by setHardLimit() and applied to G.rawError by clampJitter() as th...
Definition: pps-client.h:208
void buildJitterDistrib(int rawError)
Definition: pps-files.cpp:2067
struct timespec setSyncDelay(int timeAt, int fracSec)
Definition: pps-client.cpp:937
int writeStatusStrings(void)
Definition: pps-files.cpp:316
int correctionFifoCount
Signals that G.correctionFifo contains a full count of G.timeCorrection values.
Definition: pps-client.h:215
int intrptGPIO
The calibrate GPIO interrupt number read from pps-client.conf and passed to the driver.
Definition: pps-client.h:149
int driver_load(int ppsGPIO, int outputGPIO, int intrptGPIO)
Definition: pps-files.cpp:1668
int ppsGPIO
The PPS GPIO interrupt number read from pps-client.conf and passed to the driver. ...
Definition: pps-client.h:147
#define ADJTIMEX_SCALE
Frequency scaling required by adjtimex().
Definition: pps-client.h:63
#define INV_GAIN_1
Controller inverse proportional gain constant during active controller operation. ...
Definition: pps-client.h:52
double zeroAccum
Accumulator to test nearness to zero in isNearZero()
Definition: pps-client.h:173
bool interruptReceived
Set "true" when makeTimeCorrection() processes an interrupt time from the PPS-Client device driver...
Definition: pps-client.h:160
void getPPStime(timeval t, int timeCorrection)
Definition: pps-client.cpp:589
void bufferStatusMsg(const char *msg)
Definition: pps-files.cpp:290
int interruptTime
Fractional second part of G.t received from PPS-Client device driver.
Definition: pps-client.h:165
double integralGain
Current controller integral gain.
Definition: pps-client.h:224
#define ERROR_DISTRIB
Definition: pps-client.h:110
#define PER_NUM_INTEGRALS
Inverse of NUM_INTEGRALS.
Definition: pps-client.h:61
void initFileLocalData(void)
Definition: pps-files.cpp:85
struct G g
Declares the global variables defined in pps-client.h.
Definition: pps-client.cpp:53
void waitForPPS(bool verbose, int pps_fd)