1

Setting system time with gps timestamp_t structure in Linux

Hi Everyone,

I'm trying to write code that as soon as I get a succesfull GPS lock (Adafruit Ultimate GPS w/ GPSD) I set the system time. Right now, I can see that 'timestamp_t':

typedef double timestamp_t; /* Unix time in seconds with fractional part */

is a part of my "gps_data_t*" structure. This is a good start, however, the function I planned on using to set the system time in Linux is:

int settimeofday(const struct timeval *tv, const struct timezone *tz);

I've found information on how to convert time_t to *timeval, but how should I handle this double to convert it to *timeval?

Code:

gpsmm gps_rec("localhost", DEFAULT_GPSD_PORT);

if (gps_rec.stream(WATCH_ENABLE|WATCH_JSON) == NULL) {
    std::cout << "No GPSD running. exiting GPS thread." << std::endl;
    return;
}

//Data structure
struct gps_data_t* newdata;

//Loop until first GPS lock to set system time
while ((newdata = gps_rec.read()) == NULL) {
    //Wait for a valid read
}

//Set system time - timestamp_t vs timeval?
timeval *tv{?? newdata->time ??};
timezone *tz{ timezone(300, DST_USA)};
settimeofday(tv, tz);

Some comments and links have helped me. The confusion is differentiating between timeval, time_t, and timestamp_t. As I understand it here are the differences, please correct if not the case:

All are time in seconds after Jan 1st, 1970, but...

timeval is a structure of (2) long values, tv_sec is seconds after 1/1/1970, tv_usec is microseconds after that.

time_t is a long? that is also seconds after 1/1/1970

timestamp_t is a double that is seconds after 1/1/1970, so the decimal part could calculate the microseconds to get 'roughly' the same precision of timeval.

So conversion between all of them could go as such:

timeval time;
time_t timet;
timestamp_t timestampt;

timet = time.tv_sec;   //usec dropped
timet = timestampt;    //usec dropped
timestampt = timet;    //usec not given
timestampt = time.tv_sec;
timestampt += (time.tv_usec / 1000000)  //Denominator may be off by a few 0's

Is this painting a clearer picture?


To go the opposite way, which is what is needed for my application:

//Convert gps_data_t* member 'time' to timeval
timeval tv;
double wholeseconds, decimalseconds;
decimalseconds = modf(newdata->time, &wholeseconds);
tv.sec = static_cast<int32_t>(wholeseconds);
tv.usec = static_cast<int32_t>(decimalseconds * 1000000.0);

//Create timezone
timezone tz{ timezone(300, DST_USA)};

//Set system time
settimeofday(&tv, &tz);
DrTarr
  • 922
  • 2
  • 15
  • 34
  • 1
    First of all, just because a function expects a pointer to a structure doesn't mean you have to declare the variable to be a pointer. You must have read about the address-of operator `&` somewhere. – Some programmer dude Nov 08 '16 at 12:30
  • 1
    As for your problem, have you even tried [reading the `settimeofday` manual page](http://man7.org/linux/man-pages/man2/settimeofday.2.html)? It tells you what the structures looks like. – Some programmer dude Nov 08 '16 at 12:31
  • Yeah, I don't think the confusion is on what the settimeofday function requires, it is pretty clear on the structure of timeval. Its the timestamp_t structure from the gps data that I don't quite understand. However, I just found [this](http://stackoverflow.com/questions/26709107/how-to-convert-a-timestamp-t-to-a-real-time) link which is very helpful and I will have to try. I guess timestamp_t is similar to time_t however as a double it has a fractional component which equates to fractions of seconds, which isn't critical in this application. This may be the missing piece. – DrTarr Nov 08 '16 at 12:42
  • Take a look to that post [system time setting using c library function](http://stackoverflow.com/questions/2916170/system-time-setting-using-c-library-function) – J. Piquard Nov 08 '16 at 15:02
  • 1
    You have to convert `time.tv_usec` as double before computing the decimal part like `timestampt += ((double)time.tv_usec / 1000000.0L);`. But in my comprehension of your problem, you want to do the conversion from `timestamp_t timestampt` value to `timeval time`. – J. Piquard Nov 08 '16 at 16:12
  • You're right, I wrote code above (can't post it in comment) that I think will do what I want. Thanks – DrTarr Nov 08 '16 at 16:33
  • Why you didn't write your own answer to become **Explainer** ? Are you sure that the `struct gps_data_t* newdata;` give you a `time` value ? In that [Github gps.h](https://github.com/ukyg9e5r6k7gubiekd6/gpsd/blob/master/gps.h), `timestamp_t time;` is in the `struct gps_fix_t` and `timestamp_t online;` is in the `struct gps_data_t`. – J. Piquard Nov 08 '16 at 16:57
  • Haven't tested it yet, I'm no doing development on this computer. You are right, it's not directly in the gps_data_t* structure, it would then be newdata->fix.time. When I've compiled and tested it I'll post it as an answer. – DrTarr Nov 08 '16 at 17:33

1 Answers1

0

Here it is:

//Get first data to set system time
struct gps_data_t* firstdata;

//Loop until first GPS lock to set system time
while (((firstdata = gps_rec.read()) == NULL) ||
        (firstdata->fix.mode < 1)) {
    //Do nothing
}

//Convert gps_data_t* member 'time' to timeval
timeval tv;
double wholeseconds, decimalseconds, offsettime;
offsettime = firstdata->fix.time - (5.0 * 3600.0);  //5hr offset from GMT
decimalseconds = modf(offsettime, &wholeseconds);
tv.tv_sec = static_cast<int32_t>(wholeseconds);
tv.tv_usec = static_cast<int32_t>(decimalseconds * 1000000.0);

//Set system time
if ( settimeofday(&tv, NULL) >= 0) {
    std::cout << "Time set succesful!" << '\n';
} else {
    std::cout << "Time set failure!" << '\n';
}
DrTarr
  • 922
  • 2
  • 15
  • 34