/*
GNU General Public License
CacheWolf is a software for PocketPC, Win and Linux that
enables paperless caching.
It supports the sites geocaching.com and opencaching.de
Copyright (C) 2006 CacheWolf development team
See http://www.cachewolf.de/ for more information.
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; version 2 of the License.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
package CacheWolf.navi;
import CacheWolf.utils.MyLocale;
import CacheWolf.database.CWPoint;
import ewe.sys.Convert;
/**
* Class to caculate positions of luminaries all methods are static usage: call utc2juliandate and then getLuminaryDir in ressources/cachewolf.languages messege numbers from 6100
*
* @author Pfeffer
*
*/
public class SkyOrientation {
public final static int SUN = 0;
public final static int MOON = 1;
public static final int ALIOTH = 2; // brightest star in Grater Bear (Grosser Wagen) Rektaszension 12 h 54 m 2 s Deklination +55 Grad 57' 36"
public static final int GREATER_BEAR = ALIOTH;
public static final int ALNILAM = 3; //Orion = Alnilam = mittlerer Guertelstern Aequinoktium 2000): Rektaszension 5h36m13s; Deklination -1 Grad 12'7"
public static final int ORION = ALNILAM;
public static final int CASSIOPEIA_GAMMA = 4; // Kassiopeia Gamma: 00h 56m 42.50s +60 Grad 43' 00.3"
public static final int CASSIOPEIA = CASSIOPEIA_GAMMA;
public static final int DENEB = 5;
public static final int CYGNUS = DENEB; // Cygnus = Schwan
public static final int MIMOSA = 6; // second brightest star in Southern Cross
public static final int SOUTHERN_CROSS = MIMOSA; // SOUTHERN_CROSS = Kreus des S�dens = Crux australia
public static final CWPoint[] STARS = {
// (Deklination, Rektaszension)
/*ALIOTH*/new CWPoint(55. + 57. / 60. + 36. / 3600., (12. + 54. / 60. + 2. / 3600.) * 15.), // ALIOTH: Rektaszension 12 h 54 m 2 s Deklination +55 Grad 57' 36"
/*ALNILAM*/new CWPoint(-1. - 12. / 60. - 7. / 3600., (5. + 36. / 60. + 13. / 3600.) * 15.), // (-1. -12./60. -7./3600., (5. + 36./60. + 13./3600.)*15.) <- wikipedia // -1.19748, 5.60978 * 15.) <- www.... // (-1. -11./60. -52./3600., (5. + 36./60. + 35./3600.)*15.) <- Stellarium
/*Cassiopeia*/new CWPoint(60. + 43. / 60. + 0.3 / 3600., (0 + 56. / 60. + 42.5 / 3600.) * 15.), // CASSIOPALA_GAMMA 00h 56m 42.50s, 60 Grad 43' 00.3" <-- wikipedia, Stellarium: 57m 11s, 60 Grad 45' 29"
/*Deneb*/new CWPoint(45. + 16. / 60. + 49.2 / 3600., (20 + 41. / 60. + 25.6 / 3600.) * 15.), // im Schwan (Sommerdreieck) Quelle: Stellarium
/*Mimosa*/new CWPoint(-59. - 41. / 60. - 19. / 3600., (12 + 47. / 60. + 43.2 / 3600.) * 15.) // im Schwan (Sommerdreieck) Quelle: Stellarium
// Sirius
};
public static String[] LUMINARY_NAMES = { MyLocale.getMsg(6100, "Sun"), MyLocale.getMsg(6101, "Moon"), MyLocale.getMsg(6102, "Grater Bear"), MyLocale.getMsg(6103, "Orion"), MyLocale.getMsg(6104, "Cassiopeia"), MyLocale.getMsg(6105, "Cygnus"),
MyLocale.getMsg(6106, "Southern Cross") };
public static String[] LUMINARY_DESC = { MyLocale.getMsg(6100, "Sun"), MyLocale.getMsg(6101, "Moon"), MyLocale.getMsg(6122, "Alioth in Greater Bear"), MyLocale.getMsg(6123, "Alnilam in Orion"), MyLocale.getMsg(6124, "Cassiopeia Gamma"),
MyLocale.getMsg(6125, "Deneb in Cygnus"), MyLocale.getMsg(6126, "Becrux in Southern Cross") };
/**
* Get the friendly name of the luminary
*
* @param luminary
* @return
*/
public static String getLuminaryName(int luminary) {
return LUMINARY_NAMES[luminary];
}
/**
* Get a more exact description of the luminary
*
* @param lu
* @return
*/
public static String getLuminaryDesc(int lu) {
return LUMINARY_DESC[lu];
}
/**
* get azimuth from north and elevation for horizont for a given Luminary (planet or star)
*
* @param luminary
* one of SUN, MOON, ALIOTH, GRAETER_BEAR, ALNILAM, ORION, CASSIOPEIA_GAMMA, CASSIOPEIA
* @param jd
* julian date must be calculated in advance e.g. from utc2julian
* @param onEarth
* place on earth of the observer
* @return lon = azimuth from north, lat = elevation from horizont
*/
public static CWPoint getLuminaryDir(int luminary, double jd, CWPoint onEarth) {
switch (luminary) {
case SUN:
return getSunDir(jd, onEarth);
case MOON:
return getMoonDir(jd, onEarth);
default:
return equatorial2AzimutCoos(onEarth, jd, STARS[luminary - MOON - 1]);
}
}
/**
* @param utc
* in the format as it comes from gps DDMMYY
* @param datum
* in the format as it comes from gps HHMMSS
* @return juliandate
* @throws NumberFormatException
* if utc / datum could not be parsed successfully
*/
public static double utc2juliandate(String utc, String datum) {
try {
int tag, monat, jahr, stunde, minute, sekunde;
tag = Convert.parseInt(datum.substring(0, 2));
monat = Convert.parseInt(datum.substring(2, 4));
jahr = Convert.parseInt(datum.substring(4, 6)) + 2000;
stunde = Convert.parseInt(utc.substring(0, 2));
minute = Convert.parseInt(utc.substring(2, 4));
sekunde = Convert.parseInt(utc.substring(4, 6)); // Kommastellen werden abgeschnitten
// julianisches "Datum" jd berechnen (see http://de.wikipedia.org/wiki/Julianisches_Datum )
if (monat < 2) {
jahr--;
monat += 12;
} // verlegung des Jahres Endes auf Feb macht Berechnung von SChaltjahren einfacher
double a = (int) java.lang.Math.floor(jahr / 100.); // Alle hundert Jahre kein Schlatjahr (abrunden)
double b = 2 - a + java.lang.Math.floor(a / 4.);
double jd = java.lang.Math.floor(365.25 * (jahr + 4716.)) + java.lang.Math.floor(30.6001 * (monat + 1.)) + tag + (double) stunde / 24 + (double) minute / 1440 + (double) sekunde / 86400 + b - 1524.5;
return jd;
//double jd0 = java.lang.Math.floor(365.25*(jahr + 4716.)) + java.lang.Math.floor(30.6001*((double)monat+1.)) +(double)tag + b - 1524.5;
} catch (IndexOutOfBoundsException e) {
// wird von substring geworfen wenn datum / utc nicht genug Ziffern haben
// NumberFormatException wird au�erdem von Convert.ParseInt direkt geworfen wenn
// nicht in Int konvertiert werden kann
throw new NumberFormatException();
}
}
/**
* old version, gives the same as the new one
*
* @param utc
* @param datum
* @param lat
* @param lon
* @return
*/
public static float getSunAzimut(String utc, String datum, double lat, double lon) {
try {
int tag, monat, jahr, stunde, minute, sekunde;
tag = Convert.parseInt(datum.substring(0, 2));
monat = Convert.parseInt(datum.substring(2, 4));
jahr = Convert.parseInt(datum.substring(4, 6)) + 2000;
stunde = Convert.parseInt(utc.substring(0, 2));
minute = Convert.parseInt(utc.substring(2, 4));
sekunde = Convert.parseInt(utc.substring(4, 6)); // Kommastellen werden abgeschnitten
// julianisches "Datum" jd berechnen (see http://de.wikipedia.org/wiki/Julianisches_Datum )
if (monat < 2) {
jahr--;
monat += 12;
} // verlegung des Jahres Endes auf Feb macht Berechnung von SChaltjahren einfacher
double a = (int) java.lang.Math.floor(jahr / 100.); // Alle hundert Jahre kein Schlatjahr (abrunden)
double b = 2 - a + java.lang.Math.floor(a / 4.);
double jd = java.lang.Math.floor(365.25 * (jahr + 4716.)) + java.lang.Math.floor(30.6001 * (monat + 1.)) + tag + (double) stunde / 24 + (double) minute / 1440 + (double) sekunde / 86400 + b - 1524.5;
double jd0 = java.lang.Math.floor(365.25 * (jahr + 4716.)) + java.lang.Math.floor(30.6001 * (monat + 1.)) + tag + b - 1524.5;
// Ekliptikalkoordinaten der Sonne berechnen (see http://de.wikipedia.org/wiki/Sonnenstand )
double n = jd - 2451545.0;
double l = 280.46 + 0.9856474 * n;
double g = 357.528 + 0.9856003 * n;
double d = l + 1.915 * java.lang.Math.sin(g / 180 * java.lang.Math.PI) + 0.02 * java.lang.Math.sin(2 * g / 180 * java.lang.Math.PI);
// Rektaszension alpha und Deklination delta der Sonne berechnen
double e = 23.439 - 0.0000004 * n;
double alphaNenner = java.lang.Math.cos(d / 180 * java.lang.Math.PI);
double alpha = 180 / java.lang.Math.PI * java.lang.Math.atan(java.lang.Math.cos(e / 180 * java.lang.Math.PI) * java.lang.Math.sin(d / 180 * java.lang.Math.PI) / alphaNenner);
double delta = 180 / java.lang.Math.PI * java.lang.Math.asin(java.lang.Math.sin(e / 180 * java.lang.Math.PI) * java.lang.Math.sin(d / 180 * java.lang.Math.PI));
if (alphaNenner < 0) {
alpha += 180;
}
// Azimut
double t0 = (jd0 - 2451545.) / 36525.; // schon in t0 bzw jd0 richtig berechnet?
double thetaHG = 6.697376 + 2400.05134 * t0 + 1.002738 * (stunde + minute / 60. + sekunde / 3600.);
double theta = thetaHG * 15. + lon;
double azimutNenner = java.lang.Math.cos((theta - alpha) / 180 * java.lang.Math.PI) * java.lang.Math.sin(lat / 180 * java.lang.Math.PI) - java.lang.Math.tan(delta / 180 * java.lang.Math.PI)
* java.lang.Math.cos(lat / 180 * java.lang.Math.PI);
float azimut = (float) java.lang.Math.atan(java.lang.Math.sin((theta - alpha) / 180 * java.lang.Math.PI) / azimutNenner);
azimut = (float) (azimut * 180f / java.lang.Math.PI);
if (azimutNenner < 0)
azimut += 180.;
// null = Sueden auf Null = Norden umrechnen
azimut += 180.;
if (azimut > 360.)
azimut -= 360.;
return azimut;
} catch (IndexOutOfBoundsException e) {
// wird von substring geworfen wenn datum / utc nicht genug Ziffern haben
// NumberFormatException wird ausserdem von Convert.ParseInt direkt geworfen wenn
// nicht in Int konvertiert werden kann
throw new NumberFormatException();
}
}
public static CWPoint getSunAzimut2(String utc, String datum, double lat, double lon) {
double jd = utc2juliandate(utc, datum);
CWPoint eclCoos = getSunEclipticCoos(jd);
// calculate ecliptic coos
// convert coos
return ecliptic2AzimutCoos(new CWPoint(lat, lon), jd, eclCoos);
}
public static CWPoint getSunDir(double jd, CWPoint onEarth) {
CWPoint eclCoos = getSunEclipticCoos(jd);
// calculate ecliptic coos
// convert coos
return ecliptic2AzimutCoos(onEarth, jd, eclCoos);
}
public static CWPoint getMoonDir(double jd, CWPoint onEarth) {
CWPoint eclCoo = getMoonEclipticCoos(jd);
return ecliptic2AzimutCoos(onEarth, jd, eclCoo);
}
public static CWPoint getAlnilamDir(double jd, CWPoint onEarth) {
// Koordinaten Alnilam (mittlerer Guertelstern des Orion), Rektaszension 5h36m13s; Deklination -1�12'7 TODO Aequinoktium 2000
// Source: wikipedia
return equatorial2AzimutCoos(onEarth, jd, new CWPoint(-1. - 12. / 60. - 7. / 3600., (5. + 36. / 60. + 13. / 3600.) * 15.)); // (-1. -12./60. -7./3600., (5. + 36./60. + 13./3600.)*15.) <- wikipedia // -1.19748, 5.60978 * 15.) <- www.... // (-1. -11./60. -52./3600., (5. + 36./60. + 35./3600.)*15.) <- Stellarium
}
/**
* get the ecliptic coordinates of the sun
*
* @param juliandate
* @return
*/
public static CWPoint getSunEclipticCoos(double juliandate) {
double n = juliandate - 2451545.0;
double l = 280.46 + 0.9856474 * n;
double g = 357.528 + 0.9856003 * n;
double lambda = l + 1.915 * java.lang.Math.sin(g / 180 * java.lang.Math.PI) + 0.02 * java.lang.Math.sin(2 * g / 180 * java.lang.Math.PI);
return new CWPoint(0, lambda);
}
// the following code is adopted from http://lexikon.astronomie.info/java/sunmoon/sunmoon.html
// ignores the time difference between juliandate and TDT, which is something like 1 minute
public static CWPoint getMoonEclipticCoos(double julianDate) {
final double DEG = Math.PI / 180;
final double RAD = 1 / DEG;
double sunAnomalyMean = 360 * DEG / 365.242191 * (julianDate - 2447891.5) + 279.403303 * DEG - 282.768422 * DEG;
double D = julianDate - 2447891.5;
// Mean Moon orbit elements as of 1990.0
double l0 = 318.351648 * DEG;
double P0 = 36.340410 * DEG;
double N0 = 318.510107 * DEG;
double i = 5.145396 * DEG;
double l = 13.1763966 * DEG * D + l0;
double MMoon = l - 0.1114041 * DEG * D - P0; // Moon's mean anomaly M
double N = N0 - 0.0529539 * DEG * D; // Moon's mean ascending node longitude
double sunlon = getSunEclipticCoos(julianDate).lonDec;
double C = l - sunlon;
double Ev = 1.2739 * DEG * Math.sin(2 * C - MMoon);
double Ae = 0.1858 * DEG * Math.sin(sunAnomalyMean);
double A3 = 0.37 * DEG * Math.sin(sunAnomalyMean);
double MMoon2 = MMoon + Ev - Ae - A3; // corrected Moon anomaly
double Ec = 6.2886 * DEG * Math.sin(MMoon2); // equation of centre
double A4 = 0.214 * DEG * Math.sin(2 * MMoon2);
double l2 = l + Ev + Ec - Ae + A4; // corrected Moon's longitude
double V = 0.6583 * DEG * Math.sin(2 * (l2 - sunlon));
double l3 = l2 + V; // true orbital longitude;
double N2 = N - 0.16 * DEG * Math.sin(sunAnomalyMean);
CWPoint moonCoor = new CWPoint();
moonCoor.lonDec = ((N2 + Math.atan2(Math.sin(l3 - N2) * Math.cos(i), Math.cos(l3 - N2))) * RAD) % 360;
moonCoor.latDec = Math.asin(Math.sin(l3 - N2) * Math.sin(i)) * RAD;
//moonCoor.orbitLon = l3;
return moonCoor;
/*
double e = 0.054900;
double a = 384401; // km
double diameter0 = 0.5181*DEG; // angular diameter of Moon at a distance
double parallax0 = 0.9507*DEG; // parallax at distance a
// relative distance to semi mayor axis of lunar oribt
moonCoor.distance = (1-sqr(e)) / (1+e*Math.cos(MMoon2+Ec) );
moonCoor.diameter = diameter0/moonCoor.distance; // angular diameter in radians
moonCoor.parallax = parallax0/moonCoor.distance; // horizontal parallax in radians
moonCoor.distance *= a; // distance in km
// Age of Moon in radians since New Moon (0) - Full Moon (pi)
moonCoor.moonAge = Mod2Pi(l3-sunCoor.lon);
moonCoor.phase = 0.5*(1-Math.cos(moonCoor.moonAge)); // Moon phase, 0-1
var phases = new Array("Neumond", "Zunehmende Sichel", "Erstes Viertel", "Zunnehmender Mond",
"Vollmond", "Abnehmender Mond", "Letztes Viertel", "Abnehmende Sichel", "Neumond");
var mainPhase = 1./29.53*360*DEG; // show 'Newmoon, 'Quarter' for +/-1 day arond the actual event
var p = Mod(moonCoor.moonAge, 90.*DEG);
if (p < mainPhase || p > 90*DEG-mainPhase) p = 2*Math.round(moonCoor.moonAge / (90.*DEG));
else p = 2*Math.floor(moonCoor.moonAge / (90.*DEG))+1;
moonCoor.moonPhase = phases[p];
moonCoor.sign = Sign(moonCoor.lon);
return (float) moonCoor.lonDec;
return 0;
}
*/
}
public static CWPoint ecliptic2AzimutCoos(CWPoint onEarth, double julianDate, CWPoint ecliptic) {
CWPoint equat = ecliptic2Equatorial(ecliptic, julianDate);
return equatorial2AzimutCoos(onEarth, julianDate, equat);
}
/**
* convert rektaszension alpha and deklination delta to azimuth / elevation
*
* @param onEarth
* pos. on earth for which the azimut is wanted
* @param julianDate
* @param equatorial
* : lonDec = rektaszension (alpha), latDec = Deklination (delta)
* @return lonDec: azimuth in degrees from north, lat: elevation in degrees from horizont alogithism from wikipedia sonnenbahn
*/
public static CWPoint equatorial2AzimutCoos(CWPoint onEarth, double julianDate, CWPoint equatorial) {
double stunde = ((julianDate + 0.5) % 1) * 24;
double jd0 = julianDate - stunde / 24; // julian date at UTC 0:00
double t0 = (jd0 - 2451545.) / 36525.; // schon in t0 bzw jd0 richtig berechnet?
double thetaHG = 6.697376 + 2400.05134 * t0 + 1.002738 * stunde; // + (double)minute/60.);
double theta = thetaHG * 15. + onEarth.lonDec;
double tau = (theta - equatorial.lonDec) / 180 * Math.PI;
double phi = onEarth.latDec / 180 * Math.PI;
double azimutNenner = Math.cos(tau) * Math.sin(phi) - Math.tan(equatorial.latDec / 180 * Math.PI) * Math.cos(onEarth.latDec / 180 * java.lang.Math.PI);
float azimut = (float) java.lang.Math.atan(java.lang.Math.sin((theta - equatorial.lonDec) / 180 * Math.PI) / azimutNenner);
azimut = (float) (azimut * 180f / java.lang.Math.PI);
if (azimutNenner < 0)
azimut += 180.;
double h = 180 / Math.PI * Math.asin(Math.cos(equatorial.latDec / 180 * Math.PI) * Math.cos(tau) * Math.cos(phi) + Math.sin(equatorial.latDec / 180 * Math.PI) * Math.sin(phi));
// null = Sueden auf Null = Norden umrechnen
azimut += 180.;
if (azimut > 360.)
azimut -= 360.;
return new CWPoint(h, azimut);
}
/**
* convert from eliptical to equatorial coordinates
*
* @param juliandate
* @param eklipCoo
* ecliptic coos in degrees
* @return lon: Deklination (delta), lat: Rektaszension (alpha) in degree this is adopted from http://lexikon.astronomie.info/java/sunmoon/sunmoon.html
*/
public static CWPoint ecliptic2Equatorial(CWPoint eklipCoo, double juliandate) {
double T = (juliandate - 2451545.0) / 36525.; // Epoch 2000 January 1.5
double eps = (23. + (26 + 21.45 / 60) / 60 + T * (-46.815 + T * (-0.0006 + T * 0.00181)) / 3600) / 180 * java.lang.Math.PI; // schiefe der Ekliptik
double coseps = Math.cos(eps);
double sineps = Math.sin(eps);
double sinlon = Math.sin(eklipCoo.lonDec / 180 * Math.PI);
CWPoint equatorial = new CWPoint();
equatorial.lonDec = (180 / Math.PI * Math.atan2((sinlon * coseps - Math.tan(eklipCoo.latDec / 180 * Math.PI) * sineps), Math.cos(eklipCoo.lonDec / 180 * Math.PI))) % 360; // rektaszension (alpha)
equatorial.latDec = 180 / Math.PI * Math.asin(Math.sin(eklipCoo.latDec / 180 * Math.PI) * coseps + Math.cos(eklipCoo.latDec / 180 * Math.PI) * sineps * sinlon); // deklination (delta)
return equatorial;
}
}