/src/icu/icu4c/source/i18n/astro.cpp
Line | Count | Source (jump to first uncovered line) |
1 | | // © 2016 and later: Unicode, Inc. and others. |
2 | | // License & terms of use: http://www.unicode.org/copyright.html |
3 | | /************************************************************************ |
4 | | * Copyright (C) 1996-2012, International Business Machines Corporation |
5 | | * and others. All Rights Reserved. |
6 | | ************************************************************************ |
7 | | * 2003-nov-07 srl Port from Java |
8 | | */ |
9 | | |
10 | | #include "astro.h" |
11 | | |
12 | | #if !UCONFIG_NO_FORMATTING |
13 | | |
14 | | #include "unicode/calendar.h" |
15 | | #include <math.h> |
16 | | #include <float.h> |
17 | | #include "unicode/putil.h" |
18 | | #include "uhash.h" |
19 | | #include "umutex.h" |
20 | | #include "ucln_in.h" |
21 | | #include "putilimp.h" |
22 | | #include <stdio.h> // for toString() |
23 | | |
24 | | #if defined (PI) |
25 | | #undef PI |
26 | | #endif |
27 | | |
28 | | #ifdef U_DEBUG_ASTRO |
29 | | # include "uresimp.h" // for debugging |
30 | | |
31 | | static void debug_astro_loc(const char *f, int32_t l) |
32 | | { |
33 | | fprintf(stderr, "%s:%d: ", f, l); |
34 | | } |
35 | | |
36 | | static void debug_astro_msg(const char *pat, ...) |
37 | | { |
38 | | va_list ap; |
39 | | va_start(ap, pat); |
40 | | vfprintf(stderr, pat, ap); |
41 | | fflush(stderr); |
42 | | } |
43 | | #include "unicode/datefmt.h" |
44 | | #include "unicode/ustring.h" |
45 | | static const char * debug_astro_date(UDate d) { |
46 | | static char gStrBuf[1024]; |
47 | | static DateFormat *df = nullptr; |
48 | | if(df == nullptr) { |
49 | | df = DateFormat::createDateTimeInstance(DateFormat::MEDIUM, DateFormat::MEDIUM, Locale::getUS()); |
50 | | df->adoptTimeZone(TimeZone::getGMT()->clone()); |
51 | | } |
52 | | UnicodeString str; |
53 | | df->format(d,str); |
54 | | u_austrncpy(gStrBuf,str.getTerminatedBuffer(),sizeof(gStrBuf)-1); |
55 | | return gStrBuf; |
56 | | } |
57 | | |
58 | | // must use double parens, i.e.: U_DEBUG_ASTRO_MSG(("four is: %d",4)); |
59 | | #define U_DEBUG_ASTRO_MSG(x) {debug_astro_loc(__FILE__,__LINE__);debug_astro_msg x;} |
60 | | #else |
61 | | #define U_DEBUG_ASTRO_MSG(x) |
62 | | #endif |
63 | | |
64 | 8.41M | static inline UBool isINVALID(double d) { |
65 | 8.41M | return(uprv_isNaN(d)); |
66 | 8.41M | } |
67 | | |
68 | | static icu::UMutex ccLock; |
69 | | |
70 | | U_CDECL_BEGIN |
71 | 0 | static UBool calendar_astro_cleanup() { |
72 | 0 | return true; |
73 | 0 | } |
74 | | U_CDECL_END |
75 | | |
76 | | U_NAMESPACE_BEGIN |
77 | | |
78 | | /** |
79 | | * The number of standard hours in one sidereal day. |
80 | | * Approximately 24.93. |
81 | | * @internal |
82 | | * @deprecated ICU 2.4. This class may be removed or modified. |
83 | | */ |
84 | | #define SIDEREAL_DAY (23.93446960027) |
85 | | |
86 | | /** |
87 | | * The number of sidereal hours in one mean solar day. |
88 | | * Approximately 24.07. |
89 | | * @internal |
90 | | * @deprecated ICU 2.4. This class may be removed or modified. |
91 | | */ |
92 | | #define SOLAR_DAY (24.065709816) |
93 | | |
94 | | /** |
95 | | * The average number of solar days from one new moon to the next. This is the time |
96 | | * it takes for the moon to return the same ecliptic longitude as the sun. |
97 | | * It is longer than the sidereal month because the sun's longitude increases |
98 | | * during the year due to the revolution of the earth around the sun. |
99 | | * Approximately 29.53. |
100 | | * |
101 | | * @see #SIDEREAL_MONTH |
102 | | * @internal |
103 | | * @deprecated ICU 2.4. This class may be removed or modified. |
104 | | */ |
105 | | const double CalendarAstronomer::SYNODIC_MONTH = 29.530588853; |
106 | | |
107 | | /** |
108 | | * The average number of days it takes |
109 | | * for the moon to return to the same ecliptic longitude relative to the |
110 | | * stellar background. This is referred to as the sidereal month. |
111 | | * It is shorter than the synodic month due to |
112 | | * the revolution of the earth around the sun. |
113 | | * Approximately 27.32. |
114 | | * |
115 | | * @see #SYNODIC_MONTH |
116 | | * @internal |
117 | | * @deprecated ICU 2.4. This class may be removed or modified. |
118 | | */ |
119 | | #define SIDEREAL_MONTH 27.32166 |
120 | | |
121 | | /** |
122 | | * The average number number of days between successive vernal equinoxes. |
123 | | * Due to the precession of the earth's |
124 | | * axis, this is not precisely the same as the sidereal year. |
125 | | * Approximately 365.24 |
126 | | * |
127 | | * @see #SIDEREAL_YEAR |
128 | | * @internal |
129 | | * @deprecated ICU 2.4. This class may be removed or modified. |
130 | | */ |
131 | 2.20M | #define TROPICAL_YEAR 365.242191 |
132 | | |
133 | | /** |
134 | | * The average number of days it takes |
135 | | * for the sun to return to the same position against the fixed stellar |
136 | | * background. This is the duration of one orbit of the earth about the sun |
137 | | * as it would appear to an outside observer. |
138 | | * Due to the precession of the earth's |
139 | | * axis, this is not precisely the same as the tropical year. |
140 | | * Approximately 365.25. |
141 | | * |
142 | | * @see #TROPICAL_YEAR |
143 | | * @internal |
144 | | * @deprecated ICU 2.4. This class may be removed or modified. |
145 | | */ |
146 | | #define SIDEREAL_YEAR 365.25636 |
147 | | |
148 | | //------------------------------------------------------------------------- |
149 | | // Time-related constants |
150 | | //------------------------------------------------------------------------- |
151 | | |
152 | | /** |
153 | | * The number of milliseconds in one second. |
154 | | * @internal |
155 | | * @deprecated ICU 2.4. This class may be removed or modified. |
156 | | */ |
157 | | #define SECOND_MS U_MILLIS_PER_SECOND |
158 | | |
159 | | /** |
160 | | * The number of milliseconds in one minute. |
161 | | * @internal |
162 | | * @deprecated ICU 2.4. This class may be removed or modified. |
163 | | */ |
164 | 509k | #define MINUTE_MS U_MILLIS_PER_MINUTE |
165 | | |
166 | | /** |
167 | | * The number of milliseconds in one hour. |
168 | | * @internal |
169 | | * @deprecated ICU 2.4. This class may be removed or modified. |
170 | | */ |
171 | | #define HOUR_MS U_MILLIS_PER_HOUR |
172 | | |
173 | | /** |
174 | | * The number of milliseconds in one day. |
175 | | * @internal |
176 | | * @deprecated ICU 2.4. This class may be removed or modified. |
177 | | */ |
178 | 2.71M | #define DAY_MS U_MILLIS_PER_DAY |
179 | | |
180 | | /** |
181 | | * The start of the julian day numbering scheme used by astronomers, which |
182 | | * is 1/1/4713 BC (Julian), 12:00 GMT. This is given as the number of milliseconds |
183 | | * since 1/1/1970 AD (Gregorian), a negative number. |
184 | | * Note that julian day numbers and |
185 | | * the Julian calendar are <em>not</em> the same thing. Also note that |
186 | | * julian days start at <em>noon</em>, not midnight. |
187 | | * @internal |
188 | | * @deprecated ICU 2.4. This class may be removed or modified. |
189 | | */ |
190 | 2.20M | #define JULIAN_EPOCH_MS -210866760000000.0 |
191 | | |
192 | | |
193 | | /** |
194 | | * Milliseconds value for 0.0 January 2000 AD. |
195 | | */ |
196 | | #define EPOCH_2000_MS 946598400000.0 |
197 | | |
198 | | //------------------------------------------------------------------------- |
199 | | // Assorted private data used for conversions |
200 | | //------------------------------------------------------------------------- |
201 | | |
202 | | // My own copies of these so compilers are more likely to optimize them away |
203 | | const double CalendarAstronomer::PI = 3.14159265358979323846; |
204 | | |
205 | 2.96M | #define CalendarAstronomer_PI2 (CalendarAstronomer::PI*2.0) |
206 | | #define RAD_HOUR ( 12 / CalendarAstronomer::PI ) // radians -> hours |
207 | 2.00M | #define DEG_RAD ( CalendarAstronomer::PI / 180 ) // degrees -> radians |
208 | | #define RAD_DEG ( 180 / CalendarAstronomer::PI ) // radians -> degrees |
209 | | |
210 | | /*** |
211 | | * Given 'value', add or subtract 'range' until 0 <= 'value' < range. |
212 | | * The modulus operator. |
213 | | */ |
214 | 18.0M | inline static double normalize(double value, double range) { |
215 | 18.0M | return value - range * ClockMath::floorDivide(value, range); |
216 | 18.0M | } |
217 | | |
218 | | /** |
219 | | * Normalize an angle so that it's in the range 0 - 2pi. |
220 | | * For positive angles this is just (angle % 2pi), but the Java |
221 | | * mod operator doesn't work that way for negative numbers.... |
222 | | */ |
223 | 15.1M | inline static double norm2PI(double angle) { |
224 | 15.1M | return normalize(angle, CalendarAstronomer::PI * 2.0); |
225 | 15.1M | } |
226 | | |
227 | | /** |
228 | | * Normalize an angle into the range -PI - PI |
229 | | */ |
230 | 2.90M | inline static double normPI(double angle) { |
231 | 2.90M | return normalize(angle + CalendarAstronomer::PI, CalendarAstronomer::PI * 2.0) - CalendarAstronomer::PI; |
232 | 2.90M | } |
233 | | |
234 | | //------------------------------------------------------------------------- |
235 | | // Constructors |
236 | | //------------------------------------------------------------------------- |
237 | | |
238 | | /** |
239 | | * Construct a new <code>CalendarAstronomer</code> object that is initialized to |
240 | | * the current date and time. |
241 | | * @internal |
242 | | * @deprecated ICU 2.4. This class may be removed or modified. |
243 | | */ |
244 | | CalendarAstronomer::CalendarAstronomer(): |
245 | 0 | fTime(Calendar::getNow()), moonPosition(0,0), moonPositionSet(false) { |
246 | 0 | clearCache(); |
247 | 0 | } |
248 | | |
249 | | /** |
250 | | * Construct a new <code>CalendarAstronomer</code> object that is initialized to |
251 | | * the specified date and time. |
252 | | * @internal |
253 | | * @deprecated ICU 2.4. This class may be removed or modified. |
254 | | */ |
255 | 752k | CalendarAstronomer::CalendarAstronomer(UDate d): fTime(d), moonPosition(0,0), moonPositionSet(false) { |
256 | 752k | clearCache(); |
257 | 752k | } |
258 | | |
259 | | CalendarAstronomer::~CalendarAstronomer() |
260 | 752k | { |
261 | 752k | } |
262 | | |
263 | | //------------------------------------------------------------------------- |
264 | | // Time and date getters and setters |
265 | | //------------------------------------------------------------------------- |
266 | | |
267 | | /** |
268 | | * Set the current date and time of this <code>CalendarAstronomer</code> object. All |
269 | | * astronomical calculations are performed based on this time setting. |
270 | | * |
271 | | * @param aTime the date and time, expressed as the number of milliseconds since |
272 | | * 1/1/1970 0:00 GMT (Gregorian). |
273 | | * |
274 | | * @see #setDate |
275 | | * @see #getTime |
276 | | * @internal |
277 | | * @deprecated ICU 2.4. This class may be removed or modified. |
278 | | */ |
279 | 1.96M | void CalendarAstronomer::setTime(UDate aTime) { |
280 | 1.96M | fTime = aTime; |
281 | 1.96M | clearCache(); |
282 | 1.96M | } |
283 | | |
284 | | /** |
285 | | * Get the current time of this <code>CalendarAstronomer</code> object, |
286 | | * represented as the number of milliseconds since |
287 | | * 1/1/1970 AD 0:00 GMT (Gregorian). |
288 | | * |
289 | | * @see #setTime |
290 | | * @see #getDate |
291 | | * @internal |
292 | | * @deprecated ICU 2.4. This class may be removed or modified. |
293 | | */ |
294 | 0 | UDate CalendarAstronomer::getTime() { |
295 | 0 | return fTime; |
296 | 0 | } |
297 | | |
298 | | /** |
299 | | * Get the current time of this <code>CalendarAstronomer</code> object, |
300 | | * expressed as a "julian day number", which is the number of elapsed |
301 | | * days since 1/1/4713 BC (Julian), 12:00 GMT. |
302 | | * |
303 | | * @see #setJulianDay |
304 | | * @see #JULIAN_EPOCH_MS |
305 | | * @internal |
306 | | * @deprecated ICU 2.4. This class may be removed or modified. |
307 | | */ |
308 | 6.21M | double CalendarAstronomer::getJulianDay() { |
309 | 6.21M | if (isINVALID(julianDay)) { |
310 | 2.20M | julianDay = (fTime - JULIAN_EPOCH_MS) / static_cast<double>(DAY_MS); |
311 | 2.20M | } |
312 | 6.21M | return julianDay; |
313 | 6.21M | } |
314 | | |
315 | | //------------------------------------------------------------------------- |
316 | | // Coordinate transformations, all based on the current time of this object |
317 | | //------------------------------------------------------------------------- |
318 | | |
319 | | /** |
320 | | * Convert from ecliptic to equatorial coordinates. |
321 | | * |
322 | | * @param eclipLong The ecliptic longitude |
323 | | * @param eclipLat The ecliptic latitude |
324 | | * |
325 | | * @return The corresponding point in equatorial coordinates. |
326 | | * @internal |
327 | | * @deprecated ICU 2.4. This class may be removed or modified. |
328 | | */ |
329 | | CalendarAstronomer::Equatorial& CalendarAstronomer::eclipticToEquatorial(CalendarAstronomer::Equatorial& result, double eclipLong, double eclipLat) |
330 | 2.00M | { |
331 | | // See page 42 of "Practical Astronomy with your Calculator", |
332 | | // by Peter Duffet-Smith, for details on the algorithm. |
333 | | |
334 | 2.00M | double obliq = eclipticObliquity(); |
335 | 2.00M | double sinE = ::sin(obliq); |
336 | 2.00M | double cosE = cos(obliq); |
337 | | |
338 | 2.00M | double sinL = ::sin(eclipLong); |
339 | 2.00M | double cosL = cos(eclipLong); |
340 | | |
341 | 2.00M | double sinB = ::sin(eclipLat); |
342 | 2.00M | double cosB = cos(eclipLat); |
343 | 2.00M | double tanB = tan(eclipLat); |
344 | | |
345 | 2.00M | result.set(atan2(sinL*cosE - tanB*sinE, cosL), |
346 | 2.00M | asin(sinB*cosE + cosB*sinE*sinL) ); |
347 | 2.00M | return result; |
348 | 2.00M | } |
349 | | |
350 | | //------------------------------------------------------------------------- |
351 | | // The Sun |
352 | | //------------------------------------------------------------------------- |
353 | | |
354 | | // |
355 | | // Parameters of the Sun's orbit as of the epoch Jan 0.0 1990 |
356 | | // Angles are in radians (after multiplying by CalendarAstronomer::PI/180) |
357 | | // |
358 | 4.20M | #define JD_EPOCH 2447891.5 // Julian day of epoch |
359 | | |
360 | 2.20M | #define SUN_ETA_G (279.403303 * CalendarAstronomer::PI/180) // Ecliptic longitude at epoch |
361 | 4.41M | #define SUN_OMEGA_G (282.768422 * CalendarAstronomer::PI/180) // Ecliptic longitude of perigee |
362 | 2.20M | #define SUN_E 0.016713 // Eccentricity of orbit |
363 | | //double sunR0 1.495585e8 // Semi-major axis in KM |
364 | | //double sunTheta0 (0.533128 * CalendarAstronomer::PI/180) // Angular diameter at R0 |
365 | | |
366 | | // The following three methods, which compute the sun parameters |
367 | | // given above for an arbitrary epoch (whatever time the object is |
368 | | // set to), make only a small difference as compared to using the |
369 | | // above constants. E.g., Sunset times might differ by ~12 |
370 | | // seconds. Furthermore, the eta-g computation is befuddled by |
371 | | // Duffet-Smith's incorrect coefficients (p.86). I've corrected |
372 | | // the first-order coefficient but the others may be off too - no |
373 | | // way of knowing without consulting another source. |
374 | | |
375 | | // /** |
376 | | // * Return the sun's ecliptic longitude at perigee for the current time. |
377 | | // * See Duffett-Smith, p. 86. |
378 | | // * @return radians |
379 | | // */ |
380 | | // private double getSunOmegaG() { |
381 | | // double T = getJulianCentury(); |
382 | | // return (281.2208444 + (1.719175 + 0.000452778*T)*T) * DEG_RAD; |
383 | | // } |
384 | | |
385 | | // /** |
386 | | // * Return the sun's ecliptic longitude for the current time. |
387 | | // * See Duffett-Smith, p. 86. |
388 | | // * @return radians |
389 | | // */ |
390 | | // private double getSunEtaG() { |
391 | | // double T = getJulianCentury(); |
392 | | // //return (279.6966778 + (36000.76892 + 0.0003025*T)*T) * DEG_RAD; |
393 | | // // |
394 | | // // The above line is from Duffett-Smith, and yields manifestly wrong |
395 | | // // results. The below constant is derived empirically to match the |
396 | | // // constant he gives for the 1990 EPOCH. |
397 | | // // |
398 | | // return (279.6966778 + (-0.3262541582718024 + 0.0003025*T)*T) * DEG_RAD; |
399 | | // } |
400 | | |
401 | | // /** |
402 | | // * Return the sun's eccentricity of orbit for the current time. |
403 | | // * See Duffett-Smith, p. 86. |
404 | | // * @return double |
405 | | // */ |
406 | | // private double getSunE() { |
407 | | // double T = getJulianCentury(); |
408 | | // return 0.01675104 - (0.0000418 + 0.000000126*T)*T; |
409 | | // } |
410 | | |
411 | | /** |
412 | | * Find the "true anomaly" (longitude) of an object from |
413 | | * its mean anomaly and the eccentricity of its orbit. This uses |
414 | | * an iterative solution to Kepler's equation. |
415 | | * |
416 | | * @param meanAnomaly The object's longitude calculated as if it were in |
417 | | * a regular, circular orbit, measured in radians |
418 | | * from the point of perigee. |
419 | | * |
420 | | * @param eccentricity The eccentricity of the orbit |
421 | | * |
422 | | * @return The true anomaly (longitude) measured in radians |
423 | | */ |
424 | | static double trueAnomaly(double meanAnomaly, double eccentricity) |
425 | 2.20M | { |
426 | | // First, solve Kepler's equation iteratively |
427 | | // Duffett-Smith, p.90 |
428 | 2.20M | double delta; |
429 | 2.20M | double E = meanAnomaly; |
430 | 4.40M | do { |
431 | 4.40M | delta = E - eccentricity * ::sin(E) - meanAnomaly; |
432 | 4.40M | E = E - delta / (1 - eccentricity * ::cos(E)); |
433 | 4.40M | } |
434 | 4.40M | while (uprv_fabs(delta) > 1e-5); // epsilon = 1e-5 rad |
435 | | |
436 | 2.20M | return 2.0 * ::atan( ::tan(E/2) * ::sqrt( (1+eccentricity) |
437 | 2.20M | /(1-eccentricity) ) ); |
438 | 2.20M | } |
439 | | |
440 | | /** |
441 | | * The longitude of the sun at the time specified by this object. |
442 | | * The longitude is measured in radians along the ecliptic |
443 | | * from the "first point of Aries," the point at which the ecliptic |
444 | | * crosses the earth's equatorial plane at the vernal equinox. |
445 | | * <p> |
446 | | * Currently, this method uses an approximation of the two-body Kepler's |
447 | | * equation for the earth and the sun. It does not take into account the |
448 | | * perturbations caused by the other planets, the moon, etc. |
449 | | * @internal |
450 | | * @deprecated ICU 2.4. This class may be removed or modified. |
451 | | */ |
452 | | double CalendarAstronomer::getSunLongitude() |
453 | 2.20M | { |
454 | | // See page 86 of "Practical Astronomy with your Calculator", |
455 | | // by Peter Duffet-Smith, for details on the algorithm. |
456 | | |
457 | 2.20M | if (isINVALID(sunLongitude)) { |
458 | 2.20M | getSunLongitude(getJulianDay(), sunLongitude, meanAnomalySun); |
459 | 2.20M | } |
460 | 2.20M | return sunLongitude; |
461 | 2.20M | } |
462 | | |
463 | | /** |
464 | | * TODO Make this public when the entire class is package-private. |
465 | | */ |
466 | | /*public*/ void CalendarAstronomer::getSunLongitude(double jDay, double &longitude, double &meanAnomaly) |
467 | 2.20M | { |
468 | | // See page 86 of "Practical Astronomy with your Calculator", |
469 | | // by Peter Duffet-Smith, for details on the algorithm. |
470 | | |
471 | 2.20M | double day = jDay - JD_EPOCH; // Days since epoch |
472 | | |
473 | | // Find the angular distance the sun in a fictitious |
474 | | // circular orbit has travelled since the epoch. |
475 | 2.20M | double epochAngle = norm2PI(CalendarAstronomer_PI2/TROPICAL_YEAR*day); |
476 | | |
477 | | // The epoch wasn't at the sun's perigee; find the angular distance |
478 | | // since perigee, which is called the "mean anomaly" |
479 | 2.20M | meanAnomaly = norm2PI(epochAngle + SUN_ETA_G - SUN_OMEGA_G); |
480 | | |
481 | | // Now find the "true anomaly", e.g. the real solar longitude |
482 | | // by solving Kepler's equation for an elliptical orbit |
483 | | // NOTE: The 3rd ed. of the book lists omega_g and eta_g in different |
484 | | // equations; omega_g is to be correct. |
485 | 2.20M | longitude = norm2PI(trueAnomaly(meanAnomaly, SUN_E) + SUN_OMEGA_G); |
486 | 2.20M | } |
487 | | |
488 | | /** |
489 | | * Constant representing the winter solstice. |
490 | | * For use with {@link #getSunTime getSunTime}. |
491 | | * Note: In this case, "winter" refers to the northern hemisphere's seasons. |
492 | | * @internal |
493 | | * @deprecated ICU 2.4. This class may be removed or modified. |
494 | | */ |
495 | 4.89k | double CalendarAstronomer::WINTER_SOLSTICE() { |
496 | 4.89k | return ((CalendarAstronomer::PI*3)/2); |
497 | 4.89k | } |
498 | | |
499 | 509k | CalendarAstronomer::AngleFunc::~AngleFunc() {} |
500 | | |
501 | | /** |
502 | | * Find the next time at which the sun's ecliptic longitude will have |
503 | | * the desired value. |
504 | | * @internal |
505 | | * @deprecated ICU 2.4. This class may be removed or modified. |
506 | | */ |
507 | | class SunTimeAngleFunc : public CalendarAstronomer::AngleFunc { |
508 | | public: |
509 | | virtual ~SunTimeAngleFunc(); |
510 | 19.0k | virtual double eval(CalendarAstronomer& a) override { return a.getSunLongitude(); } |
511 | | }; |
512 | | |
513 | | SunTimeAngleFunc::~SunTimeAngleFunc() {} |
514 | | |
515 | | UDate CalendarAstronomer::getSunTime(double desired, UBool next) |
516 | 4.89k | { |
517 | 4.89k | SunTimeAngleFunc func; |
518 | 4.89k | return timeOfAngle( func, |
519 | 4.89k | desired, |
520 | 4.89k | TROPICAL_YEAR, |
521 | 4.89k | MINUTE_MS, |
522 | 4.89k | next); |
523 | 4.89k | } |
524 | | |
525 | | //------------------------------------------------------------------------- |
526 | | // The Moon |
527 | | //------------------------------------------------------------------------- |
528 | | |
529 | 2.00M | #define moonL0 (318.351648 * CalendarAstronomer::PI/180 ) // Mean long. at epoch |
530 | 2.00M | #define moonP0 ( 36.340410 * CalendarAstronomer::PI/180 ) // Mean long. of perigee |
531 | 2.00M | #define moonN0 ( 318.510107 * CalendarAstronomer::PI/180 ) // Mean long. of node |
532 | 4.00M | #define moonI ( 5.145366 * CalendarAstronomer::PI/180 ) // Inclination of orbit |
533 | | #define moonE ( 0.054900 ) // Eccentricity of orbit |
534 | | |
535 | | // These aren't used right now |
536 | | #define moonA ( 3.84401e5 ) // semi-major axis (km) |
537 | | #define moonT0 ( 0.5181 * CalendarAstronomer::PI/180 ) // Angular size at distance A |
538 | | #define moonPi ( 0.9507 * CalendarAstronomer::PI/180 ) // Parallax at distance A |
539 | | |
540 | | /** |
541 | | * The position of the moon at the time set on this |
542 | | * object, in equatorial coordinates. |
543 | | * @internal |
544 | | * @deprecated ICU 2.4. This class may be removed or modified. |
545 | | */ |
546 | | const CalendarAstronomer::Equatorial& CalendarAstronomer::getMoonPosition() |
547 | 2.00M | { |
548 | | // |
549 | | // See page 142 of "Practical Astronomy with your Calculator", |
550 | | // by Peter Duffet-Smith, for details on the algorithm. |
551 | | // |
552 | 2.00M | if (moonPositionSet == false) { |
553 | | // Calculate the solar longitude. Has the side effect of |
554 | | // filling in "meanAnomalySun" as well. |
555 | 2.00M | getSunLongitude(); |
556 | | |
557 | | // |
558 | | // Find the # of days since the epoch of our orbital parameters. |
559 | | // TODO: Convert the time of day portion into ephemeris time |
560 | | // |
561 | 2.00M | double day = getJulianDay() - JD_EPOCH; // Days since epoch |
562 | | |
563 | | // Calculate the mean longitude and anomaly of the moon, based on |
564 | | // a circular orbit. Similar to the corresponding solar calculation. |
565 | 2.00M | double meanLongitude = norm2PI(13.1763966*PI/180*day + moonL0); |
566 | 2.00M | double meanAnomalyMoon = norm2PI(meanLongitude - 0.1114041*PI/180 * day - moonP0); |
567 | | |
568 | | // |
569 | | // Calculate the following corrections: |
570 | | // Evection: the sun's gravity affects the moon's eccentricity |
571 | | // Annual Eqn: variation in the effect due to earth-sun distance |
572 | | // A3: correction factor (for ???) |
573 | | // |
574 | 2.00M | double evection = 1.2739*PI/180 * ::sin(2 * (meanLongitude - sunLongitude) |
575 | 2.00M | - meanAnomalyMoon); |
576 | 2.00M | double annual = 0.1858*PI/180 * ::sin(meanAnomalySun); |
577 | 2.00M | double a3 = 0.3700*PI/180 * ::sin(meanAnomalySun); |
578 | | |
579 | 2.00M | meanAnomalyMoon += evection - annual - a3; |
580 | | |
581 | | // |
582 | | // More correction factors: |
583 | | // center equation of the center correction |
584 | | // a4 yet another error correction (???) |
585 | | // |
586 | | // TODO: Skip the equation of the center correction and solve Kepler's eqn? |
587 | | // |
588 | 2.00M | double center = 6.2886*PI/180 * ::sin(meanAnomalyMoon); |
589 | 2.00M | double a4 = 0.2140*PI/180 * ::sin(2 * meanAnomalyMoon); |
590 | | |
591 | | // Now find the moon's corrected longitude |
592 | 2.00M | double moonLongitude = meanLongitude + evection + center - annual + a4; |
593 | | |
594 | | // |
595 | | // And finally, find the variation, caused by the fact that the sun's |
596 | | // gravitational pull on the moon varies depending on which side of |
597 | | // the earth the moon is on |
598 | | // |
599 | 2.00M | double variation = 0.6583*CalendarAstronomer::PI/180 * ::sin(2*(moonLongitude - sunLongitude)); |
600 | | |
601 | 2.00M | moonLongitude += variation; |
602 | | |
603 | | // |
604 | | // What we've calculated so far is the moon's longitude in the plane |
605 | | // of its own orbit. Now map to the ecliptic to get the latitude |
606 | | // and longitude. First we need to find the longitude of the ascending |
607 | | // node, the position on the ecliptic where it is crossed by the moon's |
608 | | // orbit as it crosses from the southern to the northern hemisphere. |
609 | | // |
610 | 2.00M | double nodeLongitude = norm2PI(moonN0 - 0.0529539*PI/180 * day); |
611 | | |
612 | 2.00M | nodeLongitude -= 0.16*PI/180 * ::sin(meanAnomalySun); |
613 | | |
614 | 2.00M | double y = ::sin(moonLongitude - nodeLongitude); |
615 | 2.00M | double x = cos(moonLongitude - nodeLongitude); |
616 | | |
617 | 2.00M | moonEclipLong = ::atan2(y*cos(moonI), x) + nodeLongitude; |
618 | 2.00M | double moonEclipLat = ::asin(y * ::sin(moonI)); |
619 | | |
620 | 2.00M | eclipticToEquatorial(moonPosition, moonEclipLong, moonEclipLat); |
621 | 2.00M | moonPositionSet = true; |
622 | 2.00M | } |
623 | 2.00M | return moonPosition; |
624 | 2.00M | } |
625 | | |
626 | | /** |
627 | | * The "age" of the moon at the time specified in this object. |
628 | | * This is really the angle between the |
629 | | * current ecliptic longitudes of the sun and the moon, |
630 | | * measured in radians. |
631 | | * |
632 | | * @internal |
633 | | * @deprecated ICU 2.4. This class may be removed or modified. |
634 | | */ |
635 | 2.00M | double CalendarAstronomer::getMoonAge() { |
636 | | // See page 147 of "Practical Astronomy with your Calculator", |
637 | | // by Peter Duffet-Smith, for details on the algorithm. |
638 | | // |
639 | | // Force the moon's position to be calculated. We're going to use |
640 | | // some the intermediate results cached during that calculation. |
641 | | // |
642 | 2.00M | getMoonPosition(); |
643 | | |
644 | 2.00M | return norm2PI(moonEclipLong - sunLongitude); |
645 | 2.00M | } |
646 | | |
647 | | /** |
648 | | * Constant representing a new moon. |
649 | | * For use with {@link #getMoonTime getMoonTime} |
650 | | * @internal |
651 | | * @deprecated ICU 2.4. This class may be removed or modified. |
652 | | */ |
653 | 504k | CalendarAstronomer::MoonAge CalendarAstronomer::NEW_MOON() { |
654 | 504k | return CalendarAstronomer::MoonAge(0); |
655 | 504k | } |
656 | | |
657 | | /** |
658 | | * Constant representing the moon's last quarter. |
659 | | * For use with {@link #getMoonTime getMoonTime} |
660 | | * @internal |
661 | | * @deprecated ICU 2.4. This class may be removed or modified. |
662 | | */ |
663 | | |
664 | | class MoonTimeAngleFunc : public CalendarAstronomer::AngleFunc { |
665 | | public: |
666 | | virtual ~MoonTimeAngleFunc(); |
667 | 1.94M | virtual double eval(CalendarAstronomer& a) override { return a.getMoonAge(); } |
668 | | }; |
669 | | |
670 | | MoonTimeAngleFunc::~MoonTimeAngleFunc() {} |
671 | | |
672 | | /*const CalendarAstronomer::MoonAge CalendarAstronomer::LAST_QUARTER() { |
673 | | return CalendarAstronomer::MoonAge((CalendarAstronomer::PI*3)/2); |
674 | | }*/ |
675 | | |
676 | | /** |
677 | | * Find the next or previous time at which the moon will be in the |
678 | | * desired phase. |
679 | | * <p> |
680 | | * @param desired The desired phase of the moon. |
681 | | * @param next <tt>true</tt> if the next occurrence of the phase |
682 | | * is desired, <tt>false</tt> for the previous occurrence. |
683 | | * @internal |
684 | | * @deprecated ICU 2.4. This class may be removed or modified. |
685 | | */ |
686 | 504k | UDate CalendarAstronomer::getMoonTime(const CalendarAstronomer::MoonAge& desired, UBool next) { |
687 | 504k | MoonTimeAngleFunc func; |
688 | 504k | return timeOfAngle( func, |
689 | 504k | desired.value, |
690 | 504k | SYNODIC_MONTH, |
691 | 504k | MINUTE_MS, |
692 | 504k | next); |
693 | 504k | } |
694 | | |
695 | | //------------------------------------------------------------------------- |
696 | | // Interpolation methods for finding the time at which a given event occurs |
697 | | //------------------------------------------------------------------------- |
698 | | |
699 | | UDate CalendarAstronomer::timeOfAngle(AngleFunc& func, double desired, |
700 | | double periodDays, double epsilon, UBool next) |
701 | 509k | { |
702 | | // Find the value of the function at the current time |
703 | 509k | double lastAngle = func.eval(*this); |
704 | | |
705 | | // Find out how far we are from the desired angle |
706 | 509k | double deltaAngle = norm2PI(desired - lastAngle) ; |
707 | | |
708 | | // Using the average period, estimate the next (or previous) time at |
709 | | // which the desired angle occurs. |
710 | 509k | double deltaT = (deltaAngle + (next ? 0.0 : - CalendarAstronomer_PI2 )) * (periodDays*DAY_MS) / CalendarAstronomer_PI2; |
711 | | |
712 | 509k | double lastDeltaT = deltaT; // Liu |
713 | 509k | UDate startTime = fTime; // Liu |
714 | | |
715 | 509k | setTime(fTime + uprv_ceil(deltaT)); |
716 | | |
717 | | // Now iterate until we get the error below epsilon. Throughout |
718 | | // this loop we use normPI to get values in the range -Pi to Pi, |
719 | | // since we're using them as correction factors rather than absolute angles. |
720 | 1.45M | do { |
721 | | // Evaluate the function at the time we've estimated |
722 | 1.45M | double angle = func.eval(*this); |
723 | | |
724 | | // Find the # of milliseconds per radian at this point on the curve |
725 | 1.45M | double factor = uprv_fabs(deltaT / normPI(angle-lastAngle)); |
726 | | |
727 | | // Correct the time estimate based on how far off the angle is |
728 | 1.45M | deltaT = normPI(desired - angle) * factor; |
729 | | |
730 | | // HACK: |
731 | | // |
732 | | // If abs(deltaT) begins to diverge we need to quit this loop. |
733 | | // This only appears to happen when attempting to locate, for |
734 | | // example, a new moon on the day of the new moon. E.g.: |
735 | | // |
736 | | // This result is correct: |
737 | | // newMoon(7508(Mon Jul 23 00:00:00 CST 1990,false))= |
738 | | // Sun Jul 22 10:57:41 CST 1990 |
739 | | // |
740 | | // But attempting to make the same call a day earlier causes deltaT |
741 | | // to diverge: |
742 | | // CalendarAstronomer.timeOfAngle() diverging: 1.348508727575625E9 -> |
743 | | // 1.3649828540224032E9 |
744 | | // newMoon(7507(Sun Jul 22 00:00:00 CST 1990,false))= |
745 | | // Sun Jul 08 13:56:15 CST 1990 |
746 | | // |
747 | | // As a temporary solution, we catch this specific condition and |
748 | | // adjust our start time by one eighth period days (either forward |
749 | | // or backward) and try again. |
750 | | // Liu 11/9/00 |
751 | 1.45M | if (uprv_fabs(deltaT) > uprv_fabs(lastDeltaT)) { |
752 | 822 | double delta = uprv_ceil (periodDays * DAY_MS / 8.0); |
753 | 822 | setTime(startTime + (next ? delta : -delta)); |
754 | 822 | return timeOfAngle(func, desired, periodDays, epsilon, next); |
755 | 822 | } |
756 | | |
757 | 1.45M | lastDeltaT = deltaT; |
758 | 1.45M | lastAngle = angle; |
759 | | |
760 | 1.45M | setTime(fTime + uprv_ceil(deltaT)); |
761 | 1.45M | } |
762 | 1.45M | while (uprv_fabs(deltaT) > epsilon); |
763 | | |
764 | 509k | return fTime; |
765 | 509k | } |
766 | | |
767 | | /** |
768 | | * Return the obliquity of the ecliptic (the angle between the ecliptic |
769 | | * and the earth's equator) at the current time. This varies due to |
770 | | * the precession of the earth's axis. |
771 | | * |
772 | | * @return the obliquity of the ecliptic relative to the equator, |
773 | | * measured in radians. |
774 | | */ |
775 | 2.00M | double CalendarAstronomer::eclipticObliquity() { |
776 | 2.00M | const double epoch = 2451545.0; // 2000 AD, January 1.5 |
777 | | |
778 | 2.00M | double T = (getJulianDay() - epoch) / 36525; |
779 | | |
780 | 2.00M | double eclipObliquity = 23.439292 |
781 | 2.00M | - 46.815/3600 * T |
782 | 2.00M | - 0.0006/3600 * T*T |
783 | 2.00M | + 0.00181/3600 * T*T*T; |
784 | | |
785 | 2.00M | return eclipObliquity * DEG_RAD; |
786 | 2.00M | } |
787 | | |
788 | | |
789 | | //------------------------------------------------------------------------- |
790 | | // Private data |
791 | | //------------------------------------------------------------------------- |
792 | 2.71M | void CalendarAstronomer::clearCache() { |
793 | 2.71M | const double INVALID = uprv_getNaN(); |
794 | | |
795 | 2.71M | julianDay = INVALID; |
796 | 2.71M | sunLongitude = INVALID; |
797 | 2.71M | meanAnomalySun = INVALID; |
798 | 2.71M | moonEclipLong = INVALID; |
799 | | |
800 | 2.71M | moonPositionSet = false; |
801 | 2.71M | } |
802 | | |
803 | | // Debugging functions |
804 | | UnicodeString CalendarAstronomer::Ecliptic::toString() const |
805 | 0 | { |
806 | | #ifdef U_DEBUG_ASTRO |
807 | | char tmp[800]; |
808 | | snprintf(tmp, sizeof(tmp), "[%.5f,%.5f]", longitude*RAD_DEG, latitude*RAD_DEG); |
809 | | return UnicodeString(tmp, ""); |
810 | | #else |
811 | 0 | return {}; |
812 | 0 | #endif |
813 | 0 | } |
814 | | |
815 | | UnicodeString CalendarAstronomer::Equatorial::toString() const |
816 | 0 | { |
817 | | #ifdef U_DEBUG_ASTRO |
818 | | char tmp[400]; |
819 | | snprintf(tmp, sizeof(tmp), "%f,%f", |
820 | | (ascension*RAD_DEG), (declination*RAD_DEG)); |
821 | | return UnicodeString(tmp, ""); |
822 | | #else |
823 | 0 | return {}; |
824 | 0 | #endif |
825 | 0 | } |
826 | | |
827 | | |
828 | | // =============== Calendar Cache ================ |
829 | | |
830 | 7 | void CalendarCache::createCache(CalendarCache** cache, UErrorCode& status) { |
831 | 7 | ucln_i18n_registerCleanup(UCLN_I18N_ASTRO_CALENDAR, calendar_astro_cleanup); |
832 | 7 | if(cache == nullptr) { |
833 | 0 | status = U_MEMORY_ALLOCATION_ERROR; |
834 | 7 | } else { |
835 | 7 | *cache = new CalendarCache(32, status); |
836 | 7 | if(U_FAILURE(status)) { |
837 | 0 | delete *cache; |
838 | 0 | *cache = nullptr; |
839 | 0 | } |
840 | 7 | } |
841 | 7 | } |
842 | | |
843 | 627k | int32_t CalendarCache::get(CalendarCache** cache, int32_t key, UErrorCode &status) { |
844 | 627k | int32_t res; |
845 | | |
846 | 627k | if(U_FAILURE(status)) { |
847 | 19 | return 0; |
848 | 19 | } |
849 | 627k | umtx_lock(&ccLock); |
850 | | |
851 | 627k | if(*cache == nullptr) { |
852 | 7 | createCache(cache, status); |
853 | 7 | if(U_FAILURE(status)) { |
854 | 0 | umtx_unlock(&ccLock); |
855 | 0 | return 0; |
856 | 0 | } |
857 | 7 | } |
858 | | |
859 | 627k | res = uhash_igeti((*cache)->fTable, key); |
860 | 627k | U_DEBUG_ASTRO_MSG(("%p: GET: [%d] == %d\n", (*cache)->fTable, key, res)); |
861 | | |
862 | 627k | umtx_unlock(&ccLock); |
863 | 627k | return res; |
864 | 627k | } |
865 | | |
866 | 52.6k | void CalendarCache::put(CalendarCache** cache, int32_t key, int32_t value, UErrorCode &status) { |
867 | 52.6k | if(U_FAILURE(status)) { |
868 | 0 | return; |
869 | 0 | } |
870 | 52.6k | umtx_lock(&ccLock); |
871 | | |
872 | 52.6k | if(*cache == nullptr) { |
873 | 0 | createCache(cache, status); |
874 | 0 | if(U_FAILURE(status)) { |
875 | 0 | umtx_unlock(&ccLock); |
876 | 0 | return; |
877 | 0 | } |
878 | 0 | } |
879 | | |
880 | 52.6k | uhash_iputi((*cache)->fTable, key, value, &status); |
881 | 52.6k | U_DEBUG_ASTRO_MSG(("%p: PUT: [%d] := %d\n", (*cache)->fTable, key, value)); |
882 | | |
883 | 52.6k | umtx_unlock(&ccLock); |
884 | 52.6k | } |
885 | | |
886 | 7 | CalendarCache::CalendarCache(int32_t size, UErrorCode &status) { |
887 | 7 | fTable = uhash_openSize(uhash_hashLong, uhash_compareLong, nullptr, size, &status); |
888 | 7 | U_DEBUG_ASTRO_MSG(("%p: Opening.\n", fTable)); |
889 | 7 | } |
890 | | |
891 | 0 | CalendarCache::~CalendarCache() { |
892 | 0 | if(fTable != nullptr) { |
893 | 0 | U_DEBUG_ASTRO_MSG(("%p: Closing.\n", fTable)); |
894 | 0 | uhash_close(fTable); |
895 | 0 | } |
896 | 0 | } |
897 | | |
898 | | U_NAMESPACE_END |
899 | | |
900 | | #endif // !UCONFIG_NO_FORMATTING |