1 /************************************************************************ 2 * Copyright (C) 1996-2007, International Business Machines Corporation * 3 * and others. All Rights Reserved. * 4 ************************************************************************ 5 * 2003-nov-07 srl Port from Java 6 */ 7 8 #ifndef ASTRO_H 9 #define ASTRO_H 10 11 #include "unicode/utypes.h" 12 13 #if !UCONFIG_NO_FORMATTING 14 15 #include "gregoimp.h" // for Math 16 #include "unicode/unistr.h" 17 18 U_NAMESPACE_BEGIN 19 20 /** 21 * <code>CalendarAstronomer</code> is a class that can perform the calculations to 22 * determine the positions of the sun and moon, the time of sunrise and 23 * sunset, and other astronomy-related data. The calculations it performs 24 * are in some cases quite complicated, and this utility class saves you 25 * the trouble of worrying about them. 26 * <p> 27 * The measurement of time is a very important part of astronomy. Because 28 * astronomical bodies are constantly in motion, observations are only valid 29 * at a given moment in time. Accordingly, each <code>CalendarAstronomer</code> 30 * object has a <code>time</code> property that determines the date 31 * and time for which its calculations are performed. You can set and 32 * retrieve this property with {@link #setDate setDate}, {@link #getDate getDate} 33 * and related methods. 34 * <p> 35 * Almost all of the calculations performed by this class, or by any 36 * astronomer, are approximations to various degrees of accuracy. The 37 * calculations in this class are mostly modelled after those described 38 * in the book 39 * <a href="http://www.amazon.com/exec/obidos/ISBN=0521356997" target="_top"> 40 * Practical Astronomy With Your Calculator</a>, by Peter J. 41 * Duffett-Smith, Cambridge University Press, 1990. This is an excellent 42 * book, and if you want a greater understanding of how these calculations 43 * are performed it a very good, readable starting point. 44 * <p> 45 * <strong>WARNING:</strong> This class is very early in its development, and 46 * it is highly likely that its API will change to some degree in the future. 47 * At the moment, it basically does just enough to support {@link IslamicCalendar} 48 * and {@link ChineseCalendar}. 49 * 50 * @author Laura Werner 51 * @author Alan Liu 52 * @internal 53 */ 54 class U_I18N_API CalendarAstronomer : public UMemory { 55 public: 56 // some classes 57 58 public: 59 /** 60 * Represents the position of an object in the sky relative to the ecliptic, 61 * the plane of the earth's orbit around the Sun. 62 * This is a spherical coordinate system in which the latitude 63 * specifies the position north or south of the plane of the ecliptic. 64 * The longitude specifies the position along the ecliptic plane 65 * relative to the "First Point of Aries", which is the Sun's position in the sky 66 * at the Vernal Equinox. 67 * <p> 68 * Note that Ecliptic objects are immutable and cannot be modified 69 * once they are constructed. This allows them to be passed and returned by 70 * value without worrying about whether other code will modify them. 71 * 72 * @see CalendarAstronomer.Equatorial 73 * @see CalendarAstronomer.Horizon 74 * @internal 75 */ 76 class U_I18N_API Ecliptic : public UMemory { 77 public: 78 /** 79 * Constructs an Ecliptic coordinate object. 80 * <p> 81 * @param lat The ecliptic latitude, measured in radians. 82 * @param lon The ecliptic longitude, measured in radians. 83 * @internal 84 */ 85 Ecliptic(double lat = 0, double lon = 0) { 86 latitude = lat; 87 longitude = lon; 88 } 89 90 /** 91 * Setter for Ecliptic Coordinate object 92 * @param lat The ecliptic latitude, measured in radians. 93 * @param lon The ecliptic longitude, measured in radians. 94 * @internal 95 */ set(double lat,double lon)96 void set(double lat, double lon) { 97 latitude = lat; 98 longitude = lon; 99 } 100 101 /** 102 * Return a string representation of this object 103 * @internal 104 */ 105 UnicodeString toString() const; 106 107 /** 108 * The ecliptic latitude, in radians. This specifies an object's 109 * position north or south of the plane of the ecliptic, 110 * with positive angles representing north. 111 * @internal 112 */ 113 double latitude; 114 115 /** 116 * The ecliptic longitude, in radians. 117 * This specifies an object's position along the ecliptic plane 118 * relative to the "First Point of Aries", which is the Sun's position 119 * in the sky at the Vernal Equinox, 120 * with positive angles representing east. 121 * <p> 122 * A bit of trivia: the first point of Aries is currently in the 123 * constellation Pisces, due to the precession of the earth's axis. 124 * @internal 125 */ 126 double longitude; 127 }; 128 129 /** 130 * Represents the position of an 131 * object in the sky relative to the plane of the earth's equator. 132 * The <i>Right Ascension</i> specifies the position east or west 133 * along the equator, relative to the sun's position at the vernal 134 * equinox. The <i>Declination</i> is the position north or south 135 * of the equatorial plane. 136 * <p> 137 * Note that Equatorial objects are immutable and cannot be modified 138 * once they are constructed. This allows them to be passed and returned by 139 * value without worrying about whether other code will modify them. 140 * 141 * @see CalendarAstronomer.Ecliptic 142 * @see CalendarAstronomer.Horizon 143 * @internal 144 */ 145 class U_I18N_API Equatorial : public UMemory { 146 public: 147 /** 148 * Constructs an Equatorial coordinate object. 149 * <p> 150 * @param asc The right ascension, measured in radians. 151 * @param dec The declination, measured in radians. 152 * @internal 153 */ 154 Equatorial(double asc = 0, double dec = 0) ascension(asc)155 : ascension(asc), declination(dec) { } 156 157 /** 158 * Setter 159 * @param asc The right ascension, measured in radians. 160 * @param dec The declination, measured in radians. 161 * @internal 162 */ set(double asc,double dec)163 void set(double asc, double dec) { 164 ascension = asc; 165 declination = dec; 166 } 167 168 /** 169 * Return a string representation of this object, with the 170 * angles measured in degrees. 171 * @internal 172 */ 173 UnicodeString toString() const; 174 175 /** 176 * Return a string representation of this object with the right ascension 177 * measured in hours, minutes, and seconds. 178 * @internal 179 */ 180 //String toHmsString() { 181 //return radToHms(ascension) + "," + radToDms(declination); 182 //} 183 184 /** 185 * The right ascension, in radians. 186 * This is the position east or west along the equator 187 * relative to the sun's position at the vernal equinox, 188 * with positive angles representing East. 189 * @internal 190 */ 191 double ascension; 192 193 /** 194 * The declination, in radians. 195 * This is the position north or south of the equatorial plane, 196 * with positive angles representing north. 197 * @internal 198 */ 199 double declination; 200 }; 201 202 /** 203 * Represents the position of an object in the sky relative to 204 * the local horizon. 205 * The <i>Altitude</i> represents the object's elevation above the horizon, 206 * with objects below the horizon having a negative altitude. 207 * The <i>Azimuth</i> is the geographic direction of the object from the 208 * observer's position, with 0 representing north. The azimuth increases 209 * clockwise from north. 210 * <p> 211 * Note that Horizon objects are immutable and cannot be modified 212 * once they are constructed. This allows them to be passed and returned by 213 * value without worrying about whether other code will modify them. 214 * 215 * @see CalendarAstronomer.Ecliptic 216 * @see CalendarAstronomer.Equatorial 217 * @internal 218 */ 219 class U_I18N_API Horizon : public UMemory { 220 public: 221 /** 222 * Constructs a Horizon coordinate object. 223 * <p> 224 * @param alt The altitude, measured in radians above the horizon. 225 * @param azim The azimuth, measured in radians clockwise from north. 226 * @internal 227 */ 228 Horizon(double alt=0, double azim=0) altitude(alt)229 : altitude(alt), azimuth(azim) { } 230 231 /** 232 * Setter for Ecliptic Coordinate object 233 * @param alt The altitude, measured in radians above the horizon. 234 * @param azim The azimuth, measured in radians clockwise from north. 235 * @internal 236 */ set(double alt,double azim)237 void set(double alt, double azim) { 238 altitude = alt; 239 azimuth = azim; 240 } 241 242 /** 243 * Return a string representation of this object, with the 244 * angles measured in degrees. 245 * @internal 246 */ 247 UnicodeString toString() const; 248 249 /** 250 * The object's altitude above the horizon, in radians. 251 * @internal 252 */ 253 double altitude; 254 255 /** 256 * The object's direction, in radians clockwise from north. 257 * @internal 258 */ 259 double azimuth; 260 }; 261 262 public: 263 //------------------------------------------------------------------------- 264 // Assorted private data used for conversions 265 //------------------------------------------------------------------------- 266 267 // My own copies of these so compilers are more likely to optimize them away 268 static const double PI; 269 270 /** 271 * The average number of solar days from one new moon to the next. This is the time 272 * it takes for the moon to return the same ecliptic longitude as the sun. 273 * It is longer than the sidereal month because the sun's longitude increases 274 * during the year due to the revolution of the earth around the sun. 275 * Approximately 29.53. 276 * 277 * @see #SIDEREAL_MONTH 278 * @internal 279 * @deprecated ICU 2.4. This class may be removed or modified. 280 */ 281 static const double SYNODIC_MONTH; 282 283 //------------------------------------------------------------------------- 284 // Constructors 285 //------------------------------------------------------------------------- 286 287 /** 288 * Construct a new <code>CalendarAstronomer</code> object that is initialized to 289 * the current date and time. 290 * @internal 291 */ 292 CalendarAstronomer(); 293 294 /** 295 * Construct a new <code>CalendarAstronomer</code> object that is initialized to 296 * the specified date and time. 297 * @internal 298 */ 299 CalendarAstronomer(UDate d); 300 301 /** 302 * Construct a new <code>CalendarAstronomer</code> object with the given 303 * latitude and longitude. The object's time is set to the current 304 * date and time. 305 * <p> 306 * @param longitude The desired longitude, in <em>degrees</em> east of 307 * the Greenwich meridian. 308 * 309 * @param latitude The desired latitude, in <em>degrees</em>. Positive 310 * values signify North, negative South. 311 * 312 * @see java.util.Date#getTime() 313 * @internal 314 */ 315 CalendarAstronomer(double longitude, double latitude); 316 317 /** 318 * Destructor 319 * @internal 320 */ 321 ~CalendarAstronomer(); 322 323 //------------------------------------------------------------------------- 324 // Time and date getters and setters 325 //------------------------------------------------------------------------- 326 327 /** 328 * Set the current date and time of this <code>CalendarAstronomer</code> object. All 329 * astronomical calculations are performed based on this time setting. 330 * 331 * @param aTime the date and time, expressed as the number of milliseconds since 332 * 1/1/1970 0:00 GMT (Gregorian). 333 * 334 * @see #setDate 335 * @see #getTime 336 * @internal 337 */ 338 void setTime(UDate aTime); 339 340 341 /** 342 * Set the current date and time of this <code>CalendarAstronomer</code> object. All 343 * astronomical calculations are performed based on this time setting. 344 * 345 * @param aTime the date and time, expressed as the number of milliseconds since 346 * 1/1/1970 0:00 GMT (Gregorian). 347 * 348 * @see #getTime 349 * @internal 350 */ setDate(UDate aDate)351 void setDate(UDate aDate) { setTime(aDate); } 352 353 /** 354 * Set the current date and time of this <code>CalendarAstronomer</code> object. All 355 * astronomical calculations are performed based on this time setting. 356 * 357 * @param jdn the desired time, expressed as a "julian day number", 358 * which is the number of elapsed days since 359 * 1/1/4713 BC (Julian), 12:00 GMT. Note that julian day 360 * numbers start at <em>noon</em>. To get the jdn for 361 * the corresponding midnight, subtract 0.5. 362 * 363 * @see #getJulianDay 364 * @see #JULIAN_EPOCH_MS 365 * @internal 366 */ 367 void setJulianDay(double jdn); 368 369 /** 370 * Get the current time of this <code>CalendarAstronomer</code> object, 371 * represented as the number of milliseconds since 372 * 1/1/1970 AD 0:00 GMT (Gregorian). 373 * 374 * @see #setTime 375 * @see #getDate 376 * @internal 377 */ 378 UDate getTime(); 379 380 /** 381 * Get the current time of this <code>CalendarAstronomer</code> object, 382 * expressed as a "julian day number", which is the number of elapsed 383 * days since 1/1/4713 BC (Julian), 12:00 GMT. 384 * 385 * @see #setJulianDay 386 * @see #JULIAN_EPOCH_MS 387 * @internal 388 */ 389 double getJulianDay(); 390 391 /** 392 * Return this object's time expressed in julian centuries: 393 * the number of centuries after 1/1/1900 AD, 12:00 GMT 394 * 395 * @see #getJulianDay 396 * @internal 397 */ 398 double getJulianCentury(); 399 400 /** 401 * Returns the current Greenwich sidereal time, measured in hours 402 * @internal 403 */ 404 double getGreenwichSidereal(); 405 406 private: 407 double getSiderealOffset(); 408 public: 409 /** 410 * Returns the current local sidereal time, measured in hours 411 * @internal 412 */ 413 double getLocalSidereal(); 414 415 /** 416 * Converts local sidereal time to Universal Time. 417 * 418 * @param lst The Local Sidereal Time, in hours since sidereal midnight 419 * on this object's current date. 420 * 421 * @return The corresponding Universal Time, in milliseconds since 422 * 1 Jan 1970, GMT. 423 */ 424 //private: 425 double lstToUT(double lst); 426 427 /** 428 * 429 * Convert from ecliptic to equatorial coordinates. 430 * 431 * @param ecliptic The ecliptic 432 * @param result Fillin result 433 * @return reference to result 434 */ 435 Equatorial& eclipticToEquatorial(Equatorial& result, const Ecliptic& ecliptic); 436 437 /** 438 * Convert from ecliptic to equatorial coordinates. 439 * 440 * @param eclipLong The ecliptic longitude 441 * @param eclipLat The ecliptic latitude 442 * 443 * @return The corresponding point in equatorial coordinates. 444 * @internal 445 */ 446 Equatorial& eclipticToEquatorial(Equatorial& result, double eclipLong, double eclipLat); 447 448 /** 449 * Convert from ecliptic longitude to equatorial coordinates. 450 * 451 * @param eclipLong The ecliptic longitude 452 * 453 * @return The corresponding point in equatorial coordinates. 454 * @internal 455 */ 456 Equatorial& eclipticToEquatorial(Equatorial& result, double eclipLong) ; 457 458 /** 459 * @internal 460 */ 461 Horizon& eclipticToHorizon(Horizon& result, double eclipLong) ; 462 463 //------------------------------------------------------------------------- 464 // The Sun 465 //------------------------------------------------------------------------- 466 467 /** 468 * The longitude of the sun at the time specified by this object. 469 * The longitude is measured in radians along the ecliptic 470 * from the "first point of Aries," the point at which the ecliptic 471 * crosses the earth's equatorial plane at the vernal equinox. 472 * <p> 473 * Currently, this method uses an approximation of the two-body Kepler's 474 * equation for the earth and the sun. It does not take into account the 475 * perturbations caused by the other planets, the moon, etc. 476 * @internal 477 */ 478 double getSunLongitude(); 479 480 /** 481 * TODO Make this public when the entire class is package-private. 482 */ 483 /*public*/ void getSunLongitude(double julianDay, double &longitude, double &meanAnomaly); 484 485 /** 486 * The position of the sun at this object's current date and time, 487 * in equatorial coordinates. 488 * @param result fillin for the result 489 * @internal 490 */ 491 Equatorial& getSunPosition(Equatorial& result); 492 493 public: 494 /** 495 * Constant representing the vernal equinox. 496 * For use with {@link #getSunTime getSunTime}. 497 * Note: In this case, "vernal" refers to the northern hemisphere's seasons. 498 * @internal 499 */ 500 // static double VERNAL_EQUINOX(); 501 502 /** 503 * Constant representing the summer solstice. 504 * For use with {@link #getSunTime getSunTime}. 505 * Note: In this case, "summer" refers to the northern hemisphere's seasons. 506 * @internal 507 */ 508 static double SUMMER_SOLSTICE(); 509 510 /** 511 * Constant representing the autumnal equinox. 512 * For use with {@link #getSunTime getSunTime}. 513 * Note: In this case, "autumn" refers to the northern hemisphere's seasons. 514 * @internal 515 */ 516 // static double AUTUMN_EQUINOX(); 517 518 /** 519 * Constant representing the winter solstice. 520 * For use with {@link #getSunTime getSunTime}. 521 * Note: In this case, "winter" refers to the northern hemisphere's seasons. 522 * @internal 523 */ 524 // static double WINTER_SOLSTICE(); 525 526 /** 527 * Find the next time at which the sun's ecliptic longitude will have 528 * the desired value. 529 * @internal 530 */ 531 UDate getSunTime(double desired, UBool next); 532 533 /** 534 * Returns the time (GMT) of sunrise or sunset on the local date to which 535 * this calendar is currently set. 536 * 537 * NOTE: This method only works well if this object is set to a 538 * time near local noon. Because of variations between the local 539 * official time zone and the geographic longitude, the 540 * computation can flop over into an adjacent day if this object 541 * is set to a time near local midnight. 542 * 543 * @internal 544 */ 545 UDate getSunRiseSet(UBool rise); 546 547 //------------------------------------------------------------------------- 548 // The Moon 549 //------------------------------------------------------------------------- 550 551 /** 552 * The position of the moon at the time set on this 553 * object, in equatorial coordinates. 554 * @internal 555 * @return const reference to internal field of calendar astronomer. Do not use outside of the lifetime of this astronomer. 556 */ 557 const Equatorial& getMoonPosition(); 558 559 /** 560 * The "age" of the moon at the time specified in this object. 561 * This is really the angle between the 562 * current ecliptic longitudes of the sun and the moon, 563 * measured in radians. 564 * 565 * @see #getMoonPhase 566 * @internal 567 */ 568 double getMoonAge(); 569 570 /** 571 * Calculate the phase of the moon at the time set in this object. 572 * The returned phase is a <code>double</code> in the range 573 * <code>0 <= phase < 1</code>, interpreted as follows: 574 * <ul> 575 * <li>0.00: New moon 576 * <li>0.25: First quarter 577 * <li>0.50: Full moon 578 * <li>0.75: Last quarter 579 * </ul> 580 * 581 * @see #getMoonAge 582 * @internal 583 */ 584 double getMoonPhase(); 585 586 class U_I18N_API MoonAge : public UMemory { 587 public: MoonAge(double l)588 MoonAge(double l) 589 : value(l) { } set(double l)590 void set(double l) { value = l; } 591 double value; 592 }; 593 594 /** 595 * Constant representing a new moon. 596 * For use with {@link #getMoonTime getMoonTime} 597 * @internal 598 */ 599 // static const MoonAge NEW_MOON(); 600 601 /** 602 * Constant representing the moon's first quarter. 603 * For use with {@link #getMoonTime getMoonTime} 604 * @internal 605 */ 606 // static const MoonAge FIRST_QUARTER(); 607 608 /** 609 * Constant representing a full moon. 610 * For use with {@link #getMoonTime getMoonTime} 611 * @internal 612 */ 613 static const MoonAge FULL_MOON(); 614 615 /** 616 * Constant representing the moon's last quarter. 617 * For use with {@link #getMoonTime getMoonTime} 618 * @internal 619 */ 620 // static const MoonAge LAST_QUARTER(); 621 622 /** 623 * Find the next or previous time at which the Moon's ecliptic 624 * longitude will have the desired value. 625 * <p> 626 * @param desired The desired longitude. 627 * @param next <tt>true</tt> if the next occurrance of the phase 628 * is desired, <tt>false</tt> for the previous occurrance. 629 * @internal 630 */ 631 UDate getMoonTime(double desired, UBool next); 632 UDate getMoonTime(const MoonAge& desired, UBool next); 633 634 /** 635 * Returns the time (GMT) of sunrise or sunset on the local date to which 636 * this calendar is currently set. 637 * @internal 638 */ 639 UDate getMoonRiseSet(UBool rise); 640 641 //------------------------------------------------------------------------- 642 // Interpolation methods for finding the time at which a given event occurs 643 //------------------------------------------------------------------------- 644 645 // private 646 class AngleFunc : public UMemory { 647 public: 648 virtual double eval(CalendarAstronomer&) = 0; 649 virtual ~AngleFunc(); 650 }; 651 friend class AngleFunc; 652 653 UDate timeOfAngle(AngleFunc& func, double desired, 654 double periodDays, double epsilon, UBool next); 655 656 class CoordFunc : public UMemory { 657 public: 658 virtual void eval(Equatorial& result, CalendarAstronomer&) = 0; 659 virtual ~CoordFunc(); 660 }; 661 friend class CoordFunc; 662 663 double riseOrSet(CoordFunc& func, UBool rise, 664 double diameter, double refraction, 665 double epsilon); 666 667 //------------------------------------------------------------------------- 668 // Other utility methods 669 //------------------------------------------------------------------------- 670 private: 671 /*** 672 * Given 'value', add or subtract 'range' until 0 <= 'value' < range. 673 * The modulus operator. 674 */ normalize(double value,double range)675 inline static double normalize(double value, double range) { 676 return value - range * Math::floorDivide(value, range); 677 } 678 679 /** 680 * Normalize an angle so that it's in the range 0 - 2pi. 681 * For positive angles this is just (angle % 2pi), but the Java 682 * mod operator doesn't work that way for negative numbers.... 683 */ norm2PI(double angle)684 inline static double norm2PI(double angle) { 685 return normalize(angle, CalendarAstronomer::PI * 2.0); 686 } 687 688 /** 689 * Normalize an angle into the range -PI - PI 690 */ normPI(double angle)691 inline static double normPI(double angle) { 692 return normalize(angle + PI, CalendarAstronomer::PI * 2.0) - PI; 693 } 694 695 /** 696 * Find the "true anomaly" (longitude) of an object from 697 * its mean anomaly and the eccentricity of its orbit. This uses 698 * an iterative solution to Kepler's equation. 699 * 700 * @param meanAnomaly The object's longitude calculated as if it were in 701 * a regular, circular orbit, measured in radians 702 * from the point of perigee. 703 * 704 * @param eccentricity The eccentricity of the orbit 705 * 706 * @return The true anomaly (longitude) measured in radians 707 */ 708 double trueAnomaly(double meanAnomaly, double eccentricity); 709 710 /** 711 * Return the obliquity of the ecliptic (the angle between the ecliptic 712 * and the earth's equator) at the current time. This varies due to 713 * the precession of the earth's axis. 714 * 715 * @return the obliquity of the ecliptic relative to the equator, 716 * measured in radians. 717 */ 718 double eclipticObliquity(); 719 720 //------------------------------------------------------------------------- 721 // Private data 722 //------------------------------------------------------------------------- 723 private: 724 /** 725 * Current time in milliseconds since 1/1/1970 AD 726 * @see java.util.Date#getTime 727 */ 728 UDate fTime; 729 730 /* These aren't used yet, but they'll be needed for sunset calculations 731 * and equatorial to horizon coordinate conversions 732 */ 733 double fLongitude; 734 double fLatitude; 735 double fGmtOffset; 736 737 // 738 // The following fields are used to cache calculated results for improved 739 // performance. These values all depend on the current time setting 740 // of this object, so the clearCache method is provided. 741 // 742 743 double julianDay; 744 double julianCentury; 745 double sunLongitude; 746 double meanAnomalySun; 747 double moonLongitude; 748 double moonEclipLong; 749 double meanAnomalyMoon; 750 double eclipObliquity; 751 double siderealT0; 752 double siderealTime; 753 754 void clearCache(); 755 756 Equatorial moonPosition; 757 UBool moonPositionSet; 758 759 /** 760 * @internal 761 */ 762 // UDate local(UDate localMillis); 763 }; 764 765 U_NAMESPACE_END 766 767 struct UHashtable; 768 769 U_NAMESPACE_BEGIN 770 771 /** 772 * Cache of month -> julian day 773 * @internal 774 */ 775 class CalendarCache : public UMemory { 776 public: 777 static int32_t get(CalendarCache** cache, int32_t key, UErrorCode &status); 778 static void put(CalendarCache** cache, int32_t key, int32_t value, UErrorCode &status); 779 virtual ~CalendarCache(); 780 private: 781 CalendarCache(int32_t size, UErrorCode& status); 782 static void createCache(CalendarCache** cache, UErrorCode& status); 783 /** 784 * not implemented 785 */ 786 CalendarCache(); 787 UHashtable *fTable; 788 }; 789 790 U_NAMESPACE_END 791 792 #endif 793 #endif 794