Go Down

Topic: Solved // Datum von gestern ohne Library berechnen (Read 759 times) previous topic - next topic

-Jan-

Jul 27, 2014, 02:27 pm Last Edit: Jul 31, 2014, 02:49 pm by -Jan- Reason: 1
Hi!

Für mein aktuelles Projekt benötige ich das Datum von gestern. Die Uhrzeit und das Datum lese ich von einer RTC (DS1307) direkt aus. Also ohne irgendeine Libary. Wie kann ich also aus dem aktuellen Datum das Datum von gestern oder vor einer Woche OHNE eine Libary berechnen?
Ich das jetzt irgendwie mit dem UnixTimestamp versucht. Mein Onkel Google hat mir leider kein Tipp gegeben...

Gruß...

jurs


Für mein aktuelles Projekt benötige ich das Datum von gestern. Die Uhrzeit und das Datum lese ich von einer RTC (DS1307) direkt aus. Also ohne irgendeine Libary. Wie kann ich also aus dem aktuellen Datum das Datum von gestern oder vor einer Woche OHNE eine Libary berechnen?


Mit zwei Funktionen. Eine Funktion, die Dir das Datum von heute in eine fortlaufende Tageszahl umwandelt. Von der Zahl ziehst Du dann 1 ab. Und dann brauchst Du noch eine gegensätzliche Funktion, die Dir die Tageszahl wieder in das Datum zurückverwandelt.

Pseudocode für die erste der beiden Funktionen findest Du sogar bei Wikipedia:
http://de.wikipedia.org/wiki/Julianisches_Datum#Chronologisches_julianisches_Datum

Serenifly

#2
Jul 27, 2014, 03:55 pm Last Edit: Jul 27, 2014, 05:01 pm by Serenifly Reason: 1
So geht es mit Unix Time. Aus der Time Library geklaut und etwas angepasst:

Code: [Select]

#define LEAP_YEAR(Y) ( ((1970+Y)>0) && !((1970+Y)%4) && ( ((1970+Y)%100) || !((1970+Y)%400) ) )
#define SECS_PER_MIN  (60UL)
#define SECS_PER_HOUR (3600UL)
#define SECS_PER_DAY  (SECS_PER_HOUR * 24UL)

const uint8_t monthDays[] = {31,28,31,30,31,30,31,31,30,31,30,31};

struct tm_t
{
uint8_t Second;
uint8_t Minute;
uint8_t Hour;
uint8_t Wday;
uint8_t Day;
uint8_t Month;
uint16_t Year;
};

void unixToDate(uint32_t unixtime, struct tm_t& tm)
{
uint8_t year, month, monthLength;
uint32_t days;

tm.Second = unixtime % 60;
unixtime /= 60;
tm.Minute =  unixtime % 60;
unixtime /= 60;
tm.Hour = unixtime % 24;
unixtime /= 24;
tm.Wday = ((unixtime + 4) % 7) + 1;

year = 0;  
days = 0;
while((unsigned)(days += (LEAP_YEAR(year) ? 366 : 365)) <= unixtime) {
year++;
}
tm.Year = year;

days -= LEAP_YEAR(year) ? 366 : 365;
unixtime -= days;

days=0;
month=0;
monthLength=0;
for (month=0; month<12; month++) {
if (month==1) {
if (LEAP_YEAR(year)) {
monthLength=29;
} else {
monthLength=28;
}
} else {
monthLength = monthDays[month];
}

if (unixtime >= monthLength) {
unixtime -= monthLength;
} else {
break;
}
}
tm.Year += 1970;
tm.Month = month + 1;
tm.Day = unixtime + 1;
}

uint32_t dateToUnix(struct tm_t& tm)
{  
unsigned int i;
uint32_t seconds;

tm.Year -= 1970;
seconds = tm.Year*(SECS_PER_DAY * 365);
for (i = 0; i < tm.Year; i++) {
if (LEAP_YEAR(i)) {
seconds +=  SECS_PER_DAY;
}
}

for (i = 1; i < tm.Month; i++) {
if ( (i == 2) && LEAP_YEAR(tm.Year)) {
seconds += SECS_PER_DAY * 29;
} else {
seconds += SECS_PER_DAY * monthDays[i-1];
}
}
seconds+= (tm.Day-1) * SECS_PER_DAY;
seconds+= tm.Hour * SECS_PER_HOUR;
seconds+= tm.Minute * SECS_PER_MIN;
seconds+= tm.Second;
return seconds;
}

void printDate(struct tm_t& tm)
{
Serial.print(tm.Day); Serial.print('/'); Serial.print(tm.Month); Serial.print('/'); Serial.println(tm.Year);
Serial.print(tm.Hour); Serial.print(':'); Serial.print(tm.Minute); Serial.print(':'); Serial.println(tm.Second);
}

void setup()
{
Serial.begin(9600);
}

void loop()
{
tm_t tm;
tm.Day = 27;
tm.Month = 6;
tm.Year = 1971;
tm.Hour = 12;
tm.Minute = 31;
tm.Second = 25;

printDate(tm);
unsigned long time = dateToUnix(tm);
Serial.println(time);
Serial.println();

time = time - 7 * SECS_PER_DAY;
Serial.println(time);

unixToDate(time, tm);
printDate(tm);
Serial.println();

delay(5000);
}



Es gibt auch einfachere Algorithmen, aber nicht alles was man im Netz (vor allem in Foren) findet geht auch :)

-Jan-

Hallo Ihr beiden,

vielen Dank für Eure Antworten. :top:
Die Lösung mit dem Unix Timestamp ist aber ein ganz schöner Brocken...

Da sieht der Algorithmus mit dem Julianischen Datum auf Wikipedia deutlich einfacher aus. Scheinbar bin ich aber zu blöd den dort gezeigten Pseudocode in Arduino-Code umzuwandeln. Mein Julianisches Datum stimmt nämlich hinten und vorne nicht. An die zweite Funktion habe ich mich schon gar nicht gewagt.  :~
Wäre super wenn mir da jemand helfen könnte!

Serenifly

Ja, julianisches Datum ist wesentlicher einfacher. Unix-Zeit sind die Sekunden seit 1.1.1970. Beim JD werden dagegen nur die Tage gezählt.

Es gibt wie gesagt auch für Unix-Zeit einfachere Algorithmen. z.B. hier in Ruby:
http://ptspts.blogspot.de/2009/11/how-to-convert-unix-timestamp-to-civil.html
Da ich aber kein Ruby spreche, habe ich es gelassen dass zu portieren

Das hier sieht auch ziemlich kompakt aus und ist C:
http://www.avrfreaks.net/index.php?name=PNphpBB2&file=viewtopic&p=439731#439731
Damit hat man aber nur unix -> tm struct

kurti

Hallo,
ich gehe mal davon aus, dass der Arduino länger laufen soll.
Vorschlag: Datum merken und erst wechseln bei Datumswechsel

also:
aktuelles Datum: 27.07.2014
gemerktes Datum: 26.07.2014
wenn sich das aktuelle Datum ändert, das gemerkte Datum auf das vorherige aktuelle Datum setzen

Nachteil: funktioniert erst nach einem Tag automatisch und das nach jeder Programaktualisierung / Reset
müsste im Programmcode korrigiert werden - Datum von gestern händisch eingeben
Speicherung der Werte im EEPROM wäre gut

Gruss
Kurti
Zitat Jurs: falsche Verkabelung ist sowieso immer wenig förderlich für das Funktionieren der Hardware



hier könnte Ihre Werbung stehen

jurs


Da sieht der Algorithmus mit dem Julianischen Datum auf Wikipedia deutlich einfacher aus. Scheinbar bin ich aber zu blöd den dort gezeigten Pseudocode in Arduino-Code umzuwandeln. Mein Julianisches Datum stimmt nämlich hinten und vorne nicht.


Der Wikipedia-Pseudocode läßt sich supereinfach in C-Code umsetzen.
Leider scheint das Ergebnis nicht ganz genau zu sein.
Für verschiedene Tage zwischen 1990 und 2014 habe ich getestet, der Wikipedia-Code bekommt zwei Tage zuviel heraus.

Falsch rechnender Code nach Wikipedia:
Code: [Select]
long julianDay_wrong(int Jahr, int Monat, int Tag)
{
  float y = Jahr + (Monat - 2.85)/12;
  float A  = (long)(367 * y) - 1.75 * (long)(y) + Tag;
  float B  = (long)(A) - 0.75 * (long)(y / 100);
  return (long)(B) + 1721117;
}

Wenn man sich darauf verlassen könnte, dass stets zwei Tage zuviel herauskommen, könnte man auch damit arbeiten. Allein, mir fehlt das rechte Vertrauen.

Hier ist eine andere Möglichkeit zum errechnen der julianischen Tageszahl, bei der das richtige Ergebnis herauskommt:

Code: [Select]
long julianDay(int Jahr, int Monat, int Tag)
{
  Jahr+=8000;
  if(Monat<3) { Jahr--; Monat+=12; }
  return (Jahr*365L) +(Jahr/4) -(Jahr/100) +(Jahr/400) -1200820 +(Monat*153+3)/5-92 +Tag-1;
}


Ein Rechenschema zum zurückrechnen des Kalenderdatums aus der Julianischen Tageszahl kennt Wikipedia auch:
http://de.wikipedia.org/wiki/Julianisches_Datum#Berechnung_des_Kalenderdatums_aus_dem_julianischen_Datum
Ob dieser Algorithmus besser ist?
Bekommst Du das selber hin?

-Jan-

#7
Jul 27, 2014, 09:40 pm Last Edit: Jul 27, 2014, 09:42 pm by -Jan- Reason: 1
Hallo,

ja, der Arduino soll in diesem Porjekt ständig laufen. Die Idee mit dem Merken des vorherigen Datums ist genial und ziemlich einfach umzusetzen. Aber ich sehe da große Probleme bei den Scheibzyklen des EEPROMs. Wenn jeden Tag, über z.B. 5 Jahre das EEPROM beschrieben wird kommt da schon was zusammen. Da ist mir das Berechnen doch lieber.

Irgendwie hatte ich mir die Unix Timestamp-Lösung deutlich einfacher vorgestellt. In Porgrammiersprachen wie PHP ist das alles nicht so aufwändig, das Datum von gestern auszugeben.  8)

Vielen Dank für den Code, jurs!!
Ja das wäre super, wenn Du die zweite Umrechnungsfunktion auch noch portieren könntest. Meine portierte Funktion gibt für heute den 09.09.-4017 aus.  =(


Rabenauge

Quote
void berechneUnixzeit()// ************** Unix-Zeitstempel berechnen ****************************************
{
  leseZeit();            // aktuelles Datum und Zeit holen
  aktStunde=aktStunde-2; //Ausgleich Zeitzone+Sommerzeit
    aktMonat = (aktMonat + 9) % 12;
  aktJahr = aktJahr -aktMonat/10;
  unixZeit= (365L*aktJahr + aktJahr/4 - aktJahr/100 + aktJahr/400 + (aktMonat*306 + 5)/10 + ( aktTag - 1 )-719468L) * 86400L + aktStunde*3600L + aktMinute*60 + aktSekunde;
}


Stammt, wie ich glaube, von jurs....
------------
Grüssle, Sly

SkobyMobil

Hallo,
"Wenn jeden Tag, über z.B. 5 Jahre das EEPROM beschrieben"

Ja, da kommt etwas zusammen, da muß man aufpassen.
Damit auch etwas zusammenkommt rechne ich hier einmal mit 50 Jahren
Das sind dann so um die 18250 Schreiboperationen.
Gruß und Spaß
Andreas
die zweite Maus bekommt den Speck...

peter_de

#10
Jul 27, 2014, 11:26 pm Last Edit: Jul 27, 2014, 11:43 pm by peter_de Reason: 1
Hallo,

warum soll/darf keine Librarie verwendet werden?


Gruß
Peter
Kaum macht man Etwas richtig, funktioniert es auch!

Rabenauge

Weil die durchweg irgendwie Murks sind..;)
Meine Uhr läuft auch längst ohne, da hat man einfach mehr Freiheiten.
------------
Grüssle, Sly

peter_de

Bei Verwendung der Time Librarie wäre die Berechnung  des Datums von gestern oder vor einer Woche in wenigen Zeilen Code erledigt.

Warum sind die durchweg irgendwie Murks?

Er kann doch seine RTC (DS1307) behalten und weiterverwenden, aber eben nur zur Berechnung eines zurückliegenden Datums die Time Librarie verwenden.

Gruß
Peter
Kaum macht man Etwas richtig, funktioniert es auch!

Rabenauge

Weil sie alle irgendwelche Macken haben: eine ist verbuggt (mit der habe ich meine RTC nicht _einmal_ gestellt bekommen, auch das mache ich inzwischen komplett "zu Fuss", ne andere frisst Unmengen Speicher- wenn mans selber macht (so schwer ist das wirklich nicht) hat man eben die absolute Kontrolle: das auslesen,was interessiert, das berechnen, was gebraucht wird (und eben auch _nur_ dann, wenn es gebraucht wird)-und fertig.
Je nachdem, was im Projekt noch alles drin ist, merkt man das ander Performance dann schon.
------------
Grüssle, Sly

kurti

Mahlzeit,

http://www.mikrocontroller.net/articles/AVR-Tutorial:_Speicher

EEPROM > 100000 Schreibzyklen

100.000 Tage / 365,25 ( Schaltjahre berücksichtigt ) = 273 Jahre

Ich denke, dass ist ausreichend.

Gruss
Kurti
Zitat Jurs: falsche Verkabelung ist sowieso immer wenig förderlich für das Funktionieren der Hardware



hier könnte Ihre Werbung stehen

Go Up