Formel für Sonnenwinkel in ein Programm überführen

SHacker

Mitglied
Hallo,
ich möchte nach Jahren mal wieder programmieren
und versuche mich gerade bei einem kleinen Programm,
das den Sonnenwinkel und die Sonnenhöhe berechnen soll.

Ich glaube mein Problem ist eher mathematischer Natur,
aber da die Mathematik ein Teilgebiet der Informatik ist versuche ich es hier einfach mal.

Es könnte aber auch sein, dass ich irgendwo einen Fehler in meinem Code habe, da mein Programmierzeit über 5 Jahre her ist.

Für die Grundlage des Programms habe ich mir einen deutschen Wikipedia Artikel als Grundlage genommen,
weshalb ich auch nach einem deutschen Forum gesucht habe, damit die selbe Basis vorhanden ist.
Da ich erstmal nur die Formel testen wollte, habe ich diese in BlueJ runter geschrieben.

Der Code ist nicht schön und die Parameter sind aktuell als Member-Variablen aufgeführt.

Nun zu dem eigentlichen Problem:
Wenn ich die Formel so ändere, dass die Fallunterscheidung für alpha (Rektaszension) (atan2) umkehre ist als im Wiki Artikel,
bekomme ich für das aufgeführte Beispiel "München" die gleichen Werte.
Gehe ich aber nur einen Tag weiter vom 6.8.2006 zum 7.8.2006,
unterscheidet sich der Sonnenwinkel um die gleiche Uhrzeit sehr stark.

Vielleicht hat ja jemand Lust und Zeit meinen Code mal zu testen und abzugleichen.

Gruß
SHacker

Wiki Artikel:

Java:
/**
 * Beschreiben Sie hier die Klasse sonne2.
 *
 * @author (Ihr Name)
 * @version (eine Versionsnummer oder ein Datum)
 */
public class sonne2
{
    private double Year = 2006;         //Jahr
    private double Month = 8;           //Monat
    private double Day = 6;             //Tag
    private double Hour = 6;            //Stunde
    private double Minute = 0;          //Minute
    private double Second = 0;          //Sekunde
    private double longitude = 11.6;    //Längengrad
    private double latitude = 48.1;     //Breitengrad

    private double B;
    private double D;                   //Tagesbuchteil
    private double JD0;                 //Julianische Tageszahl (0:00 Uhr)
    private double JD;                  //Julianische Tageszahl mit Tagesbuchteilen
    private double n;
    private double L;                   //mittlere ekliptikale Länge der Sonne
    private double g;                   //mittlere Anomalie der ekliptikale Länge der Sonne
    private double c;
    private double Lambda;              //ekliptikale Länge der Sonne
    private double epsilon;             //Ekliptik
    private double alpha;               //Rektaszension
    private double delta;               //Deklination
    private double T0;                 
    private double T;
    private double ThetahG;             //mittlere Sternzeit (Greenwich) in Stunden
    private double ThetaG;              //mittlere Sternzeit (Greenwich) im Gradmaß
    private double Theta;               //mittlere Sternzeit (Berechnungsort) im Gradmaß
    private double tau;                 //Stundenwinkel
    private double a1;
    private double a;                   //Azimuth
    private double h;                   //Höhenwinkel

    /**
     * Konstruktor für Objekte der Klasse sonne2
     */
    public sonne2()
    {

    }

    public void Berechnung()
    {   
        //Berechnungen für die Julianische Tageszahl
        //Fallunterscheidung für Schalttag, Jahresberechnung startet im März
        if (Month < 3) {
            Year -= 1;
            Month += 12;
        }

        B = 2 - Math.floor(Year/100) + Math.floor(Year/400);
        D = (Hour/24) + (Minute/1440) + (Second/86400);
        JD0 = Math.floor(365.25 * (Year + 4716)) + Math.floor(30.6001 * (Month + 1)) + Day + B - 1524.5;
        JD = JD0 + D;
        
        //Sonnenwinkelberechnung
        
        n = JD - 2451545.0;
        L = 280.460 + (0.9856474 * n);
        g = 357.528 + (0.9856003 * n);
        Lambda = (L%360) + (1.915 * Math.sin(Math.toRadians((g%360)))) + (0.01997 * Math.sin(Math.toRadians(2*(g%360))));
        epsilon = 23.439 - (0.0000004 * n);

        alpha = Math.toDegrees(Math.atan(Math.cos(Math.toRadians(epsilon)) * Math.tan(Math.toRadians(Lambda))));
        c = Math.cos(Math.toDegrees(Lambda));

        if(c > 0) {
            alpha +=  180;
        }

        delta = Math.toDegrees(Math.asin(Math.sin(Math.toRadians(epsilon)) * Math.sin(Math.toRadians(Lambda))));

        T0 = (JD0 - 2451545.0) / 36525;
        T = Hour + ((Second + (Minute * 60))/3600);

        ThetahG = 6.697376 + (2400.05134 * T0) + (1.002738 * T);
        ThetaG = (ThetahG%24) * 15;
        Theta = ThetaG + longitude;
        tau = Theta - alpha;

        a1 = (Math.cos(Math.toRadians(tau))   * Math.sin(Math.toRadians(latitude)))
           - (Math.tan(Math.toRadians(delta)) * Math.cos(Math.toRadians(latitude)));

        a = Math.toDegrees(Math.atan(Math.sin(Math.toRadians(tau)) / a1));

        if(a1 < 0) {
            a += 180;
        }

        h = Math.toDegrees(Math.asin((Math.cos(Math.toRadians(delta)) * Math.cos(Math.toRadians(latitude)) * Math.cos(Math.toRadians(tau)))
                                   + (Math.sin(Math.toRadians(delta)) * Math.sin(Math.toRadians(latitude)))));
    }
}
 
aber da die Mathematik ein Teilgebiet der Informatik ist versuche ich es hier einfach mal
das ist umstritten.

Nun zu dem eigentlichen Problem:
Wenn ich die Formel so ändere, dass die Fallunterscheidung für alpha (Rektaszension) (atan2) umkehre ist als im Wiki Artikel,
bekomme ich für das aufgeführte Beispiel "München" die gleichen Werte.
Gehe ich aber nur einen Tag weiter vom 6.8.2006 zum 7.8.2006,
unterscheidet sich der Sonnenwinkel um die gleiche Uhrzeit sehr stark.
Ich denke, das liegt einfach an double Ungenauigkeiten und Fehlerfortpflanzung. An der Formel scheint erstmal nüx Auffälliges zu sein.
 
Mathematik ein Teilgebiet der Informatik...BlueJ
Die Mathematik liefert (naturgemäss mathematische) Verhaltensweisen und Werkzeuge um die Stromflüsse im Rechner so zu steuern, dass ein menschliches Individuum irgendwelche (primär optische und akustische) (Re)Aktionen desselben mit seinen Sinnen wahrnehmen kann.
BlueJ übernimmt Hintergrundaktivitäten um das Ausführen eines Programms zu erleichtern, z.B. eine Methode aufrufen und deren Ergebnisse ausgeben (andere IDE verlangen hier mehr Java-Anweisungen zwecks selben Resultats). Bei den Datentypen oder den Berechnungen könnte die bereits genannte pauschale Verwendung von "double" (oder Ergebnisauslöschung) das Problem sein.
 
Ich habe den Fehler gefunden.

Die Ungenauigkeiten und Fehlerfortpflanzung hatte ich auch im Kopf,
aber die Zwischenwerte, bis auf ein paar,
schienen logisch, da sie sich auch nur wenig Veränderung hatten und sich auch um den richtigen Betrag geändert hatten.

Eine Sache ist mir aber aufgefallen, die Variable c = cos(Lambda) für die Fallunterscheidung wechselte das Vorzeichen.
Wobei Lambda 133° für den 6.8.2006 auf 135° für den 8.8.2006 hatte.
Ich glaubte schon mein Rechner sei defekt (RAM, MacBook 2009).

Komisch waren nur die immer gleichen und reproduzierbaren Ergebnisse.

Die Berechnung des Cosinus muss im Bogenmaß erfolgen:
Alter Code:
c = Math.cos(Math.toDegree(Lambda));
Neuer Code:
c = Math.cos(Math.toRadians(Lambda));

Da war ich wohl etwas zu schnell bei der Autovervollstänigung in BlueJ.
12 Jahre her, dass ich damit gearbeitet habe und mein MacBook etwas zu träge für meine Eingaben.
 
Ja, so ein Fehler ist schwer zu finden. Daher an der Stelle einfach einmal ein Vorschlag, wie man so Fehler deutlich reduzieren kann:

Unterteile die Methode in mehrere Methoden!

Wenn Du also viele Aufrufe vom Math.cos(Math.toRadians(...)) hast, dann pack das in eine Methode. Dann hast Du nur noch einen Aufruf cos(x) mit x in Degree.
Ebenso bei asin und so.
==> Dann hast Du einfache Methoden, die mit der Einheit arbeiten, die Du nutzen willst.

Das zweite sind Namen - hier würde ich Dir generell empfehlen, aussagekräftige Namen zu nutzen. (Und dabei dann auch auf Gross-/Kleinschreibung achten denn es gibt da gewisse Coding Styleguides, die man einhalten könnte ...)

Das kann generell helfen, die Fehlerwahrscheinlichkeit zu reduzieren.
 
Danke für die Hilfe.
Die ganzen Sachen sind mir aus meinem Studium und früheren Beruf bekannt,
bin aber seit Jahren raus (wie es sich nun mal so entwickelt).

Das ganze ist nur eine Teilfunktion, die ich für ein größeres Projekt brauche.
Top/Down-Bottom/Up.
Ging erstmal nur darum eine funktionierende Funktion/Formel für das Problem zu finden und diese zum implementieren.
Jetzt überlege ich mir das Klassendesign und die Unterfunktionen für Datum usw.

Das habe ich früher anders gemacht, mit dem Problem,
dass ich mit dem Designs der Klassen und Methode zu viel Zeit verschwendet habe und zu verkompliziert (over engineering) habe, ohne wirklich mit dem Code weiter zu kommen.
 
im Artikel steht extra ein Hinweis:

Hinweis: Die Rechnungen sind mit einer ausreichenden Stellenzahl zu führen (z. B. doppelter Genauigkeit, bei achtstelligen Taschenrechnern ist Vorsicht geboten); insbesondere für T 0 {\displaystyle T_{0}} T_{0} müssen ausreichend viele Stellen berücksichtigt werden. Es ist zu beachten, dass manche Rechenprogramme und Programmiersprachen Winkelangaben im Bogenmaß und nicht in Grad erwarten; die Winkel sind dann entsprechend umzurechnen.
 
Das heißt, um genauere Ergebnisse zu erhalten, könnte man die Verwendung von BigDecimal in betracht ziehen...

PS: Der Sonnenstand kann sich zwischen 24 Stunden auch "enorm" ändern - man darf nicht vergessen, das zwischen Min und Max nur ein halbes Jahr, also ca. 182 Tage liegen....
 

Zurück
Oben