Systemzeit mit Werten aus Konsole setzen

BallerNacken

Ensign
Registriert
März 2008
Beiträge
227
Hi Leute,

die Überschrift klingt vielleicht ein wenig merkwürdig, wusste aber nicht wie ich das am besten zusammenfasse.
Mein Problem ist folgendes:
Ich habe einen Raspberry Pi mit Raspbian. Dieser sitzt in einem Netzwerk (ohne Internetzugang) und bekommt über einen UDP Broadcast von einer GPS-Unit die aktuelle GPS-Zeit mit 20 Hz zugesendet. Die UDP-Pakete abfangen und die Zeit in die Konsole schreiben klappt schonmal ganz gut mit meinem Programm.
Nun möchte ich aber diese andauernd eintreffende GPS-Zeit nutzen, um dann den jeweiligen Zeitpunkt eines externen Events ausgeben zu können.
Die drei Möglichkeiten die ich sehe, sind:

1. Das Speichern der GPS-Zeit in einem Textfile und irgendwie bei eintreten des Events die letzte geschriebene GPS-Zeit zu nehmen.
2. Die GPS-Zeit aus den UDP-Paketen verwenden um die Systemzeit z.B. jede Sekunde neu zu setzen und das andere Programm bei einem Event auf die Systemzeit zugreifen zu lassen
3. Die GPS-Zeit bei einem Event direkt aus der Konsole abgreifen

Ich weiß nun leider nicht, ob überhaupt eine der Vorgehensweisen möglich ist. Und wenn ja, wie ich diese umsetzen kann.

Grüße,
BallerNacken
 
wenn ich das richtig verstehe: es läuft ein script/programm, dass auf irgendeinem io auf besagtes event wartet und du hast ein weiteres, dass den gps-zeit-kram macht?

dann musst du doch nur das event script leicht modifizieren in der richtung:

START
WENN <event> auf IO
DANN speichere <event> in variable X
WARTE auf gpszeitpaket
WENN gpszeitpaket ankommt, Y=X,gpszeit
SCHREIBE Y sonstwohin
GEH zu START (oder was auch immer dann halt danach passieren soll)

also zumindest wenn weniger events als zeitpakete auftreten sollte das als simple lösung funktionieren.
 
Ja das werde ich auch so oder so ähnlich machen. Meine Frage ist eher, wie ich diese Zeile in deinem Beispiel:
Code:
WENN gpszeitpaket ankommt, Y=X,gpszeit
umsetzen kann. Ich habe ja lediglich die Ausgabe der GPS-Zeit in der Konsole. Kann ich dem Programm irgendwie sagen, dass es die am nächsten liegende GPS-Zeit direkt aus der Konsole abgreifen soll?
 
in welcher/welchen programmiersprachen bewegen wir uns hier? (ich hab keinen PI aber mal gehört python ist da standard?) ich meine die gpszeit wird ja durch irgendwas in die konsole geschrieben, oder reden wir hier von einer art "hardwarekonsole" (uart oder sowas? edit: ahc nee kann eigentlich nicht sein, du hast was von udp paketen gesagt) auf einem externen gerät wo das ständig ausgespuckt wird und du dich drauf verbindest?

das ist sowieso irgendwie nicht so einfach da ohne den vorhandenen code zu helfen, wäre ggf. sinnvoller das mal beides (event script und gps script) auf pastebin zu werfen und zu verlinken, damit jemand der mehr in übung ist als ich dir da die genaue lösung sagt. ich hab jahre nichts programmiert und hab eh nur anfängerskills ;) aber anschauen kann ichs mir dann trotzdem mal.
 
Zuletzt bearbeitet:
Es handelt sich um ein c++ Programm, welches unter raspbian (Unix-dist) ausgeführt wird. Dieses empfängt die UDP-Pakete und gibt mir die Zeit in Sekunden jeder Minute aus, die darin enthalten ist. Code findest du hier
Ist noch nicht komplett fertig, da ich die Sekunden noch in ein anderes Format umbauen muss.

Das Event Script ist noch in Arbeit, wird aber wohl über einen interrupt realisiert, der auf einem der GPIO Pins auf einen Spannungsabfall wartet. Tritt dieser Spannungsabfall ein, soll eine Eventnummer und die dazu passende GPS-Zeit aus dem anderen Script in eine Txt Datei geschrieben werden.
 
interrupt handling, bzw. generell mit hardware interagieren, geht leider über mein bisschen wissen hinaus, hab mir aber mal aus neugier ein wenig kram angeschaut und das sollte/könnte dir helfen:

http://www.raspberrypi.org/phpBB3/viewtopic.php?f=44&t=7509

ich raffs ehrlich gesagt nicht so ganz ob der $trigger ein vorher festgelegter wert ist oder einfach nur spannung sinkt/steigt egal wieviel schon triggert (letzteres wäre aber ziemlich lame und klingt unwahrscheinlich), sollte sich aber wenn dir das auch nicht ganz klar ist über ausprobieren rausfinden lassen (oder schauen ob jemand eine ähnliche sache programmiert und veröffentlicht hat, "klauen" und anpassen)

aber prinzipiell scheint einfach der kernel die ausführung des programms solange zu pausieren bis der interrupt kommt, dann lässt ers wieder laufen. das programm fragt also nicht den interrupt selber ab sondern du sagst im programm nur "hallo kernel, pauser mich hier und weck mich wieder wenn interrupt kam". wenn du das richtig implementiert bekommst ist der rest eigentlich simpel:

endlosschleife{
hallo kernel
--- programm pausiert ---
--- wird von kernel geweckt ---
hol zeitpaket (alten code fast 1:1 kopieren, nur am ende keine endlosschleife mit Zeit: ausgabe sondern nach einem brauchbaren paket socket dichtmachen)
gpio wert >> $zeit.txt (oder wie auch immer du das planst)
}


das wäre meine interpretation, korrigier mich bitte wenn du das (halbwegs) gelöst hast, kann ich was lernen

vielleicht muss ich mir auch mal nen pi kaufen und rumbasteln, könnte durchaus interessant sein. nutzlos weil mir nie sinnvolle anwendungen in den sinn kommen, aber zumindest ein lehrreicher zeitvertreib ;)
 
Moin,

ich melde mich mal wieder bei euch. :)
Mein Skript ist im Moment soweit, dass es mir die Minuten seit dem 06.01.1980 (Start der GPS-Zeit) und die millisekunden zu jeder Minute ausgibt. Das Rechne ich dann alles zusammen, addiere einen offset vom 06.01.1980 bis zum 01.01.1970 und erhalte so die Sekunden.millisekunden seit dem 01.01.1970.
Diese würde ich nun gerne als Systemzeit meines OS (Raspbian) setzen. Nach einiger Recherche habe ich die Funktion
Code:
setttimeofday()
gefunden. Mit dieser kann ich laut manual die Systemzeit aus den Sekunden seit dem 01.01.1970 setzen.
Nur begreife ich nicht ganz wie? Wie kann ich da mein Variable übergeben, die meine Sekunden seit dem 01.01.1970 enthält?

Mein Code bisher:

Code:
#include <stdio.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <time.h>

#define PORT 3000

/****************************/
 int CreateSocket(int port)
/****************************/
{
    // Create a socket
    int sock = socket(AF_INET, SOCK_DGRAM, 0);

    if(sock==-1)
    {
       fprintf(stderr, "CreateSocket: socket failed\n");
       return -1;
    }

    // Bind it to the local address
    struct sockaddr_in address;

    memset(&address, 0, sizeof(address));

    address.sin_family      = AF_INET;
    address.sin_addr.s_addr = htonl(NADDR_ANY);
    address.sin_port        = htons(ort);

    if(bind(sock, (struct sockaddr *) &address, sizeof(address))==-1)
    {
       fprintf(stderr, "CreateSocket: bind failed\n");
       close(sock);
       return -1;
    }
    return sock;
}

/**********************************/
 int main(int argc, char *argv[])
/**********************************/
{
   int channel;
   long GPS_min;
   double sec_offset
   double min2sec
   double sec_total;
   
   fprintf(stderr, "UDP_Receive\n");

   int sock = CreateSocket(PORT);

   if(sock==-1)
   {
      fprintf(stderr, "Can't create the UDP socket\n");
      return 1;
   }

   unsigned char buffer[80];  // packets max 72 Byte long

   while(1)
   {
      struct sockaddr_in addr;
      socklen_t  addrlen = sizeof(addr);

      int len = recvfrom(sock, buffer, sizeof(buffer), 0, (struct sockaddr *) &addr, &addrlen);

      if(len>=72) //received complete packet
      {
         //printf("milliseconds:  %5d\n", (buffer[2] << 8) | buffer[1]);
		 ms = (buffer[2] << 8) | buffer [1]);
		 channel = buffer[62];
		 if(channel==0) //Channel 0 received
		 {
			GPS_min = (buffer[63] <<24) | (buffer[64]<<16) | (buffer[65]<<8) | buffer[66]; // "Time in minutes since GPS began (midnight 06/01/1980)"
		 }
		 //calculate seconds since 01.01.1970
		 sec_offset  = 315964800.0; //difference in seconds between 01.01.1970 und 06.01.1980
		 min2sec = GPS_min*60+sec_offset;
		 sec_total = min2sec*(ms/1000);
		 printf("Time in sec since 01.01.1970: %5d\n", sec_total);
		 //sleep(200);
		 
		 //write system time
		 time_t TimeInSeconds = sec_total;
		 struct timeval tv = {TimeInSeconds, 0};
		 settimeofday(&tv, 0);
      }
   }

   return 0 ;
}

Kann mir da wer helfen? Die Ausweichvariante wäre, die ausgerechnete Zeit manuell in das aktuelle Datum und die exakte aktuelle Uhrzeit umzuwandeln und dann alles in eine txt-Datei zu schreiben, aus der dann das anderen Programm diese Zeit lesen kann. Da sehe ich aber einige Probleme, also lieber erstmal die Variante mit der Systemzeit schreiben.

edit.: kann ich die Systemzeit dann danach überhaupt im Millisekundenbereich abfragen?

Viele Grüße,

BallerNacken
 
Lies doch den Handbuchauszug, den ich gepostet habe. Da steht alles erschöpfend drin.
 
Das habe ich! Kann es aber nicht wirklich in funktionierenden Code umsetzen. Das was ich da gemacht habe, wirft weder einen Fehler aus, noch setzt es die Systemzeit neu.
Aber gut, dann werde ich morgen nochmal ein wenig mehr rumprobieren.
 
BallerNacken schrieb:
Das habe ich! Kann es aber nicht wirklich in funktionierenden Code umsetzen. Das was ich da gemacht habe, wirft weder einen Fehler aus, noch setzt es die Systemzeit neu.
Aber gut, dann werde ich morgen nochmal ein wenig mehr rumprobieren.

Ich kann in deinem Code nirgends einen Aufruf der settimeofday()-Funktion erkennen. Hier noch mal der relevante Ausschnitt aus der von asdfman geposteten Doku:

Function: int settimeofday (const struct timeval *tp, const struct timezone *tzp)

The settimeofday function sets the current date and time according to the arguments. As for gettimeofday, time zone information is ignored if tzp is a null pointer.

You must be a privileged user in order to use settimeofday.

The return value is 0 on success and -1 on failure. The following errno error conditions are defined for this function:

EPERM
This process cannot set the time because it is not privileged.

ENOSYS
The operating system does not support setting time zone information, and tzp is not a null pointer.

Also, wo liegt das Problem? timeval-Struct anlegen und befüllen, an settimeofday() übergeben, Rückgabewert auf Fehler prüfen, fertig!


P.S. Pedantikmodus an --- davon mal abgesehen, ist das kein C++-Programm sondern ein C-Programm. --- Pedantikmodus aus


P.S. Hoppla, ich habe dein Post wohl nicht besonders aufmerksam gelesen. :o Das settimeofday() habe ich glatt übersehen. Trotzdem: Rückgabewert prüfen!
 
Zuletzt bearbeitet:
Habe mal etwas zum prüfen des Rückgabewertes zusammengebaut:

Code:
     //write system time
		 time_t TimeInSeconds = sec_total;
		 struct timeval tv = {TimeInSeconds, 0};
		 newtime = settimeofday(&tv, 0);
		 if (newtime==0){
			printf("settimeofday() successful.\n");
			}
			else{
				printf("settimeofday() failed, "
					   "errno = %d\n",errno);
				return -1;
				}

Kann ich aber erst morgen ausprobieren (hier ist es Zeit zum schlafen :) )

Danke soweit!
 
Ich hoff, das hast du beherzigt (du wirst privileged, indem du dein Programm als root lauen lässt).
Habe es so oder so immer mit sudo ausgeführt :)

Es funktioniert soweit auch alles gut. Habe es bisher zwar nicht mit dem kompletten Programm getestet, sondern mir ein kleines zum testen gebastelt, in dem ich die Ausgangswerte für die Zeit noch manuell setze. Sollte sich aber nicht zum "großen Bruder" unterscheiden.
Ein Problem habe ich allerdings noch. Mit diesem Code (Testversion):

Code:
#include <stdio.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <time.h>
#include <sys/time.h
#include <errno.h>

/**********************************/
 int main(int argc, char *argv[])
/**********************************/
{
   int channel;
   long GPS_min = 53060;
   double sec_offset;
   double min2sec;
   double sec_total;
   double ms = 14352;	
   int newtime;   


		 //calculate seconds since 01.01.1970
		 sec_offset  = 315964800.0; //difference in seconds between 01.01.1970 und 06.01.1980
		 min2sec = GPS_min*60+sec_offset;
		 sec_total = min2sec+(ms/1000);
		 printf("Time in sec since 01.01.1970: %5f\n", sec_total);
		 //sleep(200);
		 
		 //write system time
		 if (newtime==0){ 			//test for errors
		    printf("settimeofday() successful \n");
			}
			else { 
				prinf("settimeofday() failed, "
				"errno = %s\n, strerror(errno));
				  return -1;
				  }
		FILE* gps;
		gps = fopen)"GPS.txt, "w");
		fprintf(gps, "%d\n", systime);
		fclose(gps);
		return 0;
}

Bekomme ich wie korrekt die Systemzeit 12.02.1980 06:50 Uhr. Soweit so gut...ich benötige aber eine höhere Genauigkeit, soll heißen ich brauche die Millisekunden hinten dran, die ja auch von mir berechnet werden. Dazu benötige ich wohl das "tv_usec". Habe es allerdings nicht hinbekommen, dies einzubauen. Hatte es so versucht:

Code:
#include <stdio.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <time.h>
#include <sys/time.h>
#include <errno.h>

/**********************************/
 int main(int argc, char *argv[])
/**********************************/
{
   int channel;
   long GPS_min = 53060;
   double sec_offset;
   double min2sec;
   double sec_total;
   double ms = 14352;	
   int newtime;	
   struct tm mytime;
   struct timeval systime;
   char * text_time;


		 //calculate seconds since 01.01.1970
		 sec_offset  = 315964800.0; //difference in seconds between 01.01.1970 und 06.01.1980
		 min2sec = GPS_min*60+sec_offset;
		 sec_total = min2sec+(ms/1000);
		 printf("Time in sec since 01.01.1970: %5f\n", sec_total);
	     //sleep(200);
	
		 systime.tv_sec = 0;
		 systime.tv_usec = ctime(&sec_total);

		 newtime = settimeofday(&systime, 0);

		 if (newtime==0){ //test for errors
			printf("settimeofday() successful.\n");
			}
			else{
				printf("settimeofday() failed, "
					   "errno = %s\n",strerror(errno));
				return -1;
				}
		 gettimeofday(&systime, 0);
		 text_time = ctime(&systime.tv_usec);
	 	 printf("Systemtime: %s\n", text_time);

		 return 0;
}

Da bekomme ich beim Kompilieren allerdings folgenden Fehler:

error: cannot convert 'double*' to 'const time_t* {aka const long int*}' for argument '1' to 'char* ctime (const time_t*)'

Liegt also offensichtlich an dem double von "sec_total". Bekomme es aber nicht hin, den Fehler zu beheben...
 
Zuletzt bearbeitet:
Na ja, die Funktion erwartet eben die Adresse eines long int. Du gibst ihr die Adresse eines double. Schreib deine Microsekunden (oder Millsekunden oder Sekunden, oder was immer die Funktion eben als Input erwartet) eben in einen long int und gib der Funktion dann die Adresse dieser Variablen / Konstanten.

Das gibt die Fehlermeldung doch alles her, wenn du sie nur mal gründlich lesen würdest.
 
Erstmal Danke für den Hinweis, aber das tat ich schon, aber wahrscheinlich falsch. Ich habe aus double einfach einen long int gemacht...
Hätte ich die Fehlermeldung verstanden, hätte ich nicht nachgefragt. Deswegen verstehe ich Kommentare wie:
Das gibt die Fehlermeldung doch alles her, wenn du sie nur mal gründlich lesen würdest.
nicht. Foren sind da, um Fragen zu stellen. Es ist ja nun wirklich nicht so, dass ich hier von euch einen kompletten Code verlange/erwarte.
 
Ein vielleicht offensichtlicher Hinweis noch:

Während der Ausführung des Programms vergeht auch Zeit. Wenn du also deine Uhr mit einer Genauigkeit im Millisekundenbereich stellen willst, musst du berücksichtigen, wie lange dein Programm braucht, um nachdem es ein Zeitsignal erhalten hat, dieses als Systemzeit zu setzen. Und weil dieser Zeitraum nicht konstant ist (Multitasking und nicht vorhersehbare Last undsoweiter), hast du einen grundsätzlichen Fehler und die Genauigkeit im Millisekundenbereich, die du haben willst, kannst du in der Realität gar nicht erreichen. Wenn du diese Genauigkeit tatsächlich benötigst, wirst du das Problem grundsätzlich anders angehen müssen.

Mal ein aus den Fingern gesogenes Beispiel zu Problemen mit Genauigkeiten:

Mein Freund Peter fährt von Hintertupfingen nach St. Nimmerlein. Die beiden Städte sind 130km voneinander entfernt und er fährt 60 km/h. Wie lange braucht er für die Strecke? Laut Taschenrechner sind 130km / 60km/h = 2,166666666666666666666666666666666666666666666... Stunden.
Das Problem bei dieser Berechnung ist, dass die Werte, von denen man ausgeht viel ungenauer sind, als das Ergebnis, das man bekommt. Die Strecke hat eine Genauigkeit von maximal 3 signifikanten Stellen und die Geschwindigkeit von nur höchstens 2. Das Ergebnis kann also höchstens auf 2 Stellen genau sein. Es wie oben auf unendlich viele Stellen anzugeben ist einfach falsch. Mit vernünftiger Sicherheit kann man nur sagen, dass Peter so ungefähr 2,1 Stunden benötigen wird. Jede Nachkommastelle dahinter ist unsicher.

Du hast in deinem Programm mindestens einen statistischen Fehler mit der unvorhersehbaren Zeitdifferenz zwischen Erhalt deiner Daten und dem Stellen der Uhr. Es gibt sicher noch andere Fehlerquellen, die mich daran zweifeln lassen, dass es überhaupt Sinn macht, deine Uhr mit so hoher Genauigkeit stellen zu wollen.

Und jetzt hab ich schon wieder viel mehr geschrieben, als ich eigentlich wollte :E
 
Hey,

erstmal, mein Programm funktioniert soweit jetzt.

@asdfman, ja da hast du durchaus recht. Allerdings fällt mir kein weg ein, bei dem ich die Genauigkeit erreichen kann. Wenn es in meinem Programm zu einem Fehler von +- 10 ms kommen sollte, bin ich damit auf jeden Fall zufrieden. Leider lässt sich der Fehler nicht einschätzen. Oder geht das auf irgend eine Art und Weise?

Die einzige Variante die ich mir noch vorstellen könnte, wie ich es ohne eine Internetverbindung (da in einem Flugzeug), noch schneller hinbekommen könnte, wäre ein GPS Modul sirekt am Pi angeschlossen. Das gibt mir dann über die serielle Schnittstelle mit PPS sehr genaue Zeiten aus, die ich dann sofort Nutzen könnte. Das steht im Moment allerdings leider nicht zur Debatte. Ich muss es erstmal testen, wie es mit meinem jetzigen Programm funktionieren wird. Ich bekomme mit Sicherheit meine Zeiten bei jedem Event. Wie diese jedoch mit der Realität übereinstimmen, kann ich nicht sagen...
 
Ein Teil der Zeitdifferenz ist auf jeden Fall konstant, denn der Prozessor benötigt für das Ausführen des Codes eine bestimmte Mindestzeit. Diesen systematischen Fehler kannst du eliminieren, indem du ihn einfach auf die erhaltene Zeit draufschlägst. Was den statistischen Fehler angeht, sehe ich als einzige Möglichkeit, so viele Tests zu machen, bis du die statistische Abweichung genau genug kennst. Dann weißt du zumindest, welche Genauigkeit du maximal erwarten kannst.
 
Wie lange der Prozessor braucht um den Code auszuführen, kann ich ja recht simpel bestimmen. Den Rest kann ich aber nicht bestimmen, da mir sämtliche Vergleichswerte fehlen. Die Zeit, die ich vom GPS bekomme, ist die einzig Verfügbare, wenn man mal von der Systemzeit der Computer an Bord des Flugzeugs absieht. Aber diese sind ohnehin deutlich ungenauer und mit einem größeren Fehler behaftet, als die GPS-Zeit.
Ich kann meines Wissens nach, den Fehler durch empirische Tests also nicht finden, da mir ein Vergleichswert fehlt. Aber an die Betimmung der Rechendauer des Prozessors werde ich mich mal machen.
 
Zurück
Oben