[time-nuts] GPS seconds conversion on an Arduino

Andrew Rodland andrew at cleverdomain.org
Tue May 16 04:40:11 EDT 2017

On Sat, May 13, 2017 at 9:58 PM, Mark Sims <holrum at hotmail.com> wrote:
> Converting GPS seconds to Gregorian date/time on the Arduino will be an arduous task.  You take GPS seconds and add it to the GPS starring epoch to get a Julian date.  Then add in the number of leap seconds as a fraction of a day to get UTC and possibly add in a time zone offset for local time.  Don't forget to do daylight savings time conversion...  Then convert the result to Gregorian date/time for display.
> The problem is the Arduino floating point library is single precision only and does not have the resolution needed to handle the numbers involved.  Doing it with integer arithmetic (long longs) opens up a whole new can of worms.

My old Arduino NTP server (the new one is built on an ARM chip)
converted from GPS time to NTP time (which is a 32.32 bit unsigned
fixed point format, at least for v3) without any particular trouble.
Given GPS week, (integer) GPS TOW, fractional seconds, and UTC
difference in separate variables, the code amounted to:

1. Start with 2,524,953,600 (the number of seconds between the NTP
epoch (1900-01-01) and the GPS epoch (1980-01-06); for the Unix epoch
of 1970-01-01 you would need 315,964,800 instead.)
2. Subtract the current GPS-UTC difference (leap second count).
3. Add 604,800 times the GPS week.
4. Add the TOW.
5. Store this value in the upper 32 bits of the output.
6. Convert fractional seconds from whatever units they're in, to units
of 2^-32 second using an appropriate fixed-point multiply, and store
the result in the lower 32 bits of the output.

Calendrical stuff is of course its own pain, but it's still true that
you can work with whole seconds (or even whole days) there, and deal
with fractions separately, without ever touching floating-point.
Fixed-point *is* extra work, but if you make the right choices it's
not *much* extra work. Depending on your application, you can of
course lose some (or all) of the lower bits in the fractional part,
and if you have the ability to choose your epoch to be relatively
close to the present, you can lose upper bits of the integer part.
It's worth noting that since the AVR is fundamentally an 8-bit
processor and all arithmetic on larger quantities has to be
synthesized from 8-bit operations anyway, AVR-GCC helpfully provides
__int24 and __uint24 types, as there's no good reason not to. That
means that if you want to do 24.8 fixed-point, you can.

More information about the time-nuts mailing list