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