]> git.saurik.com Git - apple/icu.git/blob - icuSources/test/intltest/astrotst.cpp
ICU-57131.0.1.tar.gz
[apple/icu.git] / icuSources / test / intltest / astrotst.cpp
1 /********************************************************************
2 * COPYRIGHT:
3 * Copyright (c) 1996-2016, International Business Machines Corporation and
4 * others. All Rights Reserved.
5 ********************************************************************/
6
7 /* Test CalendarAstronomer for C++ */
8
9 #include "unicode/utypes.h"
10 #include "string.h"
11 #include "unicode/locid.h"
12
13 #if !UCONFIG_NO_FORMATTING
14
15 #include "astro.h"
16 #include "astrotst.h"
17 #include "cmemory.h"
18 #include "gregoimp.h" // for Math
19 #include "unicode/simpletz.h"
20
21
22 #define CASE(id,test) case id: name = #test; if (exec) { logln(#test "---"); logln((UnicodeString)""); test(); } break
23
24 AstroTest::AstroTest(): astro(NULL), gc(NULL) {
25 }
26
27 void AstroTest::runIndexedTest( int32_t index, UBool exec, const char* &name, char* /*par*/ )
28 {
29 if (exec) logln("TestSuite AstroTest");
30 switch (index) {
31 // CASE(0,FooTest);
32 CASE(0,TestSolarLongitude);
33 CASE(1,TestLunarPosition);
34 CASE(2,TestCoordinates);
35 CASE(3,TestCoverage);
36 CASE(4,TestSunriseTimes);
37 CASE(5,TestBasics);
38 CASE(6,TestMoonAge);
39 default: name = ""; break;
40 }
41 }
42
43 #undef CASE
44
45 #define ASSERT_OK(x) if(U_FAILURE(x)) { dataerrln("%s:%d: %s\n", __FILE__, __LINE__, u_errorName(x)); return; }
46
47
48 void AstroTest::initAstro(UErrorCode &status) {
49 if(U_FAILURE(status)) return;
50
51 if((astro != NULL) || (gc != NULL)) {
52 dataerrln("Err: initAstro() called twice!");
53 closeAstro(status);
54 if(U_SUCCESS(status)) {
55 status = U_INTERNAL_PROGRAM_ERROR;
56 }
57 }
58
59 if(U_FAILURE(status)) return;
60
61 astro = new CalendarAstronomer();
62 gc = Calendar::createInstance(TimeZone::getGMT()->clone(), status);
63 }
64
65 void AstroTest::closeAstro(UErrorCode &/*status*/) {
66 if(astro != NULL) {
67 delete astro;
68 astro = NULL;
69 }
70 if(gc != NULL) {
71 delete gc;
72 gc = NULL;
73 }
74 }
75
76 void AstroTest::TestSolarLongitude(void) {
77 UErrorCode status = U_ZERO_ERROR;
78 initAstro(status);
79 ASSERT_OK(status);
80
81 struct {
82 int32_t d[5]; double f ;
83 } tests[] = {
84 { { 1980, 7, 27, 0, 00 }, 124.114347 },
85 { { 1988, 7, 27, 00, 00 }, 124.187732 }
86 };
87
88 logln("");
89 for (uint32_t i = 0; i < UPRV_LENGTHOF(tests); i++) {
90 gc->clear();
91 gc->set(tests[i].d[0], tests[i].d[1]-1, tests[i].d[2], tests[i].d[3], tests[i].d[4]);
92
93 astro->setDate(gc->getTime(status));
94
95 double longitude = astro->getSunLongitude();
96 //longitude = 0;
97 CalendarAstronomer::Equatorial result;
98 astro->getSunPosition(result);
99 logln((UnicodeString)"Sun position is " + result.toString() + (UnicodeString)"; " /* + result.toHmsString()*/ + " Sun longitude is " + longitude );
100 }
101 closeAstro(status);
102 ASSERT_OK(status);
103 }
104
105
106
107 void AstroTest::TestLunarPosition(void) {
108 UErrorCode status = U_ZERO_ERROR;
109 initAstro(status);
110 ASSERT_OK(status);
111
112 static const double tests[][7] = {
113 { 1979, 2, 26, 16, 00, 0, 0 }
114 };
115 logln("");
116
117 for (int32_t i = 0; i < UPRV_LENGTHOF(tests); i++) {
118 gc->clear();
119 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]);
120 astro->setDate(gc->getTime(status));
121
122 const CalendarAstronomer::Equatorial& result = astro->getMoonPosition();
123 logln((UnicodeString)"Moon position is " + result.toString() + (UnicodeString)"; " /* + result->toHmsString()*/);
124 }
125
126 closeAstro(status);
127 ASSERT_OK(status);
128 }
129
130
131
132 void AstroTest::TestCoordinates(void) {
133 UErrorCode status = U_ZERO_ERROR;
134 initAstro(status);
135 ASSERT_OK(status);
136
137 CalendarAstronomer::Equatorial result;
138 astro->eclipticToEquatorial(result, 139.686111 * CalendarAstronomer::PI / 180.0, 4.875278* CalendarAstronomer::PI / 180.0);
139 logln((UnicodeString)"result is " + result.toString() + (UnicodeString)"; " /* + result.toHmsString()*/ );
140 closeAstro(status);
141 ASSERT_OK(status);
142 }
143
144
145
146 void AstroTest::TestCoverage(void) {
147 UErrorCode status = U_ZERO_ERROR;
148 initAstro(status);
149 ASSERT_OK(status);
150 GregorianCalendar *cal = new GregorianCalendar(1958, UCAL_AUGUST, 15,status);
151 UDate then = cal->getTime(status);
152 CalendarAstronomer *myastro = new CalendarAstronomer(then);
153 ASSERT_OK(status);
154
155 //Latitude: 34 degrees 05' North
156 //Longitude: 118 degrees 22' West
157 double laLat = 34 + 5./60, laLong = 360 - (118 + 22./60);
158 CalendarAstronomer *myastro2 = new CalendarAstronomer(laLong, laLat);
159
160 double eclLat = laLat * CalendarAstronomer::PI / 360;
161 double eclLong = laLong * CalendarAstronomer::PI / 360;
162
163 CalendarAstronomer::Ecliptic ecl(eclLat, eclLong);
164 CalendarAstronomer::Equatorial eq;
165 CalendarAstronomer::Horizon hor;
166
167 logln("ecliptic: " + ecl.toString());
168 CalendarAstronomer *myastro3 = new CalendarAstronomer();
169 myastro3->setJulianDay((4713 + 2000) * 365.25);
170
171 CalendarAstronomer *astronomers[] = {
172 myastro, myastro2, myastro3, myastro2 // check cache
173 };
174
175 for (uint32_t i = 0; i < UPRV_LENGTHOF(astronomers); ++i) {
176 CalendarAstronomer *anAstro = astronomers[i];
177
178 //logln("astro: " + astro);
179 logln((UnicodeString)" date: " + anAstro->getTime());
180 logln((UnicodeString)" cent: " + anAstro->getJulianCentury());
181 logln((UnicodeString)" gw sidereal: " + anAstro->getGreenwichSidereal());
182 logln((UnicodeString)" loc sidereal: " + anAstro->getLocalSidereal());
183 logln((UnicodeString)" equ ecl: " + (anAstro->eclipticToEquatorial(eq,ecl)).toString());
184 logln((UnicodeString)" equ long: " + (anAstro->eclipticToEquatorial(eq, eclLong)).toString());
185 logln((UnicodeString)" horiz: " + (anAstro->eclipticToHorizon(hor, eclLong)).toString());
186 logln((UnicodeString)" sunrise: " + (anAstro->getSunRiseSet(TRUE)));
187 logln((UnicodeString)" sunset: " + (anAstro->getSunRiseSet(FALSE)));
188 logln((UnicodeString)" moon phase: " + anAstro->getMoonPhase());
189 logln((UnicodeString)" moonrise: " + (anAstro->getMoonRiseSet(TRUE)));
190 logln((UnicodeString)" moonset: " + (anAstro->getMoonRiseSet(FALSE)));
191 logln((UnicodeString)" prev summer solstice: " + (anAstro->getSunTime(CalendarAstronomer::SUMMER_SOLSTICE(), FALSE)));
192 logln((UnicodeString)" next summer solstice: " + (anAstro->getSunTime(CalendarAstronomer::SUMMER_SOLSTICE(), TRUE)));
193 logln((UnicodeString)" prev full moon: " + (anAstro->getMoonTime(CalendarAstronomer::FULL_MOON(), FALSE)));
194 logln((UnicodeString)" next full moon: " + (anAstro->getMoonTime(CalendarAstronomer::FULL_MOON(), TRUE)));
195 }
196
197 delete myastro2;
198 delete myastro3;
199 delete myastro;
200 delete cal;
201
202 closeAstro(status);
203 ASSERT_OK(status);
204 }
205
206
207
208 void AstroTest::TestSunriseTimes(void) {
209 UErrorCode status = U_ZERO_ERROR;
210 initAstro(status);
211 ASSERT_OK(status);
212
213 // logln("Sunrise/Sunset times for San Jose, California, USA");
214 // CalendarAstronomer *astro2 = new CalendarAstronomer(-121.55, 37.20);
215 // TimeZone *tz = TimeZone::createTimeZone("America/Los_Angeles");
216
217 // We'll use a table generated by the UNSO website as our reference
218 // From: http://aa.usno.navy.mil/
219 //-Location: W079 25, N43 40
220 //-Rise and Set for the Sun for 2001
221 //-Zone: 4h West of Greenwich
222 int32_t USNO[] = {
223 6,59, 19,45,
224 6,57, 19,46,
225 6,56, 19,47,
226 6,54, 19,48,
227 6,52, 19,49,
228 6,50, 19,51,
229 6,48, 19,52,
230 6,47, 19,53,
231 6,45, 19,54,
232 6,43, 19,55,
233 6,42, 19,57,
234 6,40, 19,58,
235 6,38, 19,59,
236 6,36, 20, 0,
237 6,35, 20, 1,
238 6,33, 20, 3,
239 6,31, 20, 4,
240 6,30, 20, 5,
241 6,28, 20, 6,
242 6,27, 20, 7,
243 6,25, 20, 8,
244 6,23, 20,10,
245 6,22, 20,11,
246 6,20, 20,12,
247 6,19, 20,13,
248 6,17, 20,14,
249 6,16, 20,16,
250 6,14, 20,17,
251 6,13, 20,18,
252 6,11, 20,19,
253 };
254
255 logln("Sunrise/Sunset times for Toronto, Canada");
256 // long = 79 25", lat = 43 40"
257 CalendarAstronomer *astro3 = new CalendarAstronomer(-(79+25/60), 43+40/60);
258
259 // As of ICU4J 2.8 the ICU4J time zones implement pass-through
260 // to the underlying JDK. Because of variation in the
261 // underlying JDKs, we have to use a fixed-offset
262 // SimpleTimeZone to get consistent behavior between JDKs.
263 // The offset we want is [-18000000, 3600000] (raw, dst).
264 // [aliu 10/15/03]
265
266 // TimeZone tz = TimeZone.getTimeZone("America/Montreal");
267 TimeZone *tz = new SimpleTimeZone(-18000000 + 3600000, "Montreal(FIXED)");
268
269 GregorianCalendar *cal = new GregorianCalendar(tz->clone(), Locale::getUS(), status);
270 GregorianCalendar *cal2 = new GregorianCalendar(tz->clone(), Locale::getUS(), status);
271 cal->clear();
272 cal->set(UCAL_YEAR, 2001);
273 cal->set(UCAL_MONTH, UCAL_APRIL);
274 cal->set(UCAL_DAY_OF_MONTH, 1);
275 cal->set(UCAL_HOUR_OF_DAY, 12); // must be near local noon for getSunRiseSet to work
276
277 DateFormat *df_t = DateFormat::createTimeInstance(DateFormat::MEDIUM,Locale::getUS());
278 DateFormat *df_d = DateFormat::createDateInstance(DateFormat::MEDIUM,Locale::getUS());
279 DateFormat *df_dt = DateFormat::createDateTimeInstance(DateFormat::MEDIUM, DateFormat::MEDIUM, Locale::getUS());
280 if(!df_t || !df_d || !df_dt) {
281 dataerrln("couldn't create dateformats.");
282 return;
283 }
284 df_t->adoptTimeZone(tz->clone());
285 df_d->adoptTimeZone(tz->clone());
286 df_dt->adoptTimeZone(tz->clone());
287
288 for (int32_t i=0; i < 30; i++) {
289 logln("setDate\n");
290 astro3->setDate(cal->getTime(status));
291 logln("getRiseSet(TRUE)\n");
292 UDate sunrise = astro3->getSunRiseSet(TRUE);
293 logln("getRiseSet(FALSE)\n");
294 UDate sunset = astro3->getSunRiseSet(FALSE);
295 logln("end of getRiseSet\n");
296
297 cal2->setTime(cal->getTime(status), status);
298 cal2->set(UCAL_SECOND, 0);
299 cal2->set(UCAL_MILLISECOND, 0);
300
301 cal2->set(UCAL_HOUR_OF_DAY, USNO[4*i+0]);
302 cal2->set(UCAL_MINUTE, USNO[4*i+1]);
303 UDate exprise = cal2->getTime(status);
304 cal2->set(UCAL_HOUR_OF_DAY, USNO[4*i+2]);
305 cal2->set(UCAL_MINUTE, USNO[4*i+3]);
306 UDate expset = cal2->getTime(status);
307 // Compute delta of what we got to the USNO data, in seconds
308 int32_t deltarise = (int32_t)uprv_fabs((sunrise - exprise) / 1000);
309 int32_t deltaset = (int32_t)uprv_fabs((sunset - expset) / 1000);
310
311 // Allow a deviation of 0..MAX_DEV seconds
312 // It would be nice to get down to 60 seconds, but at this
313 // point that appears to be impossible without a redo of the
314 // algorithm using something more advanced than Duffett-Smith.
315 int32_t MAX_DEV = 180;
316 UnicodeString s1, s2, s3, s4, s5;
317 if (deltarise > MAX_DEV || deltaset > MAX_DEV) {
318 if (deltarise > MAX_DEV) {
319 errln("FAIL: (rise) " + df_d->format(cal->getTime(status),s1) +
320 ", Sunrise: " + df_dt->format(sunrise, s2) +
321 " (USNO " + df_t->format(exprise,s3) +
322 " d=" + deltarise + "s)");
323 } else {
324 logln(df_d->format(cal->getTime(status),s1) +
325 ", Sunrise: " + df_dt->format(sunrise,s2) +
326 " (USNO " + df_t->format(exprise,s3) + ")");
327 }
328 s1.remove(); s2.remove(); s3.remove(); s4.remove(); s5.remove();
329 if (deltaset > MAX_DEV) {
330 errln("FAIL: (set) " + df_d->format(cal->getTime(status),s1) +
331 ", Sunset: " + df_dt->format(sunset,s2) +
332 " (USNO " + df_t->format(expset,s3) +
333 " d=" + deltaset + "s)");
334 } else {
335 logln(df_d->format(cal->getTime(status),s1) +
336 ", Sunset: " + df_dt->format(sunset,s2) +
337 " (USNO " + df_t->format(expset,s3) + ")");
338 }
339 } else {
340 logln(df_d->format(cal->getTime(status),s1) +
341 ", Sunrise: " + df_dt->format(sunrise,s2) +
342 " (USNO " + df_t->format(exprise,s3) + ")" +
343 ", Sunset: " + df_dt->format(sunset,s4) +
344 " (USNO " + df_t->format(expset,s5) + ")");
345 }
346 cal->add(UCAL_DATE, 1, status);
347 }
348
349 // CalendarAstronomer a = new CalendarAstronomer(-(71+5/60), 42+37/60);
350 // cal.clear();
351 // cal.set(cal.YEAR, 1986);
352 // cal.set(cal.MONTH, cal.MARCH);
353 // cal.set(cal.DATE, 10);
354 // cal.set(cal.YEAR, 1988);
355 // cal.set(cal.MONTH, cal.JULY);
356 // cal.set(cal.DATE, 27);
357 // a.setDate(cal.getTime());
358 // long r = a.getSunRiseSet2(true);
359 delete astro3;
360 delete tz;
361 delete cal;
362 delete cal2;
363 delete df_t;
364 delete df_d;
365 delete df_dt;
366 closeAstro(status);
367 ASSERT_OK(status);
368 }
369
370
371
372 void AstroTest::TestBasics(void) {
373 UErrorCode status = U_ZERO_ERROR;
374 initAstro(status);
375 if (U_FAILURE(status)) {
376 dataerrln("Got error: %s", u_errorName(status));
377 return;
378 }
379
380 // Check that our JD computation is the same as the book's (p. 88)
381 GregorianCalendar *cal3 = new GregorianCalendar(TimeZone::getGMT()->clone(), Locale::getUS(), status);
382 DateFormat *d3 = DateFormat::createDateTimeInstance(DateFormat::MEDIUM,DateFormat::MEDIUM,Locale::getUS());
383 d3->setTimeZone(*TimeZone::getGMT());
384 cal3->clear();
385 cal3->set(UCAL_YEAR, 1980);
386 cal3->set(UCAL_MONTH, UCAL_JULY);
387 cal3->set(UCAL_DATE, 2);
388 logln("cal3[a]=%.1lf, d=%d\n", cal3->getTime(status), cal3->get(UCAL_JULIAN_DAY,status));
389 {
390 UnicodeString s;
391 logln(UnicodeString("cal3[a] = ") + d3->format(cal3->getTime(status),s));
392 }
393 cal3->clear();
394 cal3->set(UCAL_YEAR, 1980);
395 cal3->set(UCAL_MONTH, UCAL_JULY);
396 cal3->set(UCAL_DATE, 27);
397 logln("cal3=%.1lf, d=%d\n", cal3->getTime(status), cal3->get(UCAL_JULIAN_DAY,status));
398
399 ASSERT_OK(status);
400 {
401 UnicodeString s;
402 logln(UnicodeString("cal3 = ") + d3->format(cal3->getTime(status),s));
403 }
404 astro->setTime(cal3->getTime(status));
405 double jd = astro->getJulianDay() - 2447891.5;
406 double exp = -3444.;
407 if (jd == exp) {
408 UnicodeString s;
409 logln(d3->format(cal3->getTime(status),s) + " => " + jd);
410 } else {
411 UnicodeString s;
412 errln("FAIL: " + d3->format(cal3->getTime(status), s) + " => " + jd +
413 ", expected " + exp);
414 }
415
416 // cal3.clear();
417 // cal3.set(cal3.YEAR, 1990);
418 // cal3.set(cal3.MONTH, Calendar.JANUARY);
419 // cal3.set(cal3.DATE, 1);
420 // cal3.add(cal3.DATE, -1);
421 // astro.setDate(cal3.getTime());
422 // astro.foo();
423
424 delete cal3;
425 delete d3;
426 ASSERT_OK(status);
427 closeAstro(status);
428 ASSERT_OK(status);
429
430 }
431
432 void AstroTest::TestMoonAge(void){
433 UErrorCode status = U_ZERO_ERROR;
434 initAstro(status);
435 ASSERT_OK(status);
436
437 // more testcases are around the date 05/20/2012
438 //ticket#3785 UDate ud0 = 1337557623000.0;
439 static const double testcase[][10] = {{2012, 5, 20 , 16 , 48, 59},
440 {2012, 5, 20 , 16 , 47, 34},
441 {2012, 5, 21, 00, 00, 00},
442 {2012, 5, 20, 14, 55, 59},
443 {2012, 5, 21, 7, 40, 40},
444 {2023, 9, 25, 10,00, 00},
445 {2008, 7, 7, 15, 00, 33},
446 {1832, 9, 24, 2, 33, 41 },
447 {2016, 1, 31, 23, 59, 59},
448 {2099, 5, 20, 14, 55, 59}
449 };
450 // Moon phase angle - Got from http://www.moonsystem.to/checkupe.htm
451 static const double angle[] = {356.8493418421329, 356.8386760059673, 0.09625415252237701, 355.9986960782416, 3.5714026601303317, 124.26906744384183, 53.50364630964228,
452 357.54163205513123, 268.41779281511094, 4.82340276581624};
453 static const double precision = CalendarAstronomer::PI/32;
454 for (int32_t i = 0; i < UPRV_LENGTHOF(testcase); i++) {
455 gc->clear();
456 logln((UnicodeString)"CASE["+i+"]: Year "+(int32_t)testcase[i][0]+" Month "+(int32_t)testcase[i][1]+" Day "+
457 (int32_t)testcase[i][2]+" Hour "+(int32_t)testcase[i][3]+" Minutes "+(int32_t)testcase[i][4]+
458 " Seconds "+(int32_t)testcase[i][5]);
459 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]);
460 astro->setDate(gc->getTime(status));
461 double expectedAge = (angle[i]*CalendarAstronomer::PI)/180;
462 double got = astro->getMoonAge();
463 //logln(testString);
464 if(!(got>expectedAge-precision && got<expectedAge+precision)){
465 errln((UnicodeString)"FAIL: expected " + expectedAge +
466 " got " + got);
467 }else{
468 logln((UnicodeString)"PASS: expected " + expectedAge +
469 " got " + got);
470 }
471 }
472 closeAstro(status);
473 ASSERT_OK(status);
474 }
475
476
477 // TODO: try finding next new moon after 07/28/1984 16:00 GMT
478
479
480 #endif
481
482
483