2013-03-26 13 views
5

Próbuję znaleźć najlepszy sposób konwersji z epok sekund (od epoki NTP 1900-01-01 00:00) do ciągu datetime (MM/DD/RR , hh: mm: ss) bez żadnych bibliotek/modułów/funkcji zewnętrznych, ponieważ nie są one dostępne w urządzeniu osadzonym.Epoka od czasu do daty na ograniczonym osadzonym urządzeniu

Moją pierwszą myślą było przyjrzeć się Python datetime module source code, ale nie było to dla mnie zbyt przydatne.

Moja początkowa próba w Pythonie używa konwersji dni od 0001-01-01 do daty przy użyciu getDateFromJulianDay dostosowanej do Pythona z C++ source, w połączeniu z operacjami modulo w celu uzyskania czasu. Działa, ale czy istnieje lepszy sposób?

def getDateFromJulianDay(julianDay): 
    # Gregorian calendar starting from October 15, 1582 
    # This algorithm is from: 
    # Henry F. Fliegel and Thomas C. van Flandern. 1968. 
    # Letters to the editor: 
    #  a machine algorithm for processing calendar dates. 
    # Commun. ACM 11, 10 (October 1968), 657-. DOI=10.1145/364096.364097 
    # http://doi.acm.org/10.1145/364096.364097 
    ell = julianDay + 68569; 
    n = (4 * ell)/146097; 
    ell = ell - (146097 * n + 3)/4; 
    i = (4000 * (ell + 1))/1461001; 
    ell = ell - (1461 * i)/4 + 31; 
    j = (80 * ell)/2447; 
    d = ell - (2447 * j)/80; 
    ell = j/11; 
    m = j + 2 - (12 * ell); 
    y = 100 * (n - 49) + i + ell; 
    return y,m,d 

# NTP response (integer portion) for Monday, March 25, 2013 at 6:40:43 PM 
sec_since_1900 = 3573225643 

# 2415021 is the number of days between 0001-01-01 and 1900-01-01, 
#  the start of the NTP epoch 
(year,month,day) = getDateFromJulianDay(2415021 + sec_since_1900/60/60/24) 

seconds_into_day = sec_since_1900 % 86400 
(hour, sec_past_hour) = divmod(seconds_into_day,3600) 
(min, sec) = divmod(sec_past_hour,60) 
print 'year:',year,'month:',month,'day:',day 
print 'hour:',hour,'min:',min,'sec:',sec 

Dlaczego to robię: otrzymuję aktualny czas z serwera NTP, biorąc tym razem w wartości nominalnej do aktualizacji sprzętu zegar czasu rzeczywistego (RTC), który akceptuje tylko datę, strefa czasowa i czasowa: MM/DD/RR, gg: mm: ss, ± zz. Mam zamiar wdrożyć prawdziwe możliwości NTP w późniejszym terminie. Omówienie metod synchronizacji czasu najlepiej pozostawić w innym miejscu, na przykład this question.

Uwagi:

  • Moje urządzenie jest wbudowany Telit GC-864 modem komórkowy, który uruchamia Python 1.5.2+ a jedynie ograniczył operatorów (przeważnie właśnie podmiotów C), brak modułów, a niektóre z oczekiwaną wbudowane typy Pythona. Dokładne możliwości to here, jeśli jesteś zainteresowany. Piszę dla tego urządzenia Pythona, tak jakbym pisał kod C - nie bardzo Pythonic, wiem.
  • Zdaję sobie sprawę, że NTP najlepiej używać tylko do przesunięcia czasu, jednak z ograniczonymi opcjami, używam NTP jako bezwzględnego źródła czasu (mogę dodać czek na rollover NTP w 2036, aby umożliwić kolejne 136 lat działania) .
  • Urządzenie GC-864-V2 z aktualnym oprogramowaniem sprzętowym ma funkcję NTP, ale GC-864, którego potrzebuję, utknął w poprzedniej wersji oprogramowania układowego.
+0

Dlaczego nie radzisz sobie z datami przed 1582? –

+0

Tak, niepotrzebne - zostało zawarte w oryginalnym kodzie, zapomniałem dodać źródła C++ funkcji getDateFromJulianDay, [linia 132 tutaj] (http://qt.gitorious.org/qt/qt/blobs/4.7/src/ corelib/tools/qdatetime.cpp # line132). – swolpert

+0

Więc usuń go z opublikowanego kodu i wyraź swoje pytanie. – dkamins

Odpowiedz

0

TL; DR

Jeśli używasz Telit GC-864, interpreter Pythona pozornie wstawia jakieś opóźnienia w między każdej linii wykonanie kodu.

Dla Telit GC-864 funkcja w moim pytaniu getDateFromJulianDay(julianDay) jest szybsza niż funkcja w mojej odpowiedzi, ntp_time_to_date(ntp_time).

Więcej szczegółów

liczba linii kodu dominuje czas wykonania na GC-864 więcej niż złożoności kodu - dziwne, wiem. Funkcja getDateFromJulianDay(julianDay) w moim pytaniu ma garść złożonych operacji, może 15 linii kodu. Funkcja w mojej odpowiedzi ntp_time_to_date(ntp_time) ma prostszą złożoności obliczeniowej, ale while pętle powodują ponad 100 linii wykonanie kodu:

  • One liczby pętli od 1900 do bieżącego roku
  • Another pętla liczy od 1 miesiąca do prądu miesiąc

wyniki testu

wyniki badań Timing uruchomione na rzeczywistej GC-864 (uwaga: nie GC-864-V2), stosując takie same Wprowadzanie czasu NTP dla każdej próby (każda funkcja wyświetla "3/25/2013 18:40"). Czas został wykonany przy użyciu debugowania instrukcji printf, a terminal szeregowy na komputerze oznaczałby czas każdą linią wysyłaną przez GC-864.

getDateFromJulianDay(julianDay) prób:

  • 0.3802 sekund
  • 0.3370 sekund
  • 0.3370 sekund
  • średnia: 0.3514 sekund

ntp_time_to_date(ntp_time) prób:

  • 0,8899 sekundy
  • 0,9072 sekundy
  • 0,8986 sekundy
  • średnia: 0.8986 sekund

Zmienność częściowo wynika z modemu komórek KG-864 obsługującego zadań w sieci komórkowej okresowo.

Dla kompletności, optymalizacja funkcji typowania po to, aby zmienne long na int tak szybko, jak to możliwe, w ntp_time_to_date(ntp_time) ma dość znaczący wpływ. Bez tego optymalizacja:

  • 2.3155 sekund
  • 1.5034 sekund
  • 1.5293 sekund
  • 2.0995 sekund
  • 2.0909 sekund
  • Średnia: 1.9255 sekund

Robi wszystko obliczeniowo zaangażowany na Telita GC-864 z uruchomionymi plikami .pyo w Pythonie 1.5.2+ nie jest świetnym pomysłem. Używanie GC-864-V2, który ma wbudowaną funkcję NTP, jest możliwym rozwiązaniem dla kogoś, kto napotka na ten problem. Co więcej, nowsze urządzenia typu M2M (ang. Internet-to-Machine), czyli Internet of Things (IoT), są znacznie bardziej wydajne.

Jeśli masz podobny problem z GC-864, rozważ użycie nowszego i bardziej nowoczesnego modemu telefonu komórkowego.

6

getDateFromJulianDay funkcja pierwotnie proponowana jest zbyt intensywne obliczeniowo dla efektywnego wykorzystania na wbudowanym urządzeniu, zawierającym wiele operacji mnożenia i dzielenia dużych long zmiennych lub, w C++, longlong variables jak pierwotnie napisany.

Myślę, że upolowałem efektywną epokę, aby opracować algorytm dla urządzenia wbudowanego.

Po bezowocnym wyszukiwaniu w Google, znalazłem się z powrotem w Stack Overflow i znalazłem pytanie: Converting epoch time to “real” date/time, pytając o czas trwania własnej epoki i dostarczając odpowiedni algorytm. Ten answer na pytanie odwołuje się gmtime.c source code, i pod warunkiem podania źródła w C, co potrzebne, aby napisać algorytm konwersji Python:

/* 
* gmtime - convert the calendar time into broken down time 
*/ 
/* $Header: /opt/proj/minix/cvsroot/src/lib/ansi/gmtime.c,v 1.1.1.1 2005/04/21 14:56:05 beng Exp $ */ 

#include  <time.h> 
#include  <limits.h> 
#include  "loc_time.h" 

struct tm * 
gmtime(register const time_t *timer) 
{ 
     static struct tm br_time; 
     register struct tm *timep = &br_time; 
     time_t time = *timer; 
     register unsigned long dayclock, dayno; 
     int year = EPOCH_YR; 

     dayclock = (unsigned long)time % SECS_DAY; 
     dayno = (unsigned long)time/SECS_DAY; 

     timep->tm_sec = dayclock % 60; 
     timep->tm_min = (dayclock % 3600)/60; 
     timep->tm_hour = dayclock/3600; 
     timep->tm_wday = (dayno + 4) % 7;  /* day 0 was a thursday */ 
     while (dayno >= YEARSIZE(year)) { 
       dayno -= YEARSIZE(year); 
       year++; 
     } 
     timep->tm_year = year - YEAR0; 
     timep->tm_yday = dayno; 
     timep->tm_mon = 0; 
     while (dayno >= _ytab[LEAPYEAR(year)][timep->tm_mon]) { 
       dayno -= _ytab[LEAPYEAR(year)][timep->tm_mon]; 
       timep->tm_mon++; 
     } 
     timep->tm_mday = dayno + 1; 
     timep->tm_isdst = 0; 

     return timep; 
} 

Dodatkowo analysis w rachubę Why is gmtime implemented this way? pomogło stwierdzić, że funkcja gmtime jest dość skuteczny.

Używając raspberryginger.com minix Doxygen documentation site, udało mi się znaleźć makra C i stałe, które były zawarte w gmtime.c z loc_time.h.Odpowiedni fragment kodu:

#define YEAR0   1900     /* the first year */ 
#define EPOCH_YR  1970   /* EPOCH = Jan 1 1970 00:00:00 */ 
#define SECS_DAY  (24L * 60L * 60L) 
#define LEAPYEAR(year) (!((year) % 4) && (((year) % 100) || !((year) % 400))) 
#define YEARSIZE(year) (LEAPYEAR(year) ? 366 : 365) 
#define FIRSTSUNDAY(timp)  (((timp)->tm_yday - (timp)->tm_wday + 420) % 7) 
#define FIRSTDAYOF(timp)  (((timp)->tm_wday - (timp)->tm_yday + 420) % 7) 
#define TIME_MAX  ULONG_MAX 
#define ABB_LEN   3 

extern const int _ytab[2][10]; 

A extern const int _ytab został zdefiniowany w misc.c:

const int _ytab[2][12] = { 
       { 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 }, 
       { 31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 } 
     }; 

Niektóre inne rzeczy znalazłem:

  • gmtime.c File Reference był bardzo pomocny dla znalezienia zależności.
  • Funkcja gmtime rozpoczyna indeksowanie miesiąca, dnia tygodnia i dnia roku o wartości zero (maksymalne zakresy wynoszą odpowiednio 0-11, 0-6, 0-365), a dzień miesiąca rozpoczyna się o liczba 1, (1-31), patrz: IBM gmtime() reference.

I ponownie napisał funkcję gmtime dla Python 1.5.2+:

def is_leap_year(year): 
    return (not ((year) % 4) and (((year) % 100) or (not((year) % 400)))) 

def year_size(year): 
    if is_leap_year(year): 
     return 366 
    else: 
     return 365 

def ntp_time_to_date(ntp_time): 
    year = 1900   # EPOCH_YR for NTP 
    ytab = [ [ 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31], 
       [ 31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31] ] 

    (dayno,dayclock) = divmod(ntp_time, 86400L) 
    dayno = int(dayno) 

    # Calculate time of day from seconds on the day's clock. 
    (hour, sec_past_hour) = divmod(dayclock,3600) 
    hour = int(hour) 
    (min, sec) = divmod(int(sec_past_hour),60) 

    while (dayno >= year_size(year)): 
     dayno = dayno - year_size(year) 
     year = year + 1 
    month = 1       # NOTE: month range is (1-12) 
    while (dayno >= ytab[is_leap_year(year)][month]): 
     dayno = dayno - ytab[is_leap_year(year)][month] 
     month = month + 1 
    day = dayno + 1 

    return (year, month, day, hour, min, sec) 

Modyfikacji zrobiłem ponownie faktoringowych funkcji C++ gmtime do mojej funkcji Pythona ntp_time_to_date(ntp_time):

  • Zmieniono epokę z epoki UNIX z 1970 roku na epokę NTP z 1900 roku (the prime epoch for NTP).
  • Lekko usprawniona kalkulacja czasu dnia.
    • Porównując czas obliczeń dniowej gmtime do ntp_time_to_date:
      • Zarówno (dayclock % 3600)/60 i dayclock/3600 nastąpić za kulisami w divmod(dayclock,3600) i divmod(sec_past_hour,60).
      • Jedyną różnicą jest to, że unika divmod(sec_past_hour,60) modulo z dayclock (0-86399) o 60 poprzez dayclock % 60, a zamiast tego robi modulo z sec_past_hour (0-3599) przez 60 lat w ciągu divmod(sec_past_hour,60).
  • Usunięte zmienne i kod nie muszę na przykład dzień tygodnia.
  • Zmieniono indeksowanie Miesiąc zacząć na 1, więc jest zakres miesiąc (1-12) zamiast (0-11)
  • zmiennych typu Odrzućcie od long jak najszybciej wartości wynosiły mniej niż 65535, aby znacznie zmniejszyć wykonanie kodu czas.
    • wymaga długiego zmienne:
      • ntp_time, sekundach od 1900 (0-4294967295)
      • dayclock, sekund w dzień (0-86399)
    • Największy reszty zmiennych jest obliczony rok w terminie.

Python ntp_time_to_date funkcji (z zależnościami) jest z powodzeniem stosowana na Telit GC-864 od wbudowanego wersji Pythona 1.5.2+ oraz Pythona 2.7.3, ale oczywiście użyj biblioteki datetime, jeśli możesz.

+0

To szalone, że pyton musi być językiem, którego używasz, gdy platforma jest tak ograniczona, że ​​pierwsza funkcja była zbyt wolna. Założę się o pieniądze, że nic podobnego nigdy by się nie wydarzyło w realnym świecie, ale myślę, że byłbym w błędzie! – cwa

+0

Dbasz o sekundy przestępczości (błąd jest mniejszy niż 40 sekund, jest stały przed 1972 r. I jest widoczny tylko wtedy, gdy porównujesz czas z rzeczywistym zegarem fizycznym (lub czasem GPS), który był zsynchronizowany ze skalą UTC w tym czasie) ? http://www.eecis.udel.edu/~mills/leap.html – jfs

+0

@sebastian: Nie dbam o sekundy przestępne w tym momencie, ale to na pewno dobrze wiedzieć. Nie widziałem żadnych przesunięć w porównaniu z oddzielnym czasem synchronizacji NTP w systemie Linux (Ubuntu). Czy synchronizacja czasu Linux NTP na sekundę przestępną? – swolpert