1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 1.2 +++ b/intl/icu/source/i18n/astro.cpp Wed Dec 31 06:09:35 2014 +0100 1.3 @@ -0,0 +1,1601 @@ 1.4 +/************************************************************************ 1.5 + * Copyright (C) 1996-2012, International Business Machines Corporation 1.6 + * and others. All Rights Reserved. 1.7 + ************************************************************************ 1.8 + * 2003-nov-07 srl Port from Java 1.9 + */ 1.10 + 1.11 +#include "astro.h" 1.12 + 1.13 +#if !UCONFIG_NO_FORMATTING 1.14 + 1.15 +#include "unicode/calendar.h" 1.16 +#include <math.h> 1.17 +#include <float.h> 1.18 +#include "unicode/putil.h" 1.19 +#include "uhash.h" 1.20 +#include "umutex.h" 1.21 +#include "ucln_in.h" 1.22 +#include "putilimp.h" 1.23 +#include <stdio.h> // for toString() 1.24 + 1.25 +#if defined (PI) 1.26 +#undef PI 1.27 +#endif 1.28 + 1.29 +#ifdef U_DEBUG_ASTRO 1.30 +# include "uresimp.h" // for debugging 1.31 + 1.32 +static void debug_astro_loc(const char *f, int32_t l) 1.33 +{ 1.34 + fprintf(stderr, "%s:%d: ", f, l); 1.35 +} 1.36 + 1.37 +static void debug_astro_msg(const char *pat, ...) 1.38 +{ 1.39 + va_list ap; 1.40 + va_start(ap, pat); 1.41 + vfprintf(stderr, pat, ap); 1.42 + fflush(stderr); 1.43 +} 1.44 +#include "unicode/datefmt.h" 1.45 +#include "unicode/ustring.h" 1.46 +static const char * debug_astro_date(UDate d) { 1.47 + static char gStrBuf[1024]; 1.48 + static DateFormat *df = NULL; 1.49 + if(df == NULL) { 1.50 + df = DateFormat::createDateTimeInstance(DateFormat::MEDIUM, DateFormat::MEDIUM, Locale::getUS()); 1.51 + df->adoptTimeZone(TimeZone::getGMT()->clone()); 1.52 + } 1.53 + UnicodeString str; 1.54 + df->format(d,str); 1.55 + u_austrncpy(gStrBuf,str.getTerminatedBuffer(),sizeof(gStrBuf)-1); 1.56 + return gStrBuf; 1.57 +} 1.58 + 1.59 +// must use double parens, i.e.: U_DEBUG_ASTRO_MSG(("four is: %d",4)); 1.60 +#define U_DEBUG_ASTRO_MSG(x) {debug_astro_loc(__FILE__,__LINE__);debug_astro_msg x;} 1.61 +#else 1.62 +#define U_DEBUG_ASTRO_MSG(x) 1.63 +#endif 1.64 + 1.65 +static inline UBool isINVALID(double d) { 1.66 + return(uprv_isNaN(d)); 1.67 +} 1.68 + 1.69 +static UMutex ccLock = U_MUTEX_INITIALIZER; 1.70 + 1.71 +U_CDECL_BEGIN 1.72 +static UBool calendar_astro_cleanup(void) { 1.73 + return TRUE; 1.74 +} 1.75 +U_CDECL_END 1.76 + 1.77 +U_NAMESPACE_BEGIN 1.78 + 1.79 +/** 1.80 + * The number of standard hours in one sidereal day. 1.81 + * Approximately 24.93. 1.82 + * @internal 1.83 + * @deprecated ICU 2.4. This class may be removed or modified. 1.84 + */ 1.85 +#define SIDEREAL_DAY (23.93446960027) 1.86 + 1.87 +/** 1.88 + * The number of sidereal hours in one mean solar day. 1.89 + * Approximately 24.07. 1.90 + * @internal 1.91 + * @deprecated ICU 2.4. This class may be removed or modified. 1.92 + */ 1.93 +#define SOLAR_DAY (24.065709816) 1.94 + 1.95 +/** 1.96 + * The average number of solar days from one new moon to the next. This is the time 1.97 + * it takes for the moon to return the same ecliptic longitude as the sun. 1.98 + * It is longer than the sidereal month because the sun's longitude increases 1.99 + * during the year due to the revolution of the earth around the sun. 1.100 + * Approximately 29.53. 1.101 + * 1.102 + * @see #SIDEREAL_MONTH 1.103 + * @internal 1.104 + * @deprecated ICU 2.4. This class may be removed or modified. 1.105 + */ 1.106 +const double CalendarAstronomer::SYNODIC_MONTH = 29.530588853; 1.107 + 1.108 +/** 1.109 + * The average number of days it takes 1.110 + * for the moon to return to the same ecliptic longitude relative to the 1.111 + * stellar background. This is referred to as the sidereal month. 1.112 + * It is shorter than the synodic month due to 1.113 + * the revolution of the earth around the sun. 1.114 + * Approximately 27.32. 1.115 + * 1.116 + * @see #SYNODIC_MONTH 1.117 + * @internal 1.118 + * @deprecated ICU 2.4. This class may be removed or modified. 1.119 + */ 1.120 +#define SIDEREAL_MONTH 27.32166 1.121 + 1.122 +/** 1.123 + * The average number number of days between successive vernal equinoxes. 1.124 + * Due to the precession of the earth's 1.125 + * axis, this is not precisely the same as the sidereal year. 1.126 + * Approximately 365.24 1.127 + * 1.128 + * @see #SIDEREAL_YEAR 1.129 + * @internal 1.130 + * @deprecated ICU 2.4. This class may be removed or modified. 1.131 + */ 1.132 +#define TROPICAL_YEAR 365.242191 1.133 + 1.134 +/** 1.135 + * The average number of days it takes 1.136 + * for the sun to return to the same position against the fixed stellar 1.137 + * background. This is the duration of one orbit of the earth about the sun 1.138 + * as it would appear to an outside observer. 1.139 + * Due to the precession of the earth's 1.140 + * axis, this is not precisely the same as the tropical year. 1.141 + * Approximately 365.25. 1.142 + * 1.143 + * @see #TROPICAL_YEAR 1.144 + * @internal 1.145 + * @deprecated ICU 2.4. This class may be removed or modified. 1.146 + */ 1.147 +#define SIDEREAL_YEAR 365.25636 1.148 + 1.149 +//------------------------------------------------------------------------- 1.150 +// Time-related constants 1.151 +//------------------------------------------------------------------------- 1.152 + 1.153 +/** 1.154 + * The number of milliseconds in one second. 1.155 + * @internal 1.156 + * @deprecated ICU 2.4. This class may be removed or modified. 1.157 + */ 1.158 +#define SECOND_MS U_MILLIS_PER_SECOND 1.159 + 1.160 +/** 1.161 + * The number of milliseconds in one minute. 1.162 + * @internal 1.163 + * @deprecated ICU 2.4. This class may be removed or modified. 1.164 + */ 1.165 +#define MINUTE_MS U_MILLIS_PER_MINUTE 1.166 + 1.167 +/** 1.168 + * The number of milliseconds in one hour. 1.169 + * @internal 1.170 + * @deprecated ICU 2.4. This class may be removed or modified. 1.171 + */ 1.172 +#define HOUR_MS U_MILLIS_PER_HOUR 1.173 + 1.174 +/** 1.175 + * The number of milliseconds in one day. 1.176 + * @internal 1.177 + * @deprecated ICU 2.4. This class may be removed or modified. 1.178 + */ 1.179 +#define DAY_MS U_MILLIS_PER_DAY 1.180 + 1.181 +/** 1.182 + * The start of the julian day numbering scheme used by astronomers, which 1.183 + * is 1/1/4713 BC (Julian), 12:00 GMT. This is given as the number of milliseconds 1.184 + * since 1/1/1970 AD (Gregorian), a negative number. 1.185 + * Note that julian day numbers and 1.186 + * the Julian calendar are <em>not</em> the same thing. Also note that 1.187 + * julian days start at <em>noon</em>, not midnight. 1.188 + * @internal 1.189 + * @deprecated ICU 2.4. This class may be removed or modified. 1.190 + */ 1.191 +#define JULIAN_EPOCH_MS -210866760000000.0 1.192 + 1.193 + 1.194 +/** 1.195 + * Milliseconds value for 0.0 January 2000 AD. 1.196 + */ 1.197 +#define EPOCH_2000_MS 946598400000.0 1.198 + 1.199 +//------------------------------------------------------------------------- 1.200 +// Assorted private data used for conversions 1.201 +//------------------------------------------------------------------------- 1.202 + 1.203 +// My own copies of these so compilers are more likely to optimize them away 1.204 +const double CalendarAstronomer::PI = 3.14159265358979323846; 1.205 + 1.206 +#define CalendarAstronomer_PI2 (CalendarAstronomer::PI*2.0) 1.207 +#define RAD_HOUR ( 12 / CalendarAstronomer::PI ) // radians -> hours 1.208 +#define DEG_RAD ( CalendarAstronomer::PI / 180 ) // degrees -> radians 1.209 +#define RAD_DEG ( 180 / CalendarAstronomer::PI ) // radians -> degrees 1.210 + 1.211 +/*** 1.212 + * Given 'value', add or subtract 'range' until 0 <= 'value' < range. 1.213 + * The modulus operator. 1.214 + */ 1.215 +inline static double normalize(double value, double range) { 1.216 + return value - range * ClockMath::floorDivide(value, range); 1.217 +} 1.218 + 1.219 +/** 1.220 + * Normalize an angle so that it's in the range 0 - 2pi. 1.221 + * For positive angles this is just (angle % 2pi), but the Java 1.222 + * mod operator doesn't work that way for negative numbers.... 1.223 + */ 1.224 +inline static double norm2PI(double angle) { 1.225 + return normalize(angle, CalendarAstronomer::PI * 2.0); 1.226 +} 1.227 + 1.228 +/** 1.229 + * Normalize an angle into the range -PI - PI 1.230 + */ 1.231 +inline static double normPI(double angle) { 1.232 + return normalize(angle + CalendarAstronomer::PI, CalendarAstronomer::PI * 2.0) - CalendarAstronomer::PI; 1.233 +} 1.234 + 1.235 +//------------------------------------------------------------------------- 1.236 +// Constructors 1.237 +//------------------------------------------------------------------------- 1.238 + 1.239 +/** 1.240 + * Construct a new <code>CalendarAstronomer</code> object that is initialized to 1.241 + * the current date and time. 1.242 + * @internal 1.243 + * @deprecated ICU 2.4. This class may be removed or modified. 1.244 + */ 1.245 +CalendarAstronomer::CalendarAstronomer(): 1.246 + fTime(Calendar::getNow()), fLongitude(0.0), fLatitude(0.0), fGmtOffset(0.0), moonPosition(0,0), moonPositionSet(FALSE) { 1.247 + clearCache(); 1.248 +} 1.249 + 1.250 +/** 1.251 + * Construct a new <code>CalendarAstronomer</code> object that is initialized to 1.252 + * the specified date and time. 1.253 + * @internal 1.254 + * @deprecated ICU 2.4. This class may be removed or modified. 1.255 + */ 1.256 +CalendarAstronomer::CalendarAstronomer(UDate d): fTime(d), fLongitude(0.0), fLatitude(0.0), fGmtOffset(0.0), moonPosition(0,0), moonPositionSet(FALSE) { 1.257 + clearCache(); 1.258 +} 1.259 + 1.260 +/** 1.261 + * Construct a new <code>CalendarAstronomer</code> object with the given 1.262 + * latitude and longitude. The object's time is set to the current 1.263 + * date and time. 1.264 + * <p> 1.265 + * @param longitude The desired longitude, in <em>degrees</em> east of 1.266 + * the Greenwich meridian. 1.267 + * 1.268 + * @param latitude The desired latitude, in <em>degrees</em>. Positive 1.269 + * values signify North, negative South. 1.270 + * 1.271 + * @see java.util.Date#getTime() 1.272 + * @internal 1.273 + * @deprecated ICU 2.4. This class may be removed or modified. 1.274 + */ 1.275 +CalendarAstronomer::CalendarAstronomer(double longitude, double latitude) : 1.276 + fTime(Calendar::getNow()), moonPosition(0,0), moonPositionSet(FALSE) { 1.277 + fLongitude = normPI(longitude * (double)DEG_RAD); 1.278 + fLatitude = normPI(latitude * (double)DEG_RAD); 1.279 + fGmtOffset = (double)(fLongitude * 24. * (double)HOUR_MS / (double)CalendarAstronomer_PI2); 1.280 + clearCache(); 1.281 +} 1.282 + 1.283 +CalendarAstronomer::~CalendarAstronomer() 1.284 +{ 1.285 +} 1.286 + 1.287 +//------------------------------------------------------------------------- 1.288 +// Time and date getters and setters 1.289 +//------------------------------------------------------------------------- 1.290 + 1.291 +/** 1.292 + * Set the current date and time of this <code>CalendarAstronomer</code> object. All 1.293 + * astronomical calculations are performed based on this time setting. 1.294 + * 1.295 + * @param aTime the date and time, expressed as the number of milliseconds since 1.296 + * 1/1/1970 0:00 GMT (Gregorian). 1.297 + * 1.298 + * @see #setDate 1.299 + * @see #getTime 1.300 + * @internal 1.301 + * @deprecated ICU 2.4. This class may be removed or modified. 1.302 + */ 1.303 +void CalendarAstronomer::setTime(UDate aTime) { 1.304 + fTime = aTime; 1.305 + U_DEBUG_ASTRO_MSG(("setTime(%.1lf, %sL)\n", aTime, debug_astro_date(aTime+fGmtOffset))); 1.306 + clearCache(); 1.307 +} 1.308 + 1.309 +/** 1.310 + * Set the current date and time of this <code>CalendarAstronomer</code> object. All 1.311 + * astronomical calculations are performed based on this time setting. 1.312 + * 1.313 + * @param jdn the desired time, expressed as a "julian day number", 1.314 + * which is the number of elapsed days since 1.315 + * 1/1/4713 BC (Julian), 12:00 GMT. Note that julian day 1.316 + * numbers start at <em>noon</em>. To get the jdn for 1.317 + * the corresponding midnight, subtract 0.5. 1.318 + * 1.319 + * @see #getJulianDay 1.320 + * @see #JULIAN_EPOCH_MS 1.321 + * @internal 1.322 + * @deprecated ICU 2.4. This class may be removed or modified. 1.323 + */ 1.324 +void CalendarAstronomer::setJulianDay(double jdn) { 1.325 + fTime = (double)(jdn * DAY_MS) + JULIAN_EPOCH_MS; 1.326 + clearCache(); 1.327 + julianDay = jdn; 1.328 +} 1.329 + 1.330 +/** 1.331 + * Get the current time of this <code>CalendarAstronomer</code> object, 1.332 + * represented as the number of milliseconds since 1.333 + * 1/1/1970 AD 0:00 GMT (Gregorian). 1.334 + * 1.335 + * @see #setTime 1.336 + * @see #getDate 1.337 + * @internal 1.338 + * @deprecated ICU 2.4. This class may be removed or modified. 1.339 + */ 1.340 +UDate CalendarAstronomer::getTime() { 1.341 + return fTime; 1.342 +} 1.343 + 1.344 +/** 1.345 + * Get the current time of this <code>CalendarAstronomer</code> object, 1.346 + * expressed as a "julian day number", which is the number of elapsed 1.347 + * days since 1/1/4713 BC (Julian), 12:00 GMT. 1.348 + * 1.349 + * @see #setJulianDay 1.350 + * @see #JULIAN_EPOCH_MS 1.351 + * @internal 1.352 + * @deprecated ICU 2.4. This class may be removed or modified. 1.353 + */ 1.354 +double CalendarAstronomer::getJulianDay() { 1.355 + if (isINVALID(julianDay)) { 1.356 + julianDay = (fTime - (double)JULIAN_EPOCH_MS) / (double)DAY_MS; 1.357 + } 1.358 + return julianDay; 1.359 +} 1.360 + 1.361 +/** 1.362 + * Return this object's time expressed in julian centuries: 1.363 + * the number of centuries after 1/1/1900 AD, 12:00 GMT 1.364 + * 1.365 + * @see #getJulianDay 1.366 + * @internal 1.367 + * @deprecated ICU 2.4. This class may be removed or modified. 1.368 + */ 1.369 +double CalendarAstronomer::getJulianCentury() { 1.370 + if (isINVALID(julianCentury)) { 1.371 + julianCentury = (getJulianDay() - 2415020.0) / 36525.0; 1.372 + } 1.373 + return julianCentury; 1.374 +} 1.375 + 1.376 +/** 1.377 + * Returns the current Greenwich sidereal time, measured in hours 1.378 + * @internal 1.379 + * @deprecated ICU 2.4. This class may be removed or modified. 1.380 + */ 1.381 +double CalendarAstronomer::getGreenwichSidereal() { 1.382 + if (isINVALID(siderealTime)) { 1.383 + // See page 86 of "Practial Astronomy with your Calculator", 1.384 + // by Peter Duffet-Smith, for details on the algorithm. 1.385 + 1.386 + double UT = normalize(fTime/(double)HOUR_MS, 24.); 1.387 + 1.388 + siderealTime = normalize(getSiderealOffset() + UT*1.002737909, 24.); 1.389 + } 1.390 + return siderealTime; 1.391 +} 1.392 + 1.393 +double CalendarAstronomer::getSiderealOffset() { 1.394 + if (isINVALID(siderealT0)) { 1.395 + double JD = uprv_floor(getJulianDay() - 0.5) + 0.5; 1.396 + double S = JD - 2451545.0; 1.397 + double T = S / 36525.0; 1.398 + siderealT0 = normalize(6.697374558 + 2400.051336*T + 0.000025862*T*T, 24); 1.399 + } 1.400 + return siderealT0; 1.401 +} 1.402 + 1.403 +/** 1.404 + * Returns the current local sidereal time, measured in hours 1.405 + * @internal 1.406 + * @deprecated ICU 2.4. This class may be removed or modified. 1.407 + */ 1.408 +double CalendarAstronomer::getLocalSidereal() { 1.409 + return normalize(getGreenwichSidereal() + (fGmtOffset/(double)HOUR_MS), 24.); 1.410 +} 1.411 + 1.412 +/** 1.413 + * Converts local sidereal time to Universal Time. 1.414 + * 1.415 + * @param lst The Local Sidereal Time, in hours since sidereal midnight 1.416 + * on this object's current date. 1.417 + * 1.418 + * @return The corresponding Universal Time, in milliseconds since 1.419 + * 1 Jan 1970, GMT. 1.420 + */ 1.421 +double CalendarAstronomer::lstToUT(double lst) { 1.422 + // Convert to local mean time 1.423 + double lt = normalize((lst - getSiderealOffset()) * 0.9972695663, 24); 1.424 + 1.425 + // Then find local midnight on this day 1.426 + double base = (DAY_MS * ClockMath::floorDivide(fTime + fGmtOffset,(double)DAY_MS)) - fGmtOffset; 1.427 + 1.428 + //out(" lt =" + lt + " hours"); 1.429 + //out(" base=" + new Date(base)); 1.430 + 1.431 + return base + (long)(lt * HOUR_MS); 1.432 +} 1.433 + 1.434 + 1.435 +//------------------------------------------------------------------------- 1.436 +// Coordinate transformations, all based on the current time of this object 1.437 +//------------------------------------------------------------------------- 1.438 + 1.439 +/** 1.440 + * Convert from ecliptic to equatorial coordinates. 1.441 + * 1.442 + * @param ecliptic A point in the sky in ecliptic coordinates. 1.443 + * @return The corresponding point in equatorial coordinates. 1.444 + * @internal 1.445 + * @deprecated ICU 2.4. This class may be removed or modified. 1.446 + */ 1.447 +CalendarAstronomer::Equatorial& CalendarAstronomer::eclipticToEquatorial(CalendarAstronomer::Equatorial& result, const CalendarAstronomer::Ecliptic& ecliptic) 1.448 +{ 1.449 + return eclipticToEquatorial(result, ecliptic.longitude, ecliptic.latitude); 1.450 +} 1.451 + 1.452 +/** 1.453 + * Convert from ecliptic to equatorial coordinates. 1.454 + * 1.455 + * @param eclipLong The ecliptic longitude 1.456 + * @param eclipLat The ecliptic latitude 1.457 + * 1.458 + * @return The corresponding point in equatorial coordinates. 1.459 + * @internal 1.460 + * @deprecated ICU 2.4. This class may be removed or modified. 1.461 + */ 1.462 +CalendarAstronomer::Equatorial& CalendarAstronomer::eclipticToEquatorial(CalendarAstronomer::Equatorial& result, double eclipLong, double eclipLat) 1.463 +{ 1.464 + // See page 42 of "Practial Astronomy with your Calculator", 1.465 + // by Peter Duffet-Smith, for details on the algorithm. 1.466 + 1.467 + double obliq = eclipticObliquity(); 1.468 + double sinE = ::sin(obliq); 1.469 + double cosE = cos(obliq); 1.470 + 1.471 + double sinL = ::sin(eclipLong); 1.472 + double cosL = cos(eclipLong); 1.473 + 1.474 + double sinB = ::sin(eclipLat); 1.475 + double cosB = cos(eclipLat); 1.476 + double tanB = tan(eclipLat); 1.477 + 1.478 + result.set(atan2(sinL*cosE - tanB*sinE, cosL), 1.479 + asin(sinB*cosE + cosB*sinE*sinL) ); 1.480 + return result; 1.481 +} 1.482 + 1.483 +/** 1.484 + * Convert from ecliptic longitude to equatorial coordinates. 1.485 + * 1.486 + * @param eclipLong The ecliptic longitude 1.487 + * 1.488 + * @return The corresponding point in equatorial coordinates. 1.489 + * @internal 1.490 + * @deprecated ICU 2.4. This class may be removed or modified. 1.491 + */ 1.492 +CalendarAstronomer::Equatorial& CalendarAstronomer::eclipticToEquatorial(CalendarAstronomer::Equatorial& result, double eclipLong) 1.493 +{ 1.494 + return eclipticToEquatorial(result, eclipLong, 0); // TODO: optimize 1.495 +} 1.496 + 1.497 +/** 1.498 + * @internal 1.499 + * @deprecated ICU 2.4. This class may be removed or modified. 1.500 + */ 1.501 +CalendarAstronomer::Horizon& CalendarAstronomer::eclipticToHorizon(CalendarAstronomer::Horizon& result, double eclipLong) 1.502 +{ 1.503 + Equatorial equatorial; 1.504 + eclipticToEquatorial(equatorial, eclipLong); 1.505 + 1.506 + double H = getLocalSidereal()*CalendarAstronomer::PI/12 - equatorial.ascension; // Hour-angle 1.507 + 1.508 + double sinH = ::sin(H); 1.509 + double cosH = cos(H); 1.510 + double sinD = ::sin(equatorial.declination); 1.511 + double cosD = cos(equatorial.declination); 1.512 + double sinL = ::sin(fLatitude); 1.513 + double cosL = cos(fLatitude); 1.514 + 1.515 + double altitude = asin(sinD*sinL + cosD*cosL*cosH); 1.516 + double azimuth = atan2(-cosD*cosL*sinH, sinD - sinL * ::sin(altitude)); 1.517 + 1.518 + result.set(azimuth, altitude); 1.519 + return result; 1.520 +} 1.521 + 1.522 + 1.523 +//------------------------------------------------------------------------- 1.524 +// The Sun 1.525 +//------------------------------------------------------------------------- 1.526 + 1.527 +// 1.528 +// Parameters of the Sun's orbit as of the epoch Jan 0.0 1990 1.529 +// Angles are in radians (after multiplying by CalendarAstronomer::PI/180) 1.530 +// 1.531 +#define JD_EPOCH 2447891.5 // Julian day of epoch 1.532 + 1.533 +#define SUN_ETA_G (279.403303 * CalendarAstronomer::PI/180) // Ecliptic longitude at epoch 1.534 +#define SUN_OMEGA_G (282.768422 * CalendarAstronomer::PI/180) // Ecliptic longitude of perigee 1.535 +#define SUN_E 0.016713 // Eccentricity of orbit 1.536 +//double sunR0 1.495585e8 // Semi-major axis in KM 1.537 +//double sunTheta0 (0.533128 * CalendarAstronomer::PI/180) // Angular diameter at R0 1.538 + 1.539 +// The following three methods, which compute the sun parameters 1.540 +// given above for an arbitrary epoch (whatever time the object is 1.541 +// set to), make only a small difference as compared to using the 1.542 +// above constants. E.g., Sunset times might differ by ~12 1.543 +// seconds. Furthermore, the eta-g computation is befuddled by 1.544 +// Duffet-Smith's incorrect coefficients (p.86). I've corrected 1.545 +// the first-order coefficient but the others may be off too - no 1.546 +// way of knowing without consulting another source. 1.547 + 1.548 +// /** 1.549 +// * Return the sun's ecliptic longitude at perigee for the current time. 1.550 +// * See Duffett-Smith, p. 86. 1.551 +// * @return radians 1.552 +// */ 1.553 +// private double getSunOmegaG() { 1.554 +// double T = getJulianCentury(); 1.555 +// return (281.2208444 + (1.719175 + 0.000452778*T)*T) * DEG_RAD; 1.556 +// } 1.557 + 1.558 +// /** 1.559 +// * Return the sun's ecliptic longitude for the current time. 1.560 +// * See Duffett-Smith, p. 86. 1.561 +// * @return radians 1.562 +// */ 1.563 +// private double getSunEtaG() { 1.564 +// double T = getJulianCentury(); 1.565 +// //return (279.6966778 + (36000.76892 + 0.0003025*T)*T) * DEG_RAD; 1.566 +// // 1.567 +// // The above line is from Duffett-Smith, and yields manifestly wrong 1.568 +// // results. The below constant is derived empirically to match the 1.569 +// // constant he gives for the 1990 EPOCH. 1.570 +// // 1.571 +// return (279.6966778 + (-0.3262541582718024 + 0.0003025*T)*T) * DEG_RAD; 1.572 +// } 1.573 + 1.574 +// /** 1.575 +// * Return the sun's eccentricity of orbit for the current time. 1.576 +// * See Duffett-Smith, p. 86. 1.577 +// * @return double 1.578 +// */ 1.579 +// private double getSunE() { 1.580 +// double T = getJulianCentury(); 1.581 +// return 0.01675104 - (0.0000418 + 0.000000126*T)*T; 1.582 +// } 1.583 + 1.584 +/** 1.585 + * Find the "true anomaly" (longitude) of an object from 1.586 + * its mean anomaly and the eccentricity of its orbit. This uses 1.587 + * an iterative solution to Kepler's equation. 1.588 + * 1.589 + * @param meanAnomaly The object's longitude calculated as if it were in 1.590 + * a regular, circular orbit, measured in radians 1.591 + * from the point of perigee. 1.592 + * 1.593 + * @param eccentricity The eccentricity of the orbit 1.594 + * 1.595 + * @return The true anomaly (longitude) measured in radians 1.596 + */ 1.597 +static double trueAnomaly(double meanAnomaly, double eccentricity) 1.598 +{ 1.599 + // First, solve Kepler's equation iteratively 1.600 + // Duffett-Smith, p.90 1.601 + double delta; 1.602 + double E = meanAnomaly; 1.603 + do { 1.604 + delta = E - eccentricity * ::sin(E) - meanAnomaly; 1.605 + E = E - delta / (1 - eccentricity * ::cos(E)); 1.606 + } 1.607 + while (uprv_fabs(delta) > 1e-5); // epsilon = 1e-5 rad 1.608 + 1.609 + return 2.0 * ::atan( ::tan(E/2) * ::sqrt( (1+eccentricity) 1.610 + /(1-eccentricity) ) ); 1.611 +} 1.612 + 1.613 +/** 1.614 + * The longitude of the sun at the time specified by this object. 1.615 + * The longitude is measured in radians along the ecliptic 1.616 + * from the "first point of Aries," the point at which the ecliptic 1.617 + * crosses the earth's equatorial plane at the vernal equinox. 1.618 + * <p> 1.619 + * Currently, this method uses an approximation of the two-body Kepler's 1.620 + * equation for the earth and the sun. It does not take into account the 1.621 + * perturbations caused by the other planets, the moon, etc. 1.622 + * @internal 1.623 + * @deprecated ICU 2.4. This class may be removed or modified. 1.624 + */ 1.625 +double CalendarAstronomer::getSunLongitude() 1.626 +{ 1.627 + // See page 86 of "Practial Astronomy with your Calculator", 1.628 + // by Peter Duffet-Smith, for details on the algorithm. 1.629 + 1.630 + if (isINVALID(sunLongitude)) { 1.631 + getSunLongitude(getJulianDay(), sunLongitude, meanAnomalySun); 1.632 + } 1.633 + return sunLongitude; 1.634 +} 1.635 + 1.636 +/** 1.637 + * TODO Make this public when the entire class is package-private. 1.638 + */ 1.639 +/*public*/ void CalendarAstronomer::getSunLongitude(double jDay, double &longitude, double &meanAnomaly) 1.640 +{ 1.641 + // See page 86 of "Practial Astronomy with your Calculator", 1.642 + // by Peter Duffet-Smith, for details on the algorithm. 1.643 + 1.644 + double day = jDay - JD_EPOCH; // Days since epoch 1.645 + 1.646 + // Find the angular distance the sun in a fictitious 1.647 + // circular orbit has travelled since the epoch. 1.648 + double epochAngle = norm2PI(CalendarAstronomer_PI2/TROPICAL_YEAR*day); 1.649 + 1.650 + // The epoch wasn't at the sun's perigee; find the angular distance 1.651 + // since perigee, which is called the "mean anomaly" 1.652 + meanAnomaly = norm2PI(epochAngle + SUN_ETA_G - SUN_OMEGA_G); 1.653 + 1.654 + // Now find the "true anomaly", e.g. the real solar longitude 1.655 + // by solving Kepler's equation for an elliptical orbit 1.656 + // NOTE: The 3rd ed. of the book lists omega_g and eta_g in different 1.657 + // equations; omega_g is to be correct. 1.658 + longitude = norm2PI(trueAnomaly(meanAnomaly, SUN_E) + SUN_OMEGA_G); 1.659 +} 1.660 + 1.661 +/** 1.662 + * The position of the sun at this object's current date and time, 1.663 + * in equatorial coordinates. 1.664 + * @internal 1.665 + * @deprecated ICU 2.4. This class may be removed or modified. 1.666 + */ 1.667 +CalendarAstronomer::Equatorial& CalendarAstronomer::getSunPosition(CalendarAstronomer::Equatorial& result) { 1.668 + return eclipticToEquatorial(result, getSunLongitude(), 0); 1.669 +} 1.670 + 1.671 + 1.672 +/** 1.673 + * Constant representing the vernal equinox. 1.674 + * For use with {@link #getSunTime getSunTime}. 1.675 + * Note: In this case, "vernal" refers to the northern hemisphere's seasons. 1.676 + * @internal 1.677 + * @deprecated ICU 2.4. This class may be removed or modified. 1.678 + */ 1.679 +/*double CalendarAstronomer::VERNAL_EQUINOX() { 1.680 + return 0; 1.681 +}*/ 1.682 + 1.683 +/** 1.684 + * Constant representing the summer solstice. 1.685 + * For use with {@link #getSunTime getSunTime}. 1.686 + * Note: In this case, "summer" refers to the northern hemisphere's seasons. 1.687 + * @internal 1.688 + * @deprecated ICU 2.4. This class may be removed or modified. 1.689 + */ 1.690 +double CalendarAstronomer::SUMMER_SOLSTICE() { 1.691 + return (CalendarAstronomer::PI/2); 1.692 +} 1.693 + 1.694 +/** 1.695 + * Constant representing the autumnal equinox. 1.696 + * For use with {@link #getSunTime getSunTime}. 1.697 + * Note: In this case, "autumn" refers to the northern hemisphere's seasons. 1.698 + * @internal 1.699 + * @deprecated ICU 2.4. This class may be removed or modified. 1.700 + */ 1.701 +/*double CalendarAstronomer::AUTUMN_EQUINOX() { 1.702 + return (CalendarAstronomer::PI); 1.703 +}*/ 1.704 + 1.705 +/** 1.706 + * Constant representing the winter solstice. 1.707 + * For use with {@link #getSunTime getSunTime}. 1.708 + * Note: In this case, "winter" refers to the northern hemisphere's seasons. 1.709 + * @internal 1.710 + * @deprecated ICU 2.4. This class may be removed or modified. 1.711 + */ 1.712 +double CalendarAstronomer::WINTER_SOLSTICE() { 1.713 + return ((CalendarAstronomer::PI*3)/2); 1.714 +} 1.715 + 1.716 +CalendarAstronomer::AngleFunc::~AngleFunc() {} 1.717 + 1.718 +/** 1.719 + * Find the next time at which the sun's ecliptic longitude will have 1.720 + * the desired value. 1.721 + * @internal 1.722 + * @deprecated ICU 2.4. This class may be removed or modified. 1.723 + */ 1.724 +class SunTimeAngleFunc : public CalendarAstronomer::AngleFunc { 1.725 +public: 1.726 + virtual ~SunTimeAngleFunc(); 1.727 + virtual double eval(CalendarAstronomer& a) { return a.getSunLongitude(); } 1.728 +}; 1.729 + 1.730 +SunTimeAngleFunc::~SunTimeAngleFunc() {} 1.731 + 1.732 +UDate CalendarAstronomer::getSunTime(double desired, UBool next) 1.733 +{ 1.734 + SunTimeAngleFunc func; 1.735 + return timeOfAngle( func, 1.736 + desired, 1.737 + TROPICAL_YEAR, 1.738 + MINUTE_MS, 1.739 + next); 1.740 +} 1.741 + 1.742 +CalendarAstronomer::CoordFunc::~CoordFunc() {} 1.743 + 1.744 +class RiseSetCoordFunc : public CalendarAstronomer::CoordFunc { 1.745 +public: 1.746 + virtual ~RiseSetCoordFunc(); 1.747 + virtual void eval(CalendarAstronomer::Equatorial& result, CalendarAstronomer&a) { a.getSunPosition(result); } 1.748 +}; 1.749 + 1.750 +RiseSetCoordFunc::~RiseSetCoordFunc() {} 1.751 + 1.752 +UDate CalendarAstronomer::getSunRiseSet(UBool rise) 1.753 +{ 1.754 + UDate t0 = fTime; 1.755 + 1.756 + // Make a rough guess: 6am or 6pm local time on the current day 1.757 + double noon = ClockMath::floorDivide(fTime + fGmtOffset, (double)DAY_MS)*DAY_MS - fGmtOffset + (12*HOUR_MS); 1.758 + 1.759 + U_DEBUG_ASTRO_MSG(("Noon=%.2lf, %sL, gmtoff %.2lf\n", noon, debug_astro_date(noon+fGmtOffset), fGmtOffset)); 1.760 + setTime(noon + ((rise ? -6 : 6) * HOUR_MS)); 1.761 + U_DEBUG_ASTRO_MSG(("added %.2lf ms as a guess,\n", ((rise ? -6. : 6.) * HOUR_MS))); 1.762 + 1.763 + RiseSetCoordFunc func; 1.764 + double t = riseOrSet(func, 1.765 + rise, 1.766 + .533 * DEG_RAD, // Angular Diameter 1.767 + 34. /60.0 * DEG_RAD, // Refraction correction 1.768 + MINUTE_MS / 12.); // Desired accuracy 1.769 + 1.770 + setTime(t0); 1.771 + return t; 1.772 +} 1.773 + 1.774 +// Commented out - currently unused. ICU 2.6, Alan 1.775 +// //------------------------------------------------------------------------- 1.776 +// // Alternate Sun Rise/Set 1.777 +// // See Duffett-Smith p.93 1.778 +// //------------------------------------------------------------------------- 1.779 +// 1.780 +// // This yields worse results (as compared to USNO data) than getSunRiseSet(). 1.781 +// /** 1.782 +// * TODO Make this when the entire class is package-private. 1.783 +// */ 1.784 +// /*public*/ long getSunRiseSet2(boolean rise) { 1.785 +// // 1. Calculate coordinates of the sun's center for midnight 1.786 +// double jd = uprv_floor(getJulianDay() - 0.5) + 0.5; 1.787 +// double[] sl = getSunLongitude(jd);// double lambda1 = sl[0]; 1.788 +// Equatorial pos1 = eclipticToEquatorial(lambda1, 0); 1.789 +// 1.790 +// // 2. Add ... to lambda to get position 24 hours later 1.791 +// double lambda2 = lambda1 + 0.985647*DEG_RAD; 1.792 +// Equatorial pos2 = eclipticToEquatorial(lambda2, 0); 1.793 +// 1.794 +// // 3. Calculate LSTs of rising and setting for these two positions 1.795 +// double tanL = ::tan(fLatitude); 1.796 +// double H = ::acos(-tanL * ::tan(pos1.declination)); 1.797 +// double lst1r = (CalendarAstronomer_PI2 + pos1.ascension - H) * 24 / CalendarAstronomer_PI2; 1.798 +// double lst1s = (pos1.ascension + H) * 24 / CalendarAstronomer_PI2; 1.799 +// H = ::acos(-tanL * ::tan(pos2.declination)); 1.800 +// double lst2r = (CalendarAstronomer_PI2-H + pos2.ascension ) * 24 / CalendarAstronomer_PI2; 1.801 +// double lst2s = (H + pos2.ascension ) * 24 / CalendarAstronomer_PI2; 1.802 +// if (lst1r > 24) lst1r -= 24; 1.803 +// if (lst1s > 24) lst1s -= 24; 1.804 +// if (lst2r > 24) lst2r -= 24; 1.805 +// if (lst2s > 24) lst2s -= 24; 1.806 +// 1.807 +// // 4. Convert LSTs to GSTs. If GST1 > GST2, add 24 to GST2. 1.808 +// double gst1r = lstToGst(lst1r); 1.809 +// double gst1s = lstToGst(lst1s); 1.810 +// double gst2r = lstToGst(lst2r); 1.811 +// double gst2s = lstToGst(lst2s); 1.812 +// if (gst1r > gst2r) gst2r += 24; 1.813 +// if (gst1s > gst2s) gst2s += 24; 1.814 +// 1.815 +// // 5. Calculate GST at 0h UT of this date 1.816 +// double t00 = utToGst(0); 1.817 +// 1.818 +// // 6. Calculate GST at 0h on the observer's longitude 1.819 +// double offset = ::round(fLongitude*12/PI); // p.95 step 6; he _rounds_ to nearest 15 deg. 1.820 +// double t00p = t00 - offset*1.002737909; 1.821 +// if (t00p < 0) t00p += 24; // do NOT normalize 1.822 +// 1.823 +// // 7. Adjust 1.824 +// if (gst1r < t00p) { 1.825 +// gst1r += 24; 1.826 +// gst2r += 24; 1.827 +// } 1.828 +// if (gst1s < t00p) { 1.829 +// gst1s += 24; 1.830 +// gst2s += 24; 1.831 +// } 1.832 +// 1.833 +// // 8. 1.834 +// double gstr = (24.07*gst1r-t00*(gst2r-gst1r))/(24.07+gst1r-gst2r); 1.835 +// double gsts = (24.07*gst1s-t00*(gst2s-gst1s))/(24.07+gst1s-gst2s); 1.836 +// 1.837 +// // 9. Correct for parallax, refraction, and sun's diameter 1.838 +// double dec = (pos1.declination + pos2.declination) / 2; 1.839 +// double psi = ::acos(sin(fLatitude) / cos(dec)); 1.840 +// double x = 0.830725 * DEG_RAD; // parallax+refraction+diameter 1.841 +// double y = ::asin(sin(x) / ::sin(psi)) * RAD_DEG; 1.842 +// double delta_t = 240 * y / cos(dec) / 3600; // hours 1.843 +// 1.844 +// // 10. Add correction to GSTs, subtract from GSTr 1.845 +// gstr -= delta_t; 1.846 +// gsts += delta_t; 1.847 +// 1.848 +// // 11. Convert GST to UT and then to local civil time 1.849 +// double ut = gstToUt(rise ? gstr : gsts); 1.850 +// //System.out.println((rise?"rise=":"set=") + ut + ", delta_t=" + delta_t); 1.851 +// long midnight = DAY_MS * (time / DAY_MS); // Find UT midnight on this day 1.852 +// return midnight + (long) (ut * 3600000); 1.853 +// } 1.854 + 1.855 +// Commented out - currently unused. ICU 2.6, Alan 1.856 +// /** 1.857 +// * Convert local sidereal time to Greenwich sidereal time. 1.858 +// * Section 15. Duffett-Smith p.21 1.859 +// * @param lst in hours (0..24) 1.860 +// * @return GST in hours (0..24) 1.861 +// */ 1.862 +// double lstToGst(double lst) { 1.863 +// double delta = fLongitude * 24 / CalendarAstronomer_PI2; 1.864 +// return normalize(lst - delta, 24); 1.865 +// } 1.866 + 1.867 +// Commented out - currently unused. ICU 2.6, Alan 1.868 +// /** 1.869 +// * Convert UT to GST on this date. 1.870 +// * Section 12. Duffett-Smith p.17 1.871 +// * @param ut in hours 1.872 +// * @return GST in hours 1.873 +// */ 1.874 +// double utToGst(double ut) { 1.875 +// return normalize(getT0() + ut*1.002737909, 24); 1.876 +// } 1.877 + 1.878 +// Commented out - currently unused. ICU 2.6, Alan 1.879 +// /** 1.880 +// * Convert GST to UT on this date. 1.881 +// * Section 13. Duffett-Smith p.18 1.882 +// * @param gst in hours 1.883 +// * @return UT in hours 1.884 +// */ 1.885 +// double gstToUt(double gst) { 1.886 +// return normalize(gst - getT0(), 24) * 0.9972695663; 1.887 +// } 1.888 + 1.889 +// Commented out - currently unused. ICU 2.6, Alan 1.890 +// double getT0() { 1.891 +// // Common computation for UT <=> GST 1.892 +// 1.893 +// // Find JD for 0h UT 1.894 +// double jd = uprv_floor(getJulianDay() - 0.5) + 0.5; 1.895 +// 1.896 +// double s = jd - 2451545.0; 1.897 +// double t = s / 36525.0; 1.898 +// double t0 = 6.697374558 + (2400.051336 + 0.000025862*t)*t; 1.899 +// return t0; 1.900 +// } 1.901 + 1.902 +// Commented out - currently unused. ICU 2.6, Alan 1.903 +// //------------------------------------------------------------------------- 1.904 +// // Alternate Sun Rise/Set 1.905 +// // See sci.astro FAQ 1.906 +// // http://www.faqs.org/faqs/astronomy/faq/part3/section-5.html 1.907 +// //------------------------------------------------------------------------- 1.908 +// 1.909 +// // Note: This method appears to produce inferior accuracy as 1.910 +// // compared to getSunRiseSet(). 1.911 +// 1.912 +// /** 1.913 +// * TODO Make this when the entire class is package-private. 1.914 +// */ 1.915 +// /*public*/ long getSunRiseSet3(boolean rise) { 1.916 +// 1.917 +// // Compute day number for 0.0 Jan 2000 epoch 1.918 +// double d = (double)(time - EPOCH_2000_MS) / DAY_MS; 1.919 +// 1.920 +// // Now compute the Local Sidereal Time, LST: 1.921 +// // 1.922 +// double LST = 98.9818 + 0.985647352 * d + /*UT*15 + long*/ 1.923 +// fLongitude*RAD_DEG; 1.924 +// // 1.925 +// // (east long. positive). Note that LST is here expressed in degrees, 1.926 +// // where 15 degrees corresponds to one hour. Since LST really is an angle, 1.927 +// // it's convenient to use one unit---degrees---throughout. 1.928 +// 1.929 +// // COMPUTING THE SUN'S POSITION 1.930 +// // ---------------------------- 1.931 +// // 1.932 +// // To be able to compute the Sun's rise/set times, you need to be able to 1.933 +// // compute the Sun's position at any time. First compute the "day 1.934 +// // number" d as outlined above, for the desired moment. Next compute: 1.935 +// // 1.936 +// double oblecl = 23.4393 - 3.563E-7 * d; 1.937 +// // 1.938 +// double w = 282.9404 + 4.70935E-5 * d; 1.939 +// double M = 356.0470 + 0.9856002585 * d; 1.940 +// double e = 0.016709 - 1.151E-9 * d; 1.941 +// // 1.942 +// // This is the obliquity of the ecliptic, plus some of the elements of 1.943 +// // the Sun's apparent orbit (i.e., really the Earth's orbit): w = 1.944 +// // argument of perihelion, M = mean anomaly, e = eccentricity. 1.945 +// // Semi-major axis is here assumed to be exactly 1.0 (while not strictly 1.946 +// // true, this is still an accurate approximation). Next compute E, the 1.947 +// // eccentric anomaly: 1.948 +// // 1.949 +// double E = M + e*(180/PI) * ::sin(M*DEG_RAD) * ( 1.0 + e*cos(M*DEG_RAD) ); 1.950 +// // 1.951 +// // where E and M are in degrees. This is it---no further iterations are 1.952 +// // needed because we know e has a sufficiently small value. Next compute 1.953 +// // the true anomaly, v, and the distance, r: 1.954 +// // 1.955 +// /* r * cos(v) = */ double A = cos(E*DEG_RAD) - e; 1.956 +// /* r * ::sin(v) = */ double B = ::sqrt(1 - e*e) * ::sin(E*DEG_RAD); 1.957 +// // 1.958 +// // and 1.959 +// // 1.960 +// // r = sqrt( A*A + B*B ) 1.961 +// double v = ::atan2( B, A )*RAD_DEG; 1.962 +// // 1.963 +// // The Sun's true longitude, slon, can now be computed: 1.964 +// // 1.965 +// double slon = v + w; 1.966 +// // 1.967 +// // Since the Sun is always at the ecliptic (or at least very very close to 1.968 +// // it), we can use simplified formulae to convert slon (the Sun's ecliptic 1.969 +// // longitude) to sRA and sDec (the Sun's RA and Dec): 1.970 +// // 1.971 +// // ::sin(slon) * cos(oblecl) 1.972 +// // tan(sRA) = ------------------------- 1.973 +// // cos(slon) 1.974 +// // 1.975 +// // ::sin(sDec) = ::sin(oblecl) * ::sin(slon) 1.976 +// // 1.977 +// // As was the case when computing az, the Azimuth, if possible use an 1.978 +// // atan2() function to compute sRA. 1.979 +// 1.980 +// double sRA = ::atan2(sin(slon*DEG_RAD) * cos(oblecl*DEG_RAD), cos(slon*DEG_RAD))*RAD_DEG; 1.981 +// 1.982 +// double sin_sDec = ::sin(oblecl*DEG_RAD) * ::sin(slon*DEG_RAD); 1.983 +// double sDec = ::asin(sin_sDec)*RAD_DEG; 1.984 +// 1.985 +// // COMPUTING RISE AND SET TIMES 1.986 +// // ---------------------------- 1.987 +// // 1.988 +// // To compute when an object rises or sets, you must compute when it 1.989 +// // passes the meridian and the HA of rise/set. Then the rise time is 1.990 +// // the meridian time minus HA for rise/set, and the set time is the 1.991 +// // meridian time plus the HA for rise/set. 1.992 +// // 1.993 +// // To find the meridian time, compute the Local Sidereal Time at 0h local 1.994 +// // time (or 0h UT if you prefer to work in UT) as outlined above---name 1.995 +// // that quantity LST0. The Meridian Time, MT, will now be: 1.996 +// // 1.997 +// // MT = RA - LST0 1.998 +// double MT = normalize(sRA - LST, 360); 1.999 +// // 1.1000 +// // where "RA" is the object's Right Ascension (in degrees!). If negative, 1.1001 +// // add 360 deg to MT. If the object is the Sun, leave the time as it is, 1.1002 +// // but if it's stellar, multiply MT by 365.2422/366.2422, to convert from 1.1003 +// // sidereal to solar time. Now, compute HA for rise/set, name that 1.1004 +// // quantity HA0: 1.1005 +// // 1.1006 +// // ::sin(h0) - ::sin(lat) * ::sin(Dec) 1.1007 +// // cos(HA0) = --------------------------------- 1.1008 +// // cos(lat) * cos(Dec) 1.1009 +// // 1.1010 +// // where h0 is the altitude selected to represent rise/set. For a purely 1.1011 +// // mathematical horizon, set h0 = 0 and simplify to: 1.1012 +// // 1.1013 +// // cos(HA0) = - tan(lat) * tan(Dec) 1.1014 +// // 1.1015 +// // If you want to account for refraction on the atmosphere, set h0 = -35/60 1.1016 +// // degrees (-35 arc minutes), and if you want to compute the rise/set times 1.1017 +// // for the Sun's upper limb, set h0 = -50/60 (-50 arc minutes). 1.1018 +// // 1.1019 +// double h0 = -50/60 * DEG_RAD; 1.1020 +// 1.1021 +// double HA0 = ::acos( 1.1022 +// (sin(h0) - ::sin(fLatitude) * sin_sDec) / 1.1023 +// (cos(fLatitude) * cos(sDec*DEG_RAD)))*RAD_DEG; 1.1024 +// 1.1025 +// // When HA0 has been computed, leave it as it is for the Sun but multiply 1.1026 +// // by 365.2422/366.2422 for stellar objects, to convert from sidereal to 1.1027 +// // solar time. Finally compute: 1.1028 +// // 1.1029 +// // Rise time = MT - HA0 1.1030 +// // Set time = MT + HA0 1.1031 +// // 1.1032 +// // convert the times from degrees to hours by dividing by 15. 1.1033 +// // 1.1034 +// // If you'd like to check that your calculations are accurate or just 1.1035 +// // need a quick result, check the USNO's Sun or Moon Rise/Set Table, 1.1036 +// // <URL:http://aa.usno.navy.mil/AA/data/docs/RS_OneYear.html>. 1.1037 +// 1.1038 +// double result = MT + (rise ? -HA0 : HA0); // in degrees 1.1039 +// 1.1040 +// // Find UT midnight on this day 1.1041 +// long midnight = DAY_MS * (time / DAY_MS); 1.1042 +// 1.1043 +// return midnight + (long) (result * 3600000 / 15); 1.1044 +// } 1.1045 + 1.1046 +//------------------------------------------------------------------------- 1.1047 +// The Moon 1.1048 +//------------------------------------------------------------------------- 1.1049 + 1.1050 +#define moonL0 (318.351648 * CalendarAstronomer::PI/180 ) // Mean long. at epoch 1.1051 +#define moonP0 ( 36.340410 * CalendarAstronomer::PI/180 ) // Mean long. of perigee 1.1052 +#define moonN0 ( 318.510107 * CalendarAstronomer::PI/180 ) // Mean long. of node 1.1053 +#define moonI ( 5.145366 * CalendarAstronomer::PI/180 ) // Inclination of orbit 1.1054 +#define moonE ( 0.054900 ) // Eccentricity of orbit 1.1055 + 1.1056 +// These aren't used right now 1.1057 +#define moonA ( 3.84401e5 ) // semi-major axis (km) 1.1058 +#define moonT0 ( 0.5181 * CalendarAstronomer::PI/180 ) // Angular size at distance A 1.1059 +#define moonPi ( 0.9507 * CalendarAstronomer::PI/180 ) // Parallax at distance A 1.1060 + 1.1061 +/** 1.1062 + * The position of the moon at the time set on this 1.1063 + * object, in equatorial coordinates. 1.1064 + * @internal 1.1065 + * @deprecated ICU 2.4. This class may be removed or modified. 1.1066 + */ 1.1067 +const CalendarAstronomer::Equatorial& CalendarAstronomer::getMoonPosition() 1.1068 +{ 1.1069 + // 1.1070 + // See page 142 of "Practial Astronomy with your Calculator", 1.1071 + // by Peter Duffet-Smith, for details on the algorithm. 1.1072 + // 1.1073 + if (moonPositionSet == FALSE) { 1.1074 + // Calculate the solar longitude. Has the side effect of 1.1075 + // filling in "meanAnomalySun" as well. 1.1076 + getSunLongitude(); 1.1077 + 1.1078 + // 1.1079 + // Find the # of days since the epoch of our orbital parameters. 1.1080 + // TODO: Convert the time of day portion into ephemeris time 1.1081 + // 1.1082 + double day = getJulianDay() - JD_EPOCH; // Days since epoch 1.1083 + 1.1084 + // Calculate the mean longitude and anomaly of the moon, based on 1.1085 + // a circular orbit. Similar to the corresponding solar calculation. 1.1086 + double meanLongitude = norm2PI(13.1763966*PI/180*day + moonL0); 1.1087 + meanAnomalyMoon = norm2PI(meanLongitude - 0.1114041*PI/180 * day - moonP0); 1.1088 + 1.1089 + // 1.1090 + // Calculate the following corrections: 1.1091 + // Evection: the sun's gravity affects the moon's eccentricity 1.1092 + // Annual Eqn: variation in the effect due to earth-sun distance 1.1093 + // A3: correction factor (for ???) 1.1094 + // 1.1095 + double evection = 1.2739*PI/180 * ::sin(2 * (meanLongitude - sunLongitude) 1.1096 + - meanAnomalyMoon); 1.1097 + double annual = 0.1858*PI/180 * ::sin(meanAnomalySun); 1.1098 + double a3 = 0.3700*PI/180 * ::sin(meanAnomalySun); 1.1099 + 1.1100 + meanAnomalyMoon += evection - annual - a3; 1.1101 + 1.1102 + // 1.1103 + // More correction factors: 1.1104 + // center equation of the center correction 1.1105 + // a4 yet another error correction (???) 1.1106 + // 1.1107 + // TODO: Skip the equation of the center correction and solve Kepler's eqn? 1.1108 + // 1.1109 + double center = 6.2886*PI/180 * ::sin(meanAnomalyMoon); 1.1110 + double a4 = 0.2140*PI/180 * ::sin(2 * meanAnomalyMoon); 1.1111 + 1.1112 + // Now find the moon's corrected longitude 1.1113 + moonLongitude = meanLongitude + evection + center - annual + a4; 1.1114 + 1.1115 + // 1.1116 + // And finally, find the variation, caused by the fact that the sun's 1.1117 + // gravitational pull on the moon varies depending on which side of 1.1118 + // the earth the moon is on 1.1119 + // 1.1120 + double variation = 0.6583*CalendarAstronomer::PI/180 * ::sin(2*(moonLongitude - sunLongitude)); 1.1121 + 1.1122 + moonLongitude += variation; 1.1123 + 1.1124 + // 1.1125 + // What we've calculated so far is the moon's longitude in the plane 1.1126 + // of its own orbit. Now map to the ecliptic to get the latitude 1.1127 + // and longitude. First we need to find the longitude of the ascending 1.1128 + // node, the position on the ecliptic where it is crossed by the moon's 1.1129 + // orbit as it crosses from the southern to the northern hemisphere. 1.1130 + // 1.1131 + double nodeLongitude = norm2PI(moonN0 - 0.0529539*PI/180 * day); 1.1132 + 1.1133 + nodeLongitude -= 0.16*PI/180 * ::sin(meanAnomalySun); 1.1134 + 1.1135 + double y = ::sin(moonLongitude - nodeLongitude); 1.1136 + double x = cos(moonLongitude - nodeLongitude); 1.1137 + 1.1138 + moonEclipLong = ::atan2(y*cos(moonI), x) + nodeLongitude; 1.1139 + double moonEclipLat = ::asin(y * ::sin(moonI)); 1.1140 + 1.1141 + eclipticToEquatorial(moonPosition, moonEclipLong, moonEclipLat); 1.1142 + moonPositionSet = TRUE; 1.1143 + } 1.1144 + return moonPosition; 1.1145 +} 1.1146 + 1.1147 +/** 1.1148 + * The "age" of the moon at the time specified in this object. 1.1149 + * This is really the angle between the 1.1150 + * current ecliptic longitudes of the sun and the moon, 1.1151 + * measured in radians. 1.1152 + * 1.1153 + * @see #getMoonPhase 1.1154 + * @internal 1.1155 + * @deprecated ICU 2.4. This class may be removed or modified. 1.1156 + */ 1.1157 +double CalendarAstronomer::getMoonAge() { 1.1158 + // See page 147 of "Practial Astronomy with your Calculator", 1.1159 + // by Peter Duffet-Smith, for details on the algorithm. 1.1160 + // 1.1161 + // Force the moon's position to be calculated. We're going to use 1.1162 + // some the intermediate results cached during that calculation. 1.1163 + // 1.1164 + getMoonPosition(); 1.1165 + 1.1166 + return norm2PI(moonEclipLong - sunLongitude); 1.1167 +} 1.1168 + 1.1169 +/** 1.1170 + * Calculate the phase of the moon at the time set in this object. 1.1171 + * The returned phase is a <code>double</code> in the range 1.1172 + * <code>0 <= phase < 1</code>, interpreted as follows: 1.1173 + * <ul> 1.1174 + * <li>0.00: New moon 1.1175 + * <li>0.25: First quarter 1.1176 + * <li>0.50: Full moon 1.1177 + * <li>0.75: Last quarter 1.1178 + * </ul> 1.1179 + * 1.1180 + * @see #getMoonAge 1.1181 + * @internal 1.1182 + * @deprecated ICU 2.4. This class may be removed or modified. 1.1183 + */ 1.1184 +double CalendarAstronomer::getMoonPhase() { 1.1185 + // See page 147 of "Practial Astronomy with your Calculator", 1.1186 + // by Peter Duffet-Smith, for details on the algorithm. 1.1187 + return 0.5 * (1 - cos(getMoonAge())); 1.1188 +} 1.1189 + 1.1190 +/** 1.1191 + * Constant representing a new moon. 1.1192 + * For use with {@link #getMoonTime getMoonTime} 1.1193 + * @internal 1.1194 + * @deprecated ICU 2.4. This class may be removed or modified. 1.1195 + */ 1.1196 +const CalendarAstronomer::MoonAge CalendarAstronomer::NEW_MOON() { 1.1197 + return CalendarAstronomer::MoonAge(0); 1.1198 +} 1.1199 + 1.1200 +/** 1.1201 + * Constant representing the moon's first quarter. 1.1202 + * For use with {@link #getMoonTime getMoonTime} 1.1203 + * @internal 1.1204 + * @deprecated ICU 2.4. This class may be removed or modified. 1.1205 + */ 1.1206 +/*const CalendarAstronomer::MoonAge CalendarAstronomer::FIRST_QUARTER() { 1.1207 + return CalendarAstronomer::MoonAge(CalendarAstronomer::PI/2); 1.1208 +}*/ 1.1209 + 1.1210 +/** 1.1211 + * Constant representing a full moon. 1.1212 + * For use with {@link #getMoonTime getMoonTime} 1.1213 + * @internal 1.1214 + * @deprecated ICU 2.4. This class may be removed or modified. 1.1215 + */ 1.1216 +const CalendarAstronomer::MoonAge CalendarAstronomer::FULL_MOON() { 1.1217 + return CalendarAstronomer::MoonAge(CalendarAstronomer::PI); 1.1218 +} 1.1219 +/** 1.1220 + * Constant representing the moon's last quarter. 1.1221 + * For use with {@link #getMoonTime getMoonTime} 1.1222 + * @internal 1.1223 + * @deprecated ICU 2.4. This class may be removed or modified. 1.1224 + */ 1.1225 + 1.1226 +class MoonTimeAngleFunc : public CalendarAstronomer::AngleFunc { 1.1227 +public: 1.1228 + virtual ~MoonTimeAngleFunc(); 1.1229 + virtual double eval(CalendarAstronomer&a) { return a.getMoonAge(); } 1.1230 +}; 1.1231 + 1.1232 +MoonTimeAngleFunc::~MoonTimeAngleFunc() {} 1.1233 + 1.1234 +/*const CalendarAstronomer::MoonAge CalendarAstronomer::LAST_QUARTER() { 1.1235 + return CalendarAstronomer::MoonAge((CalendarAstronomer::PI*3)/2); 1.1236 +}*/ 1.1237 + 1.1238 +/** 1.1239 + * Find the next or previous time at which the Moon's ecliptic 1.1240 + * longitude will have the desired value. 1.1241 + * <p> 1.1242 + * @param desired The desired longitude. 1.1243 + * @param next <tt>true</tt> if the next occurrance of the phase 1.1244 + * is desired, <tt>false</tt> for the previous occurrance. 1.1245 + * @internal 1.1246 + * @deprecated ICU 2.4. This class may be removed or modified. 1.1247 + */ 1.1248 +UDate CalendarAstronomer::getMoonTime(double desired, UBool next) 1.1249 +{ 1.1250 + MoonTimeAngleFunc func; 1.1251 + return timeOfAngle( func, 1.1252 + desired, 1.1253 + SYNODIC_MONTH, 1.1254 + MINUTE_MS, 1.1255 + next); 1.1256 +} 1.1257 + 1.1258 +/** 1.1259 + * Find the next or previous time at which the moon will be in the 1.1260 + * desired phase. 1.1261 + * <p> 1.1262 + * @param desired The desired phase of the moon. 1.1263 + * @param next <tt>true</tt> if the next occurrance of the phase 1.1264 + * is desired, <tt>false</tt> for the previous occurrance. 1.1265 + * @internal 1.1266 + * @deprecated ICU 2.4. This class may be removed or modified. 1.1267 + */ 1.1268 +UDate CalendarAstronomer::getMoonTime(const CalendarAstronomer::MoonAge& desired, UBool next) { 1.1269 + return getMoonTime(desired.value, next); 1.1270 +} 1.1271 + 1.1272 +class MoonRiseSetCoordFunc : public CalendarAstronomer::CoordFunc { 1.1273 +public: 1.1274 + virtual ~MoonRiseSetCoordFunc(); 1.1275 + virtual void eval(CalendarAstronomer::Equatorial& result, CalendarAstronomer&a) { result = a.getMoonPosition(); } 1.1276 +}; 1.1277 + 1.1278 +MoonRiseSetCoordFunc::~MoonRiseSetCoordFunc() {} 1.1279 + 1.1280 +/** 1.1281 + * Returns the time (GMT) of sunrise or sunset on the local date to which 1.1282 + * this calendar is currently set. 1.1283 + * @internal 1.1284 + * @deprecated ICU 2.4. This class may be removed or modified. 1.1285 + */ 1.1286 +UDate CalendarAstronomer::getMoonRiseSet(UBool rise) 1.1287 +{ 1.1288 + MoonRiseSetCoordFunc func; 1.1289 + return riseOrSet(func, 1.1290 + rise, 1.1291 + .533 * DEG_RAD, // Angular Diameter 1.1292 + 34 /60.0 * DEG_RAD, // Refraction correction 1.1293 + MINUTE_MS); // Desired accuracy 1.1294 +} 1.1295 + 1.1296 +//------------------------------------------------------------------------- 1.1297 +// Interpolation methods for finding the time at which a given event occurs 1.1298 +//------------------------------------------------------------------------- 1.1299 + 1.1300 +UDate CalendarAstronomer::timeOfAngle(AngleFunc& func, double desired, 1.1301 + double periodDays, double epsilon, UBool next) 1.1302 +{ 1.1303 + // Find the value of the function at the current time 1.1304 + double lastAngle = func.eval(*this); 1.1305 + 1.1306 + // Find out how far we are from the desired angle 1.1307 + double deltaAngle = norm2PI(desired - lastAngle) ; 1.1308 + 1.1309 + // Using the average period, estimate the next (or previous) time at 1.1310 + // which the desired angle occurs. 1.1311 + double deltaT = (deltaAngle + (next ? 0.0 : - CalendarAstronomer_PI2 )) * (periodDays*DAY_MS) / CalendarAstronomer_PI2; 1.1312 + 1.1313 + double lastDeltaT = deltaT; // Liu 1.1314 + UDate startTime = fTime; // Liu 1.1315 + 1.1316 + setTime(fTime + uprv_ceil(deltaT)); 1.1317 + 1.1318 + // Now iterate until we get the error below epsilon. Throughout 1.1319 + // this loop we use normPI to get values in the range -Pi to Pi, 1.1320 + // since we're using them as correction factors rather than absolute angles. 1.1321 + do { 1.1322 + // Evaluate the function at the time we've estimated 1.1323 + double angle = func.eval(*this); 1.1324 + 1.1325 + // Find the # of milliseconds per radian at this point on the curve 1.1326 + double factor = uprv_fabs(deltaT / normPI(angle-lastAngle)); 1.1327 + 1.1328 + // Correct the time estimate based on how far off the angle is 1.1329 + deltaT = normPI(desired - angle) * factor; 1.1330 + 1.1331 + // HACK: 1.1332 + // 1.1333 + // If abs(deltaT) begins to diverge we need to quit this loop. 1.1334 + // This only appears to happen when attempting to locate, for 1.1335 + // example, a new moon on the day of the new moon. E.g.: 1.1336 + // 1.1337 + // This result is correct: 1.1338 + // newMoon(7508(Mon Jul 23 00:00:00 CST 1990,false))= 1.1339 + // Sun Jul 22 10:57:41 CST 1990 1.1340 + // 1.1341 + // But attempting to make the same call a day earlier causes deltaT 1.1342 + // to diverge: 1.1343 + // CalendarAstronomer.timeOfAngle() diverging: 1.348508727575625E9 -> 1.1344 + // 1.3649828540224032E9 1.1345 + // newMoon(7507(Sun Jul 22 00:00:00 CST 1990,false))= 1.1346 + // Sun Jul 08 13:56:15 CST 1990 1.1347 + // 1.1348 + // As a temporary solution, we catch this specific condition and 1.1349 + // adjust our start time by one eighth period days (either forward 1.1350 + // or backward) and try again. 1.1351 + // Liu 11/9/00 1.1352 + if (uprv_fabs(deltaT) > uprv_fabs(lastDeltaT)) { 1.1353 + double delta = uprv_ceil (periodDays * DAY_MS / 8.0); 1.1354 + setTime(startTime + (next ? delta : -delta)); 1.1355 + return timeOfAngle(func, desired, periodDays, epsilon, next); 1.1356 + } 1.1357 + 1.1358 + lastDeltaT = deltaT; 1.1359 + lastAngle = angle; 1.1360 + 1.1361 + setTime(fTime + uprv_ceil(deltaT)); 1.1362 + } 1.1363 + while (uprv_fabs(deltaT) > epsilon); 1.1364 + 1.1365 + return fTime; 1.1366 +} 1.1367 + 1.1368 +UDate CalendarAstronomer::riseOrSet(CoordFunc& func, UBool rise, 1.1369 + double diameter, double refraction, 1.1370 + double epsilon) 1.1371 +{ 1.1372 + Equatorial pos; 1.1373 + double tanL = ::tan(fLatitude); 1.1374 + double deltaT = 0; 1.1375 + int32_t count = 0; 1.1376 + 1.1377 + // 1.1378 + // Calculate the object's position at the current time, then use that 1.1379 + // position to calculate the time of rising or setting. The position 1.1380 + // will be different at that time, so iterate until the error is allowable. 1.1381 + // 1.1382 + U_DEBUG_ASTRO_MSG(("setup rise=%s, dia=%.3lf, ref=%.3lf, eps=%.3lf\n", 1.1383 + rise?"T":"F", diameter, refraction, epsilon)); 1.1384 + do { 1.1385 + // See "Practical Astronomy With Your Calculator, section 33. 1.1386 + func.eval(pos, *this); 1.1387 + double angle = ::acos(-tanL * ::tan(pos.declination)); 1.1388 + double lst = ((rise ? CalendarAstronomer_PI2-angle : angle) + pos.ascension ) * 24 / CalendarAstronomer_PI2; 1.1389 + 1.1390 + // Convert from LST to Universal Time. 1.1391 + UDate newTime = lstToUT( lst ); 1.1392 + 1.1393 + deltaT = newTime - fTime; 1.1394 + setTime(newTime); 1.1395 + U_DEBUG_ASTRO_MSG(("%d] dT=%.3lf, angle=%.3lf, lst=%.3lf, A=%.3lf/D=%.3lf\n", 1.1396 + count, deltaT, angle, lst, pos.ascension, pos.declination)); 1.1397 + } 1.1398 + while (++ count < 5 && uprv_fabs(deltaT) > epsilon); 1.1399 + 1.1400 + // Calculate the correction due to refraction and the object's angular diameter 1.1401 + double cosD = ::cos(pos.declination); 1.1402 + double psi = ::acos(sin(fLatitude) / cosD); 1.1403 + double x = diameter / 2 + refraction; 1.1404 + double y = ::asin(sin(x) / ::sin(psi)); 1.1405 + long delta = (long)((240 * y * RAD_DEG / cosD)*SECOND_MS); 1.1406 + 1.1407 + return fTime + (rise ? -delta : delta); 1.1408 +} 1.1409 + /** 1.1410 + * Return the obliquity of the ecliptic (the angle between the ecliptic 1.1411 + * and the earth's equator) at the current time. This varies due to 1.1412 + * the precession of the earth's axis. 1.1413 + * 1.1414 + * @return the obliquity of the ecliptic relative to the equator, 1.1415 + * measured in radians. 1.1416 + */ 1.1417 +double CalendarAstronomer::eclipticObliquity() { 1.1418 + if (isINVALID(eclipObliquity)) { 1.1419 + const double epoch = 2451545.0; // 2000 AD, January 1.5 1.1420 + 1.1421 + double T = (getJulianDay() - epoch) / 36525; 1.1422 + 1.1423 + eclipObliquity = 23.439292 1.1424 + - 46.815/3600 * T 1.1425 + - 0.0006/3600 * T*T 1.1426 + + 0.00181/3600 * T*T*T; 1.1427 + 1.1428 + eclipObliquity *= DEG_RAD; 1.1429 + } 1.1430 + return eclipObliquity; 1.1431 +} 1.1432 + 1.1433 + 1.1434 +//------------------------------------------------------------------------- 1.1435 +// Private data 1.1436 +//------------------------------------------------------------------------- 1.1437 +void CalendarAstronomer::clearCache() { 1.1438 + const double INVALID = uprv_getNaN(); 1.1439 + 1.1440 + julianDay = INVALID; 1.1441 + julianCentury = INVALID; 1.1442 + sunLongitude = INVALID; 1.1443 + meanAnomalySun = INVALID; 1.1444 + moonLongitude = INVALID; 1.1445 + moonEclipLong = INVALID; 1.1446 + meanAnomalyMoon = INVALID; 1.1447 + eclipObliquity = INVALID; 1.1448 + siderealTime = INVALID; 1.1449 + siderealT0 = INVALID; 1.1450 + moonPositionSet = FALSE; 1.1451 +} 1.1452 + 1.1453 +//private static void out(String s) { 1.1454 +// System.out.println(s); 1.1455 +//} 1.1456 + 1.1457 +//private static String deg(double rad) { 1.1458 +// return Double.toString(rad * RAD_DEG); 1.1459 +//} 1.1460 + 1.1461 +//private static String hours(long ms) { 1.1462 +// return Double.toString((double)ms / HOUR_MS) + " hours"; 1.1463 +//} 1.1464 + 1.1465 +/** 1.1466 + * @internal 1.1467 + * @deprecated ICU 2.4. This class may be removed or modified. 1.1468 + */ 1.1469 +/*UDate CalendarAstronomer::local(UDate localMillis) { 1.1470 + // TODO - srl ? 1.1471 + TimeZone *tz = TimeZone::createDefault(); 1.1472 + int32_t rawOffset; 1.1473 + int32_t dstOffset; 1.1474 + UErrorCode status = U_ZERO_ERROR; 1.1475 + tz->getOffset(localMillis, TRUE, rawOffset, dstOffset, status); 1.1476 + delete tz; 1.1477 + return localMillis - rawOffset; 1.1478 +}*/ 1.1479 + 1.1480 +// Debugging functions 1.1481 +UnicodeString CalendarAstronomer::Ecliptic::toString() const 1.1482 +{ 1.1483 +#ifdef U_DEBUG_ASTRO 1.1484 + char tmp[800]; 1.1485 + sprintf(tmp, "[%.5f,%.5f]", longitude*RAD_DEG, latitude*RAD_DEG); 1.1486 + return UnicodeString(tmp, ""); 1.1487 +#else 1.1488 + return UnicodeString(); 1.1489 +#endif 1.1490 +} 1.1491 + 1.1492 +UnicodeString CalendarAstronomer::Equatorial::toString() const 1.1493 +{ 1.1494 +#ifdef U_DEBUG_ASTRO 1.1495 + char tmp[400]; 1.1496 + sprintf(tmp, "%f,%f", 1.1497 + (ascension*RAD_DEG), (declination*RAD_DEG)); 1.1498 + return UnicodeString(tmp, ""); 1.1499 +#else 1.1500 + return UnicodeString(); 1.1501 +#endif 1.1502 +} 1.1503 + 1.1504 +UnicodeString CalendarAstronomer::Horizon::toString() const 1.1505 +{ 1.1506 +#ifdef U_DEBUG_ASTRO 1.1507 + char tmp[800]; 1.1508 + sprintf(tmp, "[%.5f,%.5f]", altitude*RAD_DEG, azimuth*RAD_DEG); 1.1509 + return UnicodeString(tmp, ""); 1.1510 +#else 1.1511 + return UnicodeString(); 1.1512 +#endif 1.1513 +} 1.1514 + 1.1515 + 1.1516 +// static private String radToHms(double angle) { 1.1517 +// int hrs = (int) (angle*RAD_HOUR); 1.1518 +// int min = (int)((angle*RAD_HOUR - hrs) * 60); 1.1519 +// int sec = (int)((angle*RAD_HOUR - hrs - min/60.0) * 3600); 1.1520 + 1.1521 +// return Integer.toString(hrs) + "h" + min + "m" + sec + "s"; 1.1522 +// } 1.1523 + 1.1524 +// static private String radToDms(double angle) { 1.1525 +// int deg = (int) (angle*RAD_DEG); 1.1526 +// int min = (int)((angle*RAD_DEG - deg) * 60); 1.1527 +// int sec = (int)((angle*RAD_DEG - deg - min/60.0) * 3600); 1.1528 + 1.1529 +// return Integer.toString(deg) + "\u00b0" + min + "'" + sec + "\""; 1.1530 +// } 1.1531 + 1.1532 +// =============== Calendar Cache ================ 1.1533 + 1.1534 +void CalendarCache::createCache(CalendarCache** cache, UErrorCode& status) { 1.1535 + ucln_i18n_registerCleanup(UCLN_I18N_ASTRO_CALENDAR, calendar_astro_cleanup); 1.1536 + if(cache == NULL) { 1.1537 + status = U_MEMORY_ALLOCATION_ERROR; 1.1538 + } else { 1.1539 + *cache = new CalendarCache(32, status); 1.1540 + if(U_FAILURE(status)) { 1.1541 + delete *cache; 1.1542 + *cache = NULL; 1.1543 + } 1.1544 + } 1.1545 +} 1.1546 + 1.1547 +int32_t CalendarCache::get(CalendarCache** cache, int32_t key, UErrorCode &status) { 1.1548 + int32_t res; 1.1549 + 1.1550 + if(U_FAILURE(status)) { 1.1551 + return 0; 1.1552 + } 1.1553 + umtx_lock(&ccLock); 1.1554 + 1.1555 + if(*cache == NULL) { 1.1556 + createCache(cache, status); 1.1557 + if(U_FAILURE(status)) { 1.1558 + umtx_unlock(&ccLock); 1.1559 + return 0; 1.1560 + } 1.1561 + } 1.1562 + 1.1563 + res = uhash_igeti((*cache)->fTable, key); 1.1564 + U_DEBUG_ASTRO_MSG(("%p: GET: [%d] == %d\n", (*cache)->fTable, key, res)); 1.1565 + 1.1566 + umtx_unlock(&ccLock); 1.1567 + return res; 1.1568 +} 1.1569 + 1.1570 +void CalendarCache::put(CalendarCache** cache, int32_t key, int32_t value, UErrorCode &status) { 1.1571 + if(U_FAILURE(status)) { 1.1572 + return; 1.1573 + } 1.1574 + umtx_lock(&ccLock); 1.1575 + 1.1576 + if(*cache == NULL) { 1.1577 + createCache(cache, status); 1.1578 + if(U_FAILURE(status)) { 1.1579 + umtx_unlock(&ccLock); 1.1580 + return; 1.1581 + } 1.1582 + } 1.1583 + 1.1584 + uhash_iputi((*cache)->fTable, key, value, &status); 1.1585 + U_DEBUG_ASTRO_MSG(("%p: PUT: [%d] := %d\n", (*cache)->fTable, key, value)); 1.1586 + 1.1587 + umtx_unlock(&ccLock); 1.1588 +} 1.1589 + 1.1590 +CalendarCache::CalendarCache(int32_t size, UErrorCode &status) { 1.1591 + fTable = uhash_openSize(uhash_hashLong, uhash_compareLong, NULL, size, &status); 1.1592 + U_DEBUG_ASTRO_MSG(("%p: Opening.\n", fTable)); 1.1593 +} 1.1594 + 1.1595 +CalendarCache::~CalendarCache() { 1.1596 + if(fTable != NULL) { 1.1597 + U_DEBUG_ASTRO_MSG(("%p: Closing.\n", fTable)); 1.1598 + uhash_close(fTable); 1.1599 + } 1.1600 +} 1.1601 + 1.1602 +U_NAMESPACE_END 1.1603 + 1.1604 +#endif // !UCONFIG_NO_FORMATTING