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