// © 2016 and later: Unicode, Inc. and others.1// License & terms of use: http://www.unicode.org/copyright.html2/************************************************************************3* Copyright (C) 1996-2012, International Business Machines Corporation4* and others. All Rights Reserved.5************************************************************************6* 2003-nov-07 srl Port from Java7*/89#include "astro.h"1011#if !UCONFIG_NO_FORMATTING1213#include "unicode/calendar.h"14#include <math.h>15#include <float.h>16#include "unicode/putil.h"17#include "uhash.h"18#include "umutex.h"19#include "ucln_in.h"20#include "putilimp.h"21#include <stdio.h> // for toString()2223#if defined (PI)24#undef PI25#endif2627#ifdef U_DEBUG_ASTRO28# include "uresimp.h" // for debugging2930static void debug_astro_loc(const char *f, int32_t l)31{32fprintf(stderr, "%s:%d: ", f, l);33}3435static void debug_astro_msg(const char *pat, ...)36{37va_list ap;38va_start(ap, pat);39vfprintf(stderr, pat, ap);40fflush(stderr);41}42#include "unicode/datefmt.h"43#include "unicode/ustring.h"44static const char * debug_astro_date(UDate d) {45static char gStrBuf[1024];46static DateFormat *df = NULL;47if(df == NULL) {48df = DateFormat::createDateTimeInstance(DateFormat::MEDIUM, DateFormat::MEDIUM, Locale::getUS());49df->adoptTimeZone(TimeZone::getGMT()->clone());50}51UnicodeString str;52df->format(d,str);53u_austrncpy(gStrBuf,str.getTerminatedBuffer(),sizeof(gStrBuf)-1);54return gStrBuf;55}5657// must use double parens, i.e.: U_DEBUG_ASTRO_MSG(("four is: %d",4));58#define U_DEBUG_ASTRO_MSG(x) {debug_astro_loc(__FILE__,__LINE__);debug_astro_msg x;}59#else60#define U_DEBUG_ASTRO_MSG(x)61#endif6263static inline UBool isINVALID(double d) {64return(uprv_isNaN(d));65}6667static icu::UMutex ccLock;6869U_CDECL_BEGIN70static UBool calendar_astro_cleanup(void) {71return true;72}73U_CDECL_END7475U_NAMESPACE_BEGIN7677/**78* The number of standard hours in one sidereal day.79* Approximately 24.93.80* @internal81* @deprecated ICU 2.4. This class may be removed or modified.82*/83#define SIDEREAL_DAY (23.93446960027)8485/**86* The number of sidereal hours in one mean solar day.87* Approximately 24.07.88* @internal89* @deprecated ICU 2.4. This class may be removed or modified.90*/91#define SOLAR_DAY (24.065709816)9293/**94* The average number of solar days from one new moon to the next. This is the time95* it takes for the moon to return the same ecliptic longitude as the sun.96* It is longer than the sidereal month because the sun's longitude increases97* during the year due to the revolution of the earth around the sun.98* Approximately 29.53.99*100* @see #SIDEREAL_MONTH101* @internal102* @deprecated ICU 2.4. This class may be removed or modified.103*/104const double CalendarAstronomer::SYNODIC_MONTH = 29.530588853;105106/**107* The average number of days it takes108* for the moon to return to the same ecliptic longitude relative to the109* stellar background. This is referred to as the sidereal month.110* It is shorter than the synodic month due to111* the revolution of the earth around the sun.112* Approximately 27.32.113*114* @see #SYNODIC_MONTH115* @internal116* @deprecated ICU 2.4. This class may be removed or modified.117*/118#define SIDEREAL_MONTH 27.32166119120/**121* The average number number of days between successive vernal equinoxes.122* Due to the precession of the earth's123* axis, this is not precisely the same as the sidereal year.124* Approximately 365.24125*126* @see #SIDEREAL_YEAR127* @internal128* @deprecated ICU 2.4. This class may be removed or modified.129*/130#define TROPICAL_YEAR 365.242191131132/**133* The average number of days it takes134* for the sun to return to the same position against the fixed stellar135* background. This is the duration of one orbit of the earth about the sun136* as it would appear to an outside observer.137* Due to the precession of the earth's138* axis, this is not precisely the same as the tropical year.139* Approximately 365.25.140*141* @see #TROPICAL_YEAR142* @internal143* @deprecated ICU 2.4. This class may be removed or modified.144*/145#define SIDEREAL_YEAR 365.25636146147//-------------------------------------------------------------------------148// Time-related constants149//-------------------------------------------------------------------------150151/**152* The number of milliseconds in one second.153* @internal154* @deprecated ICU 2.4. This class may be removed or modified.155*/156#define SECOND_MS U_MILLIS_PER_SECOND157158/**159* The number of milliseconds in one minute.160* @internal161* @deprecated ICU 2.4. This class may be removed or modified.162*/163#define MINUTE_MS U_MILLIS_PER_MINUTE164165/**166* The number of milliseconds in one hour.167* @internal168* @deprecated ICU 2.4. This class may be removed or modified.169*/170#define HOUR_MS U_MILLIS_PER_HOUR171172/**173* The number of milliseconds in one day.174* @internal175* @deprecated ICU 2.4. This class may be removed or modified.176*/177#define DAY_MS U_MILLIS_PER_DAY178179/**180* The start of the julian day numbering scheme used by astronomers, which181* is 1/1/4713 BC (Julian), 12:00 GMT. This is given as the number of milliseconds182* since 1/1/1970 AD (Gregorian), a negative number.183* Note that julian day numbers and184* the Julian calendar are <em>not</em> the same thing. Also note that185* julian days start at <em>noon</em>, not midnight.186* @internal187* @deprecated ICU 2.4. This class may be removed or modified.188*/189#define JULIAN_EPOCH_MS -210866760000000.0190191192/**193* Milliseconds value for 0.0 January 2000 AD.194*/195#define EPOCH_2000_MS 946598400000.0196197//-------------------------------------------------------------------------198// Assorted private data used for conversions199//-------------------------------------------------------------------------200201// My own copies of these so compilers are more likely to optimize them away202const double CalendarAstronomer::PI = 3.14159265358979323846;203204#define CalendarAstronomer_PI2 (CalendarAstronomer::PI*2.0)205#define RAD_HOUR ( 12 / CalendarAstronomer::PI ) // radians -> hours206#define DEG_RAD ( CalendarAstronomer::PI / 180 ) // degrees -> radians207#define RAD_DEG ( 180 / CalendarAstronomer::PI ) // radians -> degrees208209/***210* Given 'value', add or subtract 'range' until 0 <= 'value' < range.211* The modulus operator.212*/213inline static double normalize(double value, double range) {214return value - range * ClockMath::floorDivide(value, range);215}216217/**218* Normalize an angle so that it's in the range 0 - 2pi.219* For positive angles this is just (angle % 2pi), but the Java220* mod operator doesn't work that way for negative numbers....221*/222inline static double norm2PI(double angle) {223return normalize(angle, CalendarAstronomer::PI * 2.0);224}225226/**227* Normalize an angle into the range -PI - PI228*/229inline static double normPI(double angle) {230return normalize(angle + CalendarAstronomer::PI, CalendarAstronomer::PI * 2.0) - CalendarAstronomer::PI;231}232233//-------------------------------------------------------------------------234// Constructors235//-------------------------------------------------------------------------236237/**238* Construct a new <code>CalendarAstronomer</code> object that is initialized to239* the current date and time.240* @internal241* @deprecated ICU 2.4. This class may be removed or modified.242*/243CalendarAstronomer::CalendarAstronomer():244fTime(Calendar::getNow()), fLongitude(0.0), fLatitude(0.0), fGmtOffset(0.0), moonPosition(0,0), moonPositionSet(false) {245clearCache();246}247248/**249* Construct a new <code>CalendarAstronomer</code> object that is initialized to250* the specified date and time.251* @internal252* @deprecated ICU 2.4. This class may be removed or modified.253*/254CalendarAstronomer::CalendarAstronomer(UDate d): fTime(d), fLongitude(0.0), fLatitude(0.0), fGmtOffset(0.0), moonPosition(0,0), moonPositionSet(false) {255clearCache();256}257258/**259* Construct a new <code>CalendarAstronomer</code> object with the given260* latitude and longitude. The object's time is set to the current261* date and time.262* <p>263* @param longitude The desired longitude, in <em>degrees</em> east of264* the Greenwich meridian.265*266* @param latitude The desired latitude, in <em>degrees</em>. Positive267* values signify North, negative South.268*269* @see java.util.Date#getTime()270* @internal271* @deprecated ICU 2.4. This class may be removed or modified.272*/273CalendarAstronomer::CalendarAstronomer(double longitude, double latitude) :274fTime(Calendar::getNow()), moonPosition(0,0), moonPositionSet(false) {275fLongitude = normPI(longitude * (double)DEG_RAD);276fLatitude = normPI(latitude * (double)DEG_RAD);277fGmtOffset = (double)(fLongitude * 24. * (double)HOUR_MS / (double)CalendarAstronomer_PI2);278clearCache();279}280281CalendarAstronomer::~CalendarAstronomer()282{283}284285//-------------------------------------------------------------------------286// Time and date getters and setters287//-------------------------------------------------------------------------288289/**290* Set the current date and time of this <code>CalendarAstronomer</code> object. All291* astronomical calculations are performed based on this time setting.292*293* @param aTime the date and time, expressed as the number of milliseconds since294* 1/1/1970 0:00 GMT (Gregorian).295*296* @see #setDate297* @see #getTime298* @internal299* @deprecated ICU 2.4. This class may be removed or modified.300*/301void CalendarAstronomer::setTime(UDate aTime) {302fTime = aTime;303U_DEBUG_ASTRO_MSG(("setTime(%.1lf, %sL)\n", aTime, debug_astro_date(aTime+fGmtOffset)));304clearCache();305}306307/**308* Set the current date and time of this <code>CalendarAstronomer</code> object. All309* astronomical calculations are performed based on this time setting.310*311* @param jdn the desired time, expressed as a "julian day number",312* which is the number of elapsed days since313* 1/1/4713 BC (Julian), 12:00 GMT. Note that julian day314* numbers start at <em>noon</em>. To get the jdn for315* the corresponding midnight, subtract 0.5.316*317* @see #getJulianDay318* @see #JULIAN_EPOCH_MS319* @internal320* @deprecated ICU 2.4. This class may be removed or modified.321*/322void CalendarAstronomer::setJulianDay(double jdn) {323fTime = (double)(jdn * DAY_MS) + JULIAN_EPOCH_MS;324clearCache();325julianDay = jdn;326}327328/**329* Get the current time of this <code>CalendarAstronomer</code> object,330* represented as the number of milliseconds since331* 1/1/1970 AD 0:00 GMT (Gregorian).332*333* @see #setTime334* @see #getDate335* @internal336* @deprecated ICU 2.4. This class may be removed or modified.337*/338UDate CalendarAstronomer::getTime() {339return fTime;340}341342/**343* Get the current time of this <code>CalendarAstronomer</code> object,344* expressed as a "julian day number", which is the number of elapsed345* days since 1/1/4713 BC (Julian), 12:00 GMT.346*347* @see #setJulianDay348* @see #JULIAN_EPOCH_MS349* @internal350* @deprecated ICU 2.4. This class may be removed or modified.351*/352double CalendarAstronomer::getJulianDay() {353if (isINVALID(julianDay)) {354julianDay = (fTime - (double)JULIAN_EPOCH_MS) / (double)DAY_MS;355}356return julianDay;357}358359/**360* Return this object's time expressed in julian centuries:361* the number of centuries after 1/1/1900 AD, 12:00 GMT362*363* @see #getJulianDay364* @internal365* @deprecated ICU 2.4. This class may be removed or modified.366*/367double CalendarAstronomer::getJulianCentury() {368if (isINVALID(julianCentury)) {369julianCentury = (getJulianDay() - 2415020.0) / 36525.0;370}371return julianCentury;372}373374/**375* Returns the current Greenwich sidereal time, measured in hours376* @internal377* @deprecated ICU 2.4. This class may be removed or modified.378*/379double CalendarAstronomer::getGreenwichSidereal() {380if (isINVALID(siderealTime)) {381// See page 86 of "Practical Astronomy with your Calculator",382// by Peter Duffet-Smith, for details on the algorithm.383384double UT = normalize(fTime/(double)HOUR_MS, 24.);385386siderealTime = normalize(getSiderealOffset() + UT*1.002737909, 24.);387}388return siderealTime;389}390391double CalendarAstronomer::getSiderealOffset() {392if (isINVALID(siderealT0)) {393double JD = uprv_floor(getJulianDay() - 0.5) + 0.5;394double S = JD - 2451545.0;395double T = S / 36525.0;396siderealT0 = normalize(6.697374558 + 2400.051336*T + 0.000025862*T*T, 24);397}398return siderealT0;399}400401/**402* Returns the current local sidereal time, measured in hours403* @internal404* @deprecated ICU 2.4. This class may be removed or modified.405*/406double CalendarAstronomer::getLocalSidereal() {407return normalize(getGreenwichSidereal() + (fGmtOffset/(double)HOUR_MS), 24.);408}409410/**411* Converts local sidereal time to Universal Time.412*413* @param lst The Local Sidereal Time, in hours since sidereal midnight414* on this object's current date.415*416* @return The corresponding Universal Time, in milliseconds since417* 1 Jan 1970, GMT.418*/419double CalendarAstronomer::lstToUT(double lst) {420// Convert to local mean time421double lt = normalize((lst - getSiderealOffset()) * 0.9972695663, 24);422423// Then find local midnight on this day424double base = (DAY_MS * ClockMath::floorDivide(fTime + fGmtOffset,(double)DAY_MS)) - fGmtOffset;425426//out(" lt =" + lt + " hours");427//out(" base=" + new Date(base));428429return base + (long)(lt * HOUR_MS);430}431432433//-------------------------------------------------------------------------434// Coordinate transformations, all based on the current time of this object435//-------------------------------------------------------------------------436437/**438* Convert from ecliptic to equatorial coordinates.439*440* @param ecliptic A point in the sky in ecliptic coordinates.441* @return The corresponding point in equatorial coordinates.442* @internal443* @deprecated ICU 2.4. This class may be removed or modified.444*/445CalendarAstronomer::Equatorial& CalendarAstronomer::eclipticToEquatorial(CalendarAstronomer::Equatorial& result, const CalendarAstronomer::Ecliptic& ecliptic)446{447return eclipticToEquatorial(result, ecliptic.longitude, ecliptic.latitude);448}449450/**451* Convert from ecliptic to equatorial coordinates.452*453* @param eclipLong The ecliptic longitude454* @param eclipLat The ecliptic latitude455*456* @return The corresponding point in equatorial coordinates.457* @internal458* @deprecated ICU 2.4. This class may be removed or modified.459*/460CalendarAstronomer::Equatorial& CalendarAstronomer::eclipticToEquatorial(CalendarAstronomer::Equatorial& result, double eclipLong, double eclipLat)461{462// See page 42 of "Practical Astronomy with your Calculator",463// by Peter Duffet-Smith, for details on the algorithm.464465double obliq = eclipticObliquity();466double sinE = ::sin(obliq);467double cosE = cos(obliq);468469double sinL = ::sin(eclipLong);470double cosL = cos(eclipLong);471472double sinB = ::sin(eclipLat);473double cosB = cos(eclipLat);474double tanB = tan(eclipLat);475476result.set(atan2(sinL*cosE - tanB*sinE, cosL),477asin(sinB*cosE + cosB*sinE*sinL) );478return result;479}480481/**482* Convert from ecliptic longitude to equatorial coordinates.483*484* @param eclipLong The ecliptic longitude485*486* @return The corresponding point in equatorial coordinates.487* @internal488* @deprecated ICU 2.4. This class may be removed or modified.489*/490CalendarAstronomer::Equatorial& CalendarAstronomer::eclipticToEquatorial(CalendarAstronomer::Equatorial& result, double eclipLong)491{492return eclipticToEquatorial(result, eclipLong, 0); // TODO: optimize493}494495/**496* @internal497* @deprecated ICU 2.4. This class may be removed or modified.498*/499CalendarAstronomer::Horizon& CalendarAstronomer::eclipticToHorizon(CalendarAstronomer::Horizon& result, double eclipLong)500{501Equatorial equatorial;502eclipticToEquatorial(equatorial, eclipLong);503504double H = getLocalSidereal()*CalendarAstronomer::PI/12 - equatorial.ascension; // Hour-angle505506double sinH = ::sin(H);507double cosH = cos(H);508double sinD = ::sin(equatorial.declination);509double cosD = cos(equatorial.declination);510double sinL = ::sin(fLatitude);511double cosL = cos(fLatitude);512513double altitude = asin(sinD*sinL + cosD*cosL*cosH);514double azimuth = atan2(-cosD*cosL*sinH, sinD - sinL * ::sin(altitude));515516result.set(azimuth, altitude);517return result;518}519520521//-------------------------------------------------------------------------522// The Sun523//-------------------------------------------------------------------------524525//526// Parameters of the Sun's orbit as of the epoch Jan 0.0 1990527// Angles are in radians (after multiplying by CalendarAstronomer::PI/180)528//529#define JD_EPOCH 2447891.5 // Julian day of epoch530531#define SUN_ETA_G (279.403303 * CalendarAstronomer::PI/180) // Ecliptic longitude at epoch532#define SUN_OMEGA_G (282.768422 * CalendarAstronomer::PI/180) // Ecliptic longitude of perigee533#define SUN_E 0.016713 // Eccentricity of orbit534//double sunR0 1.495585e8 // Semi-major axis in KM535//double sunTheta0 (0.533128 * CalendarAstronomer::PI/180) // Angular diameter at R0536537// The following three methods, which compute the sun parameters538// given above for an arbitrary epoch (whatever time the object is539// set to), make only a small difference as compared to using the540// above constants. E.g., Sunset times might differ by ~12541// seconds. Furthermore, the eta-g computation is befuddled by542// Duffet-Smith's incorrect coefficients (p.86). I've corrected543// the first-order coefficient but the others may be off too - no544// way of knowing without consulting another source.545546// /**547// * Return the sun's ecliptic longitude at perigee for the current time.548// * See Duffett-Smith, p. 86.549// * @return radians550// */551// private double getSunOmegaG() {552// double T = getJulianCentury();553// return (281.2208444 + (1.719175 + 0.000452778*T)*T) * DEG_RAD;554// }555556// /**557// * Return the sun's ecliptic longitude for the current time.558// * See Duffett-Smith, p. 86.559// * @return radians560// */561// private double getSunEtaG() {562// double T = getJulianCentury();563// //return (279.6966778 + (36000.76892 + 0.0003025*T)*T) * DEG_RAD;564// //565// // The above line is from Duffett-Smith, and yields manifestly wrong566// // results. The below constant is derived empirically to match the567// // constant he gives for the 1990 EPOCH.568// //569// return (279.6966778 + (-0.3262541582718024 + 0.0003025*T)*T) * DEG_RAD;570// }571572// /**573// * Return the sun's eccentricity of orbit for the current time.574// * See Duffett-Smith, p. 86.575// * @return double576// */577// private double getSunE() {578// double T = getJulianCentury();579// return 0.01675104 - (0.0000418 + 0.000000126*T)*T;580// }581582/**583* Find the "true anomaly" (longitude) of an object from584* its mean anomaly and the eccentricity of its orbit. This uses585* an iterative solution to Kepler's equation.586*587* @param meanAnomaly The object's longitude calculated as if it were in588* a regular, circular orbit, measured in radians589* from the point of perigee.590*591* @param eccentricity The eccentricity of the orbit592*593* @return The true anomaly (longitude) measured in radians594*/595static double trueAnomaly(double meanAnomaly, double eccentricity)596{597// First, solve Kepler's equation iteratively598// Duffett-Smith, p.90599double delta;600double E = meanAnomaly;601do {602delta = E - eccentricity * ::sin(E) - meanAnomaly;603E = E - delta / (1 - eccentricity * ::cos(E));604}605while (uprv_fabs(delta) > 1e-5); // epsilon = 1e-5 rad606607return 2.0 * ::atan( ::tan(E/2) * ::sqrt( (1+eccentricity)608/(1-eccentricity) ) );609}610611/**612* The longitude of the sun at the time specified by this object.613* The longitude is measured in radians along the ecliptic614* from the "first point of Aries," the point at which the ecliptic615* crosses the earth's equatorial plane at the vernal equinox.616* <p>617* Currently, this method uses an approximation of the two-body Kepler's618* equation for the earth and the sun. It does not take into account the619* perturbations caused by the other planets, the moon, etc.620* @internal621* @deprecated ICU 2.4. This class may be removed or modified.622*/623double CalendarAstronomer::getSunLongitude()624{625// See page 86 of "Practical Astronomy with your Calculator",626// by Peter Duffet-Smith, for details on the algorithm.627628if (isINVALID(sunLongitude)) {629getSunLongitude(getJulianDay(), sunLongitude, meanAnomalySun);630}631return sunLongitude;632}633634/**635* TODO Make this public when the entire class is package-private.636*/637/*public*/ void CalendarAstronomer::getSunLongitude(double jDay, double &longitude, double &meanAnomaly)638{639// See page 86 of "Practical Astronomy with your Calculator",640// by Peter Duffet-Smith, for details on the algorithm.641642double day = jDay - JD_EPOCH; // Days since epoch643644// Find the angular distance the sun in a fictitious645// circular orbit has travelled since the epoch.646double epochAngle = norm2PI(CalendarAstronomer_PI2/TROPICAL_YEAR*day);647648// The epoch wasn't at the sun's perigee; find the angular distance649// since perigee, which is called the "mean anomaly"650meanAnomaly = norm2PI(epochAngle + SUN_ETA_G - SUN_OMEGA_G);651652// Now find the "true anomaly", e.g. the real solar longitude653// by solving Kepler's equation for an elliptical orbit654// NOTE: The 3rd ed. of the book lists omega_g and eta_g in different655// equations; omega_g is to be correct.656longitude = norm2PI(trueAnomaly(meanAnomaly, SUN_E) + SUN_OMEGA_G);657}658659/**660* The position of the sun at this object's current date and time,661* in equatorial coordinates.662* @internal663* @deprecated ICU 2.4. This class may be removed or modified.664*/665CalendarAstronomer::Equatorial& CalendarAstronomer::getSunPosition(CalendarAstronomer::Equatorial& result) {666return eclipticToEquatorial(result, getSunLongitude(), 0);667}668669670/**671* Constant representing the vernal equinox.672* For use with {@link #getSunTime getSunTime}.673* Note: In this case, "vernal" refers to the northern hemisphere's seasons.674* @internal675* @deprecated ICU 2.4. This class may be removed or modified.676*/677/*double CalendarAstronomer::VERNAL_EQUINOX() {678return 0;679}*/680681/**682* Constant representing the summer solstice.683* For use with {@link #getSunTime getSunTime}.684* Note: In this case, "summer" refers to the northern hemisphere's seasons.685* @internal686* @deprecated ICU 2.4. This class may be removed or modified.687*/688double CalendarAstronomer::SUMMER_SOLSTICE() {689return (CalendarAstronomer::PI/2);690}691692/**693* Constant representing the autumnal equinox.694* For use with {@link #getSunTime getSunTime}.695* Note: In this case, "autumn" refers to the northern hemisphere's seasons.696* @internal697* @deprecated ICU 2.4. This class may be removed or modified.698*/699/*double CalendarAstronomer::AUTUMN_EQUINOX() {700return (CalendarAstronomer::PI);701}*/702703/**704* Constant representing the winter solstice.705* For use with {@link #getSunTime getSunTime}.706* Note: In this case, "winter" refers to the northern hemisphere's seasons.707* @internal708* @deprecated ICU 2.4. This class may be removed or modified.709*/710double CalendarAstronomer::WINTER_SOLSTICE() {711return ((CalendarAstronomer::PI*3)/2);712}713714CalendarAstronomer::AngleFunc::~AngleFunc() {}715716/**717* Find the next time at which the sun's ecliptic longitude will have718* the desired value.719* @internal720* @deprecated ICU 2.4. This class may be removed or modified.721*/722class SunTimeAngleFunc : public CalendarAstronomer::AngleFunc {723public:724virtual ~SunTimeAngleFunc();725virtual double eval(CalendarAstronomer& a) override { return a.getSunLongitude(); }726};727728SunTimeAngleFunc::~SunTimeAngleFunc() {}729730UDate CalendarAstronomer::getSunTime(double desired, UBool next)731{732SunTimeAngleFunc func;733return timeOfAngle( func,734desired,735TROPICAL_YEAR,736MINUTE_MS,737next);738}739740CalendarAstronomer::CoordFunc::~CoordFunc() {}741742class RiseSetCoordFunc : public CalendarAstronomer::CoordFunc {743public:744virtual ~RiseSetCoordFunc();745virtual void eval(CalendarAstronomer::Equatorial& result, CalendarAstronomer& a) override { a.getSunPosition(result); }746};747748RiseSetCoordFunc::~RiseSetCoordFunc() {}749750UDate CalendarAstronomer::getSunRiseSet(UBool rise)751{752UDate t0 = fTime;753754// Make a rough guess: 6am or 6pm local time on the current day755double noon = ClockMath::floorDivide(fTime + fGmtOffset, (double)DAY_MS)*DAY_MS - fGmtOffset + (12*HOUR_MS);756757U_DEBUG_ASTRO_MSG(("Noon=%.2lf, %sL, gmtoff %.2lf\n", noon, debug_astro_date(noon+fGmtOffset), fGmtOffset));758setTime(noon + ((rise ? -6 : 6) * HOUR_MS));759U_DEBUG_ASTRO_MSG(("added %.2lf ms as a guess,\n", ((rise ? -6. : 6.) * HOUR_MS)));760761RiseSetCoordFunc func;762double t = riseOrSet(func,763rise,764.533 * DEG_RAD, // Angular Diameter76534. /60.0 * DEG_RAD, // Refraction correction766MINUTE_MS / 12.); // Desired accuracy767768setTime(t0);769return t;770}771772// Commented out - currently unused. ICU 2.6, Alan773// //-------------------------------------------------------------------------774// // Alternate Sun Rise/Set775// // See Duffett-Smith p.93776// //-------------------------------------------------------------------------777//778// // This yields worse results (as compared to USNO data) than getSunRiseSet().779// /**780// * TODO Make this when the entire class is package-private.781// */782// /*public*/ long getSunRiseSet2(boolean rise) {783// // 1. Calculate coordinates of the sun's center for midnight784// double jd = uprv_floor(getJulianDay() - 0.5) + 0.5;785// double[] sl = getSunLongitude(jd);// double lambda1 = sl[0];786// Equatorial pos1 = eclipticToEquatorial(lambda1, 0);787//788// // 2. Add ... to lambda to get position 24 hours later789// double lambda2 = lambda1 + 0.985647*DEG_RAD;790// Equatorial pos2 = eclipticToEquatorial(lambda2, 0);791//792// // 3. Calculate LSTs of rising and setting for these two positions793// double tanL = ::tan(fLatitude);794// double H = ::acos(-tanL * ::tan(pos1.declination));795// double lst1r = (CalendarAstronomer_PI2 + pos1.ascension - H) * 24 / CalendarAstronomer_PI2;796// double lst1s = (pos1.ascension + H) * 24 / CalendarAstronomer_PI2;797// H = ::acos(-tanL * ::tan(pos2.declination));798// double lst2r = (CalendarAstronomer_PI2-H + pos2.ascension ) * 24 / CalendarAstronomer_PI2;799// double lst2s = (H + pos2.ascension ) * 24 / CalendarAstronomer_PI2;800// if (lst1r > 24) lst1r -= 24;801// if (lst1s > 24) lst1s -= 24;802// if (lst2r > 24) lst2r -= 24;803// if (lst2s > 24) lst2s -= 24;804//805// // 4. Convert LSTs to GSTs. If GST1 > GST2, add 24 to GST2.806// double gst1r = lstToGst(lst1r);807// double gst1s = lstToGst(lst1s);808// double gst2r = lstToGst(lst2r);809// double gst2s = lstToGst(lst2s);810// if (gst1r > gst2r) gst2r += 24;811// if (gst1s > gst2s) gst2s += 24;812//813// // 5. Calculate GST at 0h UT of this date814// double t00 = utToGst(0);815//816// // 6. Calculate GST at 0h on the observer's longitude817// double offset = ::round(fLongitude*12/PI); // p.95 step 6; he _rounds_ to nearest 15 deg.818// double t00p = t00 - offset*1.002737909;819// if (t00p < 0) t00p += 24; // do NOT normalize820//821// // 7. Adjust822// if (gst1r < t00p) {823// gst1r += 24;824// gst2r += 24;825// }826// if (gst1s < t00p) {827// gst1s += 24;828// gst2s += 24;829// }830//831// // 8.832// double gstr = (24.07*gst1r-t00*(gst2r-gst1r))/(24.07+gst1r-gst2r);833// double gsts = (24.07*gst1s-t00*(gst2s-gst1s))/(24.07+gst1s-gst2s);834//835// // 9. Correct for parallax, refraction, and sun's diameter836// double dec = (pos1.declination + pos2.declination) / 2;837// double psi = ::acos(sin(fLatitude) / cos(dec));838// double x = 0.830725 * DEG_RAD; // parallax+refraction+diameter839// double y = ::asin(sin(x) / ::sin(psi)) * RAD_DEG;840// double delta_t = 240 * y / cos(dec) / 3600; // hours841//842// // 10. Add correction to GSTs, subtract from GSTr843// gstr -= delta_t;844// gsts += delta_t;845//846// // 11. Convert GST to UT and then to local civil time847// double ut = gstToUt(rise ? gstr : gsts);848// //System.out.println((rise?"rise=":"set=") + ut + ", delta_t=" + delta_t);849// long midnight = DAY_MS * (time / DAY_MS); // Find UT midnight on this day850// return midnight + (long) (ut * 3600000);851// }852853// Commented out - currently unused. ICU 2.6, Alan854// /**855// * Convert local sidereal time to Greenwich sidereal time.856// * Section 15. Duffett-Smith p.21857// * @param lst in hours (0..24)858// * @return GST in hours (0..24)859// */860// double lstToGst(double lst) {861// double delta = fLongitude * 24 / CalendarAstronomer_PI2;862// return normalize(lst - delta, 24);863// }864865// Commented out - currently unused. ICU 2.6, Alan866// /**867// * Convert UT to GST on this date.868// * Section 12. Duffett-Smith p.17869// * @param ut in hours870// * @return GST in hours871// */872// double utToGst(double ut) {873// return normalize(getT0() + ut*1.002737909, 24);874// }875876// Commented out - currently unused. ICU 2.6, Alan877// /**878// * Convert GST to UT on this date.879// * Section 13. Duffett-Smith p.18880// * @param gst in hours881// * @return UT in hours882// */883// double gstToUt(double gst) {884// return normalize(gst - getT0(), 24) * 0.9972695663;885// }886887// Commented out - currently unused. ICU 2.6, Alan888// double getT0() {889// // Common computation for UT <=> GST890//891// // Find JD for 0h UT892// double jd = uprv_floor(getJulianDay() - 0.5) + 0.5;893//894// double s = jd - 2451545.0;895// double t = s / 36525.0;896// double t0 = 6.697374558 + (2400.051336 + 0.000025862*t)*t;897// return t0;898// }899900// Commented out - currently unused. ICU 2.6, Alan901// //-------------------------------------------------------------------------902// // Alternate Sun Rise/Set903// // See sci.astro FAQ904// // http://www.faqs.org/faqs/astronomy/faq/part3/section-5.html905// //-------------------------------------------------------------------------906//907// // Note: This method appears to produce inferior accuracy as908// // compared to getSunRiseSet().909//910// /**911// * TODO Make this when the entire class is package-private.912// */913// /*public*/ long getSunRiseSet3(boolean rise) {914//915// // Compute day number for 0.0 Jan 2000 epoch916// double d = (double)(time - EPOCH_2000_MS) / DAY_MS;917//918// // Now compute the Local Sidereal Time, LST:919// //920// double LST = 98.9818 + 0.985647352 * d + /*UT*15 + long*/921// fLongitude*RAD_DEG;922// //923// // (east long. positive). Note that LST is here expressed in degrees,924// // where 15 degrees corresponds to one hour. Since LST really is an angle,925// // it's convenient to use one unit---degrees---throughout.926//927// // COMPUTING THE SUN'S POSITION928// // ----------------------------929// //930// // To be able to compute the Sun's rise/set times, you need to be able to931// // compute the Sun's position at any time. First compute the "day932// // number" d as outlined above, for the desired moment. Next compute:933// //934// double oblecl = 23.4393 - 3.563E-7 * d;935// //936// double w = 282.9404 + 4.70935E-5 * d;937// double M = 356.0470 + 0.9856002585 * d;938// double e = 0.016709 - 1.151E-9 * d;939// //940// // This is the obliquity of the ecliptic, plus some of the elements of941// // the Sun's apparent orbit (i.e., really the Earth's orbit): w =942// // argument of perihelion, M = mean anomaly, e = eccentricity.943// // Semi-major axis is here assumed to be exactly 1.0 (while not strictly944// // true, this is still an accurate approximation). Next compute E, the945// // eccentric anomaly:946// //947// double E = M + e*(180/PI) * ::sin(M*DEG_RAD) * ( 1.0 + e*cos(M*DEG_RAD) );948// //949// // where E and M are in degrees. This is it---no further iterations are950// // needed because we know e has a sufficiently small value. Next compute951// // the true anomaly, v, and the distance, r:952// //953// /* r * cos(v) = */ double A = cos(E*DEG_RAD) - e;954// /* r * ::sin(v) = */ double B = ::sqrt(1 - e*e) * ::sin(E*DEG_RAD);955// //956// // and957// //958// // r = sqrt( A*A + B*B )959// double v = ::atan2( B, A )*RAD_DEG;960// //961// // The Sun's true longitude, slon, can now be computed:962// //963// double slon = v + w;964// //965// // Since the Sun is always at the ecliptic (or at least very very close to966// // it), we can use simplified formulae to convert slon (the Sun's ecliptic967// // longitude) to sRA and sDec (the Sun's RA and Dec):968// //969// // ::sin(slon) * cos(oblecl)970// // tan(sRA) = -------------------------971// // cos(slon)972// //973// // ::sin(sDec) = ::sin(oblecl) * ::sin(slon)974// //975// // As was the case when computing az, the Azimuth, if possible use an976// // atan2() function to compute sRA.977//978// double sRA = ::atan2(sin(slon*DEG_RAD) * cos(oblecl*DEG_RAD), cos(slon*DEG_RAD))*RAD_DEG;979//980// double sin_sDec = ::sin(oblecl*DEG_RAD) * ::sin(slon*DEG_RAD);981// double sDec = ::asin(sin_sDec)*RAD_DEG;982//983// // COMPUTING RISE AND SET TIMES984// // ----------------------------985// //986// // To compute when an object rises or sets, you must compute when it987// // passes the meridian and the HA of rise/set. Then the rise time is988// // the meridian time minus HA for rise/set, and the set time is the989// // meridian time plus the HA for rise/set.990// //991// // To find the meridian time, compute the Local Sidereal Time at 0h local992// // time (or 0h UT if you prefer to work in UT) as outlined above---name993// // that quantity LST0. The Meridian Time, MT, will now be:994// //995// // MT = RA - LST0996// double MT = normalize(sRA - LST, 360);997// //998// // where "RA" is the object's Right Ascension (in degrees!). If negative,999// // add 360 deg to MT. If the object is the Sun, leave the time as it is,1000// // but if it's stellar, multiply MT by 365.2422/366.2422, to convert from1001// // sidereal to solar time. Now, compute HA for rise/set, name that1002// // quantity HA0:1003// //1004// // ::sin(h0) - ::sin(lat) * ::sin(Dec)1005// // cos(HA0) = ---------------------------------1006// // cos(lat) * cos(Dec)1007// //1008// // where h0 is the altitude selected to represent rise/set. For a purely1009// // mathematical horizon, set h0 = 0 and simplify to:1010// //1011// // cos(HA0) = - tan(lat) * tan(Dec)1012// //1013// // If you want to account for refraction on the atmosphere, set h0 = -35/601014// // degrees (-35 arc minutes), and if you want to compute the rise/set times1015// // for the Sun's upper limb, set h0 = -50/60 (-50 arc minutes).1016// //1017// double h0 = -50/60 * DEG_RAD;1018//1019// double HA0 = ::acos(1020// (sin(h0) - ::sin(fLatitude) * sin_sDec) /1021// (cos(fLatitude) * cos(sDec*DEG_RAD)))*RAD_DEG;1022//1023// // When HA0 has been computed, leave it as it is for the Sun but multiply1024// // by 365.2422/366.2422 for stellar objects, to convert from sidereal to1025// // solar time. Finally compute:1026// //1027// // Rise time = MT - HA01028// // Set time = MT + HA01029// //1030// // convert the times from degrees to hours by dividing by 15.1031// //1032// // If you'd like to check that your calculations are accurate or just1033// // need a quick result, check the USNO's Sun or Moon Rise/Set Table,1034// // <URL:http://aa.usno.navy.mil/AA/data/docs/RS_OneYear.html>.1035//1036// double result = MT + (rise ? -HA0 : HA0); // in degrees1037//1038// // Find UT midnight on this day1039// long midnight = DAY_MS * (time / DAY_MS);1040//1041// return midnight + (long) (result * 3600000 / 15);1042// }10431044//-------------------------------------------------------------------------1045// The Moon1046//-------------------------------------------------------------------------10471048#define moonL0 (318.351648 * CalendarAstronomer::PI/180 ) // Mean long. at epoch1049#define moonP0 ( 36.340410 * CalendarAstronomer::PI/180 ) // Mean long. of perigee1050#define moonN0 ( 318.510107 * CalendarAstronomer::PI/180 ) // Mean long. of node1051#define moonI ( 5.145366 * CalendarAstronomer::PI/180 ) // Inclination of orbit1052#define moonE ( 0.054900 ) // Eccentricity of orbit10531054// These aren't used right now1055#define moonA ( 3.84401e5 ) // semi-major axis (km)1056#define moonT0 ( 0.5181 * CalendarAstronomer::PI/180 ) // Angular size at distance A1057#define moonPi ( 0.9507 * CalendarAstronomer::PI/180 ) // Parallax at distance A10581059/**1060* The position of the moon at the time set on this1061* object, in equatorial coordinates.1062* @internal1063* @deprecated ICU 2.4. This class may be removed or modified.1064*/1065const CalendarAstronomer::Equatorial& CalendarAstronomer::getMoonPosition()1066{1067//1068// See page 142 of "Practical Astronomy with your Calculator",1069// by Peter Duffet-Smith, for details on the algorithm.1070//1071if (moonPositionSet == false) {1072// Calculate the solar longitude. Has the side effect of1073// filling in "meanAnomalySun" as well.1074getSunLongitude();10751076//1077// Find the # of days since the epoch of our orbital parameters.1078// TODO: Convert the time of day portion into ephemeris time1079//1080double day = getJulianDay() - JD_EPOCH; // Days since epoch10811082// Calculate the mean longitude and anomaly of the moon, based on1083// a circular orbit. Similar to the corresponding solar calculation.1084double meanLongitude = norm2PI(13.1763966*PI/180*day + moonL0);1085meanAnomalyMoon = norm2PI(meanLongitude - 0.1114041*PI/180 * day - moonP0);10861087//1088// Calculate the following corrections:1089// Evection: the sun's gravity affects the moon's eccentricity1090// Annual Eqn: variation in the effect due to earth-sun distance1091// A3: correction factor (for ???)1092//1093double evection = 1.2739*PI/180 * ::sin(2 * (meanLongitude - sunLongitude)1094- meanAnomalyMoon);1095double annual = 0.1858*PI/180 * ::sin(meanAnomalySun);1096double a3 = 0.3700*PI/180 * ::sin(meanAnomalySun);10971098meanAnomalyMoon += evection - annual - a3;10991100//1101// More correction factors:1102// center equation of the center correction1103// a4 yet another error correction (???)1104//1105// TODO: Skip the equation of the center correction and solve Kepler's eqn?1106//1107double center = 6.2886*PI/180 * ::sin(meanAnomalyMoon);1108double a4 = 0.2140*PI/180 * ::sin(2 * meanAnomalyMoon);11091110// Now find the moon's corrected longitude1111moonLongitude = meanLongitude + evection + center - annual + a4;11121113//1114// And finally, find the variation, caused by the fact that the sun's1115// gravitational pull on the moon varies depending on which side of1116// the earth the moon is on1117//1118double variation = 0.6583*CalendarAstronomer::PI/180 * ::sin(2*(moonLongitude - sunLongitude));11191120moonLongitude += variation;11211122//1123// What we've calculated so far is the moon's longitude in the plane1124// of its own orbit. Now map to the ecliptic to get the latitude1125// and longitude. First we need to find the longitude of the ascending1126// node, the position on the ecliptic where it is crossed by the moon's1127// orbit as it crosses from the southern to the northern hemisphere.1128//1129double nodeLongitude = norm2PI(moonN0 - 0.0529539*PI/180 * day);11301131nodeLongitude -= 0.16*PI/180 * ::sin(meanAnomalySun);11321133double y = ::sin(moonLongitude - nodeLongitude);1134double x = cos(moonLongitude - nodeLongitude);11351136moonEclipLong = ::atan2(y*cos(moonI), x) + nodeLongitude;1137double moonEclipLat = ::asin(y * ::sin(moonI));11381139eclipticToEquatorial(moonPosition, moonEclipLong, moonEclipLat);1140moonPositionSet = true;1141}1142return moonPosition;1143}11441145/**1146* The "age" of the moon at the time specified in this object.1147* This is really the angle between the1148* current ecliptic longitudes of the sun and the moon,1149* measured in radians.1150*1151* @see #getMoonPhase1152* @internal1153* @deprecated ICU 2.4. This class may be removed or modified.1154*/1155double CalendarAstronomer::getMoonAge() {1156// See page 147 of "Practical Astronomy with your Calculator",1157// by Peter Duffet-Smith, for details on the algorithm.1158//1159// Force the moon's position to be calculated. We're going to use1160// some the intermediate results cached during that calculation.1161//1162getMoonPosition();11631164return norm2PI(moonEclipLong - sunLongitude);1165}11661167/**1168* Calculate the phase of the moon at the time set in this object.1169* The returned phase is a <code>double</code> in the range1170* <code>0 <= phase < 1</code>, interpreted as follows:1171* <ul>1172* <li>0.00: New moon1173* <li>0.25: First quarter1174* <li>0.50: Full moon1175* <li>0.75: Last quarter1176* </ul>1177*1178* @see #getMoonAge1179* @internal1180* @deprecated ICU 2.4. This class may be removed or modified.1181*/1182double CalendarAstronomer::getMoonPhase() {1183// See page 147 of "Practical Astronomy with your Calculator",1184// by Peter Duffet-Smith, for details on the algorithm.1185return 0.5 * (1 - cos(getMoonAge()));1186}11871188/**1189* Constant representing a new moon.1190* For use with {@link #getMoonTime getMoonTime}1191* @internal1192* @deprecated ICU 2.4. This class may be removed or modified.1193*/1194const CalendarAstronomer::MoonAge CalendarAstronomer::NEW_MOON() {1195return CalendarAstronomer::MoonAge(0);1196}11971198/**1199* Constant representing the moon's first quarter.1200* For use with {@link #getMoonTime getMoonTime}1201* @internal1202* @deprecated ICU 2.4. This class may be removed or modified.1203*/1204/*const CalendarAstronomer::MoonAge CalendarAstronomer::FIRST_QUARTER() {1205return CalendarAstronomer::MoonAge(CalendarAstronomer::PI/2);1206}*/12071208/**1209* Constant representing a full moon.1210* For use with {@link #getMoonTime getMoonTime}1211* @internal1212* @deprecated ICU 2.4. This class may be removed or modified.1213*/1214const CalendarAstronomer::MoonAge CalendarAstronomer::FULL_MOON() {1215return CalendarAstronomer::MoonAge(CalendarAstronomer::PI);1216}1217/**1218* Constant representing the moon's last quarter.1219* For use with {@link #getMoonTime getMoonTime}1220* @internal1221* @deprecated ICU 2.4. This class may be removed or modified.1222*/12231224class MoonTimeAngleFunc : public CalendarAstronomer::AngleFunc {1225public:1226virtual ~MoonTimeAngleFunc();1227virtual double eval(CalendarAstronomer& a) override { return a.getMoonAge(); }1228};12291230MoonTimeAngleFunc::~MoonTimeAngleFunc() {}12311232/*const CalendarAstronomer::MoonAge CalendarAstronomer::LAST_QUARTER() {1233return CalendarAstronomer::MoonAge((CalendarAstronomer::PI*3)/2);1234}*/12351236/**1237* Find the next or previous time at which the Moon's ecliptic1238* longitude will have the desired value.1239* <p>1240* @param desired The desired longitude.1241* @param next <tt>true</tt> if the next occurrence of the phase1242* is desired, <tt>false</tt> for the previous occurrence.1243* @internal1244* @deprecated ICU 2.4. This class may be removed or modified.1245*/1246UDate CalendarAstronomer::getMoonTime(double desired, UBool next)1247{1248MoonTimeAngleFunc func;1249return timeOfAngle( func,1250desired,1251SYNODIC_MONTH,1252MINUTE_MS,1253next);1254}12551256/**1257* Find the next or previous time at which the moon will be in the1258* desired phase.1259* <p>1260* @param desired The desired phase of the moon.1261* @param next <tt>true</tt> if the next occurrence of the phase1262* is desired, <tt>false</tt> for the previous occurrence.1263* @internal1264* @deprecated ICU 2.4. This class may be removed or modified.1265*/1266UDate CalendarAstronomer::getMoonTime(const CalendarAstronomer::MoonAge& desired, UBool next) {1267return getMoonTime(desired.value, next);1268}12691270class MoonRiseSetCoordFunc : public CalendarAstronomer::CoordFunc {1271public:1272virtual ~MoonRiseSetCoordFunc();1273virtual void eval(CalendarAstronomer::Equatorial& result, CalendarAstronomer& a) override { result = a.getMoonPosition(); }1274};12751276MoonRiseSetCoordFunc::~MoonRiseSetCoordFunc() {}12771278/**1279* Returns the time (GMT) of sunrise or sunset on the local date to which1280* this calendar is currently set.1281* @internal1282* @deprecated ICU 2.4. This class may be removed or modified.1283*/1284UDate CalendarAstronomer::getMoonRiseSet(UBool rise)1285{1286MoonRiseSetCoordFunc func;1287return riseOrSet(func,1288rise,1289.533 * DEG_RAD, // Angular Diameter129034 /60.0 * DEG_RAD, // Refraction correction1291MINUTE_MS); // Desired accuracy1292}12931294//-------------------------------------------------------------------------1295// Interpolation methods for finding the time at which a given event occurs1296//-------------------------------------------------------------------------12971298UDate CalendarAstronomer::timeOfAngle(AngleFunc& func, double desired,1299double periodDays, double epsilon, UBool next)1300{1301// Find the value of the function at the current time1302double lastAngle = func.eval(*this);13031304// Find out how far we are from the desired angle1305double deltaAngle = norm2PI(desired - lastAngle) ;13061307// Using the average period, estimate the next (or previous) time at1308// which the desired angle occurs.1309double deltaT = (deltaAngle + (next ? 0.0 : - CalendarAstronomer_PI2 )) * (periodDays*DAY_MS) / CalendarAstronomer_PI2;13101311double lastDeltaT = deltaT; // Liu1312UDate startTime = fTime; // Liu13131314setTime(fTime + uprv_ceil(deltaT));13151316// Now iterate until we get the error below epsilon. Throughout1317// this loop we use normPI to get values in the range -Pi to Pi,1318// since we're using them as correction factors rather than absolute angles.1319do {1320// Evaluate the function at the time we've estimated1321double angle = func.eval(*this);13221323// Find the # of milliseconds per radian at this point on the curve1324double factor = uprv_fabs(deltaT / normPI(angle-lastAngle));13251326// Correct the time estimate based on how far off the angle is1327deltaT = normPI(desired - angle) * factor;13281329// HACK:1330//1331// If abs(deltaT) begins to diverge we need to quit this loop.1332// This only appears to happen when attempting to locate, for1333// example, a new moon on the day of the new moon. E.g.:1334//1335// This result is correct:1336// newMoon(7508(Mon Jul 23 00:00:00 CST 1990,false))=1337// Sun Jul 22 10:57:41 CST 19901338//1339// But attempting to make the same call a day earlier causes deltaT1340// to diverge:1341// CalendarAstronomer.timeOfAngle() diverging: 1.348508727575625E9 ->1342// 1.3649828540224032E91343// newMoon(7507(Sun Jul 22 00:00:00 CST 1990,false))=1344// Sun Jul 08 13:56:15 CST 19901345//1346// As a temporary solution, we catch this specific condition and1347// adjust our start time by one eighth period days (either forward1348// or backward) and try again.1349// Liu 11/9/001350if (uprv_fabs(deltaT) > uprv_fabs(lastDeltaT)) {1351double delta = uprv_ceil (periodDays * DAY_MS / 8.0);1352setTime(startTime + (next ? delta : -delta));1353return timeOfAngle(func, desired, periodDays, epsilon, next);1354}13551356lastDeltaT = deltaT;1357lastAngle = angle;13581359setTime(fTime + uprv_ceil(deltaT));1360}1361while (uprv_fabs(deltaT) > epsilon);13621363return fTime;1364}13651366UDate CalendarAstronomer::riseOrSet(CoordFunc& func, UBool rise,1367double diameter, double refraction,1368double epsilon)1369{1370Equatorial pos;1371double tanL = ::tan(fLatitude);1372double deltaT = 0;1373int32_t count = 0;13741375//1376// Calculate the object's position at the current time, then use that1377// position to calculate the time of rising or setting. The position1378// will be different at that time, so iterate until the error is allowable.1379//1380U_DEBUG_ASTRO_MSG(("setup rise=%s, dia=%.3lf, ref=%.3lf, eps=%.3lf\n",1381rise?"T":"F", diameter, refraction, epsilon));1382do {1383// See "Practical Astronomy With Your Calculator, section 33.1384func.eval(pos, *this);1385double angle = ::acos(-tanL * ::tan(pos.declination));1386double lst = ((rise ? CalendarAstronomer_PI2-angle : angle) + pos.ascension ) * 24 / CalendarAstronomer_PI2;13871388// Convert from LST to Universal Time.1389UDate newTime = lstToUT( lst );13901391deltaT = newTime - fTime;1392setTime(newTime);1393U_DEBUG_ASTRO_MSG(("%d] dT=%.3lf, angle=%.3lf, lst=%.3lf, A=%.3lf/D=%.3lf\n",1394count, deltaT, angle, lst, pos.ascension, pos.declination));1395}1396while (++ count < 5 && uprv_fabs(deltaT) > epsilon);13971398// Calculate the correction due to refraction and the object's angular diameter1399double cosD = ::cos(pos.declination);1400double psi = ::acos(sin(fLatitude) / cosD);1401double x = diameter / 2 + refraction;1402double y = ::asin(sin(x) / ::sin(psi));1403long delta = (long)((240 * y * RAD_DEG / cosD)*SECOND_MS);14041405return fTime + (rise ? -delta : delta);1406}1407/**1408* Return the obliquity of the ecliptic (the angle between the ecliptic1409* and the earth's equator) at the current time. This varies due to1410* the precession of the earth's axis.1411*1412* @return the obliquity of the ecliptic relative to the equator,1413* measured in radians.1414*/1415double CalendarAstronomer::eclipticObliquity() {1416if (isINVALID(eclipObliquity)) {1417const double epoch = 2451545.0; // 2000 AD, January 1.514181419double T = (getJulianDay() - epoch) / 36525;14201421eclipObliquity = 23.4392921422- 46.815/3600 * T1423- 0.0006/3600 * T*T1424+ 0.00181/3600 * T*T*T;14251426eclipObliquity *= DEG_RAD;1427}1428return eclipObliquity;1429}143014311432//-------------------------------------------------------------------------1433// Private data1434//-------------------------------------------------------------------------1435void CalendarAstronomer::clearCache() {1436const double INVALID = uprv_getNaN();14371438julianDay = INVALID;1439julianCentury = INVALID;1440sunLongitude = INVALID;1441meanAnomalySun = INVALID;1442moonLongitude = INVALID;1443moonEclipLong = INVALID;1444meanAnomalyMoon = INVALID;1445eclipObliquity = INVALID;1446siderealTime = INVALID;1447siderealT0 = INVALID;1448moonPositionSet = false;1449}14501451//private static void out(String s) {1452// System.out.println(s);1453//}14541455//private static String deg(double rad) {1456// return Double.toString(rad * RAD_DEG);1457//}14581459//private static String hours(long ms) {1460// return Double.toString((double)ms / HOUR_MS) + " hours";1461//}14621463/**1464* @internal1465* @deprecated ICU 2.4. This class may be removed or modified.1466*/1467/*UDate CalendarAstronomer::local(UDate localMillis) {1468// TODO - srl ?1469TimeZone *tz = TimeZone::createDefault();1470int32_t rawOffset;1471int32_t dstOffset;1472UErrorCode status = U_ZERO_ERROR;1473tz->getOffset(localMillis, true, rawOffset, dstOffset, status);1474delete tz;1475return localMillis - rawOffset;1476}*/14771478// Debugging functions1479UnicodeString CalendarAstronomer::Ecliptic::toString() const1480{1481#ifdef U_DEBUG_ASTRO1482char tmp[800];1483snprintf(tmp, sizeof(tmp), "[%.5f,%.5f]", longitude*RAD_DEG, latitude*RAD_DEG);1484return UnicodeString(tmp, "");1485#else1486return UnicodeString();1487#endif1488}14891490UnicodeString CalendarAstronomer::Equatorial::toString() const1491{1492#ifdef U_DEBUG_ASTRO1493char tmp[400];1494snprintf(tmp, sizeof(tmp), "%f,%f",1495(ascension*RAD_DEG), (declination*RAD_DEG));1496return UnicodeString(tmp, "");1497#else1498return UnicodeString();1499#endif1500}15011502UnicodeString CalendarAstronomer::Horizon::toString() const1503{1504#ifdef U_DEBUG_ASTRO1505char tmp[800];1506snprintf(tmp, sizeof(tmp), "[%.5f,%.5f]", altitude*RAD_DEG, azimuth*RAD_DEG);1507return UnicodeString(tmp, "");1508#else1509return UnicodeString();1510#endif1511}151215131514// static private String radToHms(double angle) {1515// int hrs = (int) (angle*RAD_HOUR);1516// int min = (int)((angle*RAD_HOUR - hrs) * 60);1517// int sec = (int)((angle*RAD_HOUR - hrs - min/60.0) * 3600);15181519// return Integer.toString(hrs) + "h" + min + "m" + sec + "s";1520// }15211522// static private String radToDms(double angle) {1523// int deg = (int) (angle*RAD_DEG);1524// int min = (int)((angle*RAD_DEG - deg) * 60);1525// int sec = (int)((angle*RAD_DEG - deg - min/60.0) * 3600);15261527// return Integer.toString(deg) + "\u00b0" + min + "'" + sec + "\"";1528// }15291530// =============== Calendar Cache ================15311532void CalendarCache::createCache(CalendarCache** cache, UErrorCode& status) {1533ucln_i18n_registerCleanup(UCLN_I18N_ASTRO_CALENDAR, calendar_astro_cleanup);1534if(cache == NULL) {1535status = U_MEMORY_ALLOCATION_ERROR;1536} else {1537*cache = new CalendarCache(32, status);1538if(U_FAILURE(status)) {1539delete *cache;1540*cache = NULL;1541}1542}1543}15441545int32_t CalendarCache::get(CalendarCache** cache, int32_t key, UErrorCode &status) {1546int32_t res;15471548if(U_FAILURE(status)) {1549return 0;1550}1551umtx_lock(&ccLock);15521553if(*cache == NULL) {1554createCache(cache, status);1555if(U_FAILURE(status)) {1556umtx_unlock(&ccLock);1557return 0;1558}1559}15601561res = uhash_igeti((*cache)->fTable, key);1562U_DEBUG_ASTRO_MSG(("%p: GET: [%d] == %d\n", (*cache)->fTable, key, res));15631564umtx_unlock(&ccLock);1565return res;1566}15671568void CalendarCache::put(CalendarCache** cache, int32_t key, int32_t value, UErrorCode &status) {1569if(U_FAILURE(status)) {1570return;1571}1572umtx_lock(&ccLock);15731574if(*cache == NULL) {1575createCache(cache, status);1576if(U_FAILURE(status)) {1577umtx_unlock(&ccLock);1578return;1579}1580}15811582uhash_iputi((*cache)->fTable, key, value, &status);1583U_DEBUG_ASTRO_MSG(("%p: PUT: [%d] := %d\n", (*cache)->fTable, key, value));15841585umtx_unlock(&ccLock);1586}15871588CalendarCache::CalendarCache(int32_t size, UErrorCode &status) {1589fTable = uhash_openSize(uhash_hashLong, uhash_compareLong, NULL, size, &status);1590U_DEBUG_ASTRO_MSG(("%p: Opening.\n", fTable));1591}15921593CalendarCache::~CalendarCache() {1594if(fTable != NULL) {1595U_DEBUG_ASTRO_MSG(("%p: Closing.\n", fTable));1596uhash_close(fTable);1597}1598}15991600U_NAMESPACE_END16011602#endif // !UCONFIG_NO_FORMATTING160316041605