PPS-Client  1.4.0
Client for synchronizing the system clock to a GPS PPS source
pps-serial.cpp
Go to the documentation of this file.
1 
22 #include "../client/pps-client.h"
23 extern struct G g;
24 
28 static struct serialLocalVars {
29  int serverTimeDiff[1];
30  bool threadIsBusy[1];
31  pthread_t tid[1];
32  int timeCheckEnable;
33  bool allServersQueried;
34  unsigned int lastServerUpdate;
35 
36 
37  char *serialPort;
38  int lostGPSCount;
39  bool doReadSerial;
40  int lastSerialTimeDif;
41 } f;
42 
58 bool getUTCfromGPSmessages(const char *msgbuf, timeCheckParams *tcp, time_t *gmtSeconds){
59 
60  char scnbuf[10];
61  memset(scnbuf, 0, 10);
62 
63  struct tm gmt;
64  memset(&gmt, 0, sizeof(struct tm));
65 
66  char *active, *ctmp2, *ctmp4;
67 
68  active = scnbuf;
69  ctmp2 = active + 2;
70  ctmp4 = ctmp2 + 2;
71 
72  float ftmp1, ftmp3, ftmp5, ftmp6;
73  int frac;
74 
75  char *pstr = strstr((char *)msgbuf, "$GPRMC"); // $GPRMC,205950.000,A,3614.5277,N,08051.3851,W,0.02,288.47,051217, ,,D*75
76  if (pstr != NULL){ // If a GPRMC message was received, continue.
77  char *end = pstr + 64;
78  char *pNext = strstr(pstr + 10, "$"); // Verify this is a complete message by looking for next message start symbol
79  if (pNext != NULL){ // If the message is complete, continue.
80  *end = '\0';
81  sscanf(pstr, "$GPRMC,%2d%2d%2d.%d,%1c,%f,%1c,%f,%1c,%f,%f,%2d%2d%2d,", &gmt.tm_hour, &gmt.tm_min, &gmt.tm_sec,
82  &frac, active, &ftmp1, ctmp2, &ftmp3, ctmp4, &ftmp5, &ftmp6, &gmt.tm_mday, &gmt.tm_mon, &gmt.tm_year);
83 
84  if (active[0] == 'A'){ // Verify this is an active message
85  gmt.tm_mon -= 1; // Convert to tm struct format with months: 0 to 11
86  gmt.tm_year += 100; // Convert to tm struct format with year since 1900
87  *gmtSeconds = mktime(&gmt); // True UTC seconds
88 
89  f.lostGPSCount = 0;
90  }
91  else {
92  sprintf(tcp->strbuf, "getUTCfromGPSmessages() A GPS message was received but it is not active.\n");
93  writeToLog(tcp->strbuf);
94 
95  if (f.lostGPSCount == 0) {
96  f.lostGPSCount = 1;
97  }
98  else {
99  f.lostGPSCount += 1;
100  if (f.lostGPSCount == 5){
101  sprintf(tcp->strbuf, "getUTCfromGPSmessages() Unable to connect to GPS. Will retry in %d minutes.\n", CHECK_TIME_SERIAL / 60);
102  writeToLog(tcp->strbuf);
103 
104  f.lostGPSCount = 0;
105  tcp->doReadSerial = false;
106  }
107  }
108  }
109  }
110  else { // Did not get a usable GPRMC message
111  return false;
112  }
113  }
114  else { // Did not get GPRMC message
115  return false;
116  }
117  return true;
118 }
119 
144 int waitForGPSmessages(char *msgbuf, timeCheckParams *tcp, struct timeval *t_gmt, time_t *gmtSeconds){
145 
146  int rfd = open(tcp->serialPort, O_RDONLY);
147  if (rfd == -1){
148  sprintf(tcp->strbuf, "waitForGPSmessages() Unable to open %s\n", tcp->serialPort);
149  writeToLog(tcp->strbuf);
150  close(rfd);
151  return -1;
152  }
153  fd_set rfds;
154  FD_ZERO(&rfds);
155  FD_SET(rfd, &rfds);
156 
157  struct timeval wait;
158  wait.tv_sec = 1;
159  wait.tv_usec = 0;
160  // select blocks until the serial port starts receiving GPS messages.
161 
162  int rv = select(rfd + 1, &rfds, NULL, NULL, &wait);
163  if (rv == -1){
164  sprintf(tcp->strbuf,"waitForGPSmessages() select failed with error %s.\n", strerror(errno));
165  writeToLog(tcp->strbuf);
166  close(rfd);
167  return -1;
168  }
169  if (rv == 0){
170  sprintf(tcp->strbuf,"waitForGPSmessages() No messages were available within one second.\n");
171  writeToLog(tcp->strbuf);
172  close(rfd);
173  return 0;
174  }
175  // Serial port starts buffering GPS messages here
176 
177  time_t clkSeconds = time(0); // Get local time in seconds
178  struct tm *gmt = gmtime(&clkSeconds); // Get a tm struct for UTC according to system clock
179  *gmtSeconds = mktime(gmt); // Get UTC seconds according to system clock
180 
181  gettimeofday(t_gmt, NULL); // Time at start of buffering GPS messages.
182 
183  struct timespec slp;
184  slp.tv_sec = 0;
185  slp.tv_nsec = 250000000; // ~250 chars
186 
187  nanosleep(&slp, NULL); // Sleep long enough to get ~250 chars at 9600 baud ~ 1 char per msec.
188 
189  // Read the buffered serial port messages here
190 
191  int nchars = read(rfd, msgbuf, 250);
192  if (nchars == -1){
193  sprintf(tcp->strbuf,"waitForGPSmessages() read on serial port received empty buffer.\n");
194  writeToLog(tcp->strbuf);
195  close(rfd);
196  return 0;
197  }
198 
199  close(rfd);
200  return 1;
201 }
202 
203 //void writeTimeToLog(char *strbuf, struct timeval t_gmt, struct timeval t_gmt0, struct timeval t_rdy){
204 // writeToLog(strbuf);
205 // sprintf(strbuf, " t_gmt.tv_sec: %d t_gmt.tv_usec: %d\n", (int)t_gmt.tv_sec, (int)t_gmt.tv_usec);
206 // writeToLog(strbuf);
207 // sprintf(strbuf, " t_gmt0.tv_sec: %d t_gmt0.tv_usec: %d\n", (int)t_gmt0.tv_sec, (int)t_gmt0.tv_usec);
208 // writeToLog(strbuf);
209 // sprintf(strbuf, " t_rdy.tv_sec: %d t_rdy.tv_usec: %d\n", (int)t_rdy.tv_sec, (int)t_rdy.tv_usec);
210 // writeToLog(strbuf);
211 //}
212 
226 int getTimeOffsetOverSerial(int *timeDif, timeCheckParams *tcp){
227 
228  time_t difSeconds = 0, gmt0Seconds = 0, gmtSeconds;
229 
230  char msgbuf[300];
231  memset(msgbuf, 0, 300 * sizeof(char));
232 
233  struct timeval t_gmt, t_rdy;
234 
235  int rv = waitForGPSmessages(msgbuf, tcp, &t_gmt, &gmtSeconds);
236  if (rv == 0 || rv == -1){
237  return rv;
238  }
239 
240  if (!getUTCfromGPSmessages(msgbuf, tcp, &gmt0Seconds)){
241  return 0;
242  }
243 
244  gettimeofday(&t_rdy, NULL); // Time at completion of UTC time extraction
245 
246  bool finishedInSameSecond = (t_gmt.tv_sec == t_rdy.tv_sec);
247  if (finishedInSameSecond){
248  difSeconds = gmt0Seconds - gmtSeconds;
249  *timeDif = (int)difSeconds;
250  return 1;
251  }
252  else {
253  sprintf(tcp->strbuf, "getTimeOffsetOverSerial() Discarded the data. Took over 1 second. OS latency!\n");
254  writeToLog(tcp->strbuf);
255  return 0;
256  }
257 }
258 
280 
281  tcp->threadIsBusy[0] = true;
282  int timeDif = 0;
283 
284  int isValidDif = getTimeOffsetOverSerial(&timeDif, tcp);
285  if (isValidDif > 0){
286  if (timeDif == 0){ // No time difference
287  if (f.lastSerialTimeDif != 0){
288  sprintf(tcp->strbuf, "doSerialTimeCheck() No timeDif on second read. First read was GPS error.\n");
289  writeToLog(tcp->strbuf);
290  }
291  tcp->doReadSerial = false;
292 
293  tcp->serverTimeDiff[0] = 0;
294  f.lastSerialTimeDif = 0;
295 
296  tcp->rv = 1;
297  goto end;
298  }
299  if (timeDif != 0 && f.lastSerialTimeDif == 0){ // No previous read.
300  sprintf(tcp->strbuf, "doSerialTimeCheck() timeDif detected on first read: %d\n", timeDif);
301  writeToLog(tcp->strbuf);
302 
303  tcp->serverTimeDiff[0] = 0;
304  f.lastSerialTimeDif = timeDif; // So flag timeDif for verification and continue read serial.
305 
306  tcp->rv = 0;
307  goto end;
308  }
309  if (timeDif != 0 && f.lastSerialTimeDif != 0){ // Got a timeDif on second read
310  if (timeDif == f.lastSerialTimeDif ){ // timeDif is valid.
311  sprintf(tcp->strbuf, "doSerialTimeCheck() Verified timeDif on second read: %d\n", timeDif);
312  writeToLog(tcp->strbuf);
313 
314  tcp->doReadSerial = false; // So stop read serial
315 
316  tcp->serverTimeDiff[0] = timeDif; // and send back the timeDif.
317  f.lastSerialTimeDif = 0;
318 
319  tcp->rv = 1;
320  goto end;
321  }
322  else { // timeDifs don't match
323  sprintf(tcp->strbuf, "doSerialTimeCheck() Second timeDif read: %d does not match the first: %d. Not valid.\n", timeDif, f.lastSerialTimeDif);
324  writeToLog(tcp->strbuf);
325 
326  tcp->doReadSerial = false;
327 
328  tcp->serverTimeDiff[0] = 0; // No valid timeDif so send back zero.
329  f.lastSerialTimeDif = 0;
330 
331  tcp->rv = 0;
332  goto end;
333  }
334  }
335  }
336  else if (isValidDif == 0){
337  sprintf(tcp->strbuf, "doSerialTimeCheck() Did not see a GPRMC message. Retrying.\n");
338  writeToLog(tcp->strbuf);
339  tcp->doReadSerial = true;
340  }
341  else if (isValidDif == -1){
342  tcp->rv = -1;
343  goto end;
344  }
345  tcp->rv = 0;
346 end:
347  tcp->threadIsBusy[0] = false;
348  return;
349 }
350 
366 
367  int rv = 0;
368 
369  if (tcp->threadIsBusy[0] == false){
370  rv = tcp->rv;
371  if (rv == -1){
372  sprintf(tcp->strbuf, "Time check failed with an error. See the pps-client.log\n");
373  bufferStatusMsg(tcp->strbuf);
374  return rv;
375  }
376  if (rv == 1){
377  sprintf(tcp->strbuf, "GPS Reported clock offset: %d\n", tcp->serverTimeDiff[0]);
378  bufferStatusMsg(tcp->strbuf);
379  tcp->rv = 0;
380  rv = 0;
381  }
382  g.serialTimeError = tcp->serverTimeDiff[0];
383  tcp->serverTimeDiff[0] = 0;
384 
385  f.doReadSerial = tcp->doReadSerial;
386  }
387  else {
388  sprintf(g.msgbuf, "Thread is busy.\n");
389  bufferStatusMsg(g.msgbuf);
390  return rv;
391  }
392 
393  if (g.seq_num == 1 ||
394  g.seq_num % CHECK_TIME_SERIAL == 0){ // Start a time check every CHECK_TIME_SERIAL
395  f.doReadSerial = true; // Read continues until f.doReadSerial is set false.
396 
397  sprintf(g.logbuf, "Requesting a GPS time check.\n");
398  bufferStatusMsg(g.logbuf);
399  }
400 
401  if (f.doReadSerial){
402  g.blockDetectClockChange = BLOCK_FOR_3;
403 
404  int rv = pthread_create(&((tcp->tid)[0]), &(tcp->attr), (void* (*)(void*))&doSerialTimeCheck, tcp);
405  if (rv != 0){
406  sprintf(g.logbuf, "Can't create thread : %s\n", strerror(errno));
407  writeToLog(g.logbuf);
408  return -1;
409  }
410  }
411  return rv;
412 }
413 
424  memset(&f, 0, sizeof(struct serialLocalVars));
425 
426  int buflen = strlen(g.serialPort);
427  f.serialPort = new char[buflen + 1];
428  strcpy(f.serialPort, g.serialPort);
429 
430  f.serverTimeDiff[0] = 0;
431  f.threadIsBusy[0] = false;
432 
433  tcp->tid = f.tid;
434  tcp->serverTimeDiff = f.serverTimeDiff;
435  tcp->strbuf = new char[STRBUF_SZ];
436  tcp->threadIsBusy = f.threadIsBusy;
437  tcp->serialPort = f.serialPort;
438 
439  printf("allocInitializeSerialThread() tcp->serialPort: %s\n", tcp->serialPort);
440 
441  tcp->rv = 0;
442  tcp->doReadSerial = false;
443 
444  int rv = pthread_attr_init(&(tcp->attr));
445  if (rv != 0) {
446  sprintf(g.logbuf, "Can't init pthread_attr_t object: %s\n", strerror(errno));
447  writeToLog(g.logbuf);
448  return -1;
449  }
450 
451  rv = pthread_attr_setstacksize(&(tcp->attr), PTHREAD_STACK_REQUIRED);
452  if (rv != 0){
453  sprintf(g.logbuf, "Can't set pthread_attr_setstacksize(): %s\n", strerror(errno));
454  writeToLog(g.logbuf);
455  return -1;
456  }
457 
458  rv = pthread_attr_setdetachstate(&(tcp->attr), PTHREAD_CREATE_DETACHED);
459  if (rv != 0){
460  sprintf(g.logbuf, "Can't set pthread_attr_t object state: %s\n", strerror(errno));
461  writeToLog(g.logbuf);
462  return -1;
463  }
464 
465  return 0;
466 }
467 
474  pthread_attr_destroy(&(tcp->attr));
475  delete[] tcp->strbuf;
476  if (tcp->serialPort != NULL){
477  delete[] tcp->serialPort;
478  tcp->serialPort = NULL;
479  }
480 }
void writeToLog(char *logbuf)
Definition: pps-files.cpp:247
bool doReadSerial
Flag to read serial messages from serial port.
Definition: pps-client.h:136
#define PTHREAD_STACK_REQUIRED
Stack space requirements for threads.
Definition: pps-client.h:41
#define CHECK_TIME_SERIAL
Interval between serial port time checks (about 10 minutes)
Definition: pps-client.h:78
char * strbuf
Space for messages and query strings.
Definition: pps-client.h:137
int rv
Return value of thread.
Definition: pps-client.h:140
bool getUTCfromGPSmessages(const char *msgbuf, timeCheckParams *tcp, time_t *gmtSeconds)
Definition: pps-serial.cpp:58
int * serverTimeDiff
Time difference between local time and server time.
Definition: pps-client.h:132
int allocInitializeSerialThread(timeCheckParams *tcp)
Definition: pps-serial.cpp:423
#define STRBUF_SZ
Definition: pps-client.h:88
void freeSerialThread(timeCheckParams *tcp)
Definition: pps-serial.cpp:473
struct G g
Declares the global variables defined in pps-client.h.
Definition: pps-client.cpp:53
pthread_attr_t attr
Thread attribute object.
Definition: pps-client.h:130
unsigned int seq_num
Advancing count of the number of PPS interrupt timings that have been received.
Definition: pps-client.h:155
char * serialPort
The serial port filename when serial time is used.
Definition: pps-client.h:134
Struct for passing arguments to and from threads querying SNTP time servers or GPS receivers...
Definition: pps-client.h:128
bool * threadIsBusy
True while thread is waiting for or processing a time query.
Definition: pps-client.h:139
Struct for program-wide global variables showing those important to the controller.
Definition: pps-client.h:146
int getTimeOffsetOverSerial(int *timeDif, timeCheckParams *tcp)
Definition: pps-serial.cpp:226
int makeSerialTimeQuery(timeCheckParams *tcp)
Definition: pps-serial.cpp:365
#define BLOCK_FOR_3
Blocks detection of external system clock changes for 3 seconds.
Definition: pps-client.h:77
void bufferStatusMsg(const char *msg)
Definition: pps-files.cpp:290
int waitForGPSmessages(char *msgbuf, timeCheckParams *tcp, struct timeval *t_gmt, time_t *gmtSeconds)
Definition: pps-serial.cpp:144
void doSerialTimeCheck(timeCheckParams *tcp)
Definition: pps-serial.cpp:279
pthread_t * tid
Thread id.
Definition: pps-client.h:129