0

There's many similar questions out there but I haven't found one specific to the GPS output data I am receiving. The data from my GPS is in decimal form:

GPS Week: 2145 and GPS Time: 330374.741371 (the manual says this is a double that represents the "time of week in seconds")

I'm trying to convert this time into human readable UTC time. I'm using old C++14, not 20, so I can't just use the to_utc() function I don't think. I'm mostly confused about the decimal. On this website: https://www.labsat.co.uk/index.php/en/gps-time-calculator it looks like the data is "secondsOfTheWeek.secondsOfTheDay. I'm not sure how to convert this to UTC time...

I believe this output data is the number of seconds since the GPS epoch time of midnight, Jan. 6 1980. And I know it doesn't count leap seconds so that has to be taken into account too. If I had some guidance on how to start getting this into UTC time I think I could figure out the rest, but I'm not really sure where to start...

Eventually I want to convert the time into a string to set an OS system w that time using "date -s "16 AUG 2021 13:51:00" or something like that. But first I just need to convert this GPS time.

Ted Lyngmo
  • 93,841
  • 5
  • 60
  • 108
Two Bits
  • 11
  • 1
  • You have some hints at [GPS Week Number Rollover](https://en.wikipedia.org/wiki/GPS_Week_Number_Rollover). Feed the rollover dates into the calculator you showed and it should be possible to figure out. – Ted Lyngmo Apr 28 '21 at 21:56

1 Answers1

2

There exists a free, open-source preview to the C++20 chrono bits which works with C++14.

#include "date/tz.h"
#include <chrono>

date::utc_seconds
convert(int gps_week, double gps_time)
{
    using namespace date;
    using namespace std::chrono;

    int upper = static_cast<int>(gps_time);
    auto gps_t = gps_seconds{} + weeks(gps_week) + seconds{upper};
    return clock_cast<utc_clock>(gps_t);
}

This first forms a gps_time by adding the appropriate number of weeks to the gps epoch, and then the seconds.

Next you use clock_cast to transform this into utc_time (which does include leap seconds).

This can be used like so:

#include <iostream>

int
main()
{
    using namespace date;
    using namespace std;

    cout << convert(2145, 330374.741371) << '\n';
}

Which outputs:

2021-02-17 19:45:56

The clock_cast changes the epoch from 1980-01-06 to 1970-01-01 and adds in the number of leap seconds that occur between the gps epoch and the utc time point. If the gps input happens to correspond to a leap second, this will properly print "60" in the seconds field. For example:

cout << convert(1851, 259216) << '\n';  // 2015-06-30 23:59:60

Some installation is required.

Further information

This Wikipedia article says that the time of week actually comes in units of 1.5 seconds, ranging in value from 0 to 403,199.

static_assert(403'200 * 1.5 == 7 * 24 * 60 * 60);

If one finds themself dealing with the data in this form, here is an alternate convert implementation which can deal with this input data directly:

using gps_tow = std::chrono::duration<int, std::ratio<3, 2>>;

auto
convert(date::weeks gps_week_num, gps_tow tow)
{
    using namespace date;
    return clock_cast<utc_clock>(gps_seconds{} + gps_week_num + tow);
}

The first step is to define a duration unit of 1.5 seconds. This type is called gps_tow above.

The convert function now takes two strictly typed parameters: a count of weeks, and a count of gps_tow. Then one simply adds these parts together, along with the gps epoch, and clock_cast's it to utc_clock.

It can be used like so:

cout << convert(weeks{1851}, gps_tow{172811}) << '\n';

The output for this example is:

2015-06-30 23:59:60.5
Howard Hinnant
  • 206,506
  • 52
  • 449
  • 577