Как получить текущую дату и время от времени отладки GPS в python

У меня есть gps unsegmented time, как это:

Tgps = 1092121243.0

И я хотел бы понять, что такое дата и время. Начало GPS-времени - 6 января 1980 года. Функция Python

datetime.utcfromtimestamp 

может давать секунды с 1 января 1970 года.

Я нашел следующее:

from datetime import datetime
GPSfromUTC = (datetime(1980,1,6) - datetime(1970,1,1)).total_seconds()
curDate = datetime.utcfromtimestamp(Tgps + GPSfromUTC) 

Out[83]: datetime.datetime(2014, 8, 15, 7, 0, 43)

Я не уверен, что в leapseconds они включены в функцию datetime, или я должен рассчитать их и вычесть из результата? Может быть, также существует лучшее решение этой проблемы?

Ответ 1

Время GPS синхронизируется с UTC: 1980-01-06 (UTC) == 1980-01-06 (GPS). Оба тикают в секундах СИ. Разница между временем GPS и временем UTC увеличивается с каждой (интеркалярной) скачкообразной секундой.

Чтобы найти правильное время UTC, вам нужно знать количество секунд прыжка до заданного времени GPS:

#!/usr/bin/env python
from datetime import datetime, timedelta

# utc = 1980-01-06UTC + (gps - (leap_count(2014) - leap_count(1980)))
utc = datetime(1980, 1, 6) + timedelta(seconds=1092121243.0 - (35 - 19))
print(utc)

Выход

2014-08-15 07:00:27 # (UTC)

где leap_count(date) - количество секунд прыжка, введенное до указанной даты. Из таблица TAI-UTC (примечание: сайт является авторитетным источником на секундах секундного прыжка. Он публикует Бюллетень C, объявляющий о новых високосных секундах):

1980..: 19s 
2012..: 35s

и поэтому:

(leap_count(2014) - leap_count(1980)) == (35 - 19)

Если вы находитесь в Unix, вы можете использовать часовой пояс "right", чтобы получить время UTC от времени TAI (и легко получить TAI время от времени GPS: TAI = GPS + 19 секунд (постоянное смещение):

#!/usr/bin/env python
import os
import time

os.environ['TZ'] = 'right/UTC' # TAI scale with 1970-01-01 00:00:10 (TAI) epoch
time.tzset() # Unix

from datetime import datetime, timedelta

gps_timestamp = 1092121243.0 # input
gps_epoch_as_gps = datetime(1980, 1, 6) 
# by definition
gps_time_as_gps = gps_epoch_as_gps + timedelta(seconds=gps_timestamp) 
gps_time_as_tai = gps_time_as_gps + timedelta(seconds=19) # constant offset
tai_epoch_as_tai = datetime(1970, 1, 1, 0, 0, 10)
# by definition
tai_timestamp = (gps_time_as_tai - tai_epoch_as_tai).total_seconds() 
print(datetime.utcfromtimestamp(tai_timestamp)) # "right" timezone is in effect!

Выход

2014-08-15 07:00:27 # (UTC)

Вы можете избежать изменения часового пояса, если вы извлечете список секунд прыжка из соответствующего tzfile(5). Это комбинация первых двух методов, в которых вычисление счетчика прыжка от первого метода автоматизировано и автоопределение tzdata (системный пакет для база данных tz) из второго метода:

>>> from datetime import datetime, timedelta
>>> import leapseconds
>>> leapseconds.gps_to_utc(datetime(1980,1,6) + timedelta(seconds=1092121243.0))
datetime.datetime(2014, 8, 15, 7, 0, 27)

где leapseconds.py может извлекать секунды прыжка из файла /usr/share/zoneinfo/right/UTC (часть пакета tzdata).

Все три метода дают одинаковый результат.

Ответ 2

Я использую следующую функцию, которая считает секунды прыжка:

import bisect
from datetime import datetime, timedelta

_LEAP_DATES = ((1981, 6, 30), (1982, 6, 30), (1983, 6, 30),
               (1985, 6, 30), (1987, 12, 31), (1989, 12, 31),
               (1990, 12, 31), (1992, 6, 30), (1993, 6, 30),
               (1994, 6, 30), (1995, 12, 31), (1997, 6, 30),
               (1998, 12, 31), (2005, 12, 31), (2008, 12, 31),
               (2012, 6, 30), (2015, 6, 30), (2016, 12, 31))

LEAP_DATES = tuple(datetime(i[0], i[1], i[2], 23, 59, 59) for i in _LEAP_DATES)

def leap(date):
    """
    Return the number of leap seconds since 1980-01-01

    :param date: datetime instance
    :return: leap seconds for the date (int)
    """
    # bisect.bisect returns the index `date` would have to be
    # inserted to keep `LEAP_DATES` sorted, so is the number of
    # values in `LEAP_DATES` that are less than `date`, or the
    # number of leap seconds.
    return bisect.bisect(LEAP_DATES, date)

Конечно, вам нужно периодически обновлять _LEAP_DATES, но эти обновления довольно редки.

В общем, время GPS состоит из двух чисел: недели GPS и количества секунд с начала текущей недели GPS. Поэтому вы можете использовать следующее:

def gps2utc(week, secs):
    """
    :param week: GPS week number, i.e. 1866
    :param secs: number of seconds since the beginning of `week`
    :return: datetime instance with UTC time
    """
    secs_in_week = 604800
    gps_epoch = datetime(1980, 1, 6, 0, 0, 0)
    date_before_leaps = gps_epoch + timedelta(seconds=week * secs_in_week + secs)
    return date_before_leaps - timedelta(seconds=leap(date_before_leaps))

В вашем случае week = 0, поэтому:

In [1]: gps2utc(0, 1092121243.0)
Out[1]: datetime.datetime(2014, 8, 15, 7, 0, 27)

Ответ 3

Вы можете использовать astropy.time для этого:

from astropy.time import Time
mytime = 1172361618
t = Time(mytime, format='gps')
t = Time(t, format='iso')
print t

который возвращает:

2017-03-01 00: 00: 37.000

Я думаю, что есть несколько небольших определений на уровне 1 секунды между временем UNIX, сообщенным астропией, и строгим определением POSIX, но это близко.