]>
git.saurik.com Git - apple/icu.git/blob - icuSources/test/intltest/astrotst.cpp
1 // © 2016 and later: Unicode, Inc. and others.
2 // License & terms of use: http://www.unicode.org/copyright.html
3 /********************************************************************
5 * Copyright (c) 1996-2016, International Business Machines Corporation and
6 * others. All Rights Reserved.
7 ********************************************************************/
9 /* Test CalendarAstronomer for C++ */
11 #include "unicode/utypes.h"
13 #include "unicode/locid.h"
15 #if !UCONFIG_NO_FORMATTING
20 #include "gregoimp.h" // for Math
21 #include "unicode/simpletz.h"
24 #define CASE(id,test) case id: name = #test; if (exec) { logln(#test "---"); logln((UnicodeString)""); test(); } break
26 AstroTest::AstroTest(): astro(NULL
), gc(NULL
) {
29 void AstroTest::runIndexedTest( int32_t index
, UBool exec
, const char* &name
, char* /*par*/ )
31 if (exec
) logln("TestSuite AstroTest");
34 CASE(0,TestSolarLongitude
);
35 CASE(1,TestLunarPosition
);
36 CASE(2,TestCoordinates
);
38 CASE(4,TestSunriseTimes
);
41 default: name
= ""; break;
47 #define ASSERT_OK(x) UPRV_BLOCK_MACRO_BEGIN { \
49 dataerrln("%s:%d: %s\n", __FILE__, __LINE__, u_errorName(x)); \
52 } UPRV_BLOCK_MACRO_END
55 void AstroTest::initAstro(UErrorCode
&status
) {
56 if(U_FAILURE(status
)) return;
58 if((astro
!= NULL
) || (gc
!= NULL
)) {
59 dataerrln("Err: initAstro() called twice!");
61 if(U_SUCCESS(status
)) {
62 status
= U_INTERNAL_PROGRAM_ERROR
;
66 if(U_FAILURE(status
)) return;
68 astro
= new CalendarAstronomer();
69 gc
= Calendar::createInstance(TimeZone::getGMT()->clone(), status
);
72 void AstroTest::closeAstro(UErrorCode
&/*status*/) {
83 void AstroTest::TestSolarLongitude(void) {
84 UErrorCode status
= U_ZERO_ERROR
;
89 int32_t d
[5]; double f
;
91 { { 1980, 7, 27, 0, 00 }, 124.114347 },
92 { { 1988, 7, 27, 00, 00 }, 124.187732 }
96 for (uint32_t i
= 0; i
< UPRV_LENGTHOF(tests
); i
++) {
98 gc
->set(tests
[i
].d
[0], tests
[i
].d
[1]-1, tests
[i
].d
[2], tests
[i
].d
[3], tests
[i
].d
[4]);
100 astro
->setDate(gc
->getTime(status
));
102 double longitude
= astro
->getSunLongitude();
104 CalendarAstronomer::Equatorial result
;
105 astro
->getSunPosition(result
);
106 logln((UnicodeString
)"Sun position is " + result
.toString() + (UnicodeString
)"; " /* + result.toHmsString()*/ + " Sun longitude is " + longitude
);
114 void AstroTest::TestLunarPosition(void) {
115 UErrorCode status
= U_ZERO_ERROR
;
119 static const double tests
[][7] = {
120 { 1979, 2, 26, 16, 00, 0, 0 }
124 for (int32_t i
= 0; i
< UPRV_LENGTHOF(tests
); i
++) {
126 gc
->set((int32_t)tests
[i
][0], (int32_t)tests
[i
][1]-1, (int32_t)tests
[i
][2], (int32_t)tests
[i
][3], (int32_t)tests
[i
][4]);
127 astro
->setDate(gc
->getTime(status
));
129 const CalendarAstronomer::Equatorial
& result
= astro
->getMoonPosition();
130 logln((UnicodeString
)"Moon position is " + result
.toString() + (UnicodeString
)"; " /* + result->toHmsString()*/);
139 void AstroTest::TestCoordinates(void) {
140 UErrorCode status
= U_ZERO_ERROR
;
144 CalendarAstronomer::Equatorial result
;
145 astro
->eclipticToEquatorial(result
, 139.686111 * CalendarAstronomer::PI
/ 180.0, 4.875278* CalendarAstronomer::PI
/ 180.0);
146 logln((UnicodeString
)"result is " + result
.toString() + (UnicodeString
)"; " /* + result.toHmsString()*/ );
153 void AstroTest::TestCoverage(void) {
154 UErrorCode status
= U_ZERO_ERROR
;
157 GregorianCalendar
*cal
= new GregorianCalendar(1958, UCAL_AUGUST
, 15,status
);
158 UDate then
= cal
->getTime(status
);
159 CalendarAstronomer
*myastro
= new CalendarAstronomer(then
);
162 //Latitude: 34 degrees 05' North
163 //Longitude: 118 degrees 22' West
164 double laLat
= 34 + 5./60, laLong
= 360 - (118 + 22./60);
165 CalendarAstronomer
*myastro2
= new CalendarAstronomer(laLong
, laLat
);
167 double eclLat
= laLat
* CalendarAstronomer::PI
/ 360;
168 double eclLong
= laLong
* CalendarAstronomer::PI
/ 360;
170 CalendarAstronomer::Ecliptic
ecl(eclLat
, eclLong
);
171 CalendarAstronomer::Equatorial eq
;
172 CalendarAstronomer::Horizon hor
;
174 logln("ecliptic: " + ecl
.toString());
175 CalendarAstronomer
*myastro3
= new CalendarAstronomer();
176 myastro3
->setJulianDay((4713 + 2000) * 365.25);
178 CalendarAstronomer
*astronomers
[] = {
179 myastro
, myastro2
, myastro3
, myastro2
// check cache
182 for (uint32_t i
= 0; i
< UPRV_LENGTHOF(astronomers
); ++i
) {
183 CalendarAstronomer
*anAstro
= astronomers
[i
];
185 //logln("astro: " + astro);
186 logln((UnicodeString
)" date: " + anAstro
->getTime());
187 logln((UnicodeString
)" cent: " + anAstro
->getJulianCentury());
188 logln((UnicodeString
)" gw sidereal: " + anAstro
->getGreenwichSidereal());
189 logln((UnicodeString
)" loc sidereal: " + anAstro
->getLocalSidereal());
190 logln((UnicodeString
)" equ ecl: " + (anAstro
->eclipticToEquatorial(eq
,ecl
)).toString());
191 logln((UnicodeString
)" equ long: " + (anAstro
->eclipticToEquatorial(eq
, eclLong
)).toString());
192 logln((UnicodeString
)" horiz: " + (anAstro
->eclipticToHorizon(hor
, eclLong
)).toString());
193 logln((UnicodeString
)" sunrise: " + (anAstro
->getSunRiseSet(TRUE
)));
194 logln((UnicodeString
)" sunset: " + (anAstro
->getSunRiseSet(FALSE
)));
195 logln((UnicodeString
)" moon phase: " + anAstro
->getMoonPhase());
196 logln((UnicodeString
)" moonrise: " + (anAstro
->getMoonRiseSet(TRUE
)));
197 logln((UnicodeString
)" moonset: " + (anAstro
->getMoonRiseSet(FALSE
)));
198 logln((UnicodeString
)" prev summer solstice: " + (anAstro
->getSunTime(CalendarAstronomer::SUMMER_SOLSTICE(), FALSE
)));
199 logln((UnicodeString
)" next summer solstice: " + (anAstro
->getSunTime(CalendarAstronomer::SUMMER_SOLSTICE(), TRUE
)));
200 logln((UnicodeString
)" prev full moon: " + (anAstro
->getMoonTime(CalendarAstronomer::FULL_MOON(), FALSE
)));
201 logln((UnicodeString
)" next full moon: " + (anAstro
->getMoonTime(CalendarAstronomer::FULL_MOON(), TRUE
)));
215 void AstroTest::TestSunriseTimes(void) {
216 UErrorCode status
= U_ZERO_ERROR
;
220 // logln("Sunrise/Sunset times for San Jose, California, USA");
221 // CalendarAstronomer *astro2 = new CalendarAstronomer(-121.55, 37.20);
222 // TimeZone *tz = TimeZone::createTimeZone("America/Los_Angeles");
224 // We'll use a table generated by the UNSO website as our reference
225 // From: http://aa.usno.navy.mil/
226 //-Location: W079 25, N43 40
227 //-Rise and Set for the Sun for 2001
228 //-Zone: 4h West of Greenwich
262 logln("Sunrise/Sunset times for Toronto, Canada");
263 // long = 79 25", lat = 43 40"
264 CalendarAstronomer
astro3(-(79+25/60), 43+40/60);
266 // As of ICU4J 2.8 the ICU4J time zones implement pass-through
267 // to the underlying JDK. Because of variation in the
268 // underlying JDKs, we have to use a fixed-offset
269 // SimpleTimeZone to get consistent behavior between JDKs.
270 // The offset we want is [-18000000, 3600000] (raw, dst).
273 // TimeZone tz = TimeZone.getTimeZone("America/Montreal");
274 SimpleTimeZone
tz(-18000000 + 3600000, "Montreal(FIXED)");
276 GregorianCalendar
cal(tz
.clone(), Locale::getUS(), status
);
277 GregorianCalendar
cal2(tz
.clone(), Locale::getUS(), status
);
279 cal
.set(UCAL_YEAR
, 2001);
280 cal
.set(UCAL_MONTH
, UCAL_APRIL
);
281 cal
.set(UCAL_DAY_OF_MONTH
, 1);
282 cal
.set(UCAL_HOUR_OF_DAY
, 12); // must be near local noon for getSunRiseSet to work
284 LocalPointer
<DateFormat
> df_t(DateFormat::createTimeInstance(DateFormat::MEDIUM
,Locale::getUS()));
285 LocalPointer
<DateFormat
> df_d(DateFormat::createDateInstance(DateFormat::MEDIUM
,Locale::getUS()));
286 LocalPointer
<DateFormat
> df_dt(DateFormat::createDateTimeInstance(DateFormat::MEDIUM
, DateFormat::MEDIUM
, Locale::getUS()));
287 if(!df_t
.isValid() || !df_d
.isValid() || !df_dt
.isValid()) {
288 dataerrln("couldn't create dateformats.");
292 df_t
->adoptTimeZone(tz
.clone());
293 df_d
->adoptTimeZone(tz
.clone());
294 df_dt
->adoptTimeZone(tz
.clone());
296 for (int32_t i
=0; i
< 30; i
++) {
298 astro3
.setDate(cal
.getTime(status
));
299 logln("getRiseSet(TRUE)\n");
300 UDate sunrise
= astro3
.getSunRiseSet(TRUE
);
301 logln("getRiseSet(FALSE)\n");
302 UDate sunset
= astro3
.getSunRiseSet(FALSE
);
303 logln("end of getRiseSet\n");
305 cal2
.setTime(cal
.getTime(status
), status
);
306 cal2
.set(UCAL_SECOND
, 0);
307 cal2
.set(UCAL_MILLISECOND
, 0);
309 cal2
.set(UCAL_HOUR_OF_DAY
, USNO
[4*i
+0]);
310 cal2
.set(UCAL_MINUTE
, USNO
[4*i
+1]);
311 UDate exprise
= cal2
.getTime(status
);
312 cal2
.set(UCAL_HOUR_OF_DAY
, USNO
[4*i
+2]);
313 cal2
.set(UCAL_MINUTE
, USNO
[4*i
+3]);
314 UDate expset
= cal2
.getTime(status
);
315 // Compute delta of what we got to the USNO data, in seconds
316 int32_t deltarise
= (int32_t)uprv_fabs((sunrise
- exprise
) / 1000);
317 int32_t deltaset
= (int32_t)uprv_fabs((sunset
- expset
) / 1000);
319 // Allow a deviation of 0..MAX_DEV seconds
320 // It would be nice to get down to 60 seconds, but at this
321 // point that appears to be impossible without a redo of the
322 // algorithm using something more advanced than Duffett-Smith.
323 int32_t MAX_DEV
= 180;
324 UnicodeString s1
, s2
, s3
, s4
, s5
;
325 if (deltarise
> MAX_DEV
|| deltaset
> MAX_DEV
) {
326 if (deltarise
> MAX_DEV
) {
327 errln("FAIL: (rise) " + df_d
->format(cal
.getTime(status
),s1
) +
328 ", Sunrise: " + df_dt
->format(sunrise
, s2
) +
329 " (USNO " + df_t
->format(exprise
,s3
) +
330 " d=" + deltarise
+ "s)");
332 logln(df_d
->format(cal
.getTime(status
),s1
) +
333 ", Sunrise: " + df_dt
->format(sunrise
,s2
) +
334 " (USNO " + df_t
->format(exprise
,s3
) + ")");
336 s1
.remove(); s2
.remove(); s3
.remove(); s4
.remove(); s5
.remove();
337 if (deltaset
> MAX_DEV
) {
338 errln("FAIL: (set) " + df_d
->format(cal
.getTime(status
),s1
) +
339 ", Sunset: " + df_dt
->format(sunset
,s2
) +
340 " (USNO " + df_t
->format(expset
,s3
) +
341 " d=" + deltaset
+ "s)");
343 logln(df_d
->format(cal
.getTime(status
),s1
) +
344 ", Sunset: " + df_dt
->format(sunset
,s2
) +
345 " (USNO " + df_t
->format(expset
,s3
) + ")");
348 logln(df_d
->format(cal
.getTime(status
),s1
) +
349 ", Sunrise: " + df_dt
->format(sunrise
,s2
) +
350 " (USNO " + df_t
->format(exprise
,s3
) + ")" +
351 ", Sunset: " + df_dt
->format(sunset
,s4
) +
352 " (USNO " + df_t
->format(expset
,s5
) + ")");
354 cal
.add(UCAL_DATE
, 1, status
);
357 // CalendarAstronomer a = new CalendarAstronomer(-(71+5/60), 42+37/60);
359 // cal.set(cal.YEAR, 1986);
360 // cal.set(cal.MONTH, cal.MARCH);
361 // cal.set(cal.DATE, 10);
362 // cal.set(cal.YEAR, 1988);
363 // cal.set(cal.MONTH, cal.JULY);
364 // cal.set(cal.DATE, 27);
365 // a.setDate(cal.getTime());
366 // long r = a.getSunRiseSet2(true);
373 void AstroTest::TestBasics(void) {
374 UErrorCode status
= U_ZERO_ERROR
;
376 if (U_FAILURE(status
)) {
377 dataerrln("Got error: %s", u_errorName(status
));
381 // Check that our JD computation is the same as the book's (p. 88)
382 GregorianCalendar
cal3(TimeZone::getGMT()->clone(), Locale::getUS(), status
);
383 LocalPointer
<DateFormat
> d3(DateFormat::createDateTimeInstance(DateFormat::MEDIUM
,DateFormat::MEDIUM
,Locale::getUS()));
385 dataerrln("Got error: %s", u_errorName(status
));
389 d3
->setTimeZone(*TimeZone::getGMT());
391 cal3
.set(UCAL_YEAR
, 1980);
392 cal3
.set(UCAL_MONTH
, UCAL_JULY
);
393 cal3
.set(UCAL_DATE
, 2);
394 logln("cal3[a]=%.1lf, d=%d\n", cal3
.getTime(status
), cal3
.get(UCAL_JULIAN_DAY
,status
));
397 logln(UnicodeString("cal3[a] = ") + d3
->format(cal3
.getTime(status
),s
));
400 cal3
.set(UCAL_YEAR
, 1980);
401 cal3
.set(UCAL_MONTH
, UCAL_JULY
);
402 cal3
.set(UCAL_DATE
, 27);
403 logln("cal3=%.1lf, d=%d\n", cal3
.getTime(status
), cal3
.get(UCAL_JULIAN_DAY
,status
));
408 logln(UnicodeString("cal3 = ") + d3
->format(cal3
.getTime(status
),s
));
410 astro
->setTime(cal3
.getTime(status
));
411 double jd
= astro
->getJulianDay() - 2447891.5;
415 logln(d3
->format(cal3
.getTime(status
),s
) + " => " + jd
);
418 errln("FAIL: " + d3
->format(cal3
.getTime(status
), s
) + " => " + jd
+
419 ", expected " + exp
);
423 // cal3.set(cal3.YEAR, 1990);
424 // cal3.set(cal3.MONTH, Calendar.JANUARY);
425 // cal3.set(cal3.DATE, 1);
426 // cal3.add(cal3.DATE, -1);
427 // astro.setDate(cal3.getTime());
436 void AstroTest::TestMoonAge(void){
437 UErrorCode status
= U_ZERO_ERROR
;
441 // more testcases are around the date 05/20/2012
442 //ticket#3785 UDate ud0 = 1337557623000.0;
443 static const double testcase
[][10] = {{2012, 5, 20 , 16 , 48, 59},
444 {2012, 5, 20 , 16 , 47, 34},
445 {2012, 5, 21, 00, 00, 00},
446 {2012, 5, 20, 14, 55, 59},
447 {2012, 5, 21, 7, 40, 40},
448 {2023, 9, 25, 10,00, 00},
449 {2008, 7, 7, 15, 00, 33},
450 {1832, 9, 24, 2, 33, 41 },
451 {2016, 1, 31, 23, 59, 59},
452 {2099, 5, 20, 14, 55, 59}
454 // Moon phase angle - Got from http://www.moonsystem.to/checkupe.htm
455 static const double angle
[] = {356.8493418421329, 356.8386760059673, 0.09625415252237701, 355.9986960782416, 3.5714026601303317, 124.26906744384183, 53.50364630964228,
456 357.54163205513123, 268.41779281511094, 4.82340276581624};
457 static const double precision
= CalendarAstronomer::PI
/32;
458 for (int32_t i
= 0; i
< UPRV_LENGTHOF(testcase
); i
++) {
460 logln((UnicodeString
)"CASE["+i
+"]: Year "+(int32_t)testcase
[i
][0]+" Month "+(int32_t)testcase
[i
][1]+" Day "+
461 (int32_t)testcase
[i
][2]+" Hour "+(int32_t)testcase
[i
][3]+" Minutes "+(int32_t)testcase
[i
][4]+
462 " Seconds "+(int32_t)testcase
[i
][5]);
463 gc
->set((int32_t)testcase
[i
][0], (int32_t)testcase
[i
][1]-1, (int32_t)testcase
[i
][2], (int32_t)testcase
[i
][3], (int32_t)testcase
[i
][4], (int32_t)testcase
[i
][5]);
464 astro
->setDate(gc
->getTime(status
));
465 double expectedAge
= (angle
[i
]*CalendarAstronomer::PI
)/180;
466 double got
= astro
->getMoonAge();
468 if(!(got
>expectedAge
-precision
&& got
<expectedAge
+precision
)){
469 errln((UnicodeString
)"FAIL: expected " + expectedAge
+
472 logln((UnicodeString
)"PASS: expected " + expectedAge
+
481 // TODO: try finding next new moon after 07/28/1984 16:00 GMT