Source file: /~heha/hs/Funkuhr.zip/src/Mondalter.c

/********************************
 * Projekt: Funkuhr DCF77	*
 * Mondalter-Berechnung zur 	*
 * korrekten Mondphasen-Anzeige	*
 * sowie Berechnung von		*
 * Auf- und Untergangszeiten	*
 ********************************/
#include "Funkuhr.h"
#include <math.h>

#define CS(x) cos((x)*PI180)
#define SN(x) sin((x)*PI180)
#define FRAK(x) fmod(x,1)

PointF geoloc;		// Geoposition in Grad, für AufUnterBerechnung()
int geotz;		// wie Windows in Minuten, östlich = negativ (bekloppt, aber Redmont ist nicht Moskau)
TIME_ZONE_INFORMATION geotzi;	// mit entsprechend umgerechneten Umschalt-Tagen

static double cphi;
static double sphi;

/* JavaScript-Routinen geklaut von www.computus.de und leider nicht ganz verstanden */
double Mondalter(double JDE){
 double t, d, m, m1, i;
 t = JDE / 36525.0;
 d = 297.8502042 + 445267.11151686 * t - .00163 * t * t + t * t * t / 545868.0 - t * t * t * t / 113065000.0;
 m = 357.5291092 + 35999.0502909 * t - .0001536 * t * t + t * t * t / 24490000.0;
 m1 = 134.9634114 + 477198.8676313 * t + .008997 * t * t + t * t * t / 69699.0 - t * t * t * t / 14712000.0;
 i = d + 6.289 * SN(m1) - 2.1 * SN(m) + 1.274 * SN(2 * d - m1) + .658 * SN(2 * d) + .241 * SN(2 * m1) + .110 * SN(d);
 return fmod(i,360); // Beleuchtungsgrad dazu wäre: (1-CS(i))/2;
}

typedef struct{
 double dec,ra;
}DECRA;

// Position (Sternenposition?) des Mondes zur Zeit <t>
static void Mondberechnung(double t, DECRA*decra){
 double p2 = 6.283185307;
 double arc = 206264.8062;
 double coseps = .91748;
 double sineps = .39778;
 double lo = FRAK(.606433 + 1336.855225 * t);
 double l = p2 * FRAK(.374897 + 1325.55241 * t);
 double ls = p2 * FRAK(.993133 + 99.997361 * t);
 double d = p2 * FRAK(.827361 + 1236.853086 * t);
 double f = p2 * FRAK(.259086 + 1342.227825 * t);
 double dl = 22640 * sin(l) - 4586 * sin(l - 2 * d) + 2370 * sin(2 * d) + 769 * sin(2 * l) - 668 * sin(ls) - 412 * sin(2 * f) - 212 * sin(2 * l - 2 * d) - 206 * sin(l + ls - 2 * d) + 192 * sin(l + 2 * d) - 165 * sin(ls - 2 * d) - 125 * sin(d) - 110 * sin(l + ls) + 148 * sin(l - ls) - 55 * sin(2 * f - 2 * d);
 double s = f + (dl + 412 * sin(2 * f) + 541 * sin(ls)) / arc;
 double h = f - 2 * d;
 double n = -526 * sin(h) + 44 * sin(l + h) - 31 * sin(-l + h) - 23 * sin(ls + h) + 11 * sin(-ls + h) - 25 * sin(-2 * l + f) + 21 * sin(-l + f);
 double lmoon = p2 * FRAK(lo + dl / 1296000);
 double bmoon = (18520 * sin(s) + n) / arc;
 double cb = cos(bmoon);
 double x = cb * cos(lmoon);
 double v = cb * sin(lmoon);
 double w = sin(bmoon);
 double y = coseps * v - sineps * w;
 double z = sineps * v + coseps * w;
 double rho = sqrt(1 - z * z);
 decra->dec = (360 / p2) * atan(z / rho);
 decra->ra = (48 / p2) * atan(y / (x + rho));
 if (decra->ra < 0) decra->ra += 24;
}

static void Sonnenberechnung(double t, DECRA*decra){
 double p2 = 6.283185307;	// s.o.
 double coseps = .91748;	// s.o.
 double sineps = .39778;	// s.o.
 double m = p2 * FRAK(.993133 + 99.997361 * t);	// s.o.
 double dl = 6893 * sin(m) + 72 * sin(2 * m);
 double l = p2 * FRAK(.7859453 + m / p2 + (6191.2 * t + dl) / 1296000);
 double sl = sin(l);
 double x = cos(l);
 double y = coseps * sl;
 double z = sineps * sl;
 double rho = sqrt(1 - z * z);
 decra->dec = (360 / p2) * atan(z / rho);
 decra->ra = (48 / p2) * atan(y / (x + rho));
 if (decra->ra < 0) decra->ra += 24;
}

static double LMST(double MJD) {
 double MJDO = lrint(MJD-0.5);
 double ut = (MJD - MJDO) * 24;
 double t = (MJDO - 51544.5) / 36525;	// 1 ~ 100 Jahre
 double gmst = 6.697374558 + 1.0027379093 * ut + (8640184.812866 + (.093104 - .0000062 * t) * t) * t / 3600;
 return 24 * FRAK((gmst + geoloc.X / 15) / 24);
}

static double SINALT(bool bSonne, double MJD, double Hour) {
 double MJDO = MJD + Hour / 24;		// Gebrochenes Modifiziertes Julianisches Datum
 double t = (MJDO - 51544.5) / 36525;	// 1 ~ 100 Jahre
 DECRA decra;
 double tau;
 (bSonne?Sonnenberechnung:Mondberechnung)(t,&decra);
 tau = 15 * (LMST(MJDO) - decra.ra);
 return sphi * SN(decra.dec) + cphi * CS(decra.dec) * CS(tau);
}

typedef struct{
 double ye,zero1,zero2;
 int nz;
}QUADRET;

static void QUAD(double yminus, double yo, double yplus, QUADRET*quadret) {
 double a = .5 * (yminus + yplus) - yo;
 double b = .5 * (yplus - yminus);
 double c = yo;
 double xe = -b / (2 * a);
 double dis = b * b - 4 * a * c;
 quadret->ye = (a * xe + b) * xe + c;
 quadret->nz = 0;
 quadret->zero1 = 0;
 quadret->zero2 = 0;
 if (dis >= 0){
  double dx = .5 * sqrt(dis) / fabs(a);
  quadret->zero1 = xe - dx;
  quadret->zero2 = xe + dx;
  if (fabs(quadret->zero1) <= 1) quadret->nz++;
  if (fabs(quadret->zero2) <= 1) quadret->nz++;
  if (quadret->zero1 < -1) quadret->zero1 = quadret->zero2;
 }
}

typedef struct{
 double rise,set;
}RISESET;

// liefert <true> wenn um 00:00 Uhr sichtbar
// Routine schwächelt wenn (Mond)Aufgangszeit exakt 00:00 Uhr ist
static bool AufUnterBerechnung(bool bSonne, double MJD, double sinho, RISESET*RiseSet) {
 double Hour = 1;
 double yo, yplus;
 QUADRET QuatRet;
 double yminus = SINALT(bSonne, MJD, 0) - sinho;
 bool above = yminus > 0;
 RiseSet->rise=0;
 RiseSet->set=0;
 do {
  yo = SINALT(bSonne, MJD, Hour) - sinho;
  yplus = SINALT(bSonne, MJD, Hour + 1) - sinho;
  QUAD(yminus, yo, yplus, &QuatRet);
  switch (QuatRet.nz) {	// Anzahl detektierter Nulldurchgänge
   case 1: {
    if (yminus < 0) RiseSet->rise = Hour + QuatRet.zero1;
    else RiseSet->set = Hour + QuatRet.zero1;
   }break;
   case 2: {
    if (QuatRet.ye < 0) {
     RiseSet->rise = Hour + QuatRet.zero2;
     RiseSet->set = Hour + QuatRet.zero1;
    }else{
     RiseSet->rise = Hour + QuatRet.zero1;
     RiseSet->set = Hour + QuatRet.zero2;
    }
   }break;
  }
  yminus = yplus;
  Hour += 2;
 }while (Hour < 25 && (!RiseSet->rise || !RiseSet->set));
 return above;
}

static int HH_MM(double hour, TCHAR*s, int len) {
 if (hour) {
  int m=lrint(hour*60);
  return wnsprintf(s,len,T("%02d%s%02d"),m/60,sTime,m%60);
 }else{
  return wnsprintf(s,len,T("--%s--"),sTime);
 }
}

bool AufUntergang(bool bSonne, double MJD, TCHAR*Auf, TCHAR*Unter) {
 double sinho;		// Sinus des Horizonts
 RISESET RiseSet;
 bool ret;
 static const float horizont[4]={
  8.0f/60,	//Mond: 8 Bogenminuten über geometrischem Horizont
  -50.0f/60,	//Sonne: 50 Bogenminuten unter geom. Horizont
  -6.0f,	//Dämmerung (bürgerlich): 6° unterm Horizont (hier nicht verwendet)
  -12.0f};	//Dämmerung (nautisch): 12°
// Modifiziertes Julianisches Datum == Julianisches Datum - 2400000.5
// Mittwoch, 17. Nov 00:00 UT1 1858
 MJD += geotz / 1440.0f;
 sphi = SN(geoloc.Y);	// nicht thread-sicher
 cphi = CS(geoloc.Y);
 sinho = SN(horizont[bSonne&3]);
 ret=AufUnterBerechnung(bSonne, MJD, sinho, &RiseSet);
 HH_MM(RiseSet.rise,Auf,6);
 HH_MM(RiseSet.set,Unter,6);
 return ret;
}


// EINGABE:
// date.wMonth = Umschalt-Monat
// date.wDayOfWeek = Wochentag des Umschaltens
// date.wDay = Nummer des Wochentags im Monat:
//             1..4 = 1. bis 4. Wochentag
//	       5..8 = letzter, vorletzter usw. Wochentag
// date.wYear = Jahr, das abgefragt werden soll (einsetzen!)
// AUSGABE
// date.wDay aktualisiert, also 1..31
BOOL GetTimezoneChangeDate(MYSYSTEMTIME *st) {
 FILETIME ft;
 BYTE nWeek=st->bDay-1;	// Nummer der Woche im Monat -1
 BYTE day;
 if (!st->bMonth || !st->st.wYear || (unsigned)nWeek>=8) return FALSE;
 day=st->bDayOfWeek;	// retten (0..6)
 if (nWeek>=4) { // letzter, vorletzter usw. Wochentag im Monat
  BYTE last;
  nWeek=3-nWeek;	// negativ machen (typisch: -1)
// Letzten Tag des Monats berechnen (geht nur so)
  for (st->bDay=31; !SystemTimeToFileTime(&st->st,&ft); st->bDay--) {
   if (st->bDay==28) return FALSE;	// Problem! Notbremse
  }
  FileTimeToSystemTime(&ft,&st->st);
  last=st->bDayOfWeek;
  st->bDayOfWeek=day;
  day-=last;
  if (day<=0) nWeek++;
 }else{		// Wochentag in 1.-4. Woche im Monat
  BYTE first;
// Wochentag des ersten Tages im Monat berechnen
  st->bDay=1;
  if (!SystemTimeToFileTime(&st->st,&ft)) return FALSE;
  FileTimeToSystemTime(&ft,&st->st);
  first=st->bDayOfWeek;
  st->bDayOfWeek=day;
  day-=first;
  if (day<0) nWeek++;
 }
 st->bDay+=day+nWeek*7;
 return TRUE;
}

// Initialisiert <geotz> und <geotzi> u.a. mit echten Umschalt-Daten für das aktuelle Jahr
void InitTZ() {
 geotz = 0;
 switch (GetTimeZoneInformation(&geotzi)) {
  case 1: geotz = geotzi.StandardBias; break;
  case 2: geotz = geotzi.DaylightBias; break;
 }
 geotz += geotzi.Bias;
 if (geotzi.StandardDate.wMonth) {
  SYSTEMTIME st;
  GetLocalTime(&st);
  geotzi.StandardDate.wYear=st.wYear;
  GetTimezoneChangeDate((MYSYSTEMTIME*)&geotzi.StandardDate);
  geotzi.DaylightDate.wYear=st.wYear;
  GetTimezoneChangeDate((MYSYSTEMTIME*)&geotzi.DaylightDate);
 }
}

/*
int mainCRTStartup() {
 FILETIME ft;
 GetSystemTimeAsFileTime(&ft);
// zeitzone=int((*(__int64*)&ft-*(__int64*)&ftl)>>8)/2343750;
 double MJD=*(__int64*)(&ft)/(24*60*60*10000000.0)-94187;
 char sx[8],sy[8],sz[8];
 d2s(sx,geolocation.x);
 d2s(sy,geolocation.y);
 d2s(sz,zeitzone/60.0);
 myprintf("Koordinaten: %s° / %s°, Zeitzone: %s\n",sx,sy,sz);
 myprintf("Mondalter jetzt:%4d°\n", (int)RoundInt(Mondalter(MJD-51544.5)));
 MJD=myfloor(MJD);
 for (int i=0; i<45; i++) {
  double NachtMJD = MJD+i;
  char sa[6],su[6],ma[6],mu[6],db[6],de[6],date[14];
  SYSTEMTIME st;
  *(__int64*)&ft=RoundInt((NachtMJD+94187)*(24*60*60*10000000.0));
  FileTimeToSystemTime(&ft,&st);
  if (tzi.StandardDate.wMonth) {
   if (st.wMonth==tzi.StandardDate.wMonth
   && st.wDay==tzi.StandardDate.wDay) {
    zeitzone=tzi.Bias+tzi.StandardBias;
    d2s(sz,zeitzone/60.0);
    myprintf("Zeitzone: %s\n",sz);
   }
   if (st.wMonth==tzi.DaylightDate.wMonth
   && st.wDay==tzi.DaylightDate.wDay) {
    zeitzone=tzi.Bias+tzi.DaylightBias;
    d2s(sz,zeitzone/60.0);
    myprintf("Zeitzone: %s\n",sz);
   }
  }
  AufUntergang(2,NachtMJD,sa,su);
  AufUntergang(1,NachtMJD,ma,mu);
  AufUntergang(3,NachtMJD,db,de);
  GetDateFormat(LOCALE_USER_DEFAULT,0,&st,"d. MMMM",date,14);
  myprintf("%13s:%4d°, SA=%s, SU=%s, MA=%s, MU=%s, D=%s-%s\n",
    date, (int)RoundInt(Mondalter(NachtMJD-51544.5)),sa,su,ma,mu,db,de);
 }
 return 0;
}
*/
Detected encoding: ANSI (CP1252)4
Wrong umlauts? - Assume file is ANSI (CP1252) encoded