Hallo Leute Ich muss für ein VHDL Projekt ein Zeitsignal, welches ich über GPS empfange in einen UNIX timestamp umwandeln. Der UNIX timestamp ist die Anzahl Sekunden seit dem 01.01.1970 00:00 Uhr. Das GPS Signal enthält aber normale Werte für Datum und Zeit. Hat da vielleicht jemand einen VHDL Code der diese Umwandlung macht? Ich stell mir das nicht sehr einfach vor wegen den Schaltjahren und Schaltsekunden die zu berücksichtigen sind. Danke für eure Hilfe.
Wie wärs wenn du dir den Algorithmus raussuchst wie UNIX das macht bzw. wie die entsprechenden C-Funktionen aussehen und das in VHDL ummünzt?
> Zeitsignal ... in einen UNIX timestamp umwandeln.
Nur zur Simulation oder soll das auf Hardware laufen?
Nein, es sollte schon auf der Hardware laufen. @Christopher: Daran habe ich auch schon gedacht, jedoch wird die Implementation in C einfacher sein, denke ich. Am idealsten wäre es halt schon wenn das schon jemand hätte.
Virtex4 kann kein GPS. Du solltest dir erstmal einen passenden GPS-Empfänger raussuchen und schauen wie der angesteuert wird (wohl I2C). Dann suchst du dir ein passendes I2C-VHDL Modul und pappst einen pico oder microblaze (uC-Softcore) dran und schreibst das Programm zur Umwandlung in C. MfG
Hallo Viertelgott Danke für die Antwort. Das GPS Signal liegt in Form eines seriellen Datenstreams vor. (NMEA Protokoll). Darin ist die UTC Zeit codiert. Es gelingt mir diese zu decodieren. Die Umwandlung in einen Unix Timestamp möchte ich eben in VHDL machen. Mittlerweile ist es mir gelungen mit Matlab einen Algorithmus zu schreiben, der diese Umwandlung macht. Ich denke ich kann diesen Algorithmus mit einer Statemachine nachbauen. Gruss
Hey Car, mich würde der Algorithmus interessieren! Gibt es da Besonderheiten oder muss man nur die Schalttage berücksichtigen… Welchen GPS-Empfänger möchtest du verwenden? Gruß Gast
Hallo Car, das Thema ist zwar schon ein gutes Jahr alt, aber auch mich würde der Algorithmus interessieren. Ich möchte es auf einem ATMega umsetzen. Hab zwar als Ausgangsbasis den http://de.wikipedia.org/wiki/Unixzeit genommen, allerdings bin ich der Meinung, der stimmt nicht 100%ig. Ich habe den Code nach Bascom portiert und habe bei Daten nach dem 31.12.2004 fehlerhafte Timestamps im Folgejahr nach einem Schaltjahr. Dort bin ich zwar stets um einen Tag falsch, was sich mit einer extra Abfrage ja kompensieren ließe - allerdings macht mich dies skeptisch. Ich wäre dir also dankbar, wenn ich deinen Algorithmus bekommen könnte. Gruß BoGe-Ro
Auch mich würde eine Lösung hierfür sehr interessieren! Die Quellcodes von gcc für mktime() sind extrem aufwändig und erzeugen eine gewaltige Codemenge, die z.B. in einem AVR-Controller nicht akzeptabel wäre. Ich nutze einen ATTiny85 zum Empfang eines DCF-Signals. Auch hier liegt die Zeit nur als "struct tm" vor, also aufgedröselt in Jahr, Monat, Tag, Stunde, Minute, Sekunde. Bisher wandern die Daten als solcher Bytestream per I2C nach draußen. Ich will aus Kompatibilitätsgründen aber auch gerne noch einen Unix Timestamp (time_t) mitliefern. Leider ist nix brauchbares zur Konvertierung dafür zu finden. Die Routine ist - wie schon gesagt - vermutlich auch nicht ganz trivial. Der umgekehrte Weg (time_t -> struct tm) ist sehr einfach und in etwas über 1 kb zu codieren (liegt als Quelle in glibc-x.x/time/offtime.c vor). Eigentlich müsste doch (struct tm -> time_t) dann auch einigermaßen knapp zu codieren sein, also quasi "offtime.c rückwärts" ;-). Denkbar wäre noch eine Approximation, indem man den vermuteten time_t Wert näherungsweise bestimmt, diesen in eine struct tm umrechnet und dann so lange Sekunden addiert, bis der "Probierwert" mit dem realen übereinstimmt. Das ist aber keine schöne und auch sehr rechenaufwändige Lösung. Besser wäre eine vernünftige, nicht approximative Lösung. :-/ Tschö, Volker
Sodele, hier nun Beispielcode für die Wandlung einer Zeit-Struktur tm (Jahr, Monat, Tag, Stunde, Minute, Sekunde) in einen Unix Timestamp (time_t / Sekunden seit 1.1.1970 00:00:00 Uhr) und wieder zürück. Ich habe mich mal etwas mit dem Beispielprogramm bei Wikipedia beschäftigt. Es funktioniert so weit, aber es ist noch ein Fehler drin. Der Monat muss nicht von 1 (Januar) bis 12 (Dezember) eingegeben werden, sondern natürlich im Unix "struct tm"-Format: Januar = 0, Dezember = 11!!! Dann funktioniert die Routine auch. Außerdem habe ich durch Verwendung von unsigned longs die unixzeit-Funktion so abgeändert, dass Daten bis zum Jahr 2106 (time_t 0xFFFFFFFF) verarbeitet werden können und nicht nur bis 2038, wie bei vorzeichenbehafteten 32 bit Werten. Aber ACHTUNG: Daten vor dem 1.1.1970 dürfen dann mit dieser Funktion nicht verarbeitet werden und führen zu falschen Ergebnissen! Im Folgenden mal ein auf den meisten AVR lauffähiges Programm, das mit WinAVR compiliert werden kann. In der Funktion main() wird zuerst aus einem time_t Wert (l) ein Datum als "struct tm" erzeugt (mit der Funktion __offtime()). Die Daten dieser Struktur werden danach wieder zurück in einen time_t Wert (lt) gewandelt (mit der Funktion unixzeit()).
1 | struct tm |
2 | {
|
3 | int tm_sec; /* Seconds. [0-60] (1 leap second) */ |
4 | int tm_min; /* Minutes. [0-59] */ |
5 | int tm_hour; /* Hours. [0-23] */ |
6 | int tm_mday; /* Day. [1-31] */ |
7 | int tm_mon; /* Month. [0-11] */ |
8 | int tm_year; /* Year - 1900. */ |
9 | int tm_wday; /* Day of week. [0-6] Sunday == 0 */ |
10 | int tm_yday; /* Days in year.[0-365] */ |
11 | int tm_isdst; /* DST. [-1/0/1]*/ |
12 | };
|
13 | |
14 | const unsigned short int __mon_yday[2][13] = |
15 | {
|
16 | /* Normal years. */
|
17 | { 0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334, 365 }, |
18 | /* Leap years. */
|
19 | { 0, 31, 60, 91, 121, 152, 182, 213, 244, 274, 305, 335, 366 } }; |
20 | |
21 | #define SECS_PER_HOUR (60L * 60L)
|
22 | #define SECS_PER_DAY (SECS_PER_HOUR * 24L)
|
23 | |
24 | /* Compute the `struct tm' representation of *T,
|
25 | offset OFFSET seconds east of UTC,
|
26 | and store year, yday, mon, mday, wday, hour, min, sec into *TP.
|
27 | Return nonzero if successful. */
|
28 | int __offtime (t, offset, tp) |
29 | unsigned long int *t; |
30 | long int offset; |
31 | struct tm *tp; |
32 | {
|
33 | long int days, rem, y; |
34 | const unsigned short int *ip; |
35 | |
36 | days = *t / SECS_PER_DAY; |
37 | rem = *t % SECS_PER_DAY; |
38 | rem += offset; |
39 | while (rem < 0) |
40 | {
|
41 | rem += SECS_PER_DAY; |
42 | --days; |
43 | }
|
44 | while (rem >= SECS_PER_DAY) |
45 | {
|
46 | rem -= SECS_PER_DAY; |
47 | ++days; |
48 | }
|
49 | tp->tm_hour = rem / SECS_PER_HOUR; |
50 | rem %= SECS_PER_HOUR; |
51 | tp->tm_min = rem / 60; |
52 | tp->tm_sec = rem % 60; |
53 | /* January 1, 1970 was a Thursday. */
|
54 | tp->tm_wday = (4 + days) % 7; |
55 | if (tp->tm_wday < 0) |
56 | tp->tm_wday += 7; |
57 | y = 1970; |
58 | |
59 | #define __isleap(year) ((year) % 4 == 0 && ((year) % 100 != 0 || (year) % 400 == 0))
|
60 | #define DIV(a, b) ((a) / (b) - ((a) % (b) < 0))
|
61 | #define LEAPS_THRU_END_OF(y) (DIV (y, 4) - DIV (y, 100) + DIV (y, 400))
|
62 | |
63 | while (days < 0 || days >= (__isleap (y) ? 366 : 365)) |
64 | {
|
65 | /* Guess a corrected year, assuming 365 days per year. */
|
66 | long int yg = y + days / 365 - (days % 365 < 0); |
67 | |
68 | /* Adjust DAYS and Y to match the guessed year. */
|
69 | days -= ((yg - y) * 365 |
70 | + LEAPS_THRU_END_OF (yg - 1) |
71 | - LEAPS_THRU_END_OF (y - 1)); |
72 | y = yg; |
73 | }
|
74 | tp->tm_year = y - 1900; |
75 | if (tp->tm_year != y - 1900) |
76 | {
|
77 | /* The year cannot be represented due to overflow. */
|
78 | // __set_errno (EOVERFLOW);
|
79 | return 0; |
80 | }
|
81 | tp->tm_yday = days; |
82 | ip = __mon_yday[__isleap(y)]; |
83 | for (y = 11; days < (long int) ip[y]; --y) |
84 | continue; |
85 | days -= ip[y]; |
86 | tp->tm_mon = y; |
87 | tp->tm_mday = days + 1; |
88 | return 1; |
89 | }
|
90 | |
91 | |
92 | /** Konvertiert gegliederte UTC-Angaben in Unix-Zeit.
|
93 | * Parameter und ihre Werte-Bereiche:
|
94 | * - jahr [1970..2106]
|
95 | * - monat [0..11]
|
96 | * - tag [1..31]
|
97 | * - stunde [0..23]
|
98 | * - minute [0..59]
|
99 | * - sekunde [0..59]
|
100 | */
|
101 | unsigned long unixzeit(int jahr, int monat, int tag, |
102 | int stunde, int minute, int sekunde) |
103 | {
|
104 | const short tage_bis_monatsanfang[12] = /* ohne Schalttag */ |
105 | {0,31,59,90,120,151,181,212,243,273,304,334}; |
106 | |
107 | unsigned long unix_zeit; |
108 | unsigned long jahre=jahr-1970; |
109 | int schaltjahre=((jahr-1)-1968)/4 - ((jahr-1)-1900)/100 + ((jahr-1)-1600)/400; |
110 | |
111 | unix_zeit=sekunde + 60L*minute + 60L*60L*stunde + |
112 | (tage_bis_monatsanfang[monat]+tag-1L)*60L*60L*24L + |
113 | (jahre*365L+schaltjahre)*60L*60L*24L; |
114 | |
115 | if ( (monat>2) && (jahr%4==0 && (jahr%100!=0 || jahr%400==0)) ) |
116 | unix_zeit+=60L*60L*24L; /* +Schalttag wenn jahr Schaltjahr ist */ |
117 | |
118 | return unix_zeit; |
119 | }
|
120 | |
121 | int main( void ) { |
122 | struct tm zeit_str; |
123 | unsigned long int l, lt; |
124 | |
125 | l = 3261440000L; // Sekunden seit 1.1.70 00:00:00 für Montag, 08.05.2073, 03:33:20 Uhr |
126 | |
127 | if (!__offtime( &l, 0L, &zeit_str ) ) |
128 | return( 1 ); // Fehler |
129 | |
130 | lt = unixzeit( zeit_str.tm_year+1900, zeit_str.tm_mon, zeit_str.tm_mday, zeit_str.tm_hour, zeit_str.tm_min, zeit_str.tm_sec ); |
131 | |
132 | return(0); |
133 | }
|
komisch, mein VHDL-Compiler spuckt lauter Fehler aus... Oder ob ich das mit dem Verilog Compiler probieren muss?
Klaus schrieb: > komisch, mein VHDL-Compiler spuckt lauter Fehler aus... Oder ob ich das > mit dem Verilog Compiler probieren muss? VHDL hat mit ANSI C nichts zu tun, es ist eine eigene Sprache, die mehr Pascal ähnlich ist. Mein obiger Code ist für gcc gedacht (WinAVR bzw. Atmel AVR Studio). Du musst dir WinAVR installieren, wenn du das compilieren willst, oder es in VHDL umschreiben ;-). http://winavr.sourceforge.net/ Gruß, Volker
Volker schrieb: > VHDL hat mit ANSI C nichts zu tun, es ist eine eigene Sprache, die mehr > Pascal ähnlich ist. Mein obiger Code ist für gcc gedacht (WinAVR bzw. > Atmel AVR Studio). Du musst dir WinAVR installieren, wenn du das > compilieren willst, oder es in VHDL umschreiben ;-). Komisch, und ich dachte in diesem Thread geht es um VHDL...
Klaus schrieb: > Volker schrieb: >> VHDL hat mit ANSI C nichts zu tun, es ist eine eigene Sprache, die mehr >> Pascal ähnlich ist. Mein obiger Code ist für gcc gedacht (WinAVR bzw. >> Atmel AVR Studio). Du musst dir WinAVR installieren, wenn du das >> compilieren willst, oder es in VHDL umschreiben ;-). > > Komisch, und ich dachte in diesem Thread geht es um VHDL... UPS! Au weia, da habe ich mich im Thread geirrt :-). So ist das, wenn man die Suchfunktion benutzt und dann gar nicht so genau hinschaut. SORRY SORRY!!! :-) Vielleicht schreibt das ja mal jemand in VHDL um?! Nichts für ungut, Volker
Also VHDL ist eine Hardwarebeschreibungssprache, da kann man nicht so einfach Programmcode umschreiben. Nehmen wir mal an, Du hast das NMEA Paket schon eingelesen, beispielsweise in Schieberegistern. Dann könntest Du beispielsweise folgendes machen. Du machst 2 Zeitzähler. Der eine zählt einfach 32 Bit durch (Unix-Zeit), der andere zählt in Sekunden, Minuten, Stunden, Tag, Monat, Jahr, wobei natürlich die Schaltjahre beachtet werden müssen. Jetzt setzt Du beide auf Anfang und lässt sie so lange zählen, bis die UTC-Zeit übereinstimmt. Nach spätestens 4,3 Milliarden Taktzyklen hast Du das Ergebnis. Nun ist das nicht besonders effizient. Was Du machen kannst ist dem Unix-Zähler Taktsignale zu geben, bei denen er 365 oder 366 Tage weiterzählt, sowie 1, 28, 29, 30 oder 31 Tage. Jetzt kannst Du beide Zähler zuerst um die Jahre, dann die Monate, und dann die Tage inkrementieren. Das kannst Du auch auf Stunden, Minuten und Sekunden erweitern. Damit hast Du innerhalb von grob 100 Taktzyklen die Zeit umgerechnet.
Christian Berger schrieb: > Also VHDL ist eine Hardwarebeschreibungssprache, da kann man nicht so > einfach Programmcode umschreiben. Das erinnert mich stark an die Steuerungstechnik in meinem Studium, wo wir SPS "programmiert" haben. Obwohl ich das kaum als Programmieren bezeichnen würde. Aber Variablen gabs da auch und einfache arithmetische Operatoren ebenso. > Nehmen wir mal an, Du hast das NMEA Paket schon eingelesen, > beispielsweise in Schieberegistern. > [..] Schön erklärt! Aber es dürfte schwierig sein, aus dem Unix-Zeitwert wieder den Datums- und Uhrzeitwert zurückzurechnen, oder? Allenfalls auch durch eine Approximation, so wie du es beschrieben hast... Da bin ich schon froh, dass ich mich ausschließlich aufs "richtige" Programmieren verlegt habe und mit solchen Makro- oder Beschreibungssprachen wenig am Hut habe. Ich denke, der höhere Lernaufwand lohnt sich langfristig gesehen allemal. ;-) Tschö, Volker
Hallo, wie mir grade aufgefallen ist, befindet sich leider noch ein Fehler in dem weiter oben von mir geposteten Code. Dieser tritt nur in Schaltjahren im März auf, weshalb ich ihn auch jetzt erst entdeckt habe. :-( Im Code-Teil zur Konvertierung gegliederter UTC-Angaben in Unix-Zeit heißt es: > if ( (monat>2) && (jahr%4==0 && (jahr%100!=0 || jahr%400==0)) ) > unix_zeit+=60L*60L*24L; /* +Schalttag wenn jahr Schaltjahr ist */ Es muss aber korrekt heißen:
1 | if ( (monat>1) && (jahr%4==0 && (jahr%100!=0 || jahr%400==0)) ) |
2 | unix_zeit+=60L*60L*24L; /* +Schalttag wenn jahr Schaltjahr ist */ |
Der Monat muss also auf größer 1 getestet werden und nicht auf größer 2! Sorry dafür! Gruß, Volker
Bitte melde dich an um einen Beitrag zu schreiben. Anmeldung ist kostenlos und dauert nur eine Minute.
Bestehender Account
Schon ein Account bei Google/GoogleMail? Keine Anmeldung erforderlich!
Mit Google-Account einloggen
Mit Google-Account einloggen
Noch kein Account? Hier anmelden.