Search code examples
c++gpsc++20c++-chrono

How to compute GPS time of week (gps_tow) from std::chrono::gps_clock::now?


How to compute GPS time of week (gps_tow) from std::chrono::gps_clock::now?

gps_clock::now is provided by latest compiler/std: https://en.cppreference.com/w/cpp/chrono/gps_clock/now.

How to compute GPS time of week starting from there https://godbolt.org/z/81vK3n3rG using std::chrono only?


Solution

  • You can take advantage of the fact that the gps epoch starts at the beginning of the week (where they defined the beginning of the week as Sunday 00:00:00 UTC).

    auto gps_now = gps_clock::now();
    cout << "GPS Time: " << gps_now << std::endl;
    auto gps_weeks = floor<weeks>(gps_now);
    auto gps_tow = gps_now - gps_weeks;
    cout << "GPS week: " << gps_weeks.time_since_epoch()/weeks{1} << '\n';
    cout << "GPS tow: " << gps_tow << '\n';
    

    You just floor the time to weeks precision, and then subtract off the integral number of weeks from the time to get time of week.

    This gives you a duration of the same precision that gps_now has (nanoseconds with gcc). This formats the tow as just a number of nanoseconds. You can reformat it into days, hours, minutes and fractional seconds with:

    auto gps_days = floor<days>(gps_tow);
    gps_tow -= gps_days;
    cout << "GPS tow: " << gps_days.count() << " days " << format("{:%T}", gps_tow) << '\n';
    

    The full demo currently outputs:

    GPS Time: 2023-08-26 12:49:22.394953138
    GPS week: 2276
    GPS tow: 564562394953138ns
    GPS tow: 6 days 12:49:22.394953138
    

    Note that the although the gps epoch was defined to start at the beginning of the UTC week:

    cout << format("{:%a %F %T %Z}", clock_cast<utc_clock>(gps_seconds{})) << '\n';
    

    Outputs:

    Sun 1980-01-06 00:00:00 UTC
    

    The GPS weeks and UTC weeks become unaligned by 1 second every time there is a leap second (because all GPS weeks are exactly 7*86400 seconds long). So currently the GPS week begins 18s before the UTC week:

    cout << format("{:%a %F %T %Z}", gps_seconds{weeks{2276}}) << '\n';
    cout << format("{:%a %F %T %Z}", clock_cast<utc_clock>(gps_seconds{weeks{2276}})) << '\n';
    

    Output:

    Sun 2023-08-20 00:00:00 GPS
    Sat 2023-08-19 23:59:42 UTC
    

    Note how you can use clock_cast to convert back and forth between UTC and GPS time points. This provides a more homogenous interface compared to the from_utc/to_utc static member functions. But otherwise they are the same thing. clock_cast calls from_utc/to_utc under the hood.