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